Skip to content

feat(skill-install): anc skill install <host> with hardened git clone + build-time host-map codegen - #35

Merged
brettdavies merged 19 commits into
devfrom
feat/skill-install
Apr 30, 2026
Merged

feat(skill-install): anc skill install <host> with hardened git clone + build-time host-map codegen#35
brettdavies merged 19 commits into
devfrom
feat/skill-install

Conversation

@brettdavies

@brettdavies brettdavies commented Apr 30, 2026

Copy link
Copy Markdown
Owner

Summary

Adds anc skill install <host>: a single-verb subcommand that clones the
agentnative-skill bundle into a host's canonical skills directory.
Six hosts at v0.1 (claude_code, codex, cursor, factory, kiro, opencode); the host map is
build-time-generated by build.rs::emit_skill_hosts from src/skill_install/skill.json, so adding or changing a
host is a single-file edit (or bash scripts/sync-skill-fixture.sh) followed by cargo build. No hand-maintained
Rust map.

The verb closes anc's own dogfood loop on P2 (--output {text,json}) and P5 (--dry-run). The git clone invocation
runs with named-const hardening (GIT_HARDEN_FLAGS, GIT_HARDEN_ENV_REMOVE, GIT_HARDEN_ENV_SET; the last includes
GIT_CONFIG_GLOBAL=/dev/null and GIT_CONFIG_SYSTEM=/dev/null to disable user-controlled git config). JSON envelope is
uniform across success and error and across both modes.

Changelog

Added

  • Add anc skill install <host> subcommand to install the agentnative-skill bundle into a host's canonical skills directory. Six hosts: claude_code, codex, cursor, factory, kiro, opencode.
  • Add --dry-run flag (P5): prints the resolved git clone command without spawning. Captures cleanly via eval $(anc skill install --dry-run <host>).
  • Add --output {text,json} flag (P2): JSON envelope is uniform across success and error and across dry-run / live install. Typed reason on error (destination-not-empty, destination-is-file, home-not-set, git-not-found, git-clone-failed).

Documentation

  • Add ## Install the skill section to README with one-line examples per host and the manual git clone fallback for hosts not yet in the binary's map.

Type of Change

  • feat: New feature (non-breaking change which adds functionality)

Related Issues/Stories

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing completed
  • All tests passing

Test Summary:

  • Bin unit tests: 431 pass, 1 ignored
  • tests/skill_install.rs integration: 10 active + 1 #[ignore] (live e2e clone; passes on opt-in run)
  • tests/dogfood.rs: 2 pass (no p2-* / p5-* fail against this repo)
  • tests/integration.rs: 51 pass (existing P1–P7 surface, no regressions)
  • Other test files: 25 pass (build_parser, scorecard schema, etc.)
  • Total: 519 active passing, 2 ignored, 0 failures

Manual smoke (verified before merge):

  • All 6 hosts via --dry-run: each prints the canonical git clone command for its destination.
  • Live install with HOME=/tmp/anc-{claude_code,codex,factory}-smoke anc skill install <host> → exit 0, .git/HEAD written, bundle files present.
  • Re-run on populated destination → envelope status: error, reason: destination-not-empty, exit 1 (R9 honored).
  • HOME unset → envelope reason: home-not-set, exit 1.
  • PATH without git → envelope reason: git-not-found, exit_code absent, exit 1.

Local CI parity (pre-push hook): fmt, clippy -Dwarnings, full test, cargo-deny audit, Windows compat. All green.

Files Modified

Created:

  • src/skill_install.rs: module skeleton, hardening tables, orchestrator, envelope (~830 LOC including tests).
  • src/skill_install/skill.json: vendored copy of agentnative-site/src/data/skill.json; build-time codegen input.
  • build.rs additions: emit_skill_hosts codegen function (parses skill.json, emits $OUT_DIR/generated_hosts.rs).
  • scripts/sync-skill-fixture.sh: remote-first vendor + --check drift mode (mirrors sync-spec.sh).
  • .github/workflows/skill-fixture-drift.yml: CI drift gate (runs --check on every PR).
  • tests/skill_install.rs: 11 integration tests.
  • tests/dogfood.rs: p2/p5 dogfood guards against anc itself (CRITICAL).
  • docs/plans/2026-04-30-001-feat-spec-output-envelope-shoulds-plan.md: follow-up plan.

Modified:

  • src/cli.rs: Commands::Skill { SkillCmd::Install } clap surface.
  • src/main.rs: Commands::Skill dispatch arm.
  • src/error.rs: 6 new AppError variants (MissingHome, GitNotFound, GitCloneFailed{code}, DestIsFile, DestNotEmpty, DestReadFailed).
  • Cargo.toml: tempfile dev-dep, serde_json build-dep.
  • README.md: install section.
  • AGENTS.md, CLAUDE.md: skill-verb sections (codegen architecture, hardening surface).
  • RELEASES.md: pre-release skill-fixture sync step.
  • docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md: body refreshed to as-shipped reality; added two Document Review (... revision, 2026-04-30) subsections capturing the manual-smoke + codegen revisions.

Renamed:

  • tests/fixtures/skill.jsonsrc/skill_install/skill.json (move + content update; tests/ is in cargo exclude, blocking build.rs read).

Key Features

  • Build-time host-map codegen. build.rs::emit_skill_hosts reads src/skill_install/skill.json, validates each install command tokenizes as git clone --depth 1 <url> <dest>, and emits SkillHost enum + KNOWN_HOSTS + resolve_host + host_envelope_str into $OUT_DIR/generated_hosts.rs. cargo:rerun-if-changed invalidates the build cache on JSON edits. Eliminates the Rust↔fixture drift class entirely (the original test 12 was deleted as provably redundant after the refactor).
  • Three named-const hardening tables. GIT_HARDEN_FLAGS (5 -c pairs), GIT_HARDEN_ENV_REMOVE (5 vars), GIT_HARDEN_ENV_SET (3 pairs including GIT_CONFIG_GLOBAL=/dev/null and GIT_CONFIG_SYSTEM=/dev/null, the actual defense against user-config insteadOf rewriting). Two manual-smoke bugs in the eng-review's R6c wording were caught and corrected before merge: protocol.allow=https-only is invalid git syntax (replaced with default-deny + per-protocol-allow); -c url.<repo>.insteadOf= rewrites in the wrong direction (dropped, defense routed through env-set).
  • Uniform JSON envelope. Same shape across success/error and dry-run/install. Option fields use #[serde(skip_serializing_if = "Option::is_none")] so absence is silence, not null. Field-presence rules: would_succeed (dry-run only); exit_code (live install only AND only when git was actually spawned); reason (error only).
  • CI drift gate. .github/workflows/skill-fixture-drift.yml runs scripts/sync-skill-fixture.sh --check on every PR; clones agentnative-site and cmps the live blob against the committed fixture. Drift fails CI loudly, never users silently.
  • Dogfood guards. tests/dogfood.rs runs anc check . --output json on this repo and asserts no fail on p2-* or p5-*. Without these, the dogfood claim that drove the binary-verb-vs-bash-one-liner decision (Plan: Problem Frame § "Why a binary verb, not a bash one-liner?") would break silently.

Benefits

  • Agent-native UX. anc skill install --output json --dry-run claude_code emits a parseable envelope agents can branch on without regex; live install too.
  • Zero-touch host expansion. Adding or removing a host is one JSON edit (or sync-script invocation). No Rust hand-edits, no test counter bumps for the host count, no parallel maintenance surfaces.
  • Defense-in-depth supply-chain hardening. User-controlled git config is fully neutered for the spawned process; ambient insteadOf cannot redirect the clone; HTTPS-only is enforced by default-deny + per-protocol-allow; credential/askpass paths are blocked; terminal prompts disabled.
  • Plan-as-doc trail. The plan body was refreshed to match the as-shipped surface; two Document Review (revision) subsections capture why the manual-smoke and codegen revisions happened, so future readers don't re-litigate or re-introduce the originals.

Breaking Changes

  • No breaking changes

Deployment Notes

  • No special deployment steps required

The pre-release checklist in RELEASES.md gains one step: bash scripts/sync-skill-fixture.sh && git diff src/skill_install/skill.json before tagging, to surface upstream site changes since dev was branched. The Rust map regenerates automatically on the next cargo build.

Checklist

  • Code follows project conventions and style guidelines
  • Commit messages follow Conventional Commits
  • Self-review of code completed
  • Tests added/updated and passing
  • No new warnings or errors introduced
  • Changes are backward compatible (or breaking changes documented)

Additional Context

  • Companion site PR already merged: agentnative-site#53 added factory and kiro to the canonical skill.json. CI's skill-fixture-drift workflow on this PR is green against agentnative-site/dev.
  • Plan history: This PR was driven by docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md. The plan went through /ce-doc-review, /plan-eng-review (SCOPE_REDUCED), Outside-Voice review, two implementation revisions (manual-smoke fix + codegen refactor), and final synthesis. The plan body documents the as-shipped contract; the Document Review sections at the bottom preserve the audit trail of decisions that didn't make it to the final shipped form.
  • Solutions doc: Refreshed architecture-patterns/anc-cli-output-envelope-pattern-2026-04-29.md (in solutions-docs) to fold the manual-smoke + codegen revisions into the canonical convention doc.
  • Frontmatter status flip: plan status: active → completed happens at squash-merge per the ce-work shipping convention.

Adds typed error variants the skill install module will surface:
MissingHome (R6a — $HOME unset), GitNotFound, GitCloneFailed { code },
DestIsFile, DestNotEmpty, DestReadFailed. The first five map directly
to the JSON envelope's typed reason values
(home-not-set/git-not-found/git-clone-failed/destination-is-file/
destination-not-empty); DestReadFailed surfaces metadata-read failures
with path + source io::Error context.

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1).
src/skill_install.rs lands the static surface of the new subcommand:

- SkillHost enum (ClaudeCode/Codex/Cursor/Opencode) with clap ValueEnum
  derive and rename_all = "snake_case" so surface names match
  skill.json keys verbatim.
- KNOWN_HOSTS const exposed at module boundary (R-LIST seed for a
  future `anc skill list` verb and shell-completion enumeration).
- SKILL_REPO_URL const — the canonical upstream URL for the bundle.
- GIT_HARDEN_FLAGS — five `-c key=value` pairs (R6c) defeating
  ambient git config (credential.helper, core.askPass, protocol.allow,
  http.followRedirects, url.<repo>.insteadOf).
- GIT_HARDEN_ENV_REMOVE — seven env vars stripped before spawn (never
  env_clear() — it would strip PATH and break git's helper resolution).
- GIT_TERMINAL_PROMPT_{KEY,VALUE} — *set* on the spawned process so
  git never prompts when credentials are missing.
- resolve_host pure function returning (url, dest_template).
- Module rustdoc carries the ASCII pipeline diagram (Mermaid lives in
  the plan doc only — source comments stay ASCII per repo convention).

Tests 1 and 11 from the plan land in this commit, plus two
defense-in-depth tests: insteadOf= blocker references the same URL
resolve_host uses, and clap value names round-trip to KNOWN_HOSTS.

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.3)
Adds the nested subcommand modeled on Commands::Generate { GenerateKind }:

  Commands::Skill { cmd: SkillCmd }
  SkillCmd::Install { host: SkillHost, dry_run: bool, output: OutputFormat }

The existing global Cli.quiet flag (`global = true`, env
AGENTNATIVE_QUIET) propagates automatically (D3) — no per-subcommand
wiring. main.rs dispatches via run_skill into skill_install::run_install,
which is a stub returning unimplemented!() until U1.6 lands the
pipeline. Clap's required-subcommand semantics give us test 22 for free
(bare `anc skill` prints help + exit 2 — the fork-bomb-safety
invariant).

Manual smoke (verified):
- anc skill            -> help, exit 2
- anc skill install    -> 'required argument <HOST>', exit 2
- anc skill install bogus_host -> 'invalid value', exit 2,
  possible values listed
- anc skill install --help     -> help text including manual git clone
  fallback (F5)

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.2)
Adds the two pure functions that fail-fast before `git clone` runs:

- expand_tilde / expand_tilde_with — D1 passthrough contract. Inputs
  starting with `~` or `~/` resolve against $HOME; non-tilde inputs
  pass through unchanged regardless of $HOME. MissingHome only fires
  when the input actually begins with `~` and $HOME is unset/empty
  — non-tilde inputs never read the environment. Pure helper takes
  home as Option<&str> so tests don't mutate process env (which would
  race with parallel test runs).
- check_destination — R9 conflict check. fs::canonicalize before the
  metadata read defends F4 (symlinked skills directory resolves to its
  real target before the check). Returns Absent / EmptyDir on success;
  errors with DestIsFile / DestNotEmpty / DestReadFailed on conflict.
  TOCTOU between check and clone is acknowledged residual risk;
  `git clone` itself errors on a non-empty target as backstop.
- DestinationStatus enum (Absent / EmptyDir / NonEmptyDir / File) with
  as_envelope_str() for the JSON `destination_status` field. Conflict
  variants are inferred from the AppError variant in the caller.

Tests 2-9 from the plan ship in this commit. Test 9 is unix-only — the
`std::os::unix::fs::symlink` API is not portable. Adds tempfile to
dev-dependencies; already a transitive dev-dep via insta.

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.4)
build_clone_command(url, dest) -> std::process::Command:
- Applies GIT_HARDEN_FLAGS as -c key=value pairs *before* the `clone`
  subcommand — git's required position for top-level -c options.
- Removes every GIT_HARDEN_ENV_REMOVE entry via env_remove.
- Sets GIT_TERMINAL_PROMPT=0 so git never prompts.
- Pure constructor: no spawn, no I/O. Test 10 introspects via
  get_args() / get_envs().

format_clone_command(url, dest) -> String:
- User-visible single-line form for the JSON envelope's `command`
  field and `--dry-run --output text`. Intentionally omits hardening
  flags — implementation detail. Matches skill.json install commands
  verbatim once tildes are expanded, so users can copy-paste-modify
  it as a manual fallback.

Test 10 from the plan ships in this commit, plus a sanity test on
format_clone_command's canonical shape.

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.5)
Lands the orchestrator and the uniform JSON envelope (R-OUT, C1):

InstallEnvelope struct serializes to the schema in the plan, in field
order:

    {action, host, mode, command, destination, destination_status,
     status, would_succeed?, exit_code?, reason?}

skip_serializing_if = "Option::is_none" makes the three Option fields
follow plan field-presence rules:
- would_succeed only on dry-run path
- exit_code only on live install path (and only when we actually
  spawned git; git-not-found leaves it absent)
- reason only on error paths, with typed values from the R-OUT
  taxonomy (destination-not-empty / destination-is-file /
  home-not-set / git-not-found / git-clone-failed)

compute_install_envelope() drives the full pipeline:

    resolve_host -> expand_tilde -> [dry-run? -> emit] ->
    check_destination -> spawn_git_clone

Each step's typed AppError variants map to envelope error cases with
the right typed reason; DestReadFailed propagates as AppError so
main.rs's top-level handler renders it on stderr with exit 2 (P4
internal-error reserved). MissingHome surfaces the unexpanded template
in the envelope's destination field so consumers see what would have
expanded.

emit_result_text returns the single-line `git clone …` form on
dry-run success (suitable for `eval $(anc skill install --dry-run
<host>)`), a short confirmation on live success, and `error: <reason>:
<path>` on failure.

emit_result_json pretty-prints via serde_json::to_string_pretty,
matching the existing anc check --output json style.

Manual smoke (verified):
- HOME=/home/test --dry-run claude_code (text + json)  -> exit 0,
  envelope shape matches plan example.
- HOME=/tmp/anc-smoke (with pre-populated dest) --dry-run    -> exit
  1, status=error, destination_status=non-empty-dir,
  reason=destination-not-empty, would_succeed=false.
- HOME unset --dry-run --output json  -> exit 1, status=error,
  reason=home-not-set, destination shows the literal `~` template.

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.6)
tests/fixtures/skill.json is a verbatim copy of the canonical site
file at agentnative-site/src/data/skill.json. Tracked in git as the
drift anchor between this binary's hardcoded host map and the
authoritative site contract. Test-only — no production code path
reads it.

Test 12 (host_map_matches_site_skill_json) loads the fixture via
include_str!() and asserts each Rust-map (url, dest_template) pair
reconstructs the fixture's install.<host> command verbatim. This is
the cargo-level companion to the script-based CI gate landing in
U1.9 — a Rust-side drift check fails fast in `cargo test` before
push, complementing scripts/sync-skill-fixture.sh --check on every
PR (test 26).

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.7)
Spawns the real binary via assert_cmd::Command; introspects stdout
JSON envelope / stderr / exit code. Tests cover:

- 13/14: dry-run claude_code in text + json modes — single-line clone
  command, success envelope shape (mode=dry-run, would_succeed=true).
- 15: dry-run with regular file at canonical dest — error envelope,
  reason=destination-is-file, would_succeed=false, exit 1.
- 16b: `#[ignore]` end-to-end live clone from upstream. Excluded by
  default; opt in via `cargo test -- --ignored` when vetting a
  release. (16a is unit test 10 in src/skill_install.rs.)
- 17/18: clap surface — unknown host and missing positional both exit
  2 with descriptive messages.
- 19: live install on populated dest — error envelope with
  reason=destination-not-empty and exit_code absent (we never
  spawned).
- 20: HOME unset — reason=home-not-set, destination shows the literal
  `~` template.
- 21: PATH containing no git — reason=git-not-found, exit_code absent
  (Unix only; Windows test infra would need different shape).
- 22: bare `anc skill` exits 2 + prints help — pins the
  fork-bomb-safety invariant from CLAUDE.md ("Bare invocation prints
  help"). Catches the regression where adding the subcommand drops
  arg_required_else_help on the parent.
- 23: table-driven exit-code/reason matrix — pins the P4 exit-code
  contract in one place rather than relying on per-error-path
  assertions scattered across tests 15/19/20/21.

All 10 active integration tests pass; 1 ignored (live clone).

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.8)
scripts/sync-skill-fixture.sh mirrors scripts/sync-spec.sh shape:
remote-first (clones agentnative-site at SKILL_SITE_REF, default
`dev` since the site uses a dev/main forever-branch flow), local
fallback at $HOME/dev/agentnative-site. Two modes:

  scripts/sync-skill-fixture.sh           # update fixture
  scripts/sync-skill-fixture.sh --check   # CI drift gate (test 26)

File-based comparison (cmp) preserves byte-for-byte parity including
trailing newline boundaries — earlier variable-substitution capture
($(git show ...)) silently stripped trailing whitespace and produced
phantom diffs.

.github/workflows/skill-fixture-drift.yml runs the --check on every
PR and on push to main/dev. Pinned to actions/checkout@<v6.0.2 SHA>
per the brettdavies/.github canonical table. Companion to the
cargo-level drift anchor (test 12 — host_map_matches_site_skill_json)
which runs on `cargo test` without network.

Manual smoke (verified):
- bash scripts/sync-skill-fixture.sh --check  -> ok, exit 0
- bash scripts/sync-skill-fixture.sh          -> wrote fixture, no
  diff against committed
- echo DRIFT >> fixture; --check              -> error, diff shown,
  exit 1

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.9)
Tests 24 and 25 from the plan — both CRITICAL. `anc check . --output
json` on this repo must show no `fail` status on p5-* (test 24) or
p2-* (test 25) for the new skill install verb to land.

Without these guards the dogfood claim that drove the binary-verb-
vs-bash-one-liner decision (Problem Frame: "Why a binary verb, not a
bash one-liner?") breaks silently. `anc` ships the agent-native CLI
standard and must score itself on P1-P6; the new `skill install` verb
adds --dry-run (P5) and --output {text,json} (P2) specifically to keep
that claim honest.

Tolerates warn; only fail breaks the guard. Surfaces the failing
check id + evidence for fast triage when a regression lands.

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U1.10)
…AUDE rules

