Skip to content

Scaffold fleet Skills: agents/skills source, dist build, installer - #676

Merged
ptr727 merged 24 commits into
developfrom
feature/agent-skills-phase0
Aug 13, 2026
Merged

Scaffold fleet Skills: agents/skills source, dist build, installer#676
ptr727 merged 24 commits into
developfrom
feature/agent-skills-phase0

Conversation

@ptr727

@ptr727 ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

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

ptr727 and others added 2 commits August 12, 2026 18:05
Phase 0 of converting 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.

.agents/skills/ is the one hand-authored source, read natively by
Codex and opencode. Claude Code needs a bridge: build_dist.py
generates a Claude-plugin-compatible copy at .claude-plugin/fleet-skills/,
published through .claude-plugin/marketplace.json. skills_install.py
(with skills_install.sh/.ps1 thin wrappers, mirroring
host-setup/agent-safety/install.py's shape) materializes the global
Codex/opencode skills directory and registers the marketplace with
Claude Code via the real `claude plugin marketplace add`/`install` CLI.

No skills exist yet; this only builds the carrying machinery. AGENTS.md
gains a pointer in "Where the Rules Live" naming the dependency and how
to install it.

Also logs a TODO.md cluster for defaulting `.py` to LF fleet-wide,
found while pinning the two new scripts individually: the per-path
pin list this session added four more lines to is the divergence
outweighing the reason CRLF was chosen as the default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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. 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.

Verified live: prompted a session with the skill loaded
(`claude --plugin-dir`) to blindly copy a canonical AGENTS.md over a
scratch repo's AGENTS.md carrying a local addition. The skill
triggered, identified the local section, refused the overwrite, and
proposed relocating the content before carrying the rest, exactly the
procedure it specifies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 13, 2026 01:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Scaffolds a fleet Skills distribution and install mechanism so agent governance rules can be delivered as Skills across Claude Code, Codex, and opencode, starting with a carried-instruction-file guard skill.

Changes:

  • Adds a canonical Skills source tree under .agents/skills/ and a generated Claude plugin distribution under .claude-plugin/fleet-skills/, with scripts/build_dist.py to regenerate and --check staleness detection.
  • Adds a cross-platform installer (scripts/skills_install.py plus .sh/.ps1 wrappers) with --report staleness reporting and Claude marketplace registration via the claude CLI.
  • Updates documentation and pointers (AGENTS + scripts/README) and pins LF line endings for the new Python scripts.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
TODO.md Adds a backlog cluster entry about potentially pinning *.py to LF fleet-wide.
scripts/build_dist.py Implements generator for .claude-plugin/fleet-skills/ plus --check staleness detection.
scripts/test_build_dist.py Unit tests for build_dist.py regeneration and staleness behavior.
scripts/skills_install.py Implements cross-platform installer and --report staleness reporting.
scripts/test_skills_install.py Unit tests for skills_install.py materialization and report behavior.
scripts/skills_install.sh Bash wrapper to run the installer with Python 3.
scripts/skills_install.ps1 PowerShell wrapper to run the installer with Python 3 on Windows.
scripts/README.md Documents the new generator and installer scripts and their read-only modes.
AGENTS.md Adds a pointer explaining that some rules are packaged as Skills and how to install/report.
.gitattributes Pins LF for the newly added Python scripts and their tests.
.claude-plugin/marketplace.json Adds Claude marketplace manifest for the local plugin source.
.claude-plugin/fleet-skills/.claude-plugin/plugin.json Adds generated Claude plugin manifest listing the packaged skills.
.claude-plugin/fleet-skills/.source-digest Adds generated source digest stamp used for staleness detection.
.claude-plugin/fleet-skills/skills/carried-instruction-file-guard/SKILL.md Adds generated copy of the carried instruction file guard skill.
.agents/skills/README.md Documents .agents/skills/ as the canonical, hand-authored Skills source.
.agents/skills/carried-instruction-file-guard/SKILL.md Adds the canonical carried instruction file guard skill content.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/build_dist.py Outdated
Comment thread scripts/skills_install.py
Comment thread scripts/skills_install.py Outdated
Comment thread scripts/skills_install.py Outdated
…string

- source_digest(): hash .as_posix() instead of str(Path), so the digest
  agrees across OSes over identical bytes instead of differing by path
  separator.
- source_ref(): pass a repo-relative pathspec to `git status --porcelain`
  instead of an absolute path, which under `git -C <repo>` silently and
  permanently reported dirty=False.
- materialize_global_skills(): create the target's parent directory
  before copytree, and handle the target already being a stray file or
  symlink instead of only a directory.
- skills_install.py docstring: fix the stale "dist/claude/" reference
  left over from the .claude-plugin/fleet-skills/ rename.

Adds regression tests for the digest separator and the missing-parent
case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 13, 2026 01:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (6)

.agents/skills/README.md:10

  • This paragraph is hard-wrapped. Per GOVERNANCE.md "Documentation Style Conventions" (Markdown) use one logical paragraph per line with no hard-wrap line-length limit.
Codex and opencode read this directory directly (`.agents/skills/<name>/SKILL.md`), no install
step required, walking from a downstream repo's working directory up to its own repository root.
Claude Code does not scan this path. `scripts/build_dist.py` generates a Claude-plugin-compatible
copy at `.claude-plugin/fleet-skills/`, published through `.claude-plugin/marketplace.json`.

.agents/skills/README.md:13

  • This paragraph is hard-wrapped. Per GOVERNANCE.md "Documentation Style Conventions" (Markdown) use one logical paragraph per line with no hard-wrap line-length limit.
Empty today: this is Phase 0 scaffolding. See `AGENTS.md` for how a repo depends on these skills
and `scripts/README.md` for `build_dist.py` and the installer.

.agents/skills/README.md:5

  • This paragraph is hard-wrapped. Per GOVERNANCE.md "Documentation Style Conventions" (Markdown) use one logical paragraph per line with no hard-wrap line-length limit.

This issue also appears in the following locations of the same file:

  • line 7
  • line 12
Canonical source for the fleet's Claude Code / opencode / Codex Skills, one directory per skill:
`<name>/SKILL.md` plus optional `scripts/` and `references/`. This is the only place a skill's
content is hand-authored. Everything else derived from it is generated, never hand-edited.

AGENTS.md:96

  • The new paragraph is hard-wrapped across multiple lines. Per GOVERNANCE.md "Documentation Style Conventions" (Markdown) use one logical paragraph per line with no hard-wrap line-length limit.
Some of the rules above are also packaged as Claude Code / opencode / Codex Skills, hand-authored
at [`.agents/skills/`](./.agents/skills/), so they surface automatically instead of needing to be
re-read every session. Run `scripts/skills_install.sh` (or `.ps1` on Windows) once per machine to
install them. `scripts/skills_install.py --report` says whether this machine is current. A rule
that keeps needing to be restated is a sign the install is missing or stale, not that the rule
does not exist.

scripts/skills_install.py:167

  • If claude is available but marketplace registration / plugin install fails, the script still exits 0, which makes automation think the full install succeeded. Treat a claude install failure as a non-zero exit (while still writing the stamp) so callers can detect and handle it.
    claude_registered = False
    if claude_available():
        claude_registered = register_claude_marketplace()
    else:
        print("`claude` not found on PATH, skipping Claude Code marketplace registration "
              "(Codex/opencode global skills were still installed).", file=sys.stderr)

    home.mkdir(parents=True, exist_ok=True)
    stamp_path.write_text(json.dumps(build_stamp(claude_registered), indent=2) + "\n", encoding="utf-8")
    print(f"Installed to {home / 'skills'}. Claude Code marketplace registered: {claude_registered}.")
    return 0

.agents/skills/carried-instruction-file-guard/SKILL.md:23

  • This Markdown file uses hard-wrapped prose (sentences/paragraphs split across multiple lines). Per GOVERNANCE.md "Documentation Style Conventions" (Markdown), use one logical paragraph per line with no hard-wrap line-length limit. Since this is the canonical skill source, please reflow the whole document and then regenerate the .claude-plugin/fleet-skills/ copy from it.
A downstream repo's `AGENTS.md`/`GOVERNANCE.md`/`CODESTYLE.md`/`WORKFLOW.md` can hold two different
kinds of content mixed in one file: sections that are stale copies of the hub's fleet-wide rules,
and local rules the repo wrote for a fault the fleet has never seen elsewhere. Re-vendoring the
hub's canonical version over the whole file deletes the second kind silently, because nothing
about the diff looks wrong. This has actually happened: a resync replaced a repo's `AGENTS.md`

GOVERNANCE.md "Documentation Style Conventions" (Markdown) requires
one logical paragraph per line, no hard-wrap line-length limit. Three
files this branch authored wrapped at ~95-100 chars instead:
.agents/skills/README.md, the new AGENTS.md pointer paragraph, and the
whole of carried-instruction-file-guard/SKILL.md. Reflowed all three
to single-line paragraphs and regenerated the dist copy.

skills_install.py's main() also exited 0 even when `claude` was
present but marketplace registration failed, which would make a
scripted caller think a real failure was a clean install. It now
distinguishes that from the expected case of `claude` simply not
being on PATH (a partial-but-fine install for a Codex/opencode-only
machine): only the former is a real failure and exits 1. Adds 3 tests
for the distinction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 13, 2026 01:29
@ptr727

ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Addressing the 6 suppressed findings from the round-2 review, fixed in dfdc428:

This paragraph is hard-wrapped. Per GOVERNANCE.md "Documentation Style Conventions" (Markdown) use one logical paragraph per line with no hard-wrap line-length limit.

Correct on all 4 locations (.agents/skills/README.md x3, AGENTS.md:96) plus the whole of carried-instruction-file-guard/SKILL.md you flagged separately below. All four files reflowed to single-line paragraphs; the .claude-plugin/fleet-skills/ copy regenerated from the reflowed source.

If claude is available but marketplace registration / plugin install fails, the script still exits 0, which makes automation think the full install succeeded. Treat a claude install failure as a non-zero exit (while still writing the stamp) so callers can detect and handle it.

Correct. main() now distinguishes claude simply not being on PATH (a partial-but-expected install for a Codex/opencode-only machine, still exits 0) from claude present but registration actually failing (a real failure, now exits 1, stamp still written either way). Added 3 tests covering all three paths.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (4)

scripts/skills_install.py:24

  • The Usage examples omit the scripts/ prefix even though this file lives under scripts/. This is inconsistent with AGENTS.md (which points to scripts/skills_install.py --report) and can confuse users running from repo root.
Usage: python3 skills_install.py            (installs)
       python3 skills_install.py --report   (read-only: is this machine current?)
       AGENTS_HOME=/x python3 skills_install.py   (override the global skills target, for testing)

scripts/skills_install.py:15

  • The docstring refers to wrapper names install.sh / install.ps1, but the wrappers added in this PR are skills_install.sh / skills_install.ps1. Keeping these names accurate avoids sending users to non-existent files.

This issue also appears on line 22 of the same file.

Both wrappers (install.sh, install.ps1) call this, so every OS runs one tested code path.

scripts/skills_install.sh:6

  • The wrapper's invocation examples assume running from within scripts/ (e.g., ./skills_install.sh), but AGENTS.md instructs running scripts/skills_install.sh from repo root. Aligning these examples avoids confusion.
#   ./skills_install.sh              installs the fleet skills for this user
#   AGENTS_HOME=/x ./skills_install.sh   overrides the target (testing)

scripts/skills_install.ps1:5

  • The PowerShell wrapper's usage examples assume the current directory is scripts/ (e.g., .\skills_install.ps1), but AGENTS.md instructs running it via scripts/skills_install.ps1 from repo root. Aligning the examples avoids confusion.
#   .\skills_install.ps1
#   $env:AGENTS_HOME = "C:\path"; .\skills_install.ps1   # override the target (testing)

skills_install.py's docstring still said install.sh/install.ps1 (the
agent-safety pair's names) instead of skills_install.sh/skills_install.ps1.
All three scripts' usage examples also assumed running from inside
scripts/ (./skills_install.sh) rather than from repo root
(scripts/skills_install.sh), which is what AGENTS.md actually
instructs. Labels the bare commands ("Run:"/"Or:") rather than
capitalizing the path they open on, matching the existing
git-config-example precedent in .gitattributes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ptr727

ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Addressing the 4 new suppressed findings from round 3, fixed in 3c0a66e:

The Usage examples omit the scripts/ prefix even though this file lives under scripts/. This is inconsistent with AGENTS.md (which points to scripts/skills_install.py --report) and can confuse users running from repo root.

Correct, fixed in the module docstring.

The docstring refers to wrapper names install.sh / install.ps1, but the wrappers added in this PR are skills_install.sh / skills_install.ps1.

Correct, a leftover from copying the host-setup/agent-safety/install.py shape without renaming the reference. Fixed.

The wrapper's invocation examples assume running from within scripts/ ... but AGENTS.md instructs running scripts/skills_install.sh from repo root.

Correct for both skills_install.sh and skills_install.ps1. Fixed, and labeled the bare commands ("Run:"/"Or:") rather than capitalizing the path they open on, since a path is not something a comment-casing fix can capitalize without breaking it — matching the existing .gitattributes "Configure with: git config ..." precedent.

(The 6 earlier-round suppressed findings shown again in this round's digest were already answered in the previous comment on this PR.)

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings August 13, 2026 01:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Suppressed comments (3)

scripts/test_build_dist.py:27

  • The test uses a dist/claude/... path even though this PR intentionally moved the generated output under .claude-plugin/ to avoid the repo's dist/ meaning. Using a matching temp path makes the test align with the documented layout and avoids reintroducing the old location in future edits.
        self.tmp = Path(self.enterContext(__import__("tempfile").TemporaryDirectory()))
        self.skills_src = self.tmp / ".agents" / "skills"
        self.dist_plugin = self.tmp / "dist" / "claude" / "fleet-skills"
        self.addCleanup(self._restore, build_dist.SKILLS_SRC, build_dist.DIST_PLUGIN,
                         build_dist.PLUGIN_MANIFEST, build_dist.DIGEST_STAMP)
        build_dist.SKILLS_SRC = self.skills_src
        build_dist.DIST_PLUGIN = self.dist_plugin
        build_dist.PLUGIN_MANIFEST = self.dist_plugin / ".claude-plugin" / "plugin.json"
        build_dist.DIGEST_STAMP = self.dist_plugin / ".source-digest"

scripts/build_dist.py:89

  • is_stale() only checks .source-digest. If the plugin manifest or skills/ directory is missing/corrupted but the digest stamp remains, --check can incorrectly report the dist as current. Treat missing DIST_PLUGIN, plugin.json, or skills/ as stale.
def is_stale():
    """Whether the generated plugin needs regenerating: missing, or built from different source bytes."""
    if not DIGEST_STAMP.is_file():
        return True
    return DIGEST_STAMP.read_text(encoding="utf-8").strip() != source_digest(skill_names())

scripts/skills_install.py:141

  • report() will currently crash on an unreadable/invalid JSON stamp, and it reports "current" purely on commit equality even when either the stamp or this checkout is marked dirty. Handling decode/IO errors and treating dirty sources as stale makes --report reliable for scripting.
    stamp = json.loads(stamp_path.read_text(encoding="utf-8"))
    current = source_ref()
    stale = stamp.get("source", {}).get("commit") != current.get("commit")
    print(json.dumps({"stamp": stamp, "currentCommit": current.get("commit"), "stale": stale}, indent=2))
    return 1 if stale else 0

Comment thread scripts/test_skills_install.py
- test_skills_install.py: mock.patch.object on skills_install's "print"
  attribute raised AttributeError, since the module never defines one
  (print resolves from builtins). Patches builtins.print instead.
- build_dist.py is_stale(): now also treats a missing plugin.json or a
  missing generated SKILL.md as stale, not only a missing or mismatched
  digest stamp, since a stamp surviving a partial deletion previously
  reported current over a plugin that no longer actually resolves.
- skills_install.py report(): catches a corrupt/unreadable stamp
  instead of crashing, and treats a dirty checkout as stale even when
  the commit matches, since a caller trusting "current" there would be
  trusting bytes that were never actually installed.
- test_build_dist.py: renamed a leftover dist/claude test path to
  .claude-plugin, matching the actual generated layout.

6 new tests covering all four fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 13, 2026 01:44
@ptr727

ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Addressing the 3 new suppressed findings from round 4, fixed in a4eb06a:

The test uses a dist/claude/... path even though this PR intentionally moved the generated output under .claude-plugin/ to avoid the repo's dist/ meaning.

Correct, a leftover variable-name mismatch from the rename. Renamed to .claude-plugin.

is_stale() only checks .source-digest. If the plugin manifest or skills/ directory is missing/corrupted but the digest stamp remains, --check can incorrectly report the dist as current.

Correct. Now also checks that plugin.json and each skill's generated SKILL.md actually exist, not only the digest stamp. Added 2 regression tests (deleted manifest, deleted generated skill).

report() will currently crash on an unreadable/invalid JSON stamp, and it reports "current" purely on commit equality even when either the stamp or this checkout is marked dirty.

Correct on both counts. report() now catches a decode/read error and reports stale rather than crashing, and treats a dirty checkout as stale even when the commit matches (trusting "current" there would mean trusting bytes that were never actually installed). Added 2 regression tests.

(The 10 earlier-round suppressed findings shown again in this round's digest were already answered in the previous comments on this PR.)

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Suppressed comments (4)

.agents/skills/README.md:7

  • README claims the directory is "Empty today" but this PR adds at least carried-instruction-file-guard, so the description is now inaccurate.
Empty today: this is Phase 0 scaffolding. See `AGENTS.md` for how a repo depends on these skills and `scripts/README.md` for `build_dist.py` and the installer.

AGENTS.md:91

  • This instructs running scripts/skills_install.sh from the current repo, but scripts/ is explicitly hub-hosted and "never carried" (scripts/README.md), so downstream repos that carry AGENTS.md typically won't have this path. The instruction should say to run the installer from a ProjectTemplate hub checkout.
Some of the rules above are also packaged as Claude Code / opencode / Codex Skills, hand-authored at [`.agents/skills/`](./.agents/skills/), so they surface automatically instead of needing to be re-read every session. Run `scripts/skills_install.sh` (or `.ps1` on Windows) once per machine to install them. `scripts/skills_install.py --report` says whether this machine is current. A rule that keeps needing to be restated is a sign the install is missing or stale, not that the rule does not exist.

scripts/build_dist.py:75

  • regenerate() unconditionally calls shutil.rmtree(DIST_PLUGIN) when the path exists. If a stray file or symlink exists at .claude-plugin/fleet-skills, this will raise (NotADirectoryError) instead of regenerating.
    if DIST_PLUGIN.exists():
        shutil.rmtree(DIST_PLUGIN)

scripts/build_dist.py:89

  • is_stale() docstring says it "checks the manifest" beyond the digest stamp, but the implementation only checks that PLUGIN_MANIFEST exists (not its contents). Either validate the manifest contents or adjust the docstring to match what is actually checked.
    """Whether the generated plugin needs regenerating: missing, corrupted, or built from
    different source bytes. Checks the manifest and each skill's directory too, not only the
    digest stamp, since a stamp surviving a partial deletion would otherwise report current over
    a plugin that no longer actually resolves."""

Comment thread scripts/skills_install.py Outdated
- skills_install.py materialize_global_skills(): no longer wipes the
  entire ~/.agents/skills/ target before copying. That directory is a
  shared convention, not this fleet's own, so a blanket rmtree deleted
  any skill a machine had installed from another source as a side
  effect of installing this fleet's skills. Now overlays only this
  fleet's own skill names, leaving everything else in target alone.
  Adds a regression test asserting an unrelated skill survives.
- AGENTS.md: the skills-install pointer instructed running
  scripts/skills_install.sh as if it were carried into every repo, but
  scripts/ is hub-hosted and reached, never carried (scripts/README.md).
  A downstream repo carrying AGENTS.md would not actually have that
  path. Now instructs reaching a hub checkout first, matching the
  existing Hub-Hosted Tooling convention.
- .agents/skills/README.md: dropped the "Empty today" claim, stale
  since carried-instruction-file-guard already exists.
- build_dist.py regenerate(): handles DIST_PLUGIN being a stray file
  or symlink instead of only a directory, which previously raised
  NotADirectoryError from shutil.rmtree.
- build_dist.py is_stale(): also validates the manifest's own "skills"
  list content, not only that plugin.json exists, so a hand-edited or
  corrupted manifest is caught too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 13, 2026 01:52
@ptr727

ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Addressing the 4 new suppressed findings from round 5, fixed in 2805d08:

README claims the directory is "Empty today" but this PR adds at least carried-instruction-file-guard, so the description is now inaccurate.

Correct, dropped the stale claim.

This instructs running scripts/skills_install.sh from the current repo, but scripts/ is explicitly hub-hosted and "never carried" (scripts/README.md), so downstream repos that carry AGENTS.md typically won't have this path. The instruction should say to run the installer from a ProjectTemplate hub checkout.

Correct catch, and an important one — my pointer text contradicted this repo's own existing "reach, don't carry" convention for scripts/. Reworded to instruct reaching a hub checkout first, matching how the rest of Hub-Hosted Tooling is phrased.

regenerate() unconditionally calls shutil.rmtree(DIST_PLUGIN) when the path exists. If a stray file or symlink exists at .claude-plugin/fleet-skills, this will raise (NotADirectoryError) instead of regenerating.

Correct. Now unlinks a stray file/symlink instead of only handling the directory case.

is_stale() docstring says it "checks the manifest" beyond the digest stamp, but the implementation only checks that PLUGIN_MANIFEST exists (not its contents).

Correct — closed the gap rather than just fixing the docstring: is_stale() now parses the manifest and validates its skills list actually matches the current skill set, so a hand-edited or corrupted manifest is caught too, not only a missing one.

(Earlier-round suppressed findings shown again in this round's digest were already answered in previous comments on this PR.)

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (2)

scripts/build_dist.py:83

  • regenerate() writes .source-digest without a trailing newline. This can violate the repo-wide .editorconfig insert_final_newline = true expectation and can make a freshly-regenerated tree fail style/EOL checks.
    DIGEST_STAMP.write_text(source_digest(names), encoding="utf-8")

scripts/skills_install.py:68

  • source_ref() claims to report whether "the tree is dirty", but the current git status --porcelain call is scoped only to .agents/skills. That can report dirty=False even when other repo files affecting the install (e.g. .claude-plugin/marketplace.json / generated plugin content) have local modifications, which makes --report assert "current" over bytes that were never installed from the recorded commit.
    # A repo-relative pathspec, not str(SKILLS_SRC).
    # With `git -C <repo>`, an absolute path can fail to match anything.
    # That silently and permanently reports dirty=False.
    status = git("status", "--porcelain", "--", SKILLS_SRC.relative_to(ROOT).as_posix())
    ref["dirty"] = bool(status)

- build_dist.py: .source-digest now ends with a trailing newline,
  matching this repo's .editorconfig insert_final_newline default.
- skills_install.py source_ref(): the dirty check was scoped only to
  .agents/skills/, missing a modified .claude-plugin/ (marketplace.json
  or generated plugin content), which is also read by the install.
  A change there alone silently reported dirty=False, so --report
  could assert "current" over bytes that were never installed.
  Confirmed live: editing marketplace.json now flips dirty to True,
  which it did not before this fix.

Adds 2 tests: a final-newline assertion, and a mocked git-status call
asserting both watched paths are in the pathspec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 13, 2026 03:44
@ptr727

ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Addressing the round-20 finding, fixed in b8dbd03:

tree_digest() sorts Path objects directly (sorted(skill_dir.rglob('*'))). Path ordering depends on the platform-specific string form (e.g., 'a\b' vs 'a/b'), so two OSes can hash the same files in a different order and produce different digests even though you hash relative paths with .as_posix() afterwards. Sort using the same normalized key you hash (relative_to(root).as_posix()) so the digest is stable across platforms.

Correct, fixed exactly as suggested — sorts by relative_to(root).as_posix() explicitly now, the same key that gets hashed.

No regression test added, flagging honestly rather than keeping one that doesn't prove anything: I wrote a test mocking rglob() to return files in reverse order and asserting the digest was unchanged. It passed against both the pre-fix and post-fix code, because the pre-fix code also calls sorted() on whatever rglob() returns — my mock's reordering was erased by that sorted() call in both versions, so the test never actually exercised the platform-specific divergence this finding is about. On closer look, PosixPath's own comparison already coincides with the .as_posix() string form on this Linux host, so there's no way to construct two paths that sort differently by raw Path vs by posix-string here — the bug is real but only reproducible on Windows. Discarded the test rather than keep one giving false confidence. The fix stands on inspection: sorting by the same key you hash by is strictly more correct regardless of whether I can locally prove the failure mode.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (1)

scripts/build_dist.py:13

  • The PR introduces a staleness check mode (--check) and new self-tests, but the existing CI workflow doesn’t invoke them. For example, .github/workflows/validate-task.yml currently runs scripts/test_prose_lint.py, scripts/test_repo_gate.py, and scripts/test_pr_review.py, but not scripts/test_build_dist.py, scripts/test_skills_install.py, or python3 scripts/build_dist.py --check. Without wiring these into CI, the generated .claude-plugin/fleet-skills/ tree can drift undetected and the new installer/generator behavior won’t be continuously validated.
Usage: python3 scripts/build_dist.py           regenerate the plugin from .agents/skills/
       python3 scripts/build_dist.py --check   read-only: exit 1 if the plugin is stale
"""

test_build_dist.py, test_skills_install.py, and build_dist.py --check
existed but were never invoked by validate-task.yml, so a future
regression in the generator or installer, or a .claude-plugin/fleet-skills/
tree that drifted from .agents/skills/, would go undetected.

Added the two new self-tests to the existing "Run script self-tests
step" alongside the others, and a new "Check fleet skills are current
step" running build_dist.py --check, matching the shape of the
existing "Check repo gates step".

Verified the exact commands CI will run, locally: both test files
pass and --check reports current on this tree.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 13, 2026 03:51
@ptr727

ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Addressing the round-21 finding, fixed in 028fb74:

The PR introduces a staleness check mode (--check) and new self-tests, but the existing CI workflow doesn't invoke them ... Without wiring these into CI, the generated .claude-plugin/fleet-skills/ tree can drift undetected and the new installer/generator behavior won't be continuously validated.

Correct — a real gap, not a nitpick, since it meant every fix earlier in this PR was only ever going to be checked by hand going forward. Added test_build_dist.py and test_skills_install.py to the existing "Run script self-tests step", and a new "Check fleet skills are current step" running build_dist.py --check, matching the shape of the existing "Check repo gates step". Verified locally by running the exact commands CI will run, not just trusting the workflow YAML looks right.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

Suppressed comments (1)

scripts/skills_install.py:221

  • --report can incorrectly claim a machine is current when the installer was run from a dirty checkout and the working tree later becomes clean at the same commit. The stamp already records source.dirty, but stale only checks the current checkout’s dirty bit, not the stamp’s, so a dirty install can be treated as current solely because the commit SHA matches.
    stale = bool(
        current_commit is None
        or stamp_commit != current_commit
        or current.get("dirty")
    )

report() checked whether the current checkout is dirty, but never
read the stamp's own recorded "dirty" flag from install time. A
machine installed from a dirty checkout has bytes on disk that were
never any commit's clean tree. If the working tree later becomes
clean at the same commit (by committing the changes, or by
coincidence), the commit comparison alone would pass and the dirty
install would be reported current, even though nothing about a later
clean state proves what was actually materialized at install time.

stale now also checks stamp_dirty (the stamp's own recorded flag),
alongside the current checkout's.

Verified the new test genuinely catches this before committing the
fix: reverted to the pre-fix code, confirmed it failed (0 != 1, not
just a passing assumption), then restored the fix and confirmed it
passes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 13, 2026 03:56
@ptr727

ptr727 commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Addressing the round-22 finding, fixed in 81f3c44:

--report can incorrectly claim a machine is current when the installer was run from a dirty checkout and the working tree later becomes clean at the same commit. The stamp already records source.dirty, but stale only checks the current checkout's dirty bit, not the stamp's, so a dirty install can be treated as current solely because the commit SHA matches.

Correct. stale now also reads the stamp's own recorded dirty flag, alongside the current checkout's — the two catch different things: the current flag catches a machine that's gone dirty since install, the stamp's flag catches the install itself having happened from a dirty state, which no later clean state can retroactively verify. Verified the new test actually catches this before committing: reverted to the pre-fix code first, confirmed the test failed (0 != 1), then restored the fix and confirmed it passes — not just assumed.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 5982ee0 into develop Aug 13, 2026
7 checks passed
@ptr727
ptr727 deleted the feature/agent-skills-phase0 branch August 13, 2026 04:03
ptr727 added a commit that referenced this pull request Aug 13, 2026
…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)
ptr727 added a commit that referenced this pull request Aug 13, 2026
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>
ptr727 added a commit that referenced this pull request Aug 13, 2026
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>
ptr727 added a commit that referenced this pull request Aug 13, 2026
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>
ptr727 added a commit that referenced this pull request Aug 13, 2026
… 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)
ptr727 added a commit that referenced this pull request Aug 14, 2026
## 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.
ptr727 added a commit that referenced this pull request Aug 15, 2026
…Python CI Gates (#718)

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 is `develop` itself.

## What lands

**Fleet Skills.** The `.agents/skills/` source tree, the generated
`.claude-plugin/` distribution, `scripts/build_dist.py` with its
`--check` gate, and `scripts/skills_install.py` with 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 `uv` floor in
`spec/host-tools.json` (#698), self-healing of a shadowing `uv`, `jq`,
or `git-restore-mtime` copy (#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 format` adopted and gated (#709), and the
PSScriptAnalyzer claim conditioned on repos that carry `.ps1` files
(#686).

**Conduct rules.** Triage-order and scope guardrails in
pr-review-conduct (#684), `pr_review.py wait` requesting 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 `develop` on its own pull request. The keyword fires only
on a merge into `main`, 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.md` and `AGENTS.md` sections these squashes changed
become the canonical the moment this reaches `main`, 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants