Skip to content

Commit 9b73d1a

Browse files
ptr727claude
andauthored
Route the README by Reader and Document the Cross-Owner Write Grant (#631)
The README opened straight into prose written for an agent, so a human arriving from GitHub had no path to what they actually ask: how to use this repo, how to deploy the host rules, how to diverge from one, and where to file an issue. It now forks by reader, and it carries the write-grant documentation that `TODO.md` recorded as ready in #629 without doing. ## The README, routed by reader A **Getting Started** table above the Table of Contents states the human/agent split outright, an agent starting at `AGENTS.md` and a human starting at the table, then routes six reader shapes to one destination each. Three new sections fill the gaps, placed in the slots [`spec/readme-structure.md`](./spec/readme-structure.md) already orders them in so the redesign does not fight the hub's own spec: - **Using This Repo** (Installation slot), covering the host guardrails, carrying the rules into a repository, and an honest note that the registry does not transfer, so adopting outside this fleet means running your own hub. - **Diverging From a Rule** (Configuration slot), which separates the two kinds of exception that get confused: a repository diverging from a carried unit, where the fidelity level usually already grants the freedom and the rest is recorded in `spec/divergences.json`, and a write the host guard denies. - **Questions or Issues**, previously absent entirely, saying what an issue needs and that a downstream agent filing here is the normal path rather than an exception. ## The cross-owner write grant The guard denies a `gh` write whose explicit target sits under another owner and names `GH_WRITE_GUARD_ALLOW` in the denial, and no document said how to give one. [`docs/host-setup.md`](./docs/host-setup.md) now carries it where the reader already is when the denial arrives: - The **fork asymmetry** that raises it, where `origin` is your fork and only the half of the work aimed at `upstream` denies. - The channel that works, an `env` block in the checkout's `.claude/settings.local.json`, which scopes the grant to that checkout rather than the host, and the **session restart** that loads it. - The value's exact shape: one string and never a JSON array, split on any run of whitespace or commas, with `owner/*` granting a whole owner and a token carrying no `/` silently granting nothing. Verified against `_granted_targets` and `_target_permitted`. - The **two forms that fail silently**, an inline `VAR=x gh ...` prefix and an `export` in a shell call, neither of which reaches a hook running as its own process. That is the property making a grant a deliberate act outside the session rather than something an agent does for itself once blocked. - A **confirmation that does not involve making the write**. Worth recording: feeding the hook a synthetic payload is not a usable probe from inside a session, because the payload text carries the very write shape the guard matches, so the guard denies the probe command itself. Observed twice while writing this. `host-setup/agent-safety/README.md` carries the mechanics beside the hook description and links across. ## Also here - The **license shield** moves into the closing License section, matching the form PlexCleaner carries. [`spec/readme-structure.md`](./spec/readme-structure.md) still states the old placement under Build and Distribution, so this README is deliberately ahead of the spec, and reconciling the two is the `decision`-state **README Structure Rework** cluster's edit rather than a loose end here. That cluster's `Settled` bullet asserting the old placement as fact is rewritten to record it. - **Proxmox** drops out of every supported-platform list, since it is Linux. The two remaining mentions name the actual Proxmox host in the rollout, which is correct. - `untriaged` joins the cspell word list, since the new Diverging section names the literal value the divergence report renders. - `TODO.md` loses the shipped cluster and its orphaned link definitions, and the stale "Refresh the README" chore becomes the open question this change leaves behind: whether these human entry points belong in the spec so fleet repos are measured on them. The per-section index alternative is recorded as declined, with the reason. ## Verification markdownlint, cspell, editorconfig-checker, `spec/validate.py`, and `scripts/prose_lint.py --diff develop` all clean locally. CRLF preserved in every Markdown file, LF preserved in `install.sh`. Every relative link target and every in-file anchor resolves, checked mechanically. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 6df14c0 commit 9b73d1a

6 files changed

Lines changed: 170 additions & 27 deletions

File tree

‎README.md‎

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ Agent enablement for a fleet of repositories: autonomy and repeatable quality in
1010
### Build Status <!-- omit from toc -->
1111

1212
[![Releases Build][releases-build-shield]][actions-link]\
13-
[![Last Commit][last-commit-shield]][commits-link]\
14-
[![License][license-shield]][license]
13+
[![Last Commit][last-commit-shield]][commits-link]
1514

1615
### Releases <!-- omit from toc -->
1716

@@ -28,20 +27,44 @@ Agent enablement for a fleet of repositories: autonomy and repeatable quality in
2827

2928
See [Release History][history] for the full history.
3029

30+
## Getting Started <!-- omit from toc -->
31+
32+
This repo has two kinds of reader, a human and an AI coding agent, and they enter at different doors. **An agent starts at [AGENTS.md][agents]**, which maps a task to the document that governs it and is written to be read one section at a time. **A human starts here**, and the table below is the fork: find the row that describes why you opened this page, and read the one it points at rather than the whole file.
33+
34+
| You are | Your question | Start at |
35+
| --- | --- | --- |
36+
| Browsing GitHub | What is this, and why does it exist? |[What This Repo Is][what-this-repo-is] and [What It Achieves][what-it-achieves]|
37+
| Setting up a machine where an agent runs | How do I deploy the host guardrails? |[`docs/host-setup.md`][host-setup]|
38+
| Adopting the rules in a repository | How do I stand a repo up, or bring an existing one into line? |[`STANDUP.md`][standup], then [`AUDIT.md`][audit]|
39+
| Blocked by a rule | How do I diverge from one, or grant a write the guard denies? |[Diverging From a Rule][diverging-from-a-rule]|
40+
| Reporting a defect or proposing a rule | Where does that go, and what does it need? |[Questions or Issues][questions-or-issues]|
41+
| An AI coding agent | Which document governs the task in front of me? |[`AGENTS.md`][agents]|
42+
43+
Nothing here is installed as a dependency. The rules are read, the baseline is carried into a repository as files it then owns, and the one thing that is genuinely installed is the host guardrail kit, which lands in your home directory rather than in any repository.
44+
3145
## Table of Contents <!-- omit from toc -->
3246

3347
-[What This Repo Is](#what-this-repo-is)
3448
-[What It Achieves](#what-it-achieves)
3549
-[How This Repo Operates](#how-this-repo-operates)
50+
-[Using This Repo](#using-this-repo)
51+
-[Deploy the Host Guardrails](#deploy-the-host-guardrails)
52+
-[Carry the Rules Into a Repository](#carry-the-rules-into-a-repository)
53+
-[Adopting Outside This Fleet](#adopting-outside-this-fleet)
54+
-[Diverging From a Rule](#diverging-from-a-rule)
55+
-[A Repository Diverging From a Carried Unit](#a-repository-diverging-from-a-carried-unit)
56+
-[A Write the Host Guard Denies](#a-write-the-host-guard-denies)
3657
-[Rules](#rules)
3758
-[Always](#always)
3859
-[Never](#never)
3960
-[If a C# Project](#if-a-c-project)
4061
-[If a Python Project](#if-a-python-project)
62+
-[If Both C# and Python](#if-both-c-and-python)
4163
-[If Publishing a Package (NuGet or PyPI)](#if-publishing-a-package-nuget-or-pypi)
4264
-[If a Docker Image](#if-a-docker-image)
4365
-[For a README or Human-Facing Doc](#for-a-readme-or-human-facing-doc)
4466
-[For Workflows](#for-workflows)
67+
-[Questions or Issues](#questions-or-issues)
4568
-[Development Environment Setup](#development-environment-setup)
4669
-[License](#license)
4770

@@ -59,12 +82,15 @@ This repo is the single home for those rules, a machine-readable spec they are c
5982
-**[GOVERNANCE.md][governance]** - cross-cutting rules for AI coding agents: git, branching, release model, doc style, the recurring-violation rules (comments, ASCII charset, US spelling, line endings), PR review etiquette, and workflow YAML conventions.
6083
-**[CODESTYLE.md][codestyle]** - code style for .NET and Python.
6184
-**[WORKFLOW.md][workflow]** - the CI/CD workflow contract (behavioral guarantees D1-D9) and its audit methodology.
85+
-**[STANDUP.md][standup]** - how an agent stands a repository up and carries the baseline it is owed.
6286
-**[AUDIT.md][audit]** - how an agent audits a repository against the spec and reports drift.
6387
-**[spec/][spec]** - the machine-readable ground truth: project-type requirements, the file/section baseline, required/forbidden secrets, and the preferred README structure.
6488
-**[registry/repos.json][repos]** - the fleet registry: every project, its type(s), publish mechanism, and status (cataloged or standardization backlog).
6589
-**[repo-config/][repo-config]** - branch rulesets and the apply script (kept out of `.github/`, which is Actions-owned), plus the GitHub setup reference.
90+
-**[host-setup/][host-setup-dir]** - the host guardrail kit, which is per machine rather than per repository.
6691
-**[catalog/][catalog]** - reusable reference snippets (workflow tasks, config exemplars, devcontainers) the audit compares implementations against.
6792
-**[reports/][reports]** - per-repo audit output.
93+
-**[docs/][docs]** - the human setup and reference guides: host prerequisites, SSH signing, devcontainers, and the carry procedures.
6894

6995
**The words a request is phrased in are defined here.** A repository is asked to audit itself against the hub, or to close the review loop on a pull request, and the phrasing carries the whole instruction, so each term below names the file that answers it and a request using one is a complete instruction rather than a starting point for interpretation.
7096

@@ -105,6 +131,54 @@ ProjectTemplate follows the same model it documents, and audits its own rules ag
105131
-**Review loop.** Every PR is reviewed by GitHub Copilot, and the agent drives the review loop to green and merges only with explicit maintainer permission. See [GOVERNANCE.md "PR Review Etiquette"][governance-pr-review-etiquette].
106132
-**Release.** A `develop -> main` merge is promoted through a GitHub release (tag plus a source zip, README, and LICENSE). Versioning is NBGV-driven from [version.json][version]. See [WORKFLOW.md][workflow].
107133

134+
## Using This Repo
135+
136+
Three things are deployed from here, and they land in different places. The host guardrails install once per machine, the baseline is carried once per repository, and the audit is run whenever a repository changes materially. Do them in that order on a new machine, because the guardrails bound every session that follows and retrofitting them means the sessions in between ran unguarded.
137+
138+
### Deploy the Host Guardrails
139+
140+
The guardrails are the one component that is installed rather than read, and they are **host state rather than repository content**, because they have to cover ad-hoc sessions in no project at all. They deny a mis-targeted GitHub write under your identity, and a git operation that would only land by bypassing a branch rule.
141+
142+
```shell
143+
host-setup/agent-safety/install.sh # Linux, WSL, macOS
144+
```
145+
146+
```powershell
147+
.\host-setup\agent-safety\install.ps1 # Windows, and the .\ prefix is required
148+
```
149+
150+
Restart Claude Code sessions on the machine afterward so the hook and the `CLAUDE.md` blocks load. The installer is idempotent, so re-running it is also how a machine picks up an upstream change to the guard. What it installs, how to verify it, and what it deliberately does not catch are in [`host-setup/agent-safety/README.md`][agent-safety], and the surrounding host prerequisites (git identity, SSH signing, `gh`, `docker`, `uv`) are in [`docs/host-setup.md`][host-setup].
151+
152+
### Carry the Rules Into a Repository
153+
154+
A repository that does not exist yet is stood up with [`STANDUP.md`][standup], which is ordered rather than a menu: verify commit identity and signing before the first commit, hand the maintainer what only they can supply, classify the repo and write its [registry][repos] entry, carry the instruction set before authoring anything of your own, then carry the remaining baseline, the workflows, and the settings. The two steps with a closing window are first, because signing has to be live before the first commit and the rules have to be loaded before the first authored file.
155+
156+
A repository that already exists is measured with [`AUDIT.md`][audit] instead. The audit is read-only and ends in a report under [reports/][reports], so nothing is changed by measuring, and applying what it found is a separate reviewable change per its section 10. Onboarding is complete when the repo passes, or carries a committed report plus a tracking issue for the residual deltas, per [GOVERNANCE.md "Repository Onboarding and Conformance"][governance-repository-onboarding-and-conformance].
157+
158+
The mechanical helpers that go with those procedures are documented beside them: [`docs/repo-config-carry.md`][repo-config-carry] for branch rulesets and repository settings, and [`docs/content-import.md`][content-import] for importing existing content into a new repo.
159+
160+
### Adopting Outside This Fleet
161+
162+
The rules, the spec, and the procedures are readable and reusable by anyone, and the guardrail kit installs on any host. What does not transfer is the registry, since [registry/repos.json][repos] lists this fleet's projects, and standing a repository up writes an entry in it. Adopting outside this fleet therefore means running your own hub, forked or copied, holding your own registry and your own reports, with `AGENTS.md`, `GOVERNANCE.md`, `CODESTYLE.md`, `WORKFLOW.md`, and `spec/` adapted to what your projects actually are. There is no supported mode where an outside repository points at this hub's registry.
163+
164+
## Diverging From a Rule
165+
166+
A rule that cannot be diverged from is a rule people work around silently, which is worse than the divergence, so both kinds of exception have a declared channel. The two kinds are genuinely different: one is a repository not matching a carried unit, and the other is a host guard denying a write. Neither is granted by editing the thing that blocked you.
167+
168+
### A Repository Diverging From a Carried Unit
169+
170+
**Check the unit's fidelity level first, because most apparent divergences are not divergences at all.** Each [spec/files.json][files] entry declares one of `verbatim`, `interface`, `intent`, or `presence`, defaulting to `presence`, and the two permissive levels already hand the repo its content outright. A repo rewriting a `presence` or `intent` file to suit itself is exercising the freedom the level grants rather than breaking a rule, and the audit asserts nothing beyond presence for it. See [spec/fidelity-model.md][fidelity-model] for which unit sits where and why.
171+
172+
**A divergence from a `verbatim` or `interface` unit is recorded, not hidden.** The ledger is [spec/divergences.json][divergences], where each entry names the path, the repos, a disposition, and a reason, and `spec/fidelity_honesty.py --report` joins it against live fleet reality to regenerate [reports/divergences.md][divergences-report]. A divergence recorded as `accepted` is a legitimate permanent one and no further action is owed. A live divergence with no entry renders as `UNTRIAGED`, which is the point: the audit does not care whether a difference is deliberate, only whether someone decided about it. Edit the ledger and regenerate the report, never the report.
173+
174+
**A rule that is wrong for many repositories is a hub defect rather than a per-repo exception.**[AUDIT.md][audit] section 9 says so directly, that a repeated letter miss many repos share is a signal the spec needs adjusting, so raise it here instead of accumulating one exception per repository.
175+
176+
### A Write the Host Guard Denies
177+
178+
The guard denies a `gh` write whose explicit target sits under an owner other than the checkout's `origin` owner, which is the shape that once put a stray comment on a stranger's repository. Sibling repositories under the same owner are allowed, so the denial appears only on a write that leaves the owner, and the common case that raises it is a fork, where `origin` is yours and `upstream` is the project you forked from.
179+
180+
The only way past it is a grant the maintainer makes **outside the session**, in `GH_WRITE_GUARD_ALLOW`. It is deliberately not something an agent can do for itself once blocked, so an inline `GH_WRITE_GUARD_ALLOW=owner/repo gh ...` prefix and an `export` inside a shell call both leave the write denied. The worked example, the file the grant goes in, and how to confirm one took effect are in [`docs/host-setup.md` "Granting a Write the Guard Denies"][host-setup-granting-a-write-the-guard-denies].
181+
108182
## Rules
109183

110184
A human-readable index of the rules agents enforce, implement, and audit. The authority for each is [GOVERNANCE.md][governance], [CODESTYLE.md][codestyle], and [WORKFLOW.md][workflow]. The machine-checkable form lives in [spec/][spec].
@@ -160,13 +234,36 @@ A human-readable index of the rules agents enforce, implement, and audit. The au
160234

161235
- Make GitHub Actions satisfy the [WORKFLOW.md][workflow] contract (guarantees D1-D9), which the audit verifies.
162236

237+
## Questions or Issues
238+
239+
File everything at [Issues][issues-link], including a defect in a rule, a procedure that did not survive contact with a real repository, and a proposal for a new rule. Use [Discussions][discussions-link] for an open question that is not yet a defect. This repo ships no application code, so an issue here is about the rules, the spec, the procedures, or the tooling under [`scripts/`][scripts] and [`spec/`][spec], never about a downstream project's behavior, which belongs on that project's own tracker.
240+
241+
An issue is most useful when it names the ground truth it disagrees with, so include the file and section that states the rule, the repository and branch where the problem was observed, and what you expected instead. A finding measured against a specific commit is worth more than one measured against a memory of the docs, per [GOVERNANCE.md "Verification Discipline"][governance-verification-discipline].
242+
243+
Two kinds of report are worth calling out because they are the ones that improve the procedures rather than one repository:
244+
245+
-**A procedure that could not be followed cold.** The onboarding docs are sufficient only when a context-free agent can stand a repo shape up from them alone, so a step that needed knowledge the docs never gave is a documentation defect tracked in the [conformance matrix][matrix] and fixed here rather than worked around per repo.
246+
-**A rule that many repositories miss the same way.** That is a signal the spec needs adjusting rather than a queue of per-repo exceptions, per [AUDIT.md][audit] section 9.
247+
248+
Issues are also filed here **by agents working in downstream repositories**, which is the normal path rather than an exception, since the agent that hit the gap is the one holding the evidence for it.
249+
163250
## Development Environment Setup
164251

165252
Contributors sign every commit. See [docs/ssh-signing.md][ssh-signing] for SSH commit-signing setup, [docs/host-setup.md][host-setup] for host prerequisites, and [docs/devcontainer.md][devcontainer] for devcontainer SSH-agent forwarding. Run the linters before pushing (see [GOVERNANCE.md "Running the Linters Locally"][governance-running-the-linters-locally-known-working-invocations]).
166253

254+
Changes land the same way every fleet change does: a feature branch, a squash merge into `develop`, a Copilot review loop driven to green, and a merge only with the maintainer's explicit approval. The backlog is [`TODO.md`][todo], which holds the work that is ready to pick up along with the reasoning behind each item, so read it before proposing something it already covers.
255+
167256
## License
168257

169-
See [LICENSE][license].
258+
Licensed under the [MIT License][license]\
259+
![License][license-shield]
260+
261+
<!-- Sections -->
262+
263+
[diverging-from-a-rule]: #diverging-from-a-rule
264+
[questions-or-issues]: #questions-or-issues
265+
[what-it-achieves]: #what-it-achieves
266+
[what-this-repo-is]: #what-this-repo-is
170267

171268
<!-- Shields -->
172269

@@ -178,35 +275,50 @@ See [LICENSE][license].
178275

179276
<!-- Repo -->
180277

278+
[agent-safety]: ./host-setup/agent-safety/README.md
181279
[agents]: ./AGENTS.md
182280
[audit]: ./AUDIT.md
183281
[catalog]: ./catalog/
184282
[codestyle]: ./CODESTYLE.md
283+
[content-import]: ./docs/content-import.md
185284
[devcontainer]: ./docs/devcontainer.md
285+
[divergences]: ./spec/divergences.json
286+
[divergences-report]: ./reports/divergences.md
287+
[docs]: ./docs/
288+
[fidelity-model]: ./spec/fidelity-model.md
186289
[files]: ./spec/files.json
187290
[governance]: ./GOVERNANCE.md
188291
[governance-branching-model]: ./GOVERNANCE.md#branching-model
189292
[governance-hub-hosted-tooling]: ./GOVERNANCE.md#hub-hosted-tooling
190293
[governance-pr-review-etiquette]: ./GOVERNANCE.md#pr-review-etiquette
294+
[governance-repository-onboarding-and-conformance]: ./GOVERNANCE.md#repository-onboarding-and-conformance
191295
[governance-running-the-linters-locally-known-working-invocations]: ./GOVERNANCE.md#running-the-linters-locally-known-working-invocations
296+
[governance-verification-discipline]: ./GOVERNANCE.md#verification-discipline
192297
[history]: ./HISTORY.md
193298
[host-setup]: ./docs/host-setup.md
299+
[host-setup-dir]: ./host-setup/
300+
[host-setup-granting-a-write-the-guard-denies]: ./docs/host-setup.md#granting-a-write-the-guard-denies
194301
[license]: ./LICENSE
195302
[matrix]: ./reports/conformance-matrix.md
196303
[project-types]: ./spec/project-types.json
197304
[readme-structure]: ./spec/readme-structure.md
198305
[repo-config]: ./repo-config/
306+
[repo-config-carry]: ./docs/repo-config-carry.md
199307
[reports]: ./reports/
200308
[repos]: ./registry/repos.json
309+
[scripts]: ./scripts/
201310
[spec]: ./spec/
202311
[ssh-signing]: ./docs/ssh-signing.md
203312
[standup]: ./STANDUP.md
313+
[todo]: ./TODO.md
204314
[version]: ./version.json
205315
[workflow]: ./WORKFLOW.md
206316

207317
<!-- External -->
208318

209319
[actions-link]: https://github.com/ptr727/ProjectTemplate/actions
210320
[commits-link]: https://github.com/ptr727/ProjectTemplate/commits
321+
[discussions-link]: https://github.com/ptr727/ProjectTemplate/discussions
322+
[issues-link]: https://github.com/ptr727/ProjectTemplate/issues
211323
[projecttemplate-link]: https://github.com/ptr727/ProjectTemplate
212324
[releases-link]: https://github.com/ptr727/ProjectTemplate/releases

0 commit comments

Comments
 (0)