README.md: new `## Install the skill` section with one-line examples
per host, --dry-run inspection, JSON envelope mention, and the manual
`git clone` fallback (F5) so users on a too-old anc see the escape
hatch when the site adds a host between releases.

RELEASES.md: insert step 7 in the dev→main release flow:
`bash scripts/sync-skill-fixture.sh && git diff tests/fixtures/skill.json`
to surface upstream drift before tagging. CI's skill-fixture-drift
workflow already runs --check on every PR — this step pulls the
latest content during release prep so we never ship a release with
the hardcoded host map one revision behind.

CLAUDE.md: new `## Skill Install Verb` section documenting the
hardcoded-map model, the named-const hardening surface
(GIT_HARDEN_FLAGS / GIT_HARDEN_ENV_REMOVE / GIT_TERMINAL_PROMPT=0
*set*), the two-layer drift detection (test 12 cargo-side, --check
script CI-side), and the rules for future changes
(never env_clear(), never sh -c, never reintroduce skill.json
parsing, keep SkillHost/KNOWN_HOSTS/resolve_host in lockstep).

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md (U2)
Pre-merge manual smoke surfaced two bugs in the plan's hardening
surface — both invisible to introspection-only tests because they
only fail when an actual `git` binary parses the args.

1. `protocol.allow=https-only` is **not** valid git syntax. `git`
   rejects it with `fatal: unknown value for config 'protocol.allow':
   https-only`. The HTTPS-only intent is expressed as a default-deny
   plus per-protocol allow:

       -c protocol.allow=never -c protocol.https.allow=always

   This is the documented git-config form for restricting transports.

2. `-c url.<repo>.insteadOf=` (empty value) does the **opposite** of
   blocking. git's `url.<base>.insteadOf=<value>` rewrites URLs
   starting with `<value>` to start with `<base>`. With an empty
   value, every URL matches the empty prefix, so all URLs get
   rewritten to start with `<base>` — doubling the clone URL into
   `<repo><repo>/` which 404s on the remote. The actual defense
   against `insteadOf` URL-rewriting is to disable user-controlled
   git config entirely:

       env: GIT_CONFIG_GLOBAL=/dev/null
            GIT_CONFIG_SYSTEM=/dev/null

   This blocks any `~/.gitconfig` or `/etc/gitconfig` insteadOf
   stanza from firing. `env_remove` was insufficient — git falls
   back to default config paths when the env var is unset.

Restructures the hardening into three named-const tables:

- GIT_HARDEN_FLAGS — five `-c` pairs (drop the broken insteadOf,
  split protocol.allow into deny/allow pair).
- GIT_HARDEN_ENV_REMOVE — five env vars stripped (drop
  GIT_CONFIG_{GLOBAL,SYSTEM} since we now `set` them).
- GIT_HARDEN_ENV_SET — three env vars set (the GIT_CONFIG_* pair plus
  the existing GIT_TERMINAL_PROMPT=0).

Manual smoke (verified against actual git):
- HOME=/tmp/anc-skill-smoke anc skill install claude_code -> exit 0,
  .git/HEAD points at refs/heads/main.
- Rerun -> envelope status=error, reason=destination-not-empty,
  destination_status=non-empty-dir, exit 1 (R9 honored).
- cargo test -- --ignored skill_install -> live e2e clone passes.

Plan: docs/plans/2026-04-29-002-feat-skill-subcommand-plan.md
(amends U1.5/U1.6 hardening surface to match git's actual config
syntax — the plan's R6c wording will need a follow-up note).
Updates the plan body to reflect the as-shipped hardening surface:

- Overview: name GIT_HARDEN_ENV_SET (with the GIT_CONFIG_*=/dev/null
  pair) alongside GIT_HARDEN_FLAGS / GIT_HARDEN_ENV_REMOVE.
- R6c: rewrite to three named-const tables (5 -c pairs / 5 env vars
  removed / 3 env-var pairs set). Note the two corrections vs the
  eng-review wording (protocol.allow=https-only is invalid syntax;
  url.<repo>.insteadOf= rewrites the wrong direction).
- Implementation Units (U1 module shape): add GIT_HARDEN_ENV_SET to
  the const list; build_clone_command now applies all three tables.
- Test 10: introspection test asserts every (key, value) pair in
  GIT_HARDEN_ENV_SET appears in the Command's env-set list.
- Risks table: row reflects new table counts.
- U2 docs note: CLAUDE.md content includes the GIT_HARDEN_ENV_SET
  triple.

Adds a new `Document Review (implementation revision, 2026-04-30)`
subsection capturing *why* each correction was needed, so future
readers don't re-introduce the originals. Verification (cargo test,
ignored e2e, manual smoke) is enumerated. The eng-review verdict and
Plan Rewrite Brief below it are intentionally left unchanged — those
are the audit trail of the SCOPE_REDUCED rewrite at that point in
time; the body above is the authoritative as-shipped contract.

Bumps frontmatter `deepened` to 2026-04-30.
… + kiro)

Two changes that together prep for the build.rs codegen refactor in
the next commit:

1. Move tests/fixtures/skill.json -> src/skill_install/skill.json.
   The fixture is about to become a build input for build.rs, but
   tests/ is in Cargo.toml's exclude list (so it ships nothing to
   crates.io). src/ is not excluded, so the new path is part of the
   cargo package and available at build time. The relocation appears
   as a 'new file' rather than a rename in the diff because git's
   similarity detector sees the simultaneous +2 host content update
   below the threshold; logically it's a move with one content tweak.

2. Re-vendor from agentnative-site/dev (which now contains the
   merged factory + kiro entries from agentnative-site#53). The
   fixture now advertises 6 hosts (was 4); CI's drift gate against
   upstream stays green.

scripts/sync-skill-fixture.sh updates DEST_FILE to the new path and
its leading comment explains the build-input role. The
skill-fixture-drift.yml workflow runs the sync script unchanged —
the path is internal to the script.

Note: the Rust map in src/skill_install.rs still encodes the old
4-host list at this commit, so test 1 / test 11 / test 12 disagree
with the fixture briefly — they would fail on cargo test. The next
commit lands the codegen + replaces the Rust map and resolves all
inconsistency in one motion. This intermediate commit is a
test-failing checkpoint by design.
…nual sync)

Replaces the hand-maintained Rust host map in src/skill_install.rs
with build-time codegen from src/skill_install/skill.json. To add or
change a host, edit the JSON (or run `bash scripts/sync-skill-fixture.sh`)
and `cargo build` regenerates the map. No hand edit possible.

