From 22361b8e277347636259b40330f5c341d6dfbba3 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Fri, 31 Jul 2026 14:17:35 -0700 Subject: [PATCH 1/2] State what a host must provide, and route the write-safety kit from it STANDUP.md step 0 now expects a configured host, but nothing said what a host must have. The repo invokes python3 at fifteen call sites, docker for the four linters, uvx for coverage, plus git and gh, and a fresh host discovered each of those by failure. docs/host-setup.md gains the contract: which tool, which procedure stops without it, and how to see it is present. It names no installer, because winget, brew and apt differ per platform while the requirement does not, which keeps the table true on every host and keeps per-OS specifics out of a public repo. Two facts are worth reading off it rather than learning later: python3 needs no packages, since every script here is standard library only, and the linters need only docker, since each runs as the pinned image CI uses. The write-safety kit was unreachable from this document, even though its own README calls it the first thing to deploy on a new system. It is now a step here, with the host-versus-repo distinction stated: the carried GOVERNANCE.md rules reach fleet repos, while the hook and the CLAUDE.md block cover every session on the machine, including work in no project at all, which is where the incident behind the kit happened. A closing table maps the finished host to what it can then do, so a failed row names the procedure it blocks rather than reading as a bare checklist. Every command was run as written before committing, and the referenced installer paths were confirmed to exist. Co-Authored-By: Claude Opus 5 (1M context) --- docs/host-setup.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/docs/host-setup.md b/docs/host-setup.md index 04698774..609ec9df 100644 --- a/docs/host-setup.md +++ b/docs/host-setup.md @@ -10,6 +10,22 @@ Supported hosts: > **Shell assumptions in this doc**: every command snippet below assumes a **POSIX shell** (bash/zsh) and POSIX path conventions (`~/.ssh/...`, `mkdir -p`, `$(...)` command substitution). On Windows, run them from **WSL2** or **Git Bash**, since they will not work as-is in PowerShell or `cmd.exe`. The git config and `gh` commands are portable; only the file/path manipulation differs by shell. +## What a Host Must Provide + +This section is the **contract**: which tools a host needs and which repo procedure stops working without each one. It deliberately names no installer, because `winget`, `brew` and `apt` differ per platform while the requirement does not. Per-platform install commands are tracked separately, so this table stays true on every host. + +| Tool | Needed by | Present when | +| --- | --- | --- | +| `git` | everything, and the identity and signing contract in [`STANDUP.md`][standup] step 0 | `git --version` | +| `gh` | the PR and review loop, `gh api` queries, `repo-config/configure.sh` | `gh --version` | +| `python3` | `scripts/` and `spec/` (standard library only, no packages to install) | `python3 --version` | +| `docker` | the four linters, which run as pinned images rather than local installs | `docker --version` | +| `uv` / `uvx` | coverage runs, and the Python toolchain (`ruff`, `pyright` or `mypy`) in a Python repo | `uv --version` | + +Two consequences worth reading off the table rather than discovering later. **`python3` needs no packages**, because every script here is standard library only, so a bare interpreter is enough. And **the linters need only `docker`**, not `node`, `dotnet` or a local `markdownlint`, since each runs as a pinned image, which is what keeps a local run and CI the same check. + +A missing tool is a host gap, not a repo problem. Install it and re-run, rather than working around it in a repo. + ## Git Identity Configure your name and email, used for commit authorship. **The email is the committing account's GitHub `noreply` address, never a private, personal, or invented one**, per [GOVERNANCE.md "Git and Commit Rules"][governance-git-and-commit-rules], which owns the rule and states the fleet's value. A private address trips GitHub's email-privacy push protection (GH007), and an invented one pollutes history. @@ -122,9 +138,26 @@ gh auth login --hostname github.com --git-protocol ssh Choose the SSH key generated above when prompted. +## Agent Write-Safety Kit + +Required on any host where an agent runs with the `gh` credentials logged in, and its own README calls it the first thing to deploy on a new system. Install it from this repo, since the installer is idempotent and safe to re-run to update: + +```shell +host-setup/agent-safety/install.sh # Linux, WSL, macOS, Proxmox +``` + +```powershell +host-setup\agent-safety\install.ps1 # Windows +``` + +Both wrap one `install.py`, so every platform runs the same tested path. Restart Claude Code sessions on the machine afterward so the hook and the `CLAUDE.md` block load. Details, verification, and scope limits are in [`host-setup/agent-safety/README.md`][agent-safety]. + +This is a **host** control, not a repo one. The carried `GOVERNANCE.md` rules reach fleet repos only, while the hook and the `CLAUDE.md` block cover every session on the machine, including ad-hoc work in no project at all, which is where the incident behind the kit happened. + ## Verify Host Setup ```shell +git --version && gh --version && python3 --version && docker --version && uv --version git config --global --list | grep -E "user\.|signing|gpg\." ssh-add -L # should list your public key git -c gpg.format=ssh commit -S --allow-empty -m "verify-signing" @@ -134,6 +167,18 @@ gh auth status If signing fails locally, the devcontainer will fail too, so fix here first. +**What the host can do once this passes**, which is the point of the contract above: + +| Now possible | Because | +| --- | --- | +| Stand up a new repo through [`STANDUP.md`][standup] | step 0 verifies identity and signing, and its window closes at the first commit | +| Run the four linters locally, matching CI | `docker` runs each as the same pinned image CI uses | +| Run the repo's own gates and tests | `python3` covers `scripts/` and `spec/` with no packages to install | +| Drive the PR and Copilot review loop | `gh` and an authenticated session | +| Let an agent work with the `gh` credentials live | the write-safety kit is installed | + +A host that fails any row is not ready for the procedure that row names, and the fix belongs on the host rather than in a repo. + ## Next Steps - [Devcontainer setup][devcontainer]: open the repo in the per-language .NET or Python devcontainer. @@ -141,6 +186,7 @@ If signing fails locally, the devcontainer will fail too, so fix here first. +[agent-safety]: ../host-setup/agent-safety/README.md [devcontainer]: ./devcontainer.md [governance-git-and-commit-rules]: ../GOVERNANCE.md#git-and-commit-rules [ssh-signing]: ./ssh-signing.md From e07ce24ebadaf021c72935a04d8848caa49245e5 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Fri, 31 Jul 2026 14:21:51 -0700 Subject: [PATCH 2/2] Exempt the PowerShell block from the POSIX shell assumption The doc opens by saying every command snippet below assumes a POSIX shell and will not work as-is in PowerShell. The write-safety kit step then added the first powershell block in the file, so the blanket claim became false in exactly the place a Windows reader needs it to be true. Following the note as written, that reader would run the Windows-native installer through Git Bash, or skip it as not applying. The note now carries the exception: a block marked powershell is the Windows-native form of its step, meant to run in PowerShell rather than translated. Verified that this is the only powershell fence in the file, and that every other command fence is shell. The same sentence carried a clause-joining semicolon, which the linter exempts because a colon earlier in the line makes it read as a list. That is the documented limit of the semicolon rule, caught by reading the diff rather than by the gate, and fixed here since the line was being edited. Co-Authored-By: Claude Opus 5 (1M context) --- docs/host-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/host-setup.md b/docs/host-setup.md index 609ec9df..561f9de8 100644 --- a/docs/host-setup.md +++ b/docs/host-setup.md @@ -8,7 +8,7 @@ Supported hosts: - **macOS** - both the devcontainer flow and the host-install flow. - **Windows** - the devcontainer flow requires **WSL2**, and native Windows (PowerShell + winget) is supported only for the host-install flow described in `README.md`. The bind-mounts in `.devcontainer/dotnet/devcontainer.json` and `.devcontainer/python/devcontainer.json` rely on POSIX paths and only work from Linux/macOS/WSL2. -> **Shell assumptions in this doc**: every command snippet below assumes a **POSIX shell** (bash/zsh) and POSIX path conventions (`~/.ssh/...`, `mkdir -p`, `$(...)` command substitution). On Windows, run them from **WSL2** or **Git Bash**, since they will not work as-is in PowerShell or `cmd.exe`. The git config and `gh` commands are portable; only the file/path manipulation differs by shell. +> **Shell assumptions in this doc**: every command snippet below assumes a **POSIX shell** (bash/zsh) and POSIX path conventions (`~/.ssh/...`, `mkdir -p`, `$(...)` command substitution), with one exception. A block marked `powershell` is the **Windows-native** form of the step it sits in, meant to run in PowerShell rather than translated. On Windows, run the POSIX snippets from **WSL2** or **Git Bash**, since they will not work as-is in PowerShell or `cmd.exe`. The git config and `gh` commands are portable, and only the file and path manipulation differs by shell. ## What a Host Must Provide