Skip to content

fix(project): pick newer cargo target binary by mtime#51

Merged
brettdavies merged 2 commits into
devfrom
fix/dogfood-binary-discovery
May 7, 2026
Merged

fix(project): pick newer cargo target binary by mtime#51
brettdavies merged 2 commits into
devfrom
fix/dogfood-binary-discovery

Conversation

@brettdavies
Copy link
Copy Markdown
Owner

Summary

Replaces discover_rust_binaries()'s always-prefer-release heuristic with mtime-newest-wins so dev workflows where cargo run/cargo test only refresh debug stop probing stale target/release/<bin> binaries. The v0.4.0 spec sync surfaced the trap on p2-must-schema-print against anc itself.

Changelog

Changed

  • Binary discovery in src/project.rs::discover_rust_binaries now picks the newer of target/release/<bin> and target/debug/<bin> by mtime when both exist. Ties and metadata failures fall back to debug (matches cargo's dev-flow default). CI scenarios where only one profile is built are unchanged.

Documentation

  • Add Dogfooding Safety rule 3 to CLAUDE.md describing the mtime-based selection, with a NEVER directive against reverting to the always-prefer-release shape.

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • refactor: Code refactoring (no functional changes)
  • perf: Performance improvement
  • docs: Documentation update
  • test: Adding or updating tests
  • chore: Maintenance tasks (dependencies, config, etc.)
  • ci: CI/CD configuration changes
  • style: Code style/formatting changes
  • build: Build system changes
  • BREAKING CHANGE: Breaking API change (requires major version bump)

Related Issues/Stories

  • Story: stale target/release/anc masking p2-must-schema-print regressions during v0.4.0 spec sync
  • Issue: n/a
  • Architecture: docs/solutions/test-failures/stale-release-binary-dogfood-fail-2026-05-07.md
  • Related PRs: feat(spec): sync v0.4.0 — add 11 requirement IDs across P1/P2/P4/P6/P8 #50 (v0.4.0 spec sync, where the trap was surfaced and a temporary PENDING_FAILS allowlist for p2-schema-print was added in tests/dogfood.rs)

Files Modified

Modified:

  • src/project.rs: discover_rust_binaries switches from existence-only release-over-debug preference to mtime-based selection via a new pick_newer_artifact helper. Adds two unix-gated tests (test_discover_picks_newer_artifact_by_mtime, test_discover_picks_release_when_newer).
  • CLAUDE.md: Dogfooding Safety section gains rule 3 describing the new behavior; the Rules for new behavioral checks subsection gains a NEVER directive locking the new shape in.

Created:

  • None.

Renamed:

  • None.

Deleted:

  • None.

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing completed (dogfood anc check . against this branch; p2-schema-print no longer needs a manual cargo build --release to flip from Fail to Pass)
  • All tests passing

Test Summary:

cargo test reports 588 passed, 2 ignored across 7 suites. The two new unit tests use tempfile-style temp dirs and File::set_modified (stable since 1.75) to construct binaries with controlled mtimes; gated to #[cfg(unix)] because mtime semantics under Windows file-attribute caching are not the semantics this regression asserts against. Pre-push gate (fmt, clippy -Dwarnings, test, cargo-deny, Windows compat) passed before the push.

…inary

The discover_rust_binaries() preference for target/release over
target/debug masks behavioral check failures when target/release lingers
stale from past builds. Surfaced during v0.4.0 spec sync work — the new
p2-must-schema-print check kept reporting Fail despite the schema
subcommand being visible in target/debug/anc --help; the runner was
probing a stale target/release/anc that predated the subcommand.

Adds rule 3 to the Dogfooding Safety section (current behavior +
workaround: cargo build --release before dogfooding, or trash
target/release). Adds a corresponding rule under 'Rules for new
behavioral checks' so future checks that exercise help-surface get
the right pre-flight.

Cites docs/solutions/test-failures/stale-release-binary-dogfood-fail-2026-05-07.md
for the full investigation, root cause, and fix-candidate analysis.

Pairs with the next commit on this branch which lands the mtime-based
binary-discovery fix and updates this rule to reflect the new behavior.
discover_rust_binaries() previously preferred target/release/<bin>
unconditionally when both directories existed. In dev workflows where
cargo run / cargo test only refresh debug, target/release lingers stale
from past builds and behavioral checks probed yesterday's --help. The
v0.4.0 spec sync surfaced this when the new p2-must-schema-print check
kept reporting Fail despite the schema subcommand being visible in
target/debug/anc --help — captured help was 1071 bytes vs 1145 from a
direct invocation, exactly one subcommand line shorter (the line that
lived only in debug).

The fix replaces existence-only selection with mtime comparison: when
both binaries exist, return the one with the more recent modified time.
Ties and metadata-failures fall back to debug (cargo's dev-flow default).
CI scenarios where only one profile is built are unchanged — fall
through cleanly to the existence check.

Adds two unix-gated tests:

- test_discover_picks_newer_artifact_by_mtime: stamps target/release/myapp
  one hour in the past while target/debug/myapp stays at 'now'; asserts
  Project::discover returns the debug path. Regression for the original
  bug.
- test_discover_picks_release_when_newer: symmetric — stamps debug old
  and asserts release wins. Locks in that the function still does the
  right thing after a fresh cargo build --release.

Updates the CLAUDE.md Dogfooding Safety rule (added in the previous
commit on this branch) to describe the new mtime-based behavior. The
'Rules for new behavioral checks' subsection gains a NEVER directive
against reverting to the always-prefer-release shape.

Backstory: docs/solutions/test-failures/stale-release-binary-dogfood-fail-2026-05-07.md
@brettdavies brettdavies merged commit ef7609f into dev May 7, 2026
7 checks passed
@brettdavies brettdavies deleted the fix/dogfood-binary-discovery branch May 7, 2026 21:27
brettdavies added a commit that referenced this pull request May 21, 2026
…tmatter; fix badge slug + P8 filter (#54)

## Summary

Closes the dogfood gaps surfaced by `anc check .` at the v0.4.0 release
candidate, plus the silent slug-derivation bug
behind the scorecard's badge URLs. Self-check after this branch: 44
checks, 37 pass, 3 warn, 0 fail, 0 error. Score 93%
(up from 85% at the v0.4.0 RC).

## Changelog

### Added

- Add `anc schema` top-level subcommand. Prints the embedded JSON Schema
(draft 2020-12) describing the shape of `anc check --output json`
scorecards. Closes the `p2-must-schema-print` FAIL surfaced by
self-check.
- Add `schema/scorecard.schema.json` committed at the repo root and
embedded into the binary via `include_str!`. Hand-written coverage of
the 0.5 shape (Scorecard plus ToolInfo, AncInfo, RunInfo, PlatformInfo,
TargetInfo, BadgeInfo, LevelCounts, CoverageSummary, Summary,
CheckResultView). Closes the `p2-should-schema-file` WARN.
- Add YAML frontmatter to `AGENTS.md` naming the tool, binary,
description, homepage, and repository so agent runtimes can index the
bundle. Closes the `p8-should-bundle-exists` WARN.

### Changed

- `derive_tool_name` now follows the four-tier fallback chain
`command_name -> binary basename -> manifest package name -> directory
basename`. The old shape returned the project directory basename,
producing 404-bound badge URLs for any tool whose registry slug differed
from its directory name. `anc check .` now emits `badge_url:
https://anc.dev/badge/anc.svg` (HTTP 200, matches the site's
`registry.yaml`).
- `matches_principle` gains the `(CheckGroup::P8, 8)` arm. Pre-fix,
`--principle 8` silently filtered out every P8 check because the match
table predated the new principle.
- README refreshed for v0.4.0: principle count 7 -> 8 with a P8 row,
"Example Output" rewritten against the current 44-check self-check (P2
schema, P6 standard-names + SIGTERM, P8 bundle results), "Three Check
Layers" lists Python alongside Rust under Source, JSON sample dropped
the stale `anc.commit` field. README em-dash density scrubbed from
20.5/1000 to 0/1000.
- README "Reporting issues" section folded into "Contributing" as a
three-tier intake (signal / proposal / code) that points at the shipped
`CONTRIBUTING.md` for the full breakdown. Cross-repo routing preserved.

### Documentation

- Update `--principle <PRINCIPLE>` doc from `(1-7)` to `(1-8)` in both
README and `src/cli.rs`.

## Type of Change

- [x] `feat`: New feature (non-breaking change which adds functionality)

## Related Issues/Stories

- Story: n/a
- Issue: n/a
- Architecture: Schema follow-up plan on `dev` at
`docs/plans/2026-04-30-002-feat-scorecard-json-schema-plan.md` (full
schemars-derive implementation; this PR ships the hand-written first
cut).
- Related PRs: #50, #51, #52, #53

## Testing

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

**Test Summary:**

- New `src/main.rs::tests` module covers all four tiers of
`derive_tool_name_inner` (command, binary, `Cargo.toml` manifest,
`pyproject.toml` manifest, directory fallback).
- `cargo test`: 593 passed, 2 ignored across 7 suites.
- `cargo clippy --release --all-targets -- -Dwarnings`: clean.
- `cargo fmt --check`: clean.
- `./target/release/anc check .`: 44 checks, 37 pass, 3 warn, 0 fail, 4
skip, 0 error. Score 93%.
- Live URL probe: `https://anc.dev/badge/anc.svg` returns 200.

## Files Modified

**Modified:**

- `AGENTS.md`, `README.md`, `src/cli.rs`, `src/main.rs`.
- All five `completions/anc.*` files (regenerated for the new `schema`
subcommand).

**Created:**

- `schema/scorecard.schema.json`.

**Renamed:**

- None.

**Deleted:**

- None.

## Breaking Changes

- [x] No breaking changes

## Deployment Notes

- [x] No special deployment steps required. After merge to `dev`,
cherry-pick the squash commit onto `release/v0.4.0` (PR #53) and
regenerate `CHANGELOG.md` before the release tag.
brettdavies added a commit that referenced this pull request May 21, 2026
…tmatter; fix badge slug + P8 filter (#54)

## Summary

Closes the dogfood gaps surfaced by `anc check .` at the v0.4.0 release
candidate, plus the silent slug-derivation bug
behind the scorecard's badge URLs. Self-check after this branch: 44
checks, 37 pass, 3 warn, 0 fail, 0 error. Score 93%
(up from 85% at the v0.4.0 RC).

## Changelog

### Added

- Add `anc schema` top-level subcommand. Prints the embedded JSON Schema
(draft 2020-12) describing the shape of `anc check --output json`
scorecards. Closes the `p2-must-schema-print` FAIL surfaced by
self-check.
- Add `schema/scorecard.schema.json` committed at the repo root and
embedded into the binary via `include_str!`. Hand-written coverage of
the 0.5 shape (Scorecard plus ToolInfo, AncInfo, RunInfo, PlatformInfo,
TargetInfo, BadgeInfo, LevelCounts, CoverageSummary, Summary,
CheckResultView). Closes the `p2-should-schema-file` WARN.
- Add YAML frontmatter to `AGENTS.md` naming the tool, binary,
description, homepage, and repository so agent runtimes can index the
bundle. Closes the `p8-should-bundle-exists` WARN.

### Changed

- `derive_tool_name` now follows the four-tier fallback chain
`command_name -> binary basename -> manifest package name -> directory
basename`. The old shape returned the project directory basename,
producing 404-bound badge URLs for any tool whose registry slug differed
from its directory name. `anc check .` now emits `badge_url:
https://anc.dev/badge/anc.svg` (HTTP 200, matches the site's
`registry.yaml`).
- `matches_principle` gains the `(CheckGroup::P8, 8)` arm. Pre-fix,
`--principle 8` silently filtered out every P8 check because the match
table predated the new principle.
- README refreshed for v0.4.0: principle count 7 -> 8 with a P8 row,
"Example Output" rewritten against the current 44-check self-check (P2
schema, P6 standard-names + SIGTERM, P8 bundle results), "Three Check
Layers" lists Python alongside Rust under Source, JSON sample dropped
the stale `anc.commit` field. README em-dash density scrubbed from
20.5/1000 to 0/1000.
- README "Reporting issues" section folded into "Contributing" as a
three-tier intake (signal / proposal / code) that points at the shipped
`CONTRIBUTING.md` for the full breakdown. Cross-repo routing preserved.

### Documentation

- Update `--principle <PRINCIPLE>` doc from `(1-7)` to `(1-8)` in both
README and `src/cli.rs`.

## Type of Change

- [x] `feat`: New feature (non-breaking change which adds functionality)

## Related Issues/Stories

- Story: n/a
- Issue: n/a
- Architecture: Schema follow-up plan on `dev` at
`docs/plans/2026-04-30-002-feat-scorecard-json-schema-plan.md` (full
schemars-derive implementation; this PR ships the hand-written first
cut).
- Related PRs: #50, #51, #52, #53

## Testing

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

**Test Summary:**

- New `src/main.rs::tests` module covers all four tiers of
`derive_tool_name_inner` (command, binary, `Cargo.toml` manifest,
`pyproject.toml` manifest, directory fallback).
- `cargo test`: 593 passed, 2 ignored across 7 suites.
- `cargo clippy --release --all-targets -- -Dwarnings`: clean.
- `cargo fmt --check`: clean.
- `./target/release/anc check .`: 44 checks, 37 pass, 3 warn, 0 fail, 4
skip, 0 error. Score 93%.
- Live URL probe: `https://anc.dev/badge/anc.svg` returns 200.

## Files Modified

**Modified:**

- `AGENTS.md`, `README.md`, `src/cli.rs`, `src/main.rs`.
- All five `completions/anc.*` files (regenerated for the new `schema`
subcommand).

**Created:**

- `schema/scorecard.schema.json`.

**Renamed:**

- None.

**Deleted:**

- None.

## Breaking Changes

- [x] No breaking changes

## Deployment Notes

- [x] No special deployment steps required. After merge to `dev`,
cherry-pick the squash commit onto `release/v0.4.0` (PR #53) and
regenerate `CHANGELOG.md` before the release tag.
brettdavies added a commit that referenced this pull request May 21, 2026
## Summary

Cuts v0.4.0 by cherry-picking four PRs from `dev`: #50 (spec sync, 11
new requirement IDs across P1/P2/P4/P6/P8), #51
(binary-discovery mtime fix), #52 (prose pipeline import + un-vendor of
orchestrator + doc-hygiene pass), and #54
(post-RC dogfood fixes that lift score from 85% to 93%). Adds an entire
new principle (P8 Discoverable Skill Bundle), a
P1 secret-handling check, the `anc schema` subcommand backing P2's
runtime-discoverable-schema MUST, and a fix for the
silent badge-slug bug uncovered during release polish.

## Changelog

### Added

- Add P1 secret-handling check (`p1-must-secret-non-leaky-path`): scans
target CLIs' `--help` for secret-bearing flag families (`--token`,
`--password`, `--api-key`, `--secret`, `--auth`, `--credential`) and
verifies each has either a `--*-file` companion or stdin path
advertised. Vacuous Pass when no secret-bearing flag is detected.
- Add P2 schema trio (`p2-must-schema-print`, `p2-should-schema-file`,
`p2-should-json-aliases`): runtime-discoverable output schema via
`schema` subcommand or `--schema` flag, file-export of schemas
(`schema/*.json`, `*.schema.json` at repo root), and `--json` /
`--jsonl` short aliases for `--output`.
- Add P4 closed-set rejection check (`p4-should-enumerate-valid-set`,
Rust + Python): detects clap `ValueEnum`, `PossibleValuesParser`,
`value_parser!`, and Python `argparse.choices=` / `click.Choice()`.
- Add P6 lifecycle and naming checks (`p6-must-sigterm`, Rust + Python;
`p6-may-standard-names`): SIGTERM-handler detection across
`signal_hook`, `tokio::signal::unix`, `signal.signal`, and
`loop.add_signal_handler`; community-standard-verb allow-list applied to
top-level subcommands.
- Add P8 skill-bundle suite (`p8-should-bundle-exists`,
`p8-must-bundle-install`, `p8-may-install-all`, `p8-may-bundle-update`):
repo-root detection of `AGENTS.md` / `SKILL.md` with YAML frontmatter,
plus help-surface probes for `skill install`, `skill install --all`, and
`skill update` / `skill upgrade`. Brand-new principle in the registry.
- Add `anc schema` top-level subcommand. Prints the embedded JSON Schema
(draft 2020-12) describing the shape of `anc check --output json`
scorecards. Closes the `p2-must-schema-print` FAIL surfaced by `anc
check .` self-check.
- Add `schema/scorecard.schema.json` committed at the repo root and
embedded into the binary via `include_str!`. Hand-written coverage of
the 0.5 shape (Scorecard plus ToolInfo, AncInfo, RunInfo, PlatformInfo,
TargetInfo, BadgeInfo, LevelCounts, CoverageSummary, Summary,
CheckResultView). Closes the `p2-should-schema-file` WARN.
- Add YAML frontmatter to `AGENTS.md` naming the tool, binary,
description, homepage, and repository so agent runtimes can index the
bundle. Closes the `p8-should-bundle-exists` WARN.

### Changed

- Bump CLI from 0.3.1 to 0.4.0 (MINOR; meaningful coverage growth across
five principles, including a brand-new principle).
- Binary discovery in `src/project.rs::discover_rust_binaries` now picks
the newer of `target/release/<bin>` and `target/debug/<bin>` by mtime
when both exist. Ties and metadata failures fall back to debug (matches
cargo's dev-flow default). CI scenarios where only one profile is built
are unchanged.
- `RELEASES.md` slims to operational runbook; rationale moves to
companion `RELEASES-RATIONALE.md`. Each runbook section ends with a
section-pointer at the rationale.
- `derive_tool_name` now follows the four-tier fallback chain
`command_name -> binary basename -> manifest package name -> directory
basename`. The old shape returned the project directory basename,
producing 404-bound badge URLs for any tool whose registry slug differed
from its directory name. `anc check .` now emits `badge_url:
https://anc.dev/badge/anc.svg` (HTTP 200, matches the site's
`registry.yaml`).
- `matches_principle` gains the `(CheckGroup::P8, 8)` arm. Pre-fix,
`--principle 8` silently filtered out every P8 check because the match
table predated the new principle.
- README refreshed for v0.4.0: principle count 7 -> 8 with a P8 row,
"Example Output" rewritten against the current 44-check self-check (P2
schema, P6 standard-names + SIGTERM, P8 bundle results), "Three Check
Layers" lists Python alongside Rust under Source, JSON sample dropped
the stale `anc.commit` field. README em-dash density scrubbed from
20.5/1000 to 0/1000.
- README "Reporting issues" section folded into "Contributing" as a
three-tier intake (signal / proposal / code) that points at the shipped
`CONTRIBUTING.md`. Cross-repo routing preserved.

### Documentation

- New `PRODUCT.md` at repo root codifies linter-channel voice:
second-person imperative register, three-part error shape (what failed,
why, what to do), no marketing voice in CLI surface. Inherits universal
rules from vendored `BRAND.md`.
- New `CONTRIBUTING.md` documents the three-tier intake (signal /
proposal / code), routes principle-level discussion to the spec repo,
and names the dev-setup gates.
- New `add-tool-to-registry` issue template for proposing CLI tools to
the anc100 registry.
- Document prose-scrubbing runbook in `RELEASES.md` for release-flow
artifacts using Vale + LanguageTool + unslop.
- `AGENTS.md` gains a "Voice and prose rules" section pointing at
`PRODUCT.md` for the linter-channel register and
`scripts/prose-check.sh` for the local gate.
- `scripts/SYNCS.md` documents the new `sync-prose-tooling.sh` row and
the consumer-owned status of `scripts/prose-check.sh`.
- Update `--principle <PRINCIPLE>` doc from `(1-7)` to `(1-8)` in both
README and `src/cli.rs`.

## Type of Change

- [x] `feat`: New feature (non-breaking change which adds functionality)

## Related Issues/Stories

- Story: n/a
- Issue: n/a
- Architecture: Spec sync to `agentnative-spec@v0.4.0` (vendored at
`src/principles/spec/`). Scorecard JSON Schema
follow-up plan lives on `dev` at
`docs/plans/2026-04-30-002-feat-scorecard-json-schema-plan.md` (full
schemars-derive
  implementation; this release ships the hand-written first cut).
- Related PRs: #50, #51, #52, #54

## Testing

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

**Test Summary:**

- CI green on each upstream PR (#50, #51, #52, #54) prior to dev merge.
- Triple-diff verification on release branch: ship surface matches
expected; no missed picks outside `docs/`; phantom-commit count
consistent with prior squash-merge history.
- `cargo test`: 593 passed, 2 ignored across 7 suites. New
`src/main.rs::tests` module covers all four tiers of
`derive_tool_name_inner` (command, binary, `Cargo.toml` manifest,
`pyproject.toml` manifest, directory fallback).
- `cargo clippy --release --all-targets -- -Dwarnings`: clean.
- `cargo fmt --check`: clean.
- `cargo run -- generate coverage-matrix --check`: exits 0 against the
committed artifacts.
- `bash scripts/sync-skill-fixture.sh --check`: confirms fixture matches
`agentnative-site@dev` head (`4efa8ec`).
- `./target/release/anc check .`: 44 checks, 37 pass, 3 warn, 0 fail, 4
skip, 0 error. Score 93%.
- Live URL probe: `https://anc.dev/badge/anc.svg` returns 200.
- Local pre-push hook (fmt, clippy `-Dwarnings`, test, cargo-deny,
Windows compatibility check) passes.

## Files Modified

**Modified:**

- `Cargo.lock`, `CHANGELOG.md`, `AGENTS.md`, `CLAUDE.md`, `README.md`,
`RELEASES.md`, `Cargo.toml`, `coverage/matrix.json`,
`docs/coverage-matrix.md`, `scripts/SYNCS.md`.
- `src/cli.rs`, `src/main.rs`, `src/principles/registry.rs`,
`src/project.rs`, `src/scorecard/mod.rs`, `src/types.rs`.
- `src/checks/{behavioral,project,source/{python,rust}}/mod.rs`.
- `src/principles/spec/CHANGELOG.md`, `src/principles/spec/VERSION`,
`src/principles/spec/principles/p1-p7*.md`.
- All five `completions/anc.*` files.
- `.github/ISSUE_TEMPLATE/{00-blank.yml,config.yml}`, `.gitignore`.
- `tests/build_parser.rs`, `tests/dogfood.rs`.

**Created:**

- `BRAND.md`, `PRODUCT.md`, `CONTRIBUTING.md`, `RELEASES-RATIONALE.md`,
`.vale.ini`.
- `.github/ISSUE_TEMPLATE/add-tool-to-registry.yml`.
- `scripts/prose-check.sh`, `scripts/sync-prose-tooling.sh`,
`scripts/generate-pack-readme.mjs`, `scripts/test-prose-check.mjs`.
- `schema/scorecard.schema.json`.
-
`src/checks/behavioral/{bundle_install,bundle_update,install_all,json_aliases,schema_print,secret_non_leaky_path,standard_names}.rs`.
- `src/checks/project/{bundle_exists,schema_file}.rs`.
- `src/checks/source/python/{enumerate_valid_set,sigterm}.rs`,
`src/checks/source/rust/{enumerate_valid_set,sigterm}.rs`.
- `src/principles/spec/principles/p8-discoverable-skill-bundle.md`.
- `styles/brand/{FillerAdjectives,HedgeWords,MarketingRegister}.yml`,
`styles/brand/README.md`.
- `styles/config/vocabularies/{brand,cli}/{accept,reject}.txt`.

**Renamed:**

- None.

**Deleted:**

- None.

## Breaking Changes

- [x] No breaking changes

## Deployment Notes

- [x] No special deployment steps required. After merge: tag `v0.4.0`,
push, then run `./scripts/sync-dev-after-release.sh v0.4.0` to backport
release artifacts to `dev`.
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