build.rs::emit_skill_hosts:
- Parses the manifest's install map.
- Validates each command tokenizes as exactly
  `git clone --depth 1 <url> <dest>` (six tokens, dest not ending
  .git) — mirrors agentnative-site/src/build/skill.mjs validation
  so the two binaries reject the same malformed inputs.
- Converts each snake_case JSON key to a PascalCase Rust identifier.
- Emits $OUT_DIR/generated_hosts.rs with: SkillHost enum (with clap
  ValueEnum derive + rename_all = "snake_case"), KNOWN_HOSTS const,
  resolve_host fn, host_envelope_str fn.
- Adds cargo:rerun-if-changed=src/skill_install/skill.json so the
  build cache invalidates on any JSON edit.

src/skill_install.rs:
- Drops hand-maintained SkillHost / KNOWN_HOSTS / resolve_host /
  host_envelope_str. Replaces with one include! line.
- Drops SKILL_REPO_URL const. Tests that need it call
  resolve_host(SkillHost::ClaudeCode).0 — single source of truth.
- Test 12 (host_map_matches_site_skill_json) deleted as provably
  redundant: both Rust map and fixture are now single-sourced from
  the same JSON; cargo's rerun-if-changed forces regeneration on
  any drift. A note in mod tests documents the deletion rationale.
- Test 1 now drives off KNOWN_HOSTS so adding a host to skill.json
  automatically extends coverage.

Cargo.toml: serde_json added to [build-dependencies].

CLAUDE.md, RELEASES.md, README.md: updated to describe codegen
architecture and the new fixture path. README install section gets
factory + kiro one-liners.

docs/plans: plan body updated to reflect the as-shipped surface
(build-time-derived map, src/skill_install/skill.json, no test 12).
New `Document Review (codegen refactor, 2026-04-30)` subsection
captures the rationale and verification — eng-review's hand-map
proposal is left unchanged in the audit-trail sections below.

Verification:
- cargo test: 519 pass (one fewer than pre-codegen because test 12
  was deleted), 1 ignored.
- cargo clippy --all-targets -- -Dwarnings: clean.
- bash scripts/sync-skill-fixture.sh --check: ok against
  agentnative-site dev (a2a07b6).
- Manual smoke: HOME=/tmp/anc-factory-smoke anc skill install factory
  -> exit 0, .git/HEAD at ~/.factory/skills/agent-native-cli. Same
  shape verified for the 5 other hosts via --dry-run. `anc skill
  install --help` enumerates all 6 possible values.
U2 from the plan called for an audience-appropriate AGENTS.md update
alongside CLAUDE.md; missed in the original docs commit.

Adds:
- `anc skill install` invocations to the Running anc example block
  (claude_code, codex with --dry-run, factory with --output json).
- Bare `anc skill` exits 2 — extends the fork-bomb guard note.
- New `## Skill install` section: 6-host roster, JSON envelope shape
  with field-presence rules, exit-code convention, hardening surface
  (GIT_HARDEN_FLAGS / GIT_HARDEN_ENV_REMOVE / GIT_HARDEN_ENV_SET), and
  the build.rs codegen architecture (host map auto-generated from
  src/skill_install/skill.json — no hand edits to skill_install.rs).

AGENTS.md voice is a tier terser than CLAUDE.md: enough for an agent
reading this to know what to invoke and what comes back; full
prescriptive rules + rationale stay in CLAUDE.md.
Writes the follow-up plan named in the parent plan's Pattern
Documentation Note: docs/plans/2026-04-30-001-feat-spec-output-
envelope-shoulds-plan.md.

Scope (Modest, 7 implementation units):

- agentnative-spec: 4 new SHOULDs (3 P2, 1 P4) codifying the
  output-envelope contract anc skill install already enforces. Tag
  + version bump.
- agentnative-cli: re-vendor spec, bump drift counters
  (registry_size_matches_spec 46→50; level_counts_match_spec Should
  16→20), add 4 behavioral checks under src/checks/behavioral/, one
  per new SHOULD (SRP-style — 4 distinct check IDs, no bundled
  conformance check), update SUPPRESSION_TABLE
  (output-applies-to-every-subcommand fills the empty FileTraversal
  slot), extend tests/dogfood.rs with per-ID assertions, regenerate
  coverage matrix.

Key decisions captured:

- Behavioral layer for all four checks; error-path elicitation via
  the existing bad-arg injection pattern (bad_args.rs) combined with
  --output json. This combination is genuinely new ground — the
  first time anc probes an error path on a target binary.
- output-applies-to-every-subcommand is Conditional on "CLI uses
  subcommands" (same gate as p6-must-global-flags). Other three
  are Universal.
- output-envelope-schema-uniform is distinct from existing
  p2-should-consistent-envelope: the existing one addresses
  cross-command consistency; the new one addresses success-vs-error
  consistency within a single command.
- p2-must-json-errors says "errors emitted as JSON to stderr" but
  anc's shipped envelope goes to stdout. The new SHOULD permits
  either stream during the reconciliation window; the MUST text
  reconciliation is deferred to a separate spec PR.

Parent plan updated: the Pattern Documentation Note now lists the
two follow-ups (this spec-SHOULDs plan = done; envelope solutions
doc = pending /ce-compound after PR merge) instead of describing
them as future work.

Plan written via /ce-plan; user-confirmed synthesis on the scope
before research dispatch. Research used ce-repo-research-analyst
and ce-learnings-researcher in parallel; findings cited in the
plan's Context & Research section. Not implementing today —
this commit lands the plan only.
The Pattern Documentation Note's two follow-ups are now both
resolved:

1. Spec follow-up plan — done (committed 7ddc0e0 on this branch).
2. Solutions doc — done (refreshed in place at
   solutions-docs/architecture-patterns/anc-cli-output-envelope-
   pattern-2026-04-29.md, commit 10fcb4b in solutions-docs).

Both ACs in the parent plan now reference shipped artifacts. Solo
updates remain on the parent plan: status: active -> completed at
final shipping (Phase 4 of ce-work). That flip happens when the
PR merges to dev.
Plan body refreshed to as-shipped reality and frontmatter flipped to
status: completed (matches the convention from 2026-04-23-001 spec-
vendor and 2026-04-29-001 scorecard-schema-metadata plans).

Body changes:

- Frontmatter: status active -> completed; added completed: 2026-04-30
  and shipped_in: "PR #35".
- New `## Acceptance Status (post-shipment, 2026-04-30)` section near
  the top with [x] checkboxes for every R-ID, U-ID, test scenario, and
  pre-merge checklist item. The body below remains the as-shipped
  contract; the Document Review subsections at the bottom preserve the
  audit trail of corrections that didn't make it to the final shape.
- R2 variant list: 4 -> 6 (factory + kiro added via agentnative-site#53
  before the codegen refactor).
