Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ A check with `intentRef`/`workflowRef` points at the prose section that owns the
- **carried-scope** - the repo carries no file the hub hosts rather than carries. The set is derived, not listed: the hub's git-tracked paths minus the [`spec/files.json`][files] baseline, so a file dropped from the manifest starts being reported on the next run with no retirement list to remember to edit. The remedy is the opposite of every other file finding, a **deletion**, since the repo reaches the hub's copy per [GOVERNANCE.md "Hub-Hosted Tooling"][governance-hub-hosted-tooling]. The match is on path alone, so a hit is a candidate and not a verdict: a repo's own content at a path the hub also uses matches while carrying nothing of the hub's, which the first fleet run showed twice, a KiCad tooling doc at `scripts/README.md` and per-repo formatting hooks at `.husky/pre-commit`. A [`spec/divergences.json`][divergences] `gaps` disposition decides which case a hit is, so only `retire` asserts a deletion, `accepted` closes a collision or a repo-owned file, and an untriaged hit is read before it is acted on.
- **verbatim-tree** - every applicable `trees[]` declaration in [`spec/files.json`][files] owns its target tree. The audit reports missing files as letter findings, stale or modified bytes as drift, and extra files under a pruned target as drift. An unreadable or truncated repository tree is undecided and produces drift rather than a clean result.
- **repo-setup** - every required secret for the repo's publish mechanisms is configured, and no forbidden secret is present (per [`spec/secrets.json`][secrets]).
- **runtime-secrets** - a repo-scoped runtime-secrets directory, when present, is named `.secrets/` (dotted, not a bare `secrets/`), a single opaque credential file carries no extension, and every real secret file has a tracked `<name>.example` beside it cataloged in `.secrets/README.md`. N/A for a repo with no such directory. See [GOVERNANCE.md "Repo-Scoped Secrets"][governance-repo-scoped-secrets].
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- **linter-parity** - one config per linter (`.markdownlint-cli2.jsonc`, `cspell.json`, ruff/pyright, editorconfig/csharpier, actionlint) drives the editor extension, the CLI, and CI, and CI runs each. A local hook exists and runs at minimum the diff-scoped prose gate and the eol check via `hub-fetch-run.py`, or the hub's own local script copies for the hub repo itself (`parity.hooks`, intent). A repo with none wired is a defect, and one mid-convergence on the language-formatting half stays operational.
- **recurring-violations** - comments concise and non-narrative, ASCII only (no em-dash, no smart quotes), US spelling, line endings per `.editorconfig`. These are frequent regressions, so this dimension is high priority and always runs, and each check is grep-able (see below).
- **readme-structure** - the README follows [`spec/readme-structure.md`][readme-structure] (applicable sections, in order). Mechanically checked against the declared model in [`spec/readme-sections.json`][readme-sections]: required sections present, declared sections in their relative order, `License` last, the shields each deliverable implies, the license shield in the closing License section, and the tagline and its mirrors. A heading the model does not name is dropped before the order comparison, so a repo-specific section is never a finding.
Expand Down Expand Up @@ -184,6 +185,7 @@ The convergence model: the hub audits and the agent **applies** the fixes via ta
[governance-documentation-style]: ./GOVERNANCE.md#documentation-style-conventions
[governance-hub-hosted-tooling]: ./GOVERNANCE.md#hub-hosted-tooling
[governance-pr-review-etiquette]: ./GOVERNANCE.md#pr-review-etiquette
[governance-repo-scoped-secrets]: ./GOVERNANCE.md#repo-scoped-secrets
[host-setup]: https://github.com/ptr727/ProjectTemplate/blob/main/docs/host-setup.md
[project-types]: https://github.com/ptr727/ProjectTemplate/blob/main/spec/project-types.json
[readme-sections]: https://github.com/ptr727/ProjectTemplate/blob/main/spec/readme-sections.json
Expand Down
19 changes: 19 additions & 0 deletions GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,25 @@ pull request.

