Conversation
…#674) `host-setup/` carried Linux tooling only, so every Windows setup step was a manual read of the matrix in #483. This makes that column executable, and it was written and run on a Windows host rather than translated. ## What Lands `host-setup/windows/`, four scripts and a README, mirroring the Linux design where it applies and diverging where Windows genuinely differs. | Script | Does | | --- | --- | | `install-tools.ps1` | Installs and upgrades the eight managed tools, and reports version, source, scope and status | | `upgrade-host.ps1` | Upgrades the winget packages, and updates the WSL platform behind a Docker guard | | `setup-github.ps1` | The SSH key, git identity, commit signing, the agent, and `gh` | | `setup-wsl.ps1` | Installs a distribution, and reports the Docker Desktop integration | Plus `PSScriptAnalyzerSettings.psd1` and a CI step, per-platform coverage in `scripts/test_bootstrap.py`, and the documentation. ## Three Decisions Worth Reviewing **winget is the only source.** Every tool in the contract has a winget package, measured, so the three-source machinery the Linux script needs for feeds that trail upstream has nothing to do here. A tool that ever lacks one is a finding to raise rather than a second source to add quietly. **Nothing elevates, and no scope is passed unless one is named.** Forcing user scope installs a second copy beside a machine wide one rather than upgrading it, some installers fail when launched from an already elevated process, and a user scope install made from an elevated process lands in the administrator's profile. Letting each installer raise its own prompt is the path with the fewest failures. Scope drift is reported instead, with a `SCOPE` column, and `-Reinstall` is the only action that removes anything. **A package winget will not move is reported apart, with no command beside it.** Its manifest asks for that because the application updates itself, so the version winget shows is the installer's rather than the one it runs. `MSYS2` is the worked example: it upgrades through `pacman`, and printing `winget upgrade --id MSYS2.MSYS2` beside a permanently stale number invites a reinstall over a working copy. ## The PowerShell Gate Built as the peer of the shellcheck step, per the same model: the file list comes from `git ls-files`, the checker runs as a container, and the same invocation is documented in `GOVERNANCE.md` and `OPERATIONS.md` so a local run and CI are the same check. The module version is pinned beside the image, because the image alone does not fix it. `1.23.0` rather than the newest, since `1.24.0` needs a newer `System.Management.Automation` than the image carries and fails to import after installing cleanly, which reads as a broken gate rather than a version mismatch. **The file count is printed because this gate lied on its first invocation.** It reported `no findings` while analyzing zero files, since the argument list never reached the container. That is the failure mode the repository is built against, and the count is what caught it. Two rules are excluded with their reasons in the settings file: `PSAvoidUsingWriteHost`, since these scripts write their report to the console as their whole purpose and returning exit codes through the pipeline makes `Write-Output` wrong rather than merely different, and `PSUseShouldProcessForStateChangingFunctions`, since every script already carries `-DryRun` and a confirm prompt and adding `-WhatIf` beside them would give a reader two spellings of preview. ## test_bootstrap.py Coverage now asserts per platform, since the two installers do not manage the same set and the difference is a decision: `git-restore-mtime` is not applicable on Windows, and `docker` is one winget package there where the Linux answer differs by host role. `NOT_MANAGED['windows']` is empty rather than absent, which is itself the assertion. The executability check now reads the mode **git** records rather than the filesystem's. NTFS carries no exec bit, so `st_mode` reported every file as non-executable and the check failed on Windows against a correct tree. What the loader depends on is the mode a Linux checkout gets, which is the one git stores. The loader invariant is deliberately unchanged. No `bootstrap.ps1` lands, so `bootstrap.sh` still reads exactly one path, and widening that set before a Windows loader exists would retire the invariant ahead of the thing it protects. ## Documentation `docs/host-setup.md` gains the PowerShell verification block #483 deferred until somebody had run it on a Windows host, and the real reason for `py -3` over `python`: an activated virtual environment shadows `python`, so the host gate would grade the environment rather than the host. The Store alias stub rules out `python3` and chooses nothing between the other two, which is a weaker argument than the doc previously carried. ## Verified on This Host Windows 11 Pro 10.0.26200, PowerShell 7.6.4, winget v1.29.280, WSL 2.7.10.0, unelevated. Every repository gate passes: `prose_lint`, `repo_gate`, `markdownlint`, `actionlint`, `editorconfig-checker`, `PSScriptAnalyzer` over 5 files, and all seven script self-tests. `git ls-files --eol` confirms CRLF on every new file, and no `.ps1` opens with a shebang. Both guards were proven to fire under `-DryRun` rather than printing a command: `upgrade-host.ps1 -Wsl` refuses while Docker Desktop runs, and an `-Upgrade` whose `-Scope` disagrees with the installed copy refuses and names `-Reinstall`. The coverage assertion was proven to bite by deleting `docker` from the registry and watching the test fail. The apply path ran for real: `install-tools.ps1 -Upgrade uv -Yes` moved `uv` from `0.11.32` to `0.12.2` and the re-report reads `current`. Its first attempt failed because a running process held `uv.exe`, which is why the failure path now names the holding process instead of leaving winget's bare access denial. ## Raised, Not Fixed Here Three findings about the Linux side or the fleet, none blocking this: #671 the linter category is an undocumented decision rather than a missing feature, #672 `gh` uses https where the contract prescribes ssh and neither script checks it, and #673 the Linux scripts should refuse two actions instead of letting the last one win. All three are recorded in `TODO.md`. Closing keywords are deliberately absent, per the branching model: they belong on the `develop -> main` promotion.
) ## What Phase 0 and Phase 1 of a multi-phase plan to convert the hub's instruction-heavy docs (comment style, PR-review conduct, resync safety) into Claude Code / opencode / Codex Skills, so the rules surface automatically instead of needing to be re-read every session. Opening now, early, to get a read on the foundation (the distribution mechanism, the installer) before building the remaining ~6 phases on top of it. Expect further commits landing on this same PR as later phases ship. **Phase 0 (scaffolding, no skill content yet):** - `.agents/skills/` — canonical, hand-authored home for fleet skills. Codex and opencode read this path natively, zero config. - `scripts/build_dist.py` — generates a Claude-plugin-compatible copy at `.claude-plugin/fleet-skills/` (Claude Code does not scan `.agents/skills/` directly), with `--check` for CI staleness gating. - `scripts/skills_install.py` (+ `skills_install.sh`/`.ps1` thin wrappers, mirroring `host-setup/agent-safety/install.py`'s shape) — materializes `~/.agents/skills` for Codex/opencode and registers the marketplace with Claude Code via the real `claude plugin marketplace add`/ `install` CLI, with `--report` for staleness checks. - `.claude-plugin/marketplace.json` — the Claude Code entry point. - `AGENTS.md` pointer (in "Where the Rules Live", verbatim/carried) naming the dependency. **Phase 1: `carried-instruction-file-guard` skill.** Encodes `spec/section-model.md`'s "Migrating a repo onto the split" procedure (the distinctive-phrase probe) as an active pre-flight check before any `AGENTS.md`/`GOVERNANCE.md`/`CODESTYLE.md`/`WORKFLOW.md` overwrite, rather than the prose-only advisory it was before. This is the highest-leverage skill in the plan: it directly targets the bug class that once silently deleted a downstream repo's local `AGENTS.md` additions during a resync. ## Why nested under `.claude-plugin/` instead of a top-level `dist/` First draft generated output at `dist/claude/`, which collided with this repo's existing Python-build-artifact `dist/` gitignore entry. Moved to `.claude-plugin/fleet-skills/` instead, avoiding the name clash entirely rather than punching a hole in a blanket ignore. ## Verification - Ran the real install against a scratch `AGENTS_HOME`, confirmed via `claude plugin marketplace list` / `plugin list` that registration and install actually worked, then cleaned up. - `claude plugin validate` passes clean on both manifests. - 13 new unit tests, all passing. - `repo_gate.py` (`eol`, `eol-coverage`), `prose_lint.py` (diff), `spec/validate.py` all clean. - **Live triggering test for the guard skill**: loaded it via `claude --plugin-dir`, prompted a session to blindly copy a canonical `AGENTS.md` over a scratch repo's `AGENTS.md` carrying a local addition. It triggered, identified the local section, refused the overwrite, and proposed relocating the content before carrying the rest — the exact procedure it specifies. Also logs a `TODO.md` cluster (unrelated to this PR's diff otherwise) for defaulting `.py` to LF fleet-wide, found while pinning the two new scripts individually. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…677) Phase 2 of the hub-docs-to-Skills conversion (Phase 0/1 shipped in #676). ## What this adds - **`pr-review-conduct`** — the merge gate, expected review loop, the five finding-outcomes, triage, and suppressed-finding answering rules, extracted from `GOVERNANCE.md` "PR Review Etiquette". - **`copilot-instructions-keeper`** — new content (not derived from existing prose) for keeping a downstream repo's `.github/copilot-instructions.md` in sync with the hub canonical without losing that repo's own "Disproved Claims" ledger entries, which the file's own rules say are never carried between repos. ## What changed in existing files - `GOVERNANCE.md` "PR Review Etiquette" shrinks from the full contract text to a two-paragraph pointer at the `pr-review-conduct` skill, per this project's standing decision that a skill becomes the sole canonical content for its topic once one exists (see #676's plan doc). - `.github/copilot-instructions.md` is **untouched** — the Copilot review bot reads it directly and cannot read a Skill, so its runbook content stays fully intact by design. - `AGENTS.md` "Where the Rules Live" table and closing paragraph updated to route to both new skills. - `.claude-plugin/fleet-skills/` regenerated via `scripts/build_dist.py` to carry the two new skills alongside `carried-instruction-file-guard` from Phase 1. ## Verification performed - `scripts/prose_lint.py`, CRLF byte-integrity, `editorconfig-checker` (docker), `spec/validate.py`, `scripts/test_build_dist.py` + `scripts/test_skills_install.py` (46 tests) all clean. - `scripts/build_dist.py --check` confirms the generated plugin is current relative to `.agents/skills/`. - Live-triggered both skills via `claude --plugin-dir .claude-plugin/fleet-skills -p "..."` against realistic prompts ("it's all green, go ahead and merge" for `pr-review-conduct`; "just a routine copy-over, right?" for `copilot-instructions-keeper`) — both fired correctly and produced accurate, grounded answers without the rule being restated manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Package Comment and Doc Style as a Skill Phase 3 of the hub-docs-to-Skills conversion (Phase 0/1 shipped in #676, Phase 2 in #677). ## What this adds - comment-and-doc-style, extracted from CODESTYLE.md's General section (Tooling Names and Casing, Markdown and Spelling) plus GOVERNANCE.md's Documentation Style Conventions and Pull Request Title and Commit Message Conventions sections: naming tools in prose, Markdown linting and spelling, Markdown formatting, comment philosophy, the ASCII character-set tiers, line endings, the carried-file coordination reference ban, and PR title/commit message conventions. - Three references/ files for progressive disclosure: line-endings.md (the full CRLF/LF policy), carried-doc-references.md (the full coordination-reference rule and its two exceptions), and markdown-links.md (the full reference-style-link convention). ## What changed in existing files - CODESTYLE.md's General section: Tooling Names and Casing and Markdown and Spelling shrink to pointers at the skill. Clean-Compile Verification and Analyzer Diagnostics and Suppressions are untouched, they are build-gate topics, not comment or doc style. - GOVERNANCE.md's Pull Request Title and Commit Message Conventions and Documentation Style Conventions sections shrink to pointers, per this project's standing decision that a skill becomes the sole canonical content for its topic once one exists. The Comments, Character Set, and Line Endings headings stay in place (spec/files.json and spec/project-types.json anchor to them by name), their content is now a one-line pointer each. - AGENTS.md's Where the Rules Live table routes both rows to the new skill. - .claude-plugin/fleet-skills/ regenerated via scripts/build_dist.py to carry the new skill alongside the three from Phases 1 and 2. ## Verification performed - scripts/prose_lint.py, CRLF byte-integrity (all touched files remain 100% CRLF), editorconfig-checker (docker), spec/validate.py, markdownlint-cli2, scripts/test_build_dist.py + scripts/test_skills_install.py (46 tests) all clean. - scripts/build_dist.py --check confirms the generated plugin is current relative to .agents/skills/. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Phase 4 of the hub-docs-to-Skills conversion (see #676, #677, #678 for Phases 0-3). ## What this adds - **`resync-a-repo`** (hub-only): packages `RESYNC.md` section 3's apply order for a hub-context session driving a named repo's resync. The `carried-instruction-file-guard` skill's distinctive-phrase probe is now a mandatory, non-skippable step before any verbatim re-vendor of an instruction file, the direct structural fix for the AGENTS.md-overwrite bug class rather than a rewording of existing advisory language. - **`fleet-conformance-check`** (fleet-wide, `appliesTo: "*"`, new content): the downstream-facing counterpart. Runs from inside a repo's own session with no hub checkout and no named target other than the repo itself. Confirms the local Skills install is current, confirms `AGENTS.md`'s pointer text matches the hub, self-applies what is safe (re-running the installer), and escalates anything ambiguous (a genuine local addition, a settings/workflow drift) to a hub-driven `resync-a-repo` run rather than touching it. ## Supporting changes - `RESYNC.md` section 3 step 1 gains the mandatory probe requirement and the `AGENTS.md` skill-dependency pointer as one more verbatim-carried unit in the same step. - `AGENTS.md`'s "Where the Rules Live" table and pointer paragraph route to both new skills. - `.claude-plugin/fleet-skills/` rebuilt via `build_dist.py` (now 6 skills). ## Verification - `python3 -m unittest discover -s scripts -p "test_*.py"`: 628 tests, all pass. - `python3 scripts/prose_lint.py` on all touched files: clean. - `python3 spec/validate.py`: clean. - `docker run ... mstruebing/editorconfig-checker:latest`: clean. - CRLF preserved on all touched Markdown. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Phase 5 of the hub-docs-to-Skills conversion (Phase 0/1 shipped in #676, Phase 2 in #677, Phase 3 in #678, Phase 4 in #679). ## What this adds - `dotnet-codestyle` (appliesTo csharp), `python-codestyle` (appliesTo python), and `shell-codestyle` (appliesTo *, since a shell script can appear in any repo), extracted from CODESTYLE.md's .NET, Python, and Shell sections respectively: build/analyzer requirements, language and naming conventions, logging and testing conventions, the Python build-versus-lint-only profile split and toolchain, and the shell `set -Eeuo pipefail` / pipefail-early-reader / shellcheck rules. - This is the first phase gating a skill by `appliesTo` rather than fleet-wide `*`, expressed through each skill's own trigger wording (file extensions and project shape) rather than a mechanical field, since SKILL.md frontmatter carries no such field. ## What changed in existing files - CODESTYLE.md's .NET, Python, and Shell sections shrink to pointers, per this project's standing decision that a skill becomes the sole canonical content for its topic once one exists. No spec file or test references any subheading inside these three sections, so each collapses fully rather than keeping empty subheadings. Six now-orphaned link reference definitions (MD053) are removed with them. - CODESTYLE.md's and AGENTS.md's own descriptions of the file's structure are corrected to mention the pre-existing Shell section, which neither previously named. - `.claude-plugin/fleet-skills/` regenerated via `scripts/build_dist.py` to carry the three new skills alongside the six from Phases 1-4. ## Dropped - `docker-codestyle` (Phase 5b): a content-discovery pass found no dedicated Docker style section in CODESTYLE.md, only scattered linter-invocation mentions in OPERATIONS.md and release mechanics in GOVERNANCE.md, neither of which is Docker coding style. Not authored, per the plan's own don't-invent-content instruction. ## Verification performed - `scripts/prose_lint.py`, CRLF byte-integrity (all touched/new files 100% CRLF), `editorconfig-checker` (docker), `markdownlint-cli2` (docker, 72 files, 0 issues), `spec/validate.py`, `scripts/test_prose_lint.py` (223 tests), `scripts/test_build_dist.py` (19 tests), and `scripts/test_skills_install.py` all clean. - `scripts/build_dist.py --check` confirms the generated plugin is current relative to `.agents/skills/`. - Grepped `scripts/test_*.py` and `spec/*.json` for any coupling to the trimmed CODESTYLE.md subheadings before trimming: none found. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
… Skills (#681) ## Summary Phase 6 of the hub-docs-to-Skills conversion project (see #676-#680 for prior phases). Adds three fleet-wide Claude Code / opencode / Codex Skills, all `appliesTo: "*"`: - **`git-commit-conventions`**, derived from `GOVERNANCE.md` "Git and Commit Rules": staging vs. committing, commit-means-commit-and-push, signing verified not configured, identity verified not set, never force push, careful history rewrites, no destructive git commands. - **`operational-vs-release-workflow`**, derived from `GOVERNANCE.md` "Branching Model", "Release Model", and "Operational Repositories". One skill serves both workflow models: its own trigger description reads the registry's `workflowModel` field per repo rather than gating by repo type. Two `references/` files hold the deep CI-wiring detail (branch protection and promotion mechanics; release build and publish mechanics) via progressive disclosure. - **`upstream-contribution-workflow`** (new content, not derived from any `GOVERNANCE.md` section): the maintainer's own third-party-contribution workflow, a dirty work branch on his own fork for the actual work and review iteration, squashed once clean to a second branch that carries only the intended minimal history, that clean branch opened as the PR against the upstream repo, reviewer feedback applied to the dirty branch first and then re-squashed. Triggers independent of the target repo's own type or workflow model. `GOVERNANCE.md`'s four source sections are trimmed to short pointers, per the standing "skill becomes sole canonical content" decision. Their `## ` headings stay in place, since `spec/files.json` carries all four at `verbatim` fidelity by heading name, only the body content under each shrank. Checked before trimming: no `scripts/test_*.py` couples to any of the four sections' content or subheadings (the Phase 3 gotcha). `AGENTS.md`'s "Where the Rules Live" table gains "packaged as the `X` Skill" annotations on the four affected rows, and `upstream-contribution-workflow` is added to the explanatory paragraph below the table (new content goes there, not a new table row, per the Phase 2/4 precedent, since that table pairs a task with a real `GOVERNANCE.md` section name). ## Verification - `python3 scripts/build_dist.py`: regenerated `.claude-plugin/fleet-skills/` from 12 skills clean. - `python3 scripts/test_build_dist.py`, `python3 scripts/test_skills_install.py`: pass. - `python3 scripts/prose_lint.py . --diff HEAD`: 0 violations (after fixing one `dupword` false positive caused by awkward phrasing). - `python3 scripts/repo_gate.py --check eol`: 0 issues (also re-verified by the pre-commit hook). - `npx markdownlint-cli2` against every new/changed Markdown file: 0 issues. - Verified no `scripts/test_*.py` couples to any of the four trimmed `GOVERNANCE.md` sections' content before trimming. - Verified CRLF preserved on every touched file after every edit (byte-level check, not visual). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## What
Adds `host-setup/bootstrap.ps1`, a Windows counterpart to
`host-setup/bootstrap.sh`, so a Windows host with nothing can be stood
up from a copy-paste console snippet the same way a Linux host already
can.
## Why
Windows previously carried no loader, by a documented decision
(`host-setup/README.md`, `docs/host-setup.md`,
`host-setup/windows/README.md`'s "Why There Is No bootstrap.ps1"): *"the
Windows form of that problem has no one-liner anybody here has run, and
an unverified loader is worse than none."* This PR supersedes that
decision rather than ignoring it — the reasoning is kept and repurposed
as the reason `bootstrap.ps1` does more defensive work than its Linux
peer (TLS pinning, a tree-ownership marker, execution-policy handling,
self-installing its own prerequisite) rather than as the reason none
exists.
## How
- `bootstrap.ps1` runs under Windows PowerShell 5.1, the one shell a
fresh Windows host guarantees. It finds or installs PowerShell 7 via
`winget`, then hands the rest of the run to itself under `pwsh`.
- From there it mirrors `bootstrap.sh`: resolves the ref to a commit,
fetches/extracts the tarball with an ownership marker guarding cleanup,
and drives `upgrade-host.ps1` → `install-tools.ps1` → `setup-github.ps1`
in the same fixed order (packages first, GitHub last since it's the only
step waiting on a human in a browser).
- Adds a new `-Wsl` action (report-only — `setup-wsl.ps1` has no Linux
peer, and no flag here collects a distribution name to install one
unattended).
- Sits beside `bootstrap.sh` at `host-setup/` rather than inside
`host-setup/windows/`, the same one-concern-two-platforms shape
`agent-safety/install.sh` + `install.ps1` already uses.
- Docs (`host-setup/README.md`, `docs/host-setup.md`,
`host-setup/windows/README.md`) updated to describe the new loader and
its Windows one-liner, replacing the "no bootstrap.ps1" sections.
- `scripts/test_bootstrap.py` extended: the loader-invariant tests
(fetched-tree-path, no-Python) now run against both loaders, plus a new
presence/no-shebang test for `bootstrap.ps1`.
## What this does not verify
Neither loader (the existing `bootstrap.sh` nor this new
`bootstrap.ps1`) has ever been run against a genuinely fresh host — only
dry runs and reads against an already-configured checkout. `TODO.md` now
tracks that as its own work cluster ("Neither Host Bootstrap Has Run
Against a Truly Fresh Host"), since closing it needs a human watching a
real run on a real fresh VM image, not more code.
## Verification performed
- `PSScriptAnalyzer` (pinned 1.23.0, matching CI): clean.
- `scripts/test_bootstrap.py`: passing.
- `scripts/prose_lint.py . --diff HEAD` and `scripts/repo_gate.py
--check eol`: clean (the local pre-commit gate).
- Parsed and ran `-Help` under actual Windows PowerShell 5.1.
- Full `-DryRun` runs against `develop` under `pwsh` for `-Report`,
`-Host -Yes`, `-Wsl`, and the PS5.1→pwsh7 reinvocation handoff forced
via `powershell.exe` — all completed cleanly, correctly drove the real
tool scripts, and left no orphaned trees afterward.
- Confirmed two-actions-given correctly refuses, matching the sibling
scripts' convention.
Phase 7 of the hub-docs-to-skills conversion project. ## What - New `standup-a-repo` Skill at `.agents/skills/standup-a-repo/SKILL.md`: a hub-only driver over `STANDUP.md`'s create-to-conformance procedure, run from a hub checkout for a named target repo the maintainer is standing up. - Same non-trimming pattern Phase 4 (`resync-a-repo`) used: `STANDUP.md` is hub-only and never carried downstream, so there's no duplicate-copy problem to solve by pointer-izing it. The source file stays the full authoritative procedure; the skill is a driver layered on top. - `STANDUP.md` section "2. Carry the Baseline Files" gains an explicit step to carry `AGENTS.md`'s skill-dependency pointer paragraph (naming `scripts/skills_install.py` and where the fleet's Skills live) as one more verbatim unit, the identical requirement Phase 4 added to `RESYNC.md` section 3 for a repo already stood up. - `AGENTS.md`'s "Onboarding a repo or running a conformance sweep" table row now names both `standup-a-repo` and `resync-a-repo`. - `.claude-plugin/fleet-skills/` regenerated via `scripts/build_dist.py`. ## Checks done before pushing - `spec/files.json`, `spec/project-types.json`, `scripts/test_*.py` grepped for `STANDUP.md` coupling before editing: none found, safe to edit freely. - `python3 scripts/prose_lint.py` clean on the whole tree. - No inline Markdown links in the new skill file (reference-style/backtick convention only). - No semicolons or em-dashes introduced (verified against the pre-existing baseline in each touched file). - CRLF preserved byte-for-byte in every touched/created file, no stray LF. - No mid-span code-span line wraps (`` `python3 scripts/host_gate.py` `` and `` `repo-config/configure.sh apply owner/repo release|operational` `` each stay on one line). - `python3 scripts/build_dist.py --check` reports the plugin current. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Why A real mis-triage this session, on a downstream PR ([HomeAutomation-Config#50](ptr727/HomeAutomation-Config#50)): two findings were factually real but structurally out of that repo's scope to fix (verbatim-fidelity-locked GOVERNANCE.md content owned by this hub). Neither of the skill's five outcomes named that shape cleanly, so I reached for outcome 3 ("the maintainer's call") and left the threads open with a plan to ask later — which never happened once attention moved to other in-flight work in the same session. That's an unresolved thread with no closing mechanism, exactly what outcome 3's own "never resolve this one by silence" line exists to prevent, just from the other direction (never *leave* it unresolved by silence either). ## What changed - **Outcome 2** broadened from "not real" only to also cover "real but structurally out of scope, provable" — either shape closes the thread on cited evidence (a command's output, a code path, or now also a governing rule/manifest entry establishing the boundary), rather than needing outcome 3's maintainer consultation at all. - **Outcome 3** tightened to apply only once outcome 2 is ruled out (a fix genuinely possible here, the question is value not scope), and now requires the maintainer's answer in the same turn rather than deferred — a plan to ask later is treated explicitly as resolution by silence. - New closing line: a disposition decided on one PR does not carry to a sibling PR or repo in the same batch, even when the finding shape is identical — each instance needs its own close. `.claude-plugin/fleet-skills/skills/pr-review-conduct/SKILL.md` regenerated via `scripts/build_dist.py` to match. ## Verification ``` python3 scripts/prose_lint.py .agents/skills/pr-review-conduct/SKILL.md # scope: 1 file(s) read, whole tree python3 scripts/build_dist.py --check # is current ``` No `scripts/test_*.py` coupling to this file's content (checked before editing, per the Phase 3 gotcha). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Why Twice in one session, `wait` polled the full 45-minute timeout for a review nothing had asked for: once after a push superseded the prior request, once after a re-request attempt that never actually issued a mutation. `wait` never requested anything itself, contrary to what a caller reasonably expects from a command named `wait` — the manual `requestReviews` recipe documented in `.github/copilot-instructions.md` is a separate step every caller has to remember on every push, and forgetting it fails silently (a full timeout, not an error) rather than loudly. ## What changed `wait` now issues the request itself, once, before its first poll, whenever nothing is already outstanding and the head isn't already covered. The bot id is read fresh from the repository's own last 20 PRs every time (never fabricated, never cached), and requests are additive (`union:true`) so a human reviewer requested alongside Copilot is never dropped. A repository with no Copilot review anywhere to read the id from falls back to plain polling rather than crashing or guessing. **This is a deliberate second write path**, not an oversight fixed in passing: it was previously excluded by name in a guard test (`test_the_only_writes_are_the_two_the_reply_path_owns`) asserting an explicit prior design decision, itself consistent with `.github/copilot-instructions.md`'s own text describing `requestReviews` as a manual mutation this script doesn't issue. Renamed that test and updated its assertions for the third deliberate write (now `test_the_only_writes_are_the_three_named_here`), added the mutation and runbook-mention checks alongside the existing two, and added an explicit ban on `union:false` (the destructive clear-and-recover form stays a manual runbook step, never automatic). Added targeted tests for all three auto-request shapes: bot id resolved and the request fires, no bot id found anywhere and it falls back to polling, already requested and it is not re-requested. Each was verified to fail against the pre-fix code before trusting it (reverted `pr_review.py` alone, ran the three new tests, confirmed 3 of 4 failed as expected — the fourth, "already requested," passed even pre-fix since that no-op case existed by coincidence of no request-issuing code existing yet, not something this PR broke). ## Verification ``` cd scripts && python3 -m unittest test_pr_review # 231 tests, all pass python3 scripts/prose_lint.py scripts/pr_review.py scripts/test_pr_review.py # clean uvx ruff check scripts/pr_review.py scripts/test_pr_review.py # 23 pre-existing errors, unchanged by this diff, none in new code ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
GOVERNANCE.md's "Running the Linters Locally" bullet unconditionally claimed CI runs PSScriptAnalyzer and linked `PSScriptAnalyzerSettings.psd1`, true for the hub itself but not for every downstream repo. Hit as a real Copilot finding on both [Blog#94](ptr727/Blog#94) (a stale carried `validate-task.yml` never wired the step) and [HomeAutomation-Config#50](ptr727/HomeAutomation-Config#50) (the repo carries no `.ps1` files at all, so the link 404s). Neither was fixable per-repo without either breaking byte-parity on the verbatim bullet or inventing content the repo does not need, so both were replied-and-deferred as the maintainer's call. This conditions both bullets on a repo carrying `.ps1` files instead, closing the recurring gap at its source rather than continuing to defer it repo by repo through the rest of the Phase 8 rollout. Not manifest-tracked ("Running the Linters Locally" is not a declared verbatim section in `spec/files.json`), so this is a hub-only prose fix with no spec/audit coupling to update. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Why The skill-based fleet system has defined procedures but undefined seams between them. The skills install has no home in the cold-start flow, a failed tool floor names no install remedy, session entry never checks skill staleness, and WORKFLOW.md, AUDIT.md, and the conduct rules carry no skill. Each gap was real but recorded nowhere, so each session rediscovered them. ## What - `docs/fleet-map.md` (hub-only): maps the five entry points into the fleet system with a flowchart each, registers twelve gaps (G1-G12) with a defined handoff and a closing test per gap, resolves the skills install model as global with its wiring gaps enumerated, proposes four new skills (`audit-a-repo`, `workflow-ci-contract`, `skill-lifecycle`, `agent-conduct`) with scope and overlap settled, evaluates ASD-STE100 against a constrained house style, and phases the closure work P0-P4. Carries its own maintenance rule: a PR closing a gap edits its register row in the same change. - `docs/peer-messaging.md` (hub-only): declares the live agent-to-agent messaging method and its four safety rules, resolving the TODO.md location decision to a hub-only doc until cross-host is verified. Closes register gap G11. - `TODO.md`: the peer-messaging cluster's two open bullets resolve by pointer to the new docs, and one added line establishes the fleet-map register as the home for adoption gaps so the two files do not fork. ## Verification - `prose_lint.py --diff HEAD`, `repo_gate.py --check eol`, and `markdownlint-cli2` all pass on the three files. - All seven mermaid blocks parse as `flowchart-v2` via `mermaid.parse` (mermaid 11). - All three files are CRLF, verified with `git ls-files --eol`. Generated with [Claude Code](https://claude.com/claude-code)
) ## Why `install-tools.sh` already detected a copy of `uv`, `jq`, or `git-restore-mtime` earlier on `PATH` than `$BIN_DIR` (`tool_note`'s report note), but nothing acted on it. `--install`/`--upgrade` only ever touched `$BIN_DIR`, so a `--report` run right after still repeated the same note, unchanged. Worse: `apply_tool()` reads a tool's version via `command -v`, so a shadow that already happened to be current made the tool report as `current` and return before ever reaching its own install function. That is exactly how `uv` installed per #483's own documented method (`curl -LsSf https://astral.sh/uv/install.sh | sh`, into `~/.local/bin`, ahead of `/usr/local/bin` on a stock Debian `PATH`) never converged on repeated `--upgrade` runs, per the repro in #688. ## What changed Went with option (b) from the issue (detect and self-heal), since option (a) (ride the vendor's own install path, the pattern already used for `gh`/`node`'s apt repos) doesn't fit `uv` the way it fits those two: the astral installer's own path is a per-user `~/.local/bin`, where this script installs shared tools system-wide as root into `/usr/local/bin`. - `tool_shadow_path()` factors the existing PATH-vs-`$BIN_DIR` check out of `tool_note()`, so the report and the fix below share one answer. Uses `type -P` rather than `command -v`, so an alias or shell function shadowing the name (no file behind it) is never mistaken for a shadow. - `tool_unshadow()` acts on it, looping per tool name until nothing shadows `$BIN_DIR` any more (PATH can stack more than one copy ahead of it), prompting before each removal and respecting `--yes`/`--dry-run` the same way `apt_install_displacing`'s confirm already does. A distro package's own file is never removed this way (`dpkg-query -S`), since `rm -f`-ing it would desync dpkg's database from the filesystem, matching `jq_install`'s own design note that the distro `jq` package stays installed. Covers `uv`'s `uvx` companion from the same archive too, since it would otherwise stay shadowed while `uv` itself got fixed. - `apply_tool()` only unshadows *ahead of* the version read under `--upgrade` (which brings `$BIN_DIR` current regardless of what gets removed) or when the managed copy at `$BIN_DIR` does not exist yet (nothing there to protect). `--install` with a managed copy already present leaves a shadow alone rather than risk removing a newer copy with nothing to replace it, and logs an explicit note pointing at `--upgrade` instead of going silent. Fixes #688. ## Verification - `bash -n host-setup/linux/install-tools.sh` and `--help`/`--list` still run. - `shellcheck` clean via the pinned `koalaman/shellcheck:stable` image. - `scripts/prose_lint.py --diff HEAD` clean. - Functional harnesses sourced the script's functions against simulated `PATH` states and confirmed: `tool_shadow_path` finds a real shadow and ignores a same-named alias/function with no file behind it; `tool_unshadow` removes a single shadow, removes two stacked shadows in one call, leaves a distro-package-owned path alone with a warning, leaves a declined path alone with a warning, and performs one simulated pass under `--dry-run` rather than looping forever; and `apply_tool` leaves a newer shadow untouched (with an explicit note) under `--install` when an older managed copy already exists, but removes it under `--upgrade`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Why The fleet map's P1 phase closes the skills install model. A bootstrapped host ended with every tool and no fleet skills, a failed tool floor named no runnable remedy, a bare `host_gate.py` run silently skipped an enclosing repo's overlay, nothing stated the skills staleness cadence, and the register's G8 row described a CI gap that #676 had already closed before the register merged. ## What One commit per register row, each editing its row in `docs/fleet-map.md` per the maintenance rule: - **G8, recorded closed.** `validate-task.yml` has run `build_dist.py --check` since #676, which predates the register's merge, so the row's `Checked` claim was stale on arrival and the row now records the resolution and its provenance. - **G2, bare-run overlay warning.** `host_gate.py` warns when a bare run's working directory sits inside a repo carrying a `host-tools.json` overlay it did not read, naming the `--repo` re-run that counts it. An explicit `--repo` and `--no-local` stay silent. STANDUP section 0 states the residual case the warning cannot cover, a target that does not exist yet. - **G3, runnable remedy.** Each floored `spec/host-tools.json` entry carries a per-platform `remedy` command beside its `source`, printed as a `REMEDY:` line under a below-floor failure, with `host-setup/` paths resolved against the checkout so the command is runnable as printed. The schema and `spec/validate.py` require it on every hub floor, and `test_bootstrap.py` keeps the mapping total per platform (git-restore-mtime/windows is the recorded not-applicable exception) and pointing at tools the installers actually manage. - **G1, skills in the cold start.** The bootstrap host mode ends with an `install-skills` step on both platforms, a `--skills` action runs it alone, and the report mode reads the stamp beside the other status lines. The loaders hand their resolved commit to the installer via `SKILLS_SOURCE_COMMIT`, since a tarball tree has no `.git`, so the stamp stays checkable and `--report` can exit zero on a fresh host. `docs/host-setup.md` gains the "Fleet Skills Install" section and verify lines, and `README.md` "Using This Repo" names four deployed things. The `claude` CLI stays out of the tool catalog: the installer degrades without it, lands the overlay half, and records the partial install. The new drivers keep the loader single-path and no-Python invariants intact, and the two recorded exceptions (Python last-in-line, independent fetchability) are stated in `host-setup/README.md`. Cross-links #671 and #673, which touch the same scripts and stay open on their own tracks. - **G6, staleness cadence.** Stated at both trigger surfaces: `docs/host-setup.md` and the `fleet-conformance-check` skill's new "Refresh cadence" section (plugin regenerated via `build_dist.py`), routing the restated-rule symptom to `skills_install.py --report`. ## Verification Every `validate-task.yml` gate run locally and green: the ten script and spec self-tests, including new cases for the warning, the remedy mapping, and the handed-in commit, plus `repo_gate.py`, `prose_lint.py` with CI's check list, `spec/validate.py`, `build_dist.py --check`, markdownlint, cspell on README and HISTORY, shellcheck and PSScriptAnalyzer via the same pinned containers CI uses, and all seven fleet-map mermaid blocks parse.
Implements phase 2 of the docs/fleet-map.md adoption roadmap: the four proposed skills ship, the AGENTS.md rule map carries a disposition per doc-only GOVERNANCE section, and the G9, G10, and G12 register rows close. ## What ships - **skill-lifecycle** (G10, authored first so the other three follow its procedure): the .agents/skills/ source-versus-generated split, build_dist.py regenerate and --check semantics, skills_install.py install and stamp semantics, the doc-packaging pattern, and trigger-description conventions. .agents/skills/README.md defers to it for procedure, which is the row's closing test. - **audit-a-repo** (G9): packages AUDIT.md in the kept-authority shape, completing the standup/resync/measure triangle, with fleet-conformance-check remaining the in-repo self-check. - **workflow-ci-contract** (G9): packages the WORKFLOW.md contract, with references/ splits for the D-guarantee catalog and the 5A/5B/5C test methodology. The git half stays with operational-vs-release-workflow, and the two descriptions state the split. - **agent-conduct** (G12): narrow decision-moment triggers only (about to claim done, about to assume, a failure just surfaced a lesson), per the proposal's deliberately-not-always-on decision. Its three GOVERNANCE sections keep the full rules and carry the surfacing pointer. - **G9 disposition sweep**: the four surfaced rule-map rows are annotated, and a paragraph after the table states why each remaining unannotated section is doc-only by decision. ## Deviation from the doc, recorded in the doc fleet-map.md said each skill ships as its own pull request. All four ship here in one at the maintainer's direction, recorded in the G9 provenance note, since skill-lifecycle only needed to be authored (not merged) before the others follow it. ## Verification - python3 scripts/build_dist.py run and committed together with the source, --check passes. - python3 scripts/prose_lint.py . clean whole-tree, repo_gate.py --check eol clean, all 644 script tests pass, markdownlint-cli2 0 issues over 98 files. - New files are CRLF, verified by byte scan. Generated with [Claude Code](https://claude.com/claude-code)
Closes the three P3 audit-depth register rows in `docs/fleet-map.md`, each per the maintainer's recorded disposition. ## G4: deletion sweeps miss prose Split by what a pattern can reach. The named-path half is mechanized: a `dead-path` rule in `scripts/prose_lint.py` reports a Markdown mention (backtick span, inline link target, or reference definition) of a path git once tracked and the tree no longer holds. Keying on deletion history is what scopes it: proposed files a backlog names, another repository's layout, and refs like `origin/develop` have no history here and stay silent, and manifest-declared carried paths are exempt since the hub's own instances retire to catalog snippets. The rule joins the default set and the CI prose step, the CI checkout fetches full history (`fetch-depth: 0`) because the rule stands down loudly in a shallow clone rather than pass blind. The name-shaped half (a description naming no path) stays the manual RESYNC section 4 read, `accepted` as manual: no pattern reaches it. The rule's first tree-wide run caught a real instance: `docs/host-setup.md` still described bind-mounts in the deleted `.devcontainer/` definitions, fixed here by re-pointing at `catalog/snippets/devcontainer/`. ## G5: intent-fidelity drift is invisible The advisory ships in `spec/audit.py`: per applicable intent unit, the audit compares when the hub canonical and the repo copy each last changed, and a canonical changing later reports the copy as possibly trailing, at DRIFT and never failing. A copy content-identical to the canonical is skipped (current, and the `fidelity_honesty.py` promotion candidate). No reconciliation record exists anywhere, so last-modified is the implementable proxy, and the blind spot (a copy touched without reconciling reads current) is stated in RESYNC section 5, AUDIT.md, and `spec/fidelity-model.md` rather than papered over. Verified read-only against a fleet repo: carried intent files reported as trailing hub changes made weeks earlier, findings no prior check produced. ## G7: operational develop PR-only is prose-enforced Closed `accepted`, mirroring the `spec/divergences.json` vocabulary, recorded in STANDUP.md section 0B beside its uneven-protection warning. The allowance is the operational model's foundation per WORKFLOW.md "Branch Model", and the considered standup-time tightening (apply the release payload, swap after the closing audit) was declined: it adds a forgettable swap step and a window where the live ruleset contradicts the registry's declared model. Also retires the stale G11 phase cell (`P0` -> `closed`), which trailed its own closure, per the register's maintenance rule. ## Verification - All seven `scripts/test_*.py` suites pass, including the new `TestDeadPath` (deletion signature, shallow stand-down, manifest exemption, tree-clean assertion). - `prose_lint.py` full default sweep clean with `dead-path` enabled, `charset-unknown` clean, `build_dist.py --check` current. - markdownlint and editorconfig-checker clean on every changed file, CRLF preserved, all 7 `fleet-map.md` mermaid blocks parse. - mypy and pyright report zero deltas against the develop baseline.
Addresses #695. `uv` carried a null minimum with no floor measured. The issue offered Windows winget versions (0.12.2, with winget already offering 0.12.4) as an unverified starting point, explicitly asking for someone with broader fleet data to firm it up into a real measured or target floor. Cross-checked against this Linux host (uv 0.12.4, via `host-setup/linux/install-tools.sh`'s always-fetch-latest install), the lower of the two independently observed versions becomes a **target** floor (`0.12.2`) in the same style `jq`'s floor already uses: anchored to what an independently set-up host already provides rather than to a measured breakage, with real `source`/`remedy` entries per platform. `docker` is deliberately left at `null`. Unlike `uv`, it has no remedy anchor anywhere in this repo (it isn't in either host-setup tool list, so there's no install/upgrade command to cite), and its existing `why` already argues the daemon version has never been what broke a run since the linters run as pinned images. Per this file's own stated philosophy ("a floor nobody can justify becomes a host failure nobody can act on"), adding a floor there would be unfounded. Also fixes a latent bug in `scripts/test_host_gate.py`'s `test_the_contract_table_carries_every_declared_floor`: the compound `` `uv` / `uvx` `` table cell normalized to a key (`uvuvx`) that neither tool name matched, which only surfaced now that `uv` actually carries a floor to check. Verified locally: - `python3 scripts/host_gate.py` reports `uv 0.12.4 meets the 0.12.2 floor` - `python3 -m unittest scripts.test_host_gate` — 72 passed - `python3 scripts/test_bootstrap.py` — OK - `python3 scripts/prose_lint.py spec/host-tools.json docs/host-setup.md` — clean - `python3 scripts/repo_gate.py --check eol` — clean
Closes out the actionable half of the fleet-map P4 roadmap item on STE structural checks. ## What - **`sentence-length` rule in `scripts/prose_lint.py`**: flags a Markdown prose sentence over 25 words, ASD-STE100's descriptive cap. Opt-in like `sentence-split` (not in `DEFAULT_RULES`, not in CI), because the rule counts 1686 over-cap sentences in the corpus at landing, so a default gate would fail whole files nobody is editing. A table row, a heading, a link definition, and a blockquote are exempt, an inline code span and a double-quoted quotation each count as one word. Eight new cases in `scripts/test_prose_lint.py`, including one pinning the opt-in status. - **Decision recording**, per the fleet-map hook that the decision lands when the first structural check ships: a `Sentence Structure` subsection in GOVERNANCE.md `Documentation Style Conventions`, the full house-style rules in the `comment-and-doc-style` skill (plugin regenerated), and a `Decision` paragraph plus P4 row edit in `docs/fleet-map.md` per the maintenance rule. - **OPERATIONS.md staleness fix**: the runbook's mirrored prose gate omitted `--check dead-path` after #692 added it to CI, and its rule counts predated the rule. Both now match CI, and the opt-in note names both unlinted rules. ## Why opt-in The corpus measures 26 percent of sentences over 20 words and 1686 over the 25-word cap, so the house style is adopted for new prose while the check stays reachable by name and by `--diff` scoping. Promotion into `DEFAULT_RULES` or CI is a separate decision, taken if and when the corpus converges. The changed lines of this PR lint clean under the new rule. ## Verification - `python3 scripts/test_prose_lint.py` (243 tests), `test_repo_gate.py`, `test_pr_review.py`, `test_build_dist.py`, `spec/audit.py --selftest`, `gh-write-guard.py --selftest`: all pass - `repo_gate.py`, `build_dist.py --check`, the CI prose gate including `dead-path`, `charset-unknown` summary (0 findings), `spec/validate.py`, `jq` sweep, `markdownlint-cli2` on the changed files: all clean - `ruff` at latest adds no new finding over develop's pre-existing nine, `mypy` passes - Line endings byte-verified, CRLF Markdown and LF Python unchanged Generated with [Claude Code](https://claude.com/claude-code)
…696) ## Summary Fixes #693. `winget list --id OpenJS.NodeJS.LTS --exact` echoes back the queried id for a package that only shares a publisher and name with what's actually installed. On a host running `OpenJS.NodeJS` (the Current channel), that made every report and action believe `OpenJS.NodeJS.LTS` was installed, and `-Upgrade`/`-Reinstall` would have targeted that wrong id. Node also ships under many more ids than Current/LTS — one per pinned major back to 4 (`OpenJS.NodeJS.4` … `.23`) — any of which is fine on a host as long as its version clears the LTS floor. ## Fix - Added a `Family` field to the tools registry in `install-tools.ps1` (empty for every tool but `node`, which gets `'OpenJS.NodeJS'`). - Added `Resolve-ToolPackage`, which cross-references the already-correctly-read installed version against a `winget search --query <family>` catalog listing. A version that uniquely matches one of the sibling ids identifies which one is truly installed, without ever trusting winget's id-scoped `list --id --exact` correlation. - That resolved id (`$state.Package`) now flows through status, notes, the report's SOURCE column, and every install/upgrade/remove action instead of the hardcoded default. - Other 7 managed tools are untouched (`Family = ''` short-circuits before any extra winget call). ## Verification (done on a live host) Host had `OpenJS.NodeJS` (Current, 26.7.0) installed, not `OpenJS.NodeJS.LTS`. - `-Report node` → now shows `SOURCE=OpenJS.NodeJS` (was misleadingly `OpenJS.NodeJS.LTS`), status still correctly `current` (26.7.0 ≥ LTS floor 24.19.0). - `-Upgrade node -DryRun` → correctly no-ops (already current). - `-Reinstall node -DryRun` → now issues `winget uninstall --id OpenJS.NodeJS` / `winget install --id OpenJS.NodeJS`, i.e. it reinstalls the package that's actually there instead of silently swapping to LTS. - Full `-Report` across all 8 tools → other 7 unchanged, no regressions. - Script parses clean; PSScriptAnalyzer shows only pre-existing, unrelated `Write-Host` warnings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…on floor (#701) Closes the docker gaps tracked in #695: no version floor was declared, and `host-setup/linux/install-tools.sh` carried no install path for docker at all. - `spec/host-tools.json`: docker now carries a target floor of 29.6.2, anchored to the Windows host issue #695 recorded (Docker Desktop 4.85.0, Engine 29.6.2), plus source and remedy for linux, macos and windows. - `host-setup/linux/install-tools.sh`: a real docker install/upgrade path, the same apt-repository shape `gh` and `node` already use here - removes old conflicting packages, registers download.docker.com's own repo with a verified signing key, installs `docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin`. Inside a detected WSL distribution it refuses the native install outright, with no override, and points at Docker Desktop's own WSL integration instead. - `host-setup/windows/install-tools.ps1`: before installing or upgrading docker, checks that `wsl.exe` is present and WSL is at or above 2.1.5 (Docker Desktop's own documented floor), and skips with a named remedy otherwise. It never runs `wsl --install`/`wsl --update` itself - those stay `upgrade-host.ps1 -Wsl` and `setup-wsl.ps1`, a person's own action. - `scripts/test_bootstrap.py`, `docs/host-setup.md`, `host-setup/windows/README.md` updated to match: the now-stale `NOT_MANAGED['linux']['docker']` exception is removed, the floor table and comparison table are brought current. ## Verification On this machine (Linux, docker 29.7.1 already installed): `spec/validate.py`, `scripts/host_gate.py`, `scripts/test_bootstrap.py` all pass; `shellcheck` and `PSScriptAnalyzer` via the pinned CI images are clean on both modified scripts; `markdownlint-cli2` and `editorconfig-checker` are clean. Exercised `--report`, `--install`/`--upgrade --dry-run`, and a simulated-WSL run (`WSL_DISTRO_NAME` set) against the real download.docker.com repository - the GPG key fetch and verification succeeded and the WSL path correctly refused with no apt commands attempted. A real install/upgrade on a native Windows host and inside an actual WSL distribution has not been run in this session - the maintainer is following up with both separately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Gives README.md, STANDUP.md, RESYNC.md, and AUDIT.md the same at-a-glance mermaid diagrams `docs/fleet-map.md` already uses, so a reader gets the shape of the flow inside the procedure doc itself instead of only in the hub-only summary map. Also adds a small matching diagram to `AGENTS.md`'s Fleet Bootstrap section, and appends the governing `D`-guarantee number to 3 already-existing `WORKFLOW.md` diagram labels (no new diagrams there, no structural change). **What changed** - `README.md`: entry-doors flowchart in "How This Repo Operates", matching the "Getting Started" table's rows. - `STANDUP.md`: full section-by-section procedure map (0 through 5). - `RESYNC.md`: primary route/measure/apply/ship flow, plus a detail diagram for the section 4 deletion remedy. - `AUDIT.md`: primary measurement-pipeline map (0 through 8), plus a section 9/10 escalate-vs-converge diagram. - `AGENTS.md`: a small Fleet Bootstrap diagram reusing README's door names, so the agent-entry and human-entry views are the same graph. - `WORKFLOW.md`: 3 existing diagram node labels get a `(D#)` tag appended; label text only, no graph-shape change. **Design principle**: every new node label opens with the section number or name it maps to, so each diagram doubles as a navigable index of its own document and a renamed/renumbered section shows up as a mismatched node rather than drifting silently. **Scope note**: `docs/fleet-map.md` itself is intentionally untouched (no register gap is closing, no disagreement with a procedure doc to fix). `AUDIT.md` and `AGENTS.md` were added to scope beyond the three files first requested, since they complete the same triangle `fleet-map.md` names and close a real divergence risk (`AGENTS.md`'s Fleet Bootstrap and README's Getting Started table already described the same routing in two independent prose blocks). **Verification**: markdownlint-cli2, cspell, and editorconfig-checker all ran clean via Docker over the six changed files. Every diagram's node set was cross-checked against its source doc's actual headings and table rows for completeness, order, and no invented door. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Why A re-analysis of `docs/fleet-map.md`, applying the same gap-finding method that created it, after the P0-P4 closures, the #689/#701 install-model changes, and the #702 procedure-doc diagrams. ## What - **Diagram authority**: #702 put authoritative flow diagrams into STANDUP.md, RESYNC.md, AUDIT.md, and AGENTS.md. The map's own copies of the STANDUP and RESYNC flows were coarser duplicates with no cross-reference in either direction, and by the map's own rule the procedure doc wins. The two derivative diagrams are replaced with pointer sentences, and the Entry Points intro states the ownership rule. The five diagrams no procedure doc draws (System Map, Pre-Agent Cold Start, Daily Development, Hub-Side Operations, G3 remedy loop) stay. - **Maintenance rule extended**: it covered register rows but not drawn flows, which is exactly the class this batch fixes. It also cited G4 for register staleness, which is at best an analogy, so the sentence states the class directly. - **Stale content**: the vestigial `Checked` evidence-anchor sentence (the shipped register has no such cells, and the `ba392f9` pin is historical) is removed, the cold-start tool node points at `spec/host-tools.json` instead of an enumeration that drifted twice in two weeks (#689, #701), and the dead "dashed arrows mark a gap" clause (zero dashed arrows exist) is removed. - **Unmapped entry points surfaced**: OPERATIONS.md is named as the runnable form of the Hub-Side Operations door and of the daily-dev gates node, and `scripts/repo_gate.py`, `scripts/pr_review.py`, and `spec/audit.py` get reference links. - **Cross-links**: #699 and #700 join the Decision Ledger Cross-References per the #671-cluster precedent, and the #689 PATH self-heal is noted in the cold-start prose. - **Discoverability**: README links the map from the doors diagram it sits behind. AGENTS.md is deliberately untouched (byte-locked Fleet Bootstrap, and a carried file must not reference a hub-only doc). - `worktree` joins `cspell.json` words (used by the #699 cross-link). ## Flag, not a task `docs/fleet-map.md` and the #702 diagrams exist only on `develop` (main is 22 commits behind and lacks the file entirely). A `develop` to `main` promotion is a maintainer decision and is not part of this change. ## Verification All nine CI prose_lint checks pass tree-wide and `--diff develop` is clean, markdownlint reports 0 issues on both edited files, cspell is clean on README and leaves only the six pre-existing fleet-map findings (the file is not CI spell-gated), editorconfig-checker passes (CRLF preserved, diff is line-scoped), `spec/validate.py` and `jq` pass, all 6 remaining mermaid fences (5 in fleet-map, README doors) parse OK via mermaid@11, and a two-way reference-link check (every use defined, every relative target exists) passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…#705) Stop Docker Desktop and Update WSL During a Docker Install or Upgrade #701 checked the WSL platform floor before a docker install or upgrade and only named the remedy, upgrade-host.ps1 -Wsl. That left docker to skip outright whenever WSL was behind, since that script also refuses while Docker Desktop is running. Verifying #701 on a real Windows host surfaced two more gaps. Docker Desktop's own WSL integration goes stale across most engine bumps, surfacing as "WSL integration with distro '<name>' unexpectedly stopped". The in-app "Restart the WSL integration" button does not clear it. And wsl --update raises its own UAC prompt, which a headless run cannot answer. - `install-tools.ps1`: a docker install, upgrade or reinstall now drives the whole maintenance window itself. It stops Docker Desktop through its own CLI (`docker desktop stop`/`start`), runs `wsl --update` where the floor is not met, shuts every WSL distro down with `wsl --shutdown` so each one's integration remounts fresh, and restarts Docker Desktop once the package itself is settled. Confirmed first, unless `-Yes` was given. A non-elevated, non-interactive run refuses to start `wsl --update` rather than hang on its UAC prompt, and restores Docker Desktop to how it found it. The WSL-readiness check moved from an unconditional early gate to just before real work happens, so an already-current docker no longer fails a run over an unrelated stale WSL platform. - `README.md`: documents the moved boundary. WSL used to be strictly read-only here, naming `upgrade-host.ps1 -Wsl` as a person's own step. A docker version bump already needing the same stop-then- restart window for its own integration is what moved it. ## Verification Exercised live on a real Windows host, not simulated: a real docker upgrade (4.85.0 -> 4.86.0) through winget; the WSL-below-floor path through a real `wsl --update` past an actual UAC prompt (WSL 2.7.10.0 -> 2.7.11.0), re-verifying the floor and restarting Docker Desktop afterward; the plain engine-bump stop, shutdown, restart cycle; the declined-prompt and neither-condition-true branches, through mocked extraction of the real functions; and the headless-UAC-refusal guard, both tripping unelevated and bypassing elevated correctly. `Invoke-ScriptAnalyzer` against the repo's own settings, a parse check, `pytest scripts/test_bootstrap.py scripts/test_host_gate.py` (82 passed), and `host_gate.py` all pass.
…704) Closes #700. ## What - **Every ruff and mypy finding is fixed**: 88 ruff findings (`PLW1510`, `SIM117`, `EXE001`, `BLE001`, `ISC004`, and others) and 5 mypy errors at the configured contract (latest defaults plus `extend-select = ["I"]`) are at zero. `subprocess.run` calls carry an explicit `check=False` (behavior-preserving), nested `with`/`if` blocks are merged, shebang files carry the exec bit, and the deliberate blind catches (hook and sweep isolation boundaries) carry scoped `# noqa: BLE001` with their why comments. - **CI carries the Python tooling** (`validate-task.yml`): `uvx ruff@latest check .`, `uvx mypy@latest`, and the self-test surface under `uvx coverage@latest` with `--append` and a report, informational with no threshold. `astral-sh/setup-uv` is SHA-pinned per the catalog precedent. - **The unit tests move to `scripts/tests/`**, with imports, repo-root constants, `.gitattributes` pins, and the `.editorconfig` LF section (`scripts/**.py`, since an EditorConfig `*` stops at `/`) updated, and every prose reference swept (`OPERATIONS.md`, `scripts/README.md`, `docs/fleet-map.md`, host-setup READMEs, the skill sources plus regenerated plugin). - **Linter versus utility is a documented naming split**: a gate checks and exits non-zero and carries a `_lint`/`_gate` suffix (`prose_lint`, `repo_gate`, `host_gate`), a utility carries none (`build_dist`, `pr_review`, `skills_install`). - **The law amendments**: the `python-codestyle` skill's Scripts profile now requires Python tooling in CI, coverage included, and states the `tests/` layout and naming split fleet-wide. `CODESTYLE.md` Shell, `GOVERNANCE.md` Repository Layout, `spec/project-types.json` (`python.coverage.codecov` note), and `spec/host-tools.json` (the python floor's why no longer claims no Python linter runs in CI) follow. ## Decisions left open, deliberately - **`ruff format` is not adopted**: a `format --check` gate would reformat 19 files (~12k diff lines) in the same PR. Measured and left for a separate decision. - **The gates stay at `scripts/prose_lint.py` / `scripts/repo_gate.py`** rather than moving to a `scripts/lint/` directory, because those paths are named in carried text (`.github/copilot-instructions.md` points every fleet repo at them), so a move stales the whole fleet. The issue's naming-separation floor is met instead. ## Verification All gates run locally the way CI runs them: ruff, mypy, the unittest suite under coverage (all seven files, plus the audit, write-guard, and agent-safety self-tests), `build_dist.py --check`, `repo_gate.py`, the nine-rule prose gate, `spec/validate.py`, actionlint, editorconfig-checker, markdownlint, cspell, shellcheck, and PSScriptAnalyzer. `test_install.py` passes on a clean tree, and OPERATIONS.md now documents its dirty-checkout STALE verdict as by design. After merge the machines need `python3 scripts/skills_install.py` re-run per the documented cadence, since the `python-codestyle`, `shell-codestyle`, and `skill-lifecycle` skills changed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Closes #707, the follow-up #704 deliberately deferred. ## What - **`df12931` is the one-time reformat**: `uvx ruff@latest format` over `scripts/`, `spec/`, and `host-setup/` (19 files, ~12k diff lines), format-only except for the two source-scan test assertions the new quoting exposed in `scripts/tests/test_pr_review.py`. The `add_argument` option scan now matches either quote and a wrapped call and holds a floor of five options, since the quoting change had silently emptied it to one match and an empty scan reads exactly like a pass. The `--match` assertion reads the formatted double quotes. - **`52884ab` holds the tree there**: a `uvx ruff@latest format --check .` step in `validate-task.yml` beside the lint step, the same invocation in the OPERATIONS.md runbook, and the `python-codestyle` skill's Scripts profile naming `format --check` in its CI tooling sentence, with the plugin regenerated. ## Verification `format --check` reports 117 files already formatted, `ruff check`, mypy, and the full unittest suite pass, `test_install.py` passes on the clean tree, and `build_dist.py --check`, `repo_gate.py` (eol, eol-coverage, sha-pin), the nine-rule prose gate, actionlint, and markdownlint are all clean. Every `.py` stays LF. After merge the machines need `python3 scripts/skills_install.py` re-run, since `python-codestyle` changed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary The `host-setup/windows/` directory carries a README documenting the Windows-specific nuances, while `host-setup/linux/` carries none, and the top-level `host-setup/README.md` stays platform-agnostic by design. This adds the missing Linux counterpart. The new `host-setup/linux/README.md` documents the decisions the four Linux scripts embody: - The three kinds of source (distro apt, upstream apt repository, released binary) and why each tool uses the one it does, plus the keyring-proved-not-trusted `gpgv` check and sha256 verification. - PATH shadowing in both directions, including the deliberately shadowed distro `jq` and the dpkg-owned-file exception. - Report semantics: the stale-apt-cache caveat, what `unmanaged` means here versus on Windows, docker read from the CLI, and failures-collected versus refusals-fatal. - The docker-on-WSL skip, `node` displacing distro packages, and the dotnet feed-mixing rule. - Release-upgrade guards (Proxmox refusal, one release at a time, the Debian codename rewrite scope, sources backup), WSL restart handling, the `setup-github.sh` browser gates and read-only `--status`, and the `install-skills.sh` fetchability exception. - A Verification section separating read-only commands, dry runs, and the two guards that refuse under `--dry-run`. The comparison table stays in the Windows README, referenced rather than duplicated, so the two columns cannot drift. `cspell.json` gains the new technical terms the README uses. ## Verification - `prose_lint.py` clean on the default checks, and the opt-in `sentence-length` check reports zero findings. - `markdownlint-cli2`, `cspell`, and `editorconfig-checker` clean. - `scripts/tests/test_bootstrap.py`, `scripts/tests/test_host_gate.py`, and `repo_gate.py` pass. - Every reference-style link target verified to exist on `develop`, including the relocated `scripts/tests/test_bootstrap.py`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Why Closes the root cause behind #706: an agent on a Windows host concluded commit signing was broken and stopped work, based on `git config --get commit.gpgsign && ssh-add -L`. That check tests one specific delivery path (an agent holding the key). `gpg.format=ssh` can sign straight from a key file with no `ssh-agent` running at all -- this host's actual setup -- so the probe failed while signing itself worked cleanly the whole time. A follow-up comment on the same issue chased a second red herring: mixed GPG/SSH signatures in `develop`'s history, read as evidence of a still-misconfigured fleet host. Investigating it here found no such host -- every GPG-signed commit on `develop` is a squash-merge, committed and signed by `GitHub <noreply@github.com>` itself, server-side, regardless of what the PR author signed with locally. That's structural on every fleet repo, not drift. ## What In `.agents/skills/git-commit-conventions/SKILL.md` (`.claude-plugin/` regenerated via `scripts/build_dist.py`): - Bans agent-liveness probes (`ssh-add -L`, a `gpg-agent` check) as an invalid signing test, and states why. - Replaces it with a real scratch commit read back via git's own `%G?` verdict character -- tech-agnostic across SSH agent-backed, SSH key-file, GPG agent-backed, and GPG keyring signing, and immune to git version/locale text differences that a "Good" grep would not be. - The same scratch commit's `%ae` now backs the identity check too, so one probe verifies both signing and identity instead of trusting `git config` values that don't prove what lands on the commit object. - Documents the GitHub-committer/GPG-at-every-squash-merge pattern so an agent checks `commit.committer.name` before treating a differing signature type in history as a clue worth chasing. Verified the new probe directly on this host (`sig=G email=ptr727@users.noreply.github.com`) before committing with it. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds the rule from #712: an agent must not claim platform-specific code (PowerShell on Windows, a macOS-only `mktemp`/`ssh-agent` behavior, a WSL-specific path quirk) works, is verified, or is fixed unless it actually executed that code on that platform. Reasoning by structural analogy to an already-tested equivalent on a different platform is a plausible first pass, not verification, and has to be reported as exactly that. Three spots, matching the existing pattern where GOVERNANCE.md keeps the canonical rule text and the skills excerpt or reference it at their decision moment: - `GOVERNANCE.md` "Verification Discipline": the full rule, alongside the other green-but-wrong checks in that section. - `.agents/skills/agent-conduct/SKILL.md` "Before Claiming Done": the same rule, abbreviated to match its sibling bullets there. - `.agents/skills/pr-review-conduct/SKILL.md` outcome 1 ("Real, so fix it"): a caveat that a platform-specific review finding is not closed by a SHA alone, since that is the exact moment the incident behind #712 happened (round 4 of PR #708's review loop). Per "Issue-closing keywords" in "Branching Model," the closing keyword belongs on the `develop -> main` promotion PR, not here. This references #712 rather than closing it. Local gates run: `prose_lint.py` (full check set, diff-scoped), `repo_gate.py`, `unittest discover -s scripts/tests` (665 tests), `spec/validate.py`. All green.
…-worktree Skill (#717) ## What this does One consolidated update covering #711 and #714, per the maintainer's direction that the two land together rather than as scattered rules. It includes the `fix-714-branch-verification` branch (all of PR #716 plus its unpushed Copilot nit-fix commit), so **this PR supersedes #716**, which can be closed unmerged. ### The worktree mandate (#711) - **GOVERNANCE.md "Repository Boundaries and Write Safety"** gains the affirmative task-start rule: every task, a continuation of a prior session's task included, isolates into its own git worktree before its first file edit, based on the branch work starts on per "Branching Model". The primary checkout is the maintainer's own surface. - **New `repo-worktree` skill** (kept-authority shape: GOVERNANCE keeps the law, the skill is the task-start surface) carrying the mandate's trigger moments plus the mechanics as its own content: base-branch choice, the #699 layout convention (`~/repos/<Repo>`, `~/repos/worktrees/<Repo>-<task-slug>`), the `git worktree add` + `EnterWorktree path:` sequence, listing, and post-merge cleanup. The physical migration of existing checkouts stays tracked in #699. - **`git-commit-conventions` tightened**: stage by explicit path, never `git add -A` or `git add .`, and the pre-commit `git status` check generalizes from "the maintainer's own uncommitted edits" to "any change this session did not make", with ask-or-stop as the response, never bundling. The GOVERNANCE "Git and Commit Rules" summary matches. - **Host-wide agent-safety block** (`host-setup/agent-safety/claude-md-safety.md`) gains the same task-start bullet, naming "worktree" in so many words so Claude Code's `EnterWorktree` tool is armed even in sessions no `AGENTS.md` reaches. Machines pick it up on their next installer re-run (#365 cadence). - **AGENTS.md map** annotates the boundaries row with the new skill and updates the no-skill-by-decision paragraph and the closing skill inventory. ### The base-branch rule (#714, from PR #716) Carried in unchanged apart from consolidation cross-links: GitHub's default-branch setting reads `main` but work starts on `develop`, so a worktree defaulted to "the default branch" silently misses in-flight content, and a "does not exist" claim names the branch it was checked against. ## Verification - `build_dist.py --check` clean (both skill trees regenerated and committed together) - `prose_lint.py`, `repo_gate.py`, `host_gate.py` all pass - `test_build_dist.py` (19), `test_prose_lint.py` (244), `test_repo_gate.py` (70), `test_host_gate.py` (72), `test_skills_install.py` (29), `host-setup/agent-safety/test_install.py` (45), `spec/validate.py` all pass - Signed-commit probe run before committing (sig=G, noreply identity on author and committer) Closes #711. Closes #714. Supersedes #716. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR promotes a large batch of previously-squashed changes from develop into the canonical branch, primarily landing the fleet Skills system (source + generated distribution + installer), expanded host setup automation (Windows + Linux), and strengthened Python/CI gating and documentation.
Changes:
- Add fleet Skills source tree (
.agents/skills/), generated Claude plugin distribution (.claude-plugin/), and cross-platform installers/wrappers. - Extend host bootstrap/setup flows to install Skills (new host-setup install scripts + bootstrap integration) and formalize tool-floor “remedy” commands in the host-tools spec/schema.
- Update governance/docs and operational runbooks (WORKFLOW/AUDIT/README/OPERATIONS) to reflect the new guarantees, diagrams, and CI/tooling expectations.
Reviewed changes
Copilot reviewed 91 out of 115 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
WORKFLOW.md |
Updates mermaid diagrams/labels to reference D-guarantees and clarify workflow contract seams. |
spec/project-types.json |
Refines Python coverage check wording for lint-only vs build profiles. |
spec/host-tools.schema.json |
Requires remedy alongside source for any declared version floor in host-tools entries. |
spec/host-tools-local.schema.json |
Extends overlay schema to allow a remedy object in repo-local overrides. |
spec/fidelity-model.md |
Clarifies intent fidelity behavior (presence + staleness advisory) in the audit model. |
spec/host-tools.json |
Adds per-platform “remedy” commands for floored tools so below-floor findings are self-fixing. |
scripts/skills_install.py |
(Referenced by wrappers) Acts as the single cross-platform Skills installer entry point. |
scripts/skills_install.sh |
Adds a POSIX wrapper that locates Python 3.7+ and runs skills_install.py. |
scripts/skills_install.ps1 |
Adds a Windows wrapper that probes Python launchers and runs skills_install.py. |
scripts/test_bootstrap.py |
Removes the old location of bootstrap tests (moved under scripts/tests/). |
scripts/tests/test_bootstrap.py |
Updates/bootstrap-tests to cover Linux + Windows bootstraps and validate remedy coverage. |
README.md |
Adds an “Install the Fleet Skills” section and entry-point diagram/linking to the fleet map. |
OPERATIONS.md |
Updates “Run the gates the way CI runs them” to include Python tooling + coverage and PSScriptAnalyzer invocation. |
PSScriptAnalyzerSettings.psd1 |
Adds repo settings for PSScriptAnalyzer with documented exclusions. |
host-setup/bootstrap.sh |
Adds a --skills mode and installs Skills as part of the --host flow; reports Skills state. |
host-setup/linux/install-skills.sh |
Adds Linux host-setup entry point to drive the Skills installer from a fetched tree. |
host-setup/windows/install-skills.ps1 |
Adds Windows host-setup entry point to drive the Skills installer from a fetched tree. |
host-setup/agent-safety/README.md |
Updates safety-doc wording to include the “isolate into worktree before first edit” rule. |
host-setup/agent-safety/claude-md-safety.md |
Adds an explicit worktree mandate bullet to the host-wide safety block content. |
docs/peer-messaging.md |
Adds hub-only documentation for local peer messaging between agent sessions. |
cspell.json |
Adds newly-used technical terms to the wordlist. |
AUDIT.md |
Adds/updates mermaid procedure diagrams and clarifies audit verdict/staleness semantics. |
.gitattributes |
Updates LF pinning for moved Python tests and newly-added scripts (build/install/test surfaces). |
.editorconfig |
Expands LF pinning to cover Python under scripts/tests/ via a recursive scripts/**.py rule. |
.claude-plugin/marketplace.json |
Adds Claude marketplace metadata for the generated fleet-skills plugin distribution. |
.claude-plugin/fleet-skills/.claude-plugin/plugin.json |
Declares the generated plugin and skill list for Claude Code. |
.claude-plugin/fleet-skills/.source-digest |
Tracks the generated distribution digest for change detection. |
.claude-plugin/fleet-skills/skills/* |
Adds the generated Claude-plugin copies of the Skills (mirrors .agents/skills/). |
.agents/skills/README.md |
Introduces documentation for the Skills source tree and the source-vs-generated split. |
.agents/skills/* |
Adds the canonical Skills source tree used by Codex/opencode and to generate .claude-plugin/. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This was referenced Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thirty-one squashes,
56f4d7d..d54862a. 115 files, +20436/-5298.Merge with a merge commit, never a squash, and never with
--delete-branch. This pull request's head isdevelopitself.What lands
Fleet Skills. The
.agents/skills/source tree, the generated.claude-plugin/distribution,scripts/build_dist.pywith its--checkgate, andscripts/skills_install.pywith its host stamp (#676). Packaged as skills on top of the scaffold: PR review conduct and Copilot instructions upkeep (#677), comment and doc style (#678), resync-a-repo and fleet-conformance-check (#679), the per-language codestyles (#680), git commit conventions and operational vs release workflow (#681), stand up a repo (#683), and repo-worktree (#717). Coverage gaps closed in three passes (#690, #691, #692) plus the P4 sentence-length opt-in (#697).Host setup. The Windows host-setup tooling and its PowerShell gate (#674), the Windows bootstrap loader (#682), Docker install and upgrade on Linux and Windows with a version floor (#701, #705), a
uvfloor inspec/host-tools.json(#698), self-healing of a shadowinguv,jq, orgit-restore-mtimecopy (#689), node's real winget package id (#696), and a README for the Linux host-setup nuances (#710).Python and CI. Python tooling in CI with the script tests moved to
scripts/tests(#704),ruff formatadopted and gated (#709), and the PSScriptAnalyzer claim conditioned on repos that carry.ps1files (#686).Conduct rules. Triage-order and scope guardrails in pr-review-conduct (#684),
pr_review.py waitrequesting a review rather than only polling for one (#685), a tech-agnostic signed-commit verification (#708), execution rather than analogy to verify platform-specific code (#715), and a unique worktree for every task (#717).Docs. The fleet map and gap register with peer messaging declared (#687), mermaid flow diagrams in the kept-authority docs (#702), and the map pointed at the shipped diagrams and current tooling (#703).
Issues this promotion closes
Each landed on
developon its own pull request. The keyword fires only on a merge intomain, so it sits here rather than on the feature pull requests.Closes #700
Closes #707
Closes #711
Closes #712
Closes #714
Closes #688
#699 stays open on purpose: #717 shipped the layout convention and the skill, and the physical migration of existing checkouts is still tracked there.
Review record
Every squash closed its own Copilot loop on its own pull request before merging to
develop. This promotion carries no new content of its own, so its review is the merged tree as a whole.Consequence worth stating
The
GOVERNANCE.mdandAGENTS.mdsections these squashes changed become the canonical the moment this reachesmain, and every carrying repository reads as drifted from that point until it resyncs. That is the ordinary consequence of a canonical moving rather than a defect. The Skills installer added here is also how a machine picks the new skills up, so a session that keeps restating a rule already packaged as a skill is the signal to run it.