- Module shape SkillHost / KNOWN_HOSTS lists: 4 -> 6, annotated as
  build-generated.
- Open Questions / Resolved During Planning: "Map source" updated
  from hardcoded constants to build-time codegen; "Drift handling"
  notes that fixture-vs-Rust-map drift is now structurally impossible;
  "Hosts at launch" updated to six.
- Context & Research / Relevant Code and Patterns + Institutional
  Learnings: replaced tests/fixtures/ pattern reference with the spec-
  vendor src/principles/spec/ build-input pattern.
- External References: dropped the "Rust map is hand-maintained to
  match" line; replaced with the codegen-from-vendored-fixture
  description.
- System-Wide Impact / Interaction graph: added build.rs codegen hook,
  src/skill_install/skill.json, Cargo.toml dep changes, sync script,
  and CI workflow to the touched-files list.

Pattern Documentation Note follow-ups (top-level visibility):

- Spec-SHOULDs follow-up plan: docs/plans/2026-04-30-001-feat-spec-
  output-envelope-shoulds-plan.md (queued).
- Solutions doc: refreshed in solutions-docs commit 10fcb4b.

Squash merge to dev follows.
@brettdavies
brettdavies merged commit 97a74df into dev Apr 30, 2026
7 checks passed
@brettdavies
brettdavies deleted the feat/skill-install branch April 30, 2026 07:49
@brettdavies brettdavies mentioned this pull request May 1, 2026
16 tasks
brettdavies added a commit that referenced this pull request May 1, 2026
)

## Summary

The `git cherry HEAD origin/dev` check added in #45 is noisier than its
comment implied. In a squash-merge workflow, every historical commit
ever consolidated into a release squash shows as `+` forever — the
squash commit's patch-id never matches the individual dev commits it
absorbed.

During v0.3.0 release prep this check produced **55 `+` lines**. All
audited; zero real misses. Sources:

- **Historical squash patch-id mismatch** — pre-v0.2.0 commits squashed
into prior release tags
- **Conflict-resolution drift** — e.g. `#35`'s cherry-pick stripped
`docs/plans` files; same intent, different patch-id
- **Intentional skips** — docs-only commits, the `abf1c6a` v0.2.0
backport, the `70fc42c` URL-revert prep step

This PR expands the comment to (a) name the three expected noise
sources, (b) define what a real miss actually looks like, and (c) give
the two-command triage recipe (`git show --stat <sha>` then `git diff
origin/main..HEAD -- <those-files>`). The check is now correctly framed
as "review me, don't autoblock."

Same edit applied to all 7 sibling brettdavies repos with `RELEASES.md`
(working-tree only, no commits) — those land via separate per-repo PRs.

## Changelog

### Documentation

- `RELEASES.md` § "Releasing dev to main" step 4 — expanded the `git
cherry` patch-id check comment with squash-merge triage guidance (three
expected noise sources, what a real miss looks like, and a two-command
triage recipe). Discovered during v0.3.0 prep when the check produced 55
noisy `+` lines that all turned out to be expected; the original comment
didn't explain that this is normal in a squash-merge workflow.

## Type of Change

- [x] `docs`: Documentation update

## Related Issues/Stories

- Story: discovered immediately after merging #45 — the new `git cherry`
step produced 55 `+` lines on the v0.3.0 release branch, all of which
audited as expected (squash-merge patch-id mismatch, conflict-resolution
drift, intentional skips). The original comment didn't make this clear,
so a future operator could read the output as a release-blocker when it
isn't.
- Issue: n/a — caught during release ceremony.
- Architecture: `RELEASES.md` § "Releasing dev to main" step 4 —
operator-facing flow, not runtime behavior.
- Related PRs: builds on #45 (which introduced the `git cherry` check).
This release (v0.3.0) cherry-picks this PR after merge so both the check
and its triage guidance ship together.

## Testing

- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [x] Manual testing completed
- [x] All tests passing

**Test Summary:**

- Unit tests: n/a — operator-facing markdown only
- Integration tests: n/a
- Coverage: n/a. Manual: ran `git cherry HEAD origin/dev | grep '^+'`
against the v0.3.0 release branch live; output produced exactly the
noise classes described in the new comment (historical squash +
conflict-resolution + intentional skips). The triage recipe (`git show
--stat <sha>` + `git diff origin/main..HEAD -- <files>`) was used to
validate every flagged line; zero real misses.

## Files Modified

**Modified:**

- `RELEASES.md` — `git cherry` patch-id check comment expanded with
squash-merge triage guidance.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Breaking Changes

- [x] No breaking changes

Pure comment-prose expansion; the command is unchanged.

## Deployment Notes

- [x] No special deployment steps required

## Checklist

- [x] Code follows project conventions and style guidelines
- [x] Commit messages follow [Conventional
Commits](https://www.conventionalcommits.org/)
- [x] Self-review of code completed
- [x] Tests added/updated and passing
- [x] No new warnings or errors introduced
- [x] Changes are backward compatible (or breaking changes documented)
brettdavies added a commit that referenced this pull request May 1, 2026
)

## Summary

The `git cherry HEAD origin/dev` check added in #45 is noisier than its
comment implied. In a squash-merge workflow, every historical commit
ever consolidated into a release squash shows as `+` forever — the
squash commit's patch-id never matches the individual dev commits it
absorbed.

During v0.3.0 release prep this check produced **55 `+` lines**. All
audited; zero real misses. Sources:

- **Historical squash patch-id mismatch** — pre-v0.2.0 commits squashed
into prior release tags
- **Conflict-resolution drift** — e.g. `#35`'s cherry-pick stripped
`docs/plans` files; same intent, different patch-id
- **Intentional skips** — docs-only commits, the `abf1c6a` v0.2.0
backport, the `70fc42c` URL-revert prep step

This PR expands the comment to (a) name the three expected noise
sources, (b) define what a real miss actually looks like, and (c) give
the two-command triage recipe (`git show --stat <sha>` then `git diff
origin/main..HEAD -- <those-files>`). The check is now correctly framed
as "review me, don't autoblock."

Same edit applied to all 7 sibling brettdavies repos with `RELEASES.md`
(working-tree only, no commits) — those land via separate per-repo PRs.

## Changelog

### Documentation

- `RELEASES.md` § "Releasing dev to main" step 4 — expanded the `git
cherry` patch-id check comment with squash-merge triage guidance (three
expected noise sources, what a real miss looks like, and a two-command
triage recipe). Discovered during v0.3.0 prep when the check produced 55
noisy `+` lines that all turned out to be expected; the original comment
didn't explain that this is normal in a squash-merge workflow.

## Type of Change

- [x] `docs`: Documentation update

## Related Issues/Stories