Line-ending governance for an operational repo is in [Line Endings](#line-endings), where its `[*]` default follows the consuming app's native platform per the registry `lineEndings` field, not the fleet LF default.

### Repo-Scoped Secrets

A repo whose own stacks or scripts read local runtime credentials from disk, most commonly an operational repo's Docker Compose stack, keeps them in a dotted `.secrets/` directory at the repo root. This is the repo-scoped counterpart to the host-scoped `~/.secrets/` convention a repo's own `OPERATIONS.md` may document, and it is a different thing from `spec/secrets.json`, the CI/GitHub Actions secret-name registry `spec/audit.py` cross-checks. `spec/secrets.json` governs what a workflow reads from GitHub Actions. This convention governs what a repo's own process reads from its own checkout.

- **The directory is named `.secrets/`, dotted, never a bare `secrets/`.**
- **A single opaque credential file carries no extension** (`homeassistant_db_password`, not `homeassistant_db_password.txt`), the same reason `README` and `LICENSE` carry none. It is a security property, read but never sourced, not a formatting preference.
- **A structured credential keeps its format's extension** (`.json` for structured config).
- **The shared env file is named for what it configures**, not a bare `.env` (`docker.env` for a repo whose stacks are Docker Compose), so a second env-shaped file added later stays unambiguous.
- **Every real secret file has a tracked `<name>.example` beside it**, and only the `.example` files plus a `README.md` catalog are un-ignored:

```gitignore
**/.secrets/*
!**/.secrets/*.example
!**/.secrets/README.md
```

A fresh checkout then documents its own required shape without ever exposing a real value. This negation keeps a real secret file out of a **new** commit. It does not remove one already tracked: `.gitignore` has no effect on a path git already follows. A real secret file found tracked is removed from the index (`git rm --cached <path>`) and its credential is rotated, not just added to `.gitignore` going forward.
- **`.secrets/README.md` is a catalog**, one row per file naming what it holds and what consumes it, plus a short note on how the directory relates to `~/.secrets/` where the repo also touches that.

## Repository Onboarding and Conformance

Every fleet repo is a standard-style repo the hub audits **downward** against its declared type, the model the fleet uses because managing downstream divergence is too costly. Three obligations follow, and they are not optional:
Expand Down
3 changes: 3 additions & 0 deletions STANDUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ Carry `AGENTS.md`'s skill-dependency pointer paragraph, the one naming `scripts/

**`OPERATIONS.md` is required on every repo**, not optional, so it appears in the baseline above with `appliesTo: "*"`. It is presence-checked only, the same footing as `README.md` and `HISTORY.md`, so its content is entirely the repo's own and a repo with little to say still carries the file as a stub, meaning those six headings with no content under them, for which this repo's own `OPERATIONS.md` is the worked example. Do not read the `operational` workflow model into the requirement, because that selector describes where config lives rather than whether the repo has runbooks, and a repo that publishes to a package registry or deploys a site has operational surface under either model. It is the operational analogue of `ARCHITECTURE.md`, and it is where an `AGENTS.md` split puts the repo-specific half, so real runbooks (a deploy procedure, a rollback, a retention policy, a credential rotation) go there rather than into a carried file. It is agent-instruction content, so it takes the inline-link exception the Markdown rules name rather than the reference-style default. `ARCHITECTURE.md` and `TODO.md` stay advisory and are required by no selector, so a repo with nothing to say in one carries no file rather than an empty one.

**A repo whose own stacks or scripts read local runtime credentials from disk documents that under `OPERATIONS.md`'s `Configuration Layout` heading**, and the directory follows the repo-scoped secrets convention in [GOVERNANCE.md][governance-repo-scoped-secrets] rather than an ad hoc layout invented per repo.

Choose the destination while scaffolding rather than after. Repo-specific content left in a carried file is drift, which the audit lists as an undeclared section to reconcile, and reconciling it later means moving prose that downstream readers have already started trusting in the wrong place.

**Wire a local commit hook here, not after the fact.** `.husky/pre-commit` and `.pre-commit-config.yaml` are deliberately excluded from the baseline above (each repo's own formatters make the content repo-owned, per [`spec/divergences.json`][divergences]), so nothing in the carry step above wires one. Copy and adapt the applicable catalog snippet, `catalog/snippets/husky/` for Husky.Net or `catalog/snippets/pre-commit/` for the Python `pre-commit` framework, each with `catalog/snippets/hub-fetch-run.py` alongside it, and enable it (`git config core.hooksPath .husky`, or `uv tool install pre-commit` once then `pre-commit install`) before the section 5 audit run. The Husky.Net snippet needs one more step per clone, `dotnet tool restore` then `dotnet husky install`, which generates `.husky/_/husky.sh`, the file the hook sources. A freshly stood-up repo with nothing wired starts pre-failed on `parity.hooks`.
Expand Down Expand Up @@ -246,6 +248,7 @@ The same [`AUDIT.md`][audit] run is the on-demand audit for any known repo, and
[git-commit-conventions]: ./.agents/skills/git-commit-conventions/SKILL.md
[governance]: ./GOVERNANCE.md
[governance-git-and-commit-rules]: ./GOVERNANCE.md#git-and-commit-rules
[governance-repo-scoped-secrets]: ./GOVERNANCE.md#repo-scoped-secrets
[governance-repository-boundaries-and-write-safety]: ./GOVERNANCE.md#repository-boundaries-and-write-safety
[host-gate]: ./scripts/host_gate.py
[host-setup]: ./docs/host-setup.md
Expand Down
7 changes: 7 additions & 0 deletions spec/project-types.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@
{ "id": "setup.dependabot.ecosystems", "verdict": "intent", "assert": "For each ecosystem the repo's tree implies, .github/dependabot.yml declares it (dual-target main+develop per the fleet norm): github-actions when .github/workflows/ is present (its workflows reference actions, else their versions go stale and a stood-up merge-bot has no PRs to auto-merge), devcontainers when a .devcontainer is present. A missing implied ecosystem is a drift finding. Language ecosystems (nuget/uv/npm) are directory-scoped, audited by inspection.", "intentRef": "GOVERNANCE.md#branching-model" }
]
},
"runtime-secrets": {
"appliesTo": "*",
"checks": [
{ "id": "runtimesecrets.naming", "verdict": "letter", "assert": "A repo-scoped runtime-secrets directory is named .secrets/ (dotted, not a bare secrets/), a single opaque credential file inside it carries no extension, a structured credential keeps its own format's extension, and the shared env file is named for what it configures rather than a bare .env. A bare secrets/ directory or a .txt-suffixed single-value secret file is drift. N/A for a repo carrying no repo-scoped secrets directory.", "intentRef": "GOVERNANCE.md#repo-scoped-secrets" },
{ "id": "runtimesecrets.catalog", "verdict": "letter", "assert": "Every real secret file in a repo-scoped .secrets/ directory has a tracked <name>.example beside it, .gitignore un-ignores only the .example files plus README.md, and that README.md catalogs each file: what it holds and what consumes it. N/A for a repo carrying no repo-scoped secrets directory.", "intentRef": "GOVERNANCE.md#repo-scoped-secrets" }
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
]
},
"linter-parity": {
"appliesTo": "*",
"checks": [
Expand Down