- Story: discovered immediately after merging #45 — the new `git cherry`
step produced 55 `+` lines on the v0.3.0 release branch, all of which
audited as expected (squash-merge patch-id mismatch, conflict-resolution
drift, intentional skips). The original comment didn't make this clear,
so a future operator could read the output as a release-blocker when it
isn't.
- Issue: n/a — caught during release ceremony.
- Architecture: `RELEASES.md` § "Releasing dev to main" step 4 —
operator-facing flow, not runtime behavior.
- Related PRs: builds on #45 (which introduced the `git cherry` check).
This release (v0.3.0) cherry-picks this PR after merge so both the check
and its triage guidance ship together.

## Testing

- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [x] Manual testing completed
- [x] All tests passing

**Test Summary:**

- Unit tests: n/a — operator-facing markdown only
- Integration tests: n/a
- Coverage: n/a. Manual: ran `git cherry HEAD origin/dev | grep '^+'`
against the v0.3.0 release branch live; output produced exactly the
noise classes described in the new comment (historical squash +
conflict-resolution + intentional skips). The triage recipe (`git show
--stat <sha>` + `git diff origin/main..HEAD -- <files>`) was used to
validate every flagged line; zero real misses.

## Files Modified

**Modified:**

- `RELEASES.md` — `git cherry` patch-id check comment expanded with
squash-merge triage guidance.

**Created:**

- None.

**Renamed:**

- None.

**Deleted:**

- None.

## Breaking Changes

- [x] No breaking changes

Pure comment-prose expansion; the command is unchanged.

## Deployment Notes

- [x] No special deployment steps required

## Checklist

- [x] Code follows project conventions and style guidelines
- [x] Commit messages follow [Conventional
Commits](https://www.conventionalcommits.org/)
- [x] Self-review of code completed
- [x] Tests added/updated and passing
- [x] No new warnings or errors introduced
- [x] Changes are backward compatible (or breaking changes documented)
brettdavies added a commit that referenced this pull request May 1, 2026
## Summary

v0.3.0 promotes 15 PRs from `dev` to `main`. Headline: scorecard schema
bumps `0.3` → `0.5` (cumulative — `anc.commit` removed before tag), a
new `anc skill install <host>` subcommand with hardened `git clone` +
build-time host-map codegen, and a published agent-native badge surface
(text-mode hint + JSON `badge` block) gated on a 80% eligibility floor.
Plus PII fix on `target.path`, source-quality cleanup, README refresh,
cross-repo sync map, and tooling-side hardening (PR template
`**Renamed:**` subsection, `sync-spec` modernization,
`sync-skill-fixture` drift gate, triple-diff release-flow check with
squash-merge triage guidance).

The release-prep also surfaced four corrections that came in via the
canonical PR-to-dev flow during this ship: `#43` resynced the
`skill.json` build-time fixture against upstream `agentnative-site/dev`
(drift was blocking every open PR); `#42` corrected cross-repo URLs in
`.github/ISSUE_TEMPLATE/` (broken since the v0.1.1 squash on `main`);
`#45` replaced the release-flow's single-direction leak check with a
triple-diff plus `git cherry` patch-id check (the latter caught the URL
drift that motivated the change); `#46` added squash-merge-noise triage
guidance to the `git cherry` step (the check produced 55 noisy `+` lines
on first run, all expected, but the original comment didn't explain
that). `#47` dropped the `anc.commit` field from the scorecard JSON
before the v0.3.0 tag, since the `cargo:rerun-if-changed` `.git/`
watches made cached builds fragile and `anc.version` is sufficient build
identity.

## Changelog

<!-- Per the changelog generation rules, the canonical changelog is in
CHANGELOG.md.
This release section reflects the same content; generate-changelog.sh
extracts
from each contributing PR's ## Changelog block. The bullets below are a
     curated highlight set for the PR body itself. -->

### Added

- `anc skill install <host>` subcommand for six hosts (`claude_code`,
`codex`, `cursor`, `factory`, `kiro`, `opencode`) with `--dry-run` and
`--output {text,json}` flags and a uniform JSON envelope across success,
error, and dry-run paths (#35).
- Scorecard `--output json` self-describing metadata: four top-level
blocks (`tool`, `anc`, `run`, `target`) + a `badge` block with
`eligible`, `score_pct`, `embed_markdown`, `scorecard_url`, `badge_url`,
`convention_url` (#34, #36).
- `--output text` post-summary agent-native badge embed hint when the
tool clears the 80% eligibility floor; "do not nag" rule below (#36).

### Changed

- Scorecard `schema_version` bumped `0.3` → `0.4` → `0.5` (cumulative —
`anc.commit` field removed before the v0.3.0 tag, see #47) (#34, #36).
- `sync-spec.sh` modernized — remote-first tag resolution, `SPEC_REF`
env override removed (#33).
- `p7-naked-println` source check now exempts `build.rs` at any crate
root (#38).
- README refreshed for current state (schema 0.5, badge block,
`--audit-profile`, basename `target.path`); `rust-version` bumped `1.87`
→ `1.88` (#34, #40).
- `--output json` scorecard `anc` block no longer includes a `commit`
field — `anc.version` is the build identity (#47).

### Fixed

- `target.path` in `--output json` now emits the basename of the
resolved target instead of the canonicalized absolute path — eliminates
a home-dir / username PII leak that flowed into committed scorecards,
badge URLs, and agent-posted artifacts (#39).
- Eliminated four `.unwrap()` calls on infallible paths across
`src/skill_install.rs` and `build.rs`, replaced with `.expect("…")`
naming the contract (#38).
- Cross-repo URLs in `.github/ISSUE_TEMPLATE/` corrected: spec →
`agentnative`, site → `agentnative-site`, double-`cli` typo →
`agentnative-cli` (#42).

### Documentation

- `scripts/SYNCS.md` cross-repo sync map (#41).
- `RELEASES.md` § "After publish — sync `dev` with the release" + new
`sync-dev-after-release.sh` script formalizing the post-publish backport
convention (#37).
- `RELEASES.md` § "Releasing dev to main" step 4 replaced with a
triple-diff verification block (A: main→release, B: release→dev, C:
dev→main) plus a `git cherry` patch-id check (#45), with
squash-merge-noise triage guidance added in #46.
- README `## Install the skill` section + `[![agent-native]]` badge row
(#35, #40).

## Type of Change

- [x] `feat`: New feature (non-breaking change which adds functionality)
- [x] `fix`: Bug fix (non-breaking change which fixes an issue)
- [x] `chore`: Maintenance tasks (dependencies, config, etc.)
- [x] `docs`: Documentation update

## Related Issues/Stories

- Story: v0.3.0 release — twelve PRs across two days of work post-v0.2.0
plus four release-prep PRs (#42, #43, #45, #46) opened during the
release ceremony to fix issues caught by the new triple-diff
verification, plus #47 dropping `anc.commit` before the tag.
- Issue: n/a
- Architecture: scorecard schema evolution (`0.3 → 0.4 → 0.5`) covered
in CLAUDE.md `## Scorecard v0.5 Fields`; skill-install architecture
covered in CLAUDE.md `## Skill Install Verb`; release ceremony in
`RELEASES.md`.
- Related PRs: #30, #33, #34, #35, #36, #37, #38, #39, #40, #41, #42,
#43, #45, #46, #47 — all merged to `dev` and cherry-picked here in
chronological order.

## Testing

- [x] Unit tests added/updated
- [x] Integration tests added/updated
- [x] Manual testing completed
- [x] All tests passing

**Test Summary:**

- Unit tests: 535+ passing across all #34, #35, #36, #38, #39 PRs
(counts varied as PRs landed). #47 simplified to ~520+ after dropping
the `anc_commit_when_present_looks_like_short_sha` test.
- Integration tests: full suite green at every cherry-pick step. New
tests added: `tests/scorecard_metadata_security.rs` (red-team
regressions for hostile binaries), `tests/scorecard_schema_v05.rs`
(schema drift guard with `target.path` regression test),
`tests/skill_install.rs` (10 active + 1 ignored e2e), `tests/dogfood.rs`
(p2/p5 dogfood guards).
- Coverage: pre-push hook (CI mirror) green at every step — fmt, clippy
`-Dwarnings`, full test suite, cargo-deny, Windows compat (libc grep +
cross-target clippy).
- Triple-diff verification (the new step from #45) confirmed only
`docs/plans/`, `docs/brainstorms/`, `docs/ideation/` (guarded paths) and
the `docs/solutions` symlink remain on dev. Release-prep files
(`CHANGELOG.md`, `Cargo.{toml,lock}`, `completions/*`) will backport to
dev via `scripts/sync-dev-after-release.sh` post-publish per the
convention from #37. The `git cherry` patch-id check produced 55 `+`
lines on first run; all audited as expected (squash-merge patch-id
mismatch + conflict-resolution drift + intentional skips), zero real
misses — motivated #46's triage guidance.

## Files Modified

**Modified:**

- `Cargo.toml`, `Cargo.lock` — version `0.2.0 → 0.3.0`; `time =
"=0.3.47"` added; `rust-version` bumped.
- `CHANGELOG.md` — v0.3.0 release notes prepended.
- `README.md`, `AGENTS.md`, `CLAUDE.md` — updated for schema 0.4/0.5,
skill-install architecture, audit-profile, basename `target.path`, badge
surface, `anc.commit` removal.
- `RELEASES.md` — post-publish backport step (#37); skill-fixture sync
step; step 4 replaced with triple-diff + `git cherry` check (#45);
squash-merge triage guidance added (#46).
- `build.rs` — `emit_skill_hosts` codegen, `ANC_VERSION` emission (no
longer Git-SHA-aware after #47).
- `src/build_info.rs` — `ANC_VERSION` re-export; `ANC_COMMIT` and its
test removed in #47.
- `src/main.rs`, `src/cli.rs`, `src/error.rs`, `src/scorecard/mod.rs`,
`src/argv.rs` — schema 0.4/0.5 metadata, skill-install dispatch, badge
computation, basename helper. `AncInfo` shrinks to one field after #47.
- `src/checks/source/rust/naked_println.rs` — build.rs exemption.
- `src/skill_install/skill.json` — fixture refresh (`source.commit` pin
and `verify` block removed by upstream).
- `scripts/sync-spec.sh` — remote-first, `SPEC_REF` removed.
- `scripts/hooks/pre-push` — Windows cross-clippy step.
- `.github/pull_request_template.md` — `**Renamed:**` sub-header.
-
`.github/ISSUE_TEMPLATE/{config,false-positive,feature-request,scoring-bug}.yml`
— corrected cross-repo URLs.
- `completions/anc.{bash,zsh,fish,elvish,powershell}` — regenerated.

**Created:**

- `src/output.rs`, `src/skill_install.rs`,
`src/skill_install/skill.json`.
- `tests/dogfood.rs`, `tests/scorecard_metadata_security.rs`,
`tests/scorecard_schema_v05.rs`, `tests/skill_install.rs`,
`tests/fixtures/hostile-{hang,nonzero-exit,stdout-flood}/probe.sh`.
- `scripts/sync-skill-fixture.sh`, `scripts/sync-dev-after-release.sh`,
`scripts/SYNCS.md`.
- `.github/workflows/skill-fixture-drift.yml`.
- `.github/ISSUE_TEMPLATE/00-blank.yml`.

**Renamed:**

- `tests/scorecard_schema_v04.rs` → `tests/scorecard_schema_v05.rs`
(drift guard now covers `badge.*` keys).
- `tests/fixtures/skill.json` → `src/skill_install/skill.json` (codegen
needs to read from a path inside cargo's package).

**Deleted:**

- `.github/ISSUE_TEMPLATE/grade-a-cli.yml`, `pressure-test.yml`,
`spec-question.yml` — duplicates of the spec repo's set; routed via
`config.yml`.
- `src/build_info.rs::ANC_COMMIT` const (and its test) — dropped before
tag, see #47.

## Breaking Changes

- [x] No breaking changes

The scorecard `schema_version` bumps (`0.3` → `0.4` → `0.5`) are
additive within the documented `0.x` pre-launch policy. Pre-`0.4`
consumers feature-detect the four metadata blocks; pre-`0.5` consumers
feature-detect the `badge` key. `target.path` value semantics changed
(basename instead of absolute path) — schema shape is unchanged, so
consumers reading the field at all will continue to parse it; consumers
using it for cross-host correlation should already have migrated to
`tool.name`. `anc.commit` was added in 0.4 but removed before the v0.3.0
tag (#47); no public consumer of the field exists pre-launch.

## Deployment Notes

- [x] No special deployment steps required

Standard pipeline: tag push triggers crates.io publish (Trusted
Publishing, OIDC), GitHub Release with all 5 platform archives +
sha256sums, then dispatch to `brettdavies/homebrew-tap` for formula
update. After `finalize-release.yml` flips `make_latest: true`, run
`./scripts/sync-dev-after-release.sh v0.3.0` to backport `Cargo.toml`,
`Cargo.lock`, `CHANGELOG.md` to dev (new convention from #37).

## Checklist

- [x] Code follows project conventions and style guidelines
- [x] Commit messages follow [Conventional
Commits](https://www.conventionalcommits.org/)
- [x] Self-review of code completed
- [x] Tests added/updated and passing
- [x] No new warnings or errors introduced
- [x] Changes are backward compatible (or breaking changes documented)
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.

1 participant