ci: add workflows and rulesets to main - #5
Merged
Conversation
Add CI, release, finalize-release, guard-docs, guard-provenance workflows and branch ruleset snapshots. Enables CI to trigger on PRs targeting main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
25 tasks
brettdavies
added a commit
that referenced
this pull request
Apr 30, 2026
#36) ## Summary Closes the badge-discovery gap from todo `017`: after a passing `anc check .` run, the CLI now points authors at the agent-native badge embed snippet for *their* tool, instead of requiring them to land on the site, navigate to their scorecard page, and find the embed snippet there. Surface #5 of the cross-repo badge-surface design (surfaces #1–#4 shipped on the site/spec last wave). End-to-end on this repo (`agentnative-cli`, score 90%): ```text 🏆 Score: 90% — your tool qualifies for the agent-native badge. Embed in your README: [](https://anc.dev/score/agentnative-cli) Convention: https://anc.dev/badge ``` JSON consumers see the same data as a structured `badge` block — eligibility, score percent, embed snippet, scorecard/badge URLs, and the convention pointer. ## Changelog ### Added - `--output text` now appends an agent-native badge embed hint after the summary line when the tool clears the 80% eligibility floor. Below the floor, nothing badge-related is printed (the convention's "do not nag" rule). - `--output json` scorecard now includes a `badge` block (`eligible`, `score_pct`, `embed_markdown`, `scorecard_url`, `badge_url`, `convention_url`). `embed_markdown` is `null` below the floor; `scorecard_url` / `badge_url` are populated whenever a tool slug exists, since the site renders an SVG for every scored tool. ### Changed - Bumped scorecard `schema_version` from `"0.4"` to `"0.5"`. Pre-`0.5` consumers feature-detect the new `badge` key and continue to work. ## Type of Change - [x] `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: surface #5 of the badge-surface design (todo `017`, local-only at `.context/compound-engineering/todos/017-completed-p1-agent-native-badge-hint-on-passing-check.md`) - Issue: n/a (todo-tracked) - Architecture: `agentnative-site` `docs/plans/2026-04-23-002-feat-badge-surface-plan.md` (cross-repo coordinator for surfaces #1–#4); `agentnative-spec` `feat/badge-claim-convention` branch (spec-side claim convention) - Related PRs: surfaces #1–#4 (site/spec, already shipped) ## Testing - [x] Unit tests added/updated - [x] Integration tests added/updated - [x] Manual testing completed - [x] All tests passing **Test Summary:** - Unit tests: 443 passing (1 ignored) - Integration tests: 90 passing (1 network-gated ignored) - Coverage: no per-line coverage tool configured; pre-push hook (CI mirror) green — fmt, clippy `-Dwarnings`, full suite, cargo-deny, Windows compat Detail: - 11 new unit tests in `src/scorecard/mod.rs` cover `compute_badge` across eligible / below-floor / at-floor / Skips- excluded / no-scoring-data / empty-slug, plus `BadgeInfo::text_hint` and `format_text` integration. Full module passes 50/50. - 2 new end-to-end tests in `tests/scorecard_schema_v05.rs`: `schema_v05_badge_block_reflects_live_tool_slug` (asserts `badge.*` URLs share the live `tool.name` slug) and `schema_v05_badge_eligibility_flag_matches_score` (asserts `eligible == (score_pct >= 80)` and that `embed_markdown` is present iff `eligible`). - Schema drift guard updated end-to-end: `tests/integration.rs` `test_scorecard_json_has_stable_top_level_keys` now includes `badge` in EXPECTED, still bidirectional ("no unexpected keys" also fails on accidental additions). ## Files Modified **Modified:** - `src/scorecard/mod.rs` — `BADGE_ELIGIBILITY_FLOOR_PCT`, `BADGE_BASE_URL`, `BadgeInfo` struct, `compute_badge()`, `BadgeInfo::text_hint()`, `score_pct()` helper, `Scorecard.badge` field, `format_text` extended to accept `Option<&BadgeInfo>`. - `src/main.rs` — `derive_tool_name()` factored out of `build_tool_info` (cheap slug derivation, no version probe), used by the text-mode badge path so the hint and JSON `embed_markdown` always agree on the slug. - `tests/integration.rs`, `tests/scorecard_metadata_security.rs` — `schema_version` assertions bumped to `"0.5"`, `badge` added to the stable-keys EXPECTED set. - `AGENTS.md`, `CLAUDE.md` — documented the `0.5` `badge` block, eligibility floor, do-not-nag contract, and the text-mode hint path. **Created:** - (none — every new symbol lives inside an existing module) **Renamed:** - `tests/scorecard_schema_v04.rs` → `tests/scorecard_schema_v05.rs` (drift guard now covers `badge.*` keys). **Deleted:** - (none) ## Key Features - Single source of truth for the badge slug: text-mode and JSON both pass through `tool.name` (text-mode via the cheap `derive_tool_name`, JSON via `build_tool_info` → `metadata.tool.name`). Both call the same `compute_badge` so the printed embed snippet and the JSON `embed_markdown` cannot disagree. - Empty-slug guard: a derivable tool name is required for eligibility. Without one we can't construct the embed URL truthfully — emitting a placeholder would mislead authors. Pinned by `compute_badge_empty_slug_is_ineligible_even_at_perfect_score`. - Score formula matches the site leaderboard exactly: `pass / (pass + warn + fail)` rounded to integer percent. Skips and Errors are excluded from both sides of the ratio. Pinned by `compute_badge_skips_excluded_from_denominator`. ## Benefits - **Discovery**: a tool author who runs `anc check .` and clears the floor sees the embed snippet for *their* tool without leaving the terminal — no round-trip to the site. - **Safety by default**: below-floor runs print nothing badge-related. There is no "you're at 73%, try harder" nag. - **Consumer feature-detection**: the schema bump is additive. Pre-`0.5` consumers continue to work; new consumers can rely on `badge.*` paths being populated on every `--output json` run. - **Cross-surface consistency**: the same canonical `https://anc.dev/badge/<slug>.svg` and `/score/<slug>` URLs render in both the text hint and the JSON, derived from one tool slug, so a README link copied from either surface lands on the same scorecard. ## Breaking Changes - [x] No breaking changes - [ ] Breaking changes described below: The schema bump from `0.4` to `0.5` is additive. Pre-`0.5` consumers feature-detect the new `badge` key and continue to work. ## Deployment Notes - [x] No special deployment steps required - [ ] Deployment steps documented below: The eligibility floor (80%) is hardcoded as a named const (`BADGE_ELIGIBILITY_FLOOR_PCT`) sourced from the site's published convention (`agentnative-site/content/badge.md`). When the spec convention merges off `agentnative-spec` `feat/badge-claim-convention`, the floor will move into the vendored spec via `sync-spec` — that is a follow-up, not a blocker. ## Screenshots/Recordings Captured by self-dogfooding `cargo run -- check .` against this repo on the `feat/badge-embed-hint` HEAD. **Eligible run (text-mode tail) — score 90%, badge hint appended:** ```text 33 checks: 26 pass, 2 warn, 1 fail, 4 skip, 0 error 🏆 Score: 90% — your tool qualifies for the agent-native badge. Embed in your README: [](https://anc.dev/score/agentnative-cli) Convention: https://anc.dev/badge ``` **Eligible run (`--output json | jq '{schema_version, tool, badge}'`) — same slug, same URLs, structured fields:** ```json { "schema_version": "0.5", "tool": { "name": "agentnative-cli", "binary": "anc", "version": "0.1.0" }, "badge": { "eligible": true, "score_pct": 90, "embed_markdown": "[](https://anc.dev/score/agentnative-cli)", "scorecard_url": "https://anc.dev/score/agentnative-cli", "badge_url": "https://anc.dev/badge/agentnative-cli.svg", "convention_url": "https://anc.dev/badge" } } ``` **Below-floor run (`anc check . --principle 99 | tail -10`) — no badge hint, no nag:** ```text Code Quality [FAIL] No .unwrap() in source (code-unwrap) /home/brett/dev/agentnative-cli/build.rs:70:20 — path.file_name().unwrap().to_str().unwrap() /home/brett/dev/agentnative-cli/build.rs:70:20 — path.file_name().unwrap() /home/brett/dev/agentnative-cli/src/skill_install.rs:574:22 — v.to_possible_value().unwrap() /home/brett/dev/agentnative-cli/src/skill_install.rs:613:28 — parsed.to_possible_value().unwrap() 1 checks: 0 pass, 0 warn, 1 fail, 0 skip, 0 error ``` Confirms the do-not-nag contract: nothing badge-related appears below the eligibility floor. ## 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) ## Additional Context The todo flagged three open questions; this PR resolves them: - **Tool slug source** → derived from `tool.name` (project basename / command name / binary basename). A future `--tool-slug` flag can override this if registry slugs ever diverge from `tool.name`. Out of scope here. - **Eligibility floor source** → hardcoded const, sourced from the site convention. Migrates to the vendored spec once `agentnative-spec` `feat/badge-claim-convention` merges. - **Output format gating** → JSON gets the structured `badge` block on every run; text mode appends a hint only when eligible. UUIDv7 run identifier and content-addressable scorecard hash were both considered for inclusion in this schema bump and intentionally deferred — neither has a current consumer (the site doesn't archive per-run, the badge doesn't pin to a run, no signing story exists), and pre-launch additive bumps are cheap precisely so we don't have to pre-add hooks. Either is a clean additive bump when a consumer materializes.
11 tasks
brettdavies
added a commit
that referenced
this pull request
Jun 4, 2026
…tests (#80) ## Summary Closes a P0 correctness regression in `code-unwrap` surfaced by the adversarial review of merged PR #77. PR #77 (commit 817d6fa) introduced a tree-sitter walk that exempts `.unwrap()` calls inside `#[cfg(test)]`-gated items. The cfg-args parser, however, treats `not(...)` identically to `any(...)` and `all(...)`: it descends into the inner argument list without polarity tracking and returns `true` whenever the bare identifier `test` appears at any depth. The effect was that `#[cfg(not(test))]` on production-only code silently exempted that code from the audit, a regression confirmed both by reading the parser and by an injected adversarial test that panicked under the buggy logic. This PR threads a `negated: bool` parameter through `cfg_args_contain_test`. The bare-test branch returns `true` only at even parity (`!negated`); at odd parity (under a `not(...)`) the predicate means production code and the walker keeps scanning siblings in case another predicate resolves to test-gated. The recursive call for `not(...)` flips polarity; `any(...)` and `all(...)` preserve it. Double-negation (`not(not(test))`) correctly restores even parity. Also corrects the inner-attribute comment block (which claimed sticky propagation across all siblings while the code actually does one-shot reset) and pins the intentional asymmetry with a named test. Per plan #4, the diff is surgical: only the parser function, the inner-attribute comment, and the test module are touched. ## Changelog ### Fixed - `code-unwrap`: `#[cfg(not(test))]` no longer silently exempts production-only code from the audit. The cfg-args parser now tracks polarity through `not(...)` wrappers; `not(test)`, `any(not(test), unix)`, and `all(not(test), feature = "x")` correctly classify as production. Polarity-pinning tests cover the double-negation case (`not(not(test))`), the `not(any(test))` shape, `cfg_attr(test, …)` non-gating, and the inner-attribute one-shot reset asymmetry. ## Type of Change - [x] `fix`: Bug fix (non-breaking change which fixes an issue) ## Related Issues/Stories - Plan: `docs/plans/2026-06-03-004-fix-pr77-code-unwrap-cfg-not-test-polarity-plan.md` - Origin: adversarial review of merged PR #77 (commit 817d6fa) - Umbrella plan (audit philosophy, supersedes plans #2 and #5): `docs/plans/2026-06-03-001-refactor-audit-philosophy-structure-over-vocabulary-plan.md` ## Testing - [x] Unit tests added/updated - [x] All tests passing **Test Summary:** - `cargo test`: 846 passed, 2 ignored (8 suites). 9 new unit tests in `src/audits/source/rust/unwrap.rs`: `cfg_not_test_does_not_exempt_production_unwrap`, `cfg_any_not_test_unix_does_not_exempt`, `cfg_all_not_test_feature_does_not_exempt`, `cfg_any_test_or_not_feature_still_exempts`, `cfg_attr_test_does_not_exempt`, `inner_attribute_one_shot_consumes_on_use_not_fn`, `mod_tests_without_gate_does_not_exempt`, `cfg_not_not_test_does_exempt`, `cfg_not_any_test_does_not_exempt`. - `cargo clippy --all-targets -- -Dwarnings`: clean. - Dogfood (`anc audit .`): `code-unwrap` flags one production unwrap at `src/audits/behavioral/json_errors.rs:192` (`trimmed.chars().next().unwrap()`). The unwrap has no cfg gate; it surfaces because PR #79 added it and the audit wasn't re-dogfooded after that merge. Verified identical behavior on pre-fix `dev` via `git stash` + rebuild, ruling out a regression from this PR. Owned by plan #5 (PR #79 follow-up) or addressable as a one-line fix in a separate PR. - Dogfood (`anc audit ~/dev/xurl-rs`): `code-unwrap` continues to `pass`. xurl-rs has zero `#[cfg(not(test))]` items, so the polarity fix is a no-op against that target. ## Files Modified **Modified:** - `src/audits/source/rust/unwrap.rs`: thread `negated: bool` through `cfg_args_contain_test`; flip polarity on `not(...)` recursion (preserve on `any`/`all`); rewrite the inner-attribute comment block to match one-shot reset semantics and name the pinning test; add 9 negative-case unit tests. **Created:** - None. **Renamed:** - None. **Deleted:** - None. ## Breaking Changes - [x] No breaking changes The audit becomes strictly more accurate. Callers that previously relied on `#[cfg(not(test))]` exempting production code from `code-unwrap` were doing so accidentally, since that path was an unintended consequence of PR #77's missing polarity tracking. ## Deployment Notes - [x] No special deployment steps required ## Checklist - [x] Code follows project conventions and style guidelines - [x] Commit messages follow Conventional Commits - [x] Self-review of code completed - [x] Tests added/updated and passing - [x] No new warnings or errors introduced - [x] Changes are backward compatible
12 tasks
brettdavies
added a commit
that referenced
this pull request
Jun 4, 2026
… platform verbs (#83) ## Summary Follow-up to merged PR #76 addressing two coupled gaps surfaced during the post-merge adversarial review. `.anc.toml [p6] domain_verbs` lets any CLI declare its own subcommand vocabulary as "standard," but the scorecard row gave no signal whether the Pass was assisted. A domain-verb-assisted Pass was byte-identical in JSON to a built-ins-only Pass. Separately, of the 18 verbs PR #76 added to `STANDARD_VERBS`, 7 are X/Mastodon-specific (`post`, `repost`, `unrepost`, `quote`, `like`, `unlike`, `dm`). Their inclusion encoded one platform's vocabulary as universal, and let any social CLI Pass without exercising the `.anc.toml` opt-in PR #76 introduced. This PR adds scorecard transparency for domain-verb-assisted Passes and trims the platform-specific verbs out of the built-in list. Schema bumps `0.7 → 0.8` (additive). ## Changelog ### Added - Schema `0.8`: `using_domain_verbs: Option<bool>` and `domain_match_count: Option<usize>` on each `AuditResultView` row. Populated when `p6-standard-names` Passes via `.anc.toml [p6] domain_verbs` recognition; absent (not `null`) for rows that did not consult `domain_verbs`. - `MitigationInfo` carrier on `AuditResult` (typed, audit-agnostic) tracking built-in vs domain match counts and the first 5 matched domain-verb names in encounter order. - Pass row's `evidence` field is populated with the built-in vs domain ratio when assisted by `domain_verbs`, e.g. `"7/8 subcommands standard (3 via .anc.toml [p6].domain_verbs: [post, like, repost])"`. Rows without mitigation keep the historical `evidence: null` on Pass. ### Changed - `STANDARD_VERBS` no longer recognizes `post`, `repost`, `unrepost`, `quote`, `like`, `unlike`, `dm`. Social and platform-specific CLIs that previously Passed via built-ins must now declare these in `.anc.toml [p6] domain_verbs` (documented). - `audit_standard_names` returns a `StandardNamesResult` struct (status + optional `MitigationInfo`) instead of bare `AuditStatus`. - Warn evidence on `p6-standard-names` includes a pointer to `docs/solutions/architecture-patterns/anc-toml-domain-verbs-pattern-2026-06-03.md` so authors discover the opt-in. - `CLAUDE.md` schema history block updated to document the `0.6`, `0.7`, and `0.8` additions (the inline history had drifted at `0.5`). ### Fixed - Audit-identity collapse from PR #76: a `.anc.toml`-assisted Pass is now distinguishable in the scorecard JSON from an unassisted one. - Verb-list dilution from PR #76: platform-specific vocabulary no longer inflates the cross-CLI built-in list. ## Type of Change - [x] `fix`: Bug fix (non-breaking change which fixes an issue) ## Related Issues/Stories - Plan: `docs/plans/2026-06-03-003-fix-pr76-domain-verbs-scorecard-transparency-plan.md` - Origin: adversarial review of merged PR #76 (commit 9263c5c). - Umbrella plan (audit philosophy, supersedes plans #2 and #5 at the design level): `docs/plans/2026-06-03-001-refactor-audit-philosophy-structure-over-vocabulary-plan.md` - Pattern documentation: `docs/solutions/architecture-patterns/anc-toml-domain-verbs-pattern-2026-06-03.md` (already committed and pushed to the symlinked `solutions-docs` repo, commit `402354b`). ## Testing - [x] Unit tests added/updated - [x] Integration tests updated - [x] All tests passing **Test Summary:** - `cargo test`: 855 passed, 2 ignored (8 suites). Net +7 from baseline: 5 new R5 adversarial tests in `audit_standard_names`, 2 new format-evidence tests pinning the 5-example truncation contract. - `cargo clippy --all-targets -- -Dwarnings`: clean. - `cargo fmt --check`: clean. - `anc emit coverage-matrix --check`: clean. - Dogfood: `anc audit . --output json | jq '.results[] | select(.id == "p6-may-standard-names")'` returns `status: "pass"` with `using_domain_verbs` and `domain_match_count` absent from the row keys (`anc` itself has no `.anc.toml`). ## Files Modified **Modified:** - `src/types.rs`: new `MitigationInfo` struct; new optional `mitigation: Option<MitigationInfo>` field on `AuditResult`. - `src/audits/behavioral/standard_names.rs`: refactored `audit_standard_names` to return `StandardNamesResult`; bifurcated built-in vs domain match counting; populates `MitigationInfo` on domain-assisted Pass; trimmed 7 platform-specific verbs from `STANDARD_VERBS`; appends docs URL to Warn evidence; 13 unit tests (8 updated to the new return shape + 5 new adversarial + 2 format-evidence pinning tests). - `src/audits/behavioral/mod.rs`: made `standard_names` `pub(crate)` so the scorecard view layer can call `format_pass_evidence`. - `src/scorecard/mod.rs`: `SCHEMA_VERSION` bumped to `"0.8"`; `AuditResultView` gains `using_domain_verbs` and `domain_match_count`; `from_row` reads `r.mitigation`, populates the fields, and synthesizes Pass evidence prose via `format_pass_evidence`. - `schema/scorecard.schema.json`: `$id` pinned to `scorecard-v0.8`. - `tests/standard_names_integration.rs`: fixture retuned to `archive` + `follow` + `mentions` so the `.anc.toml` loader is genuinely exercised after the platform-verb trim. - `tests/integration.rs`, `tests/scorecard_metadata_security.rs`, `tests/scorecard_schema_v05.rs`: schema-version assertions bumped to `"0.8"`; the v0.7 → v0.8 schema `$id` rename pinned by `schema_v05_target_path_carries_no_separators` regression test. - `CLAUDE.md`: schema history block updated (was stuck at `0.5`); new `MitigationInfo` semantics block mirrors the existing per-version addition pattern. - ~77 audit and test files: mechanical `mitigation: None` insert in every `AuditResult { ... }` constructor. This is the unavoidable churn of adding a typed transparency field to the canonical result carrier. `cargo check` verified the sed hit only valid sites. **Created:** - None. (`docs/solutions/architecture-patterns/anc-toml-domain-verbs-pattern-2026-06-03.md` lives in the symlinked `solutions-docs` repo and was committed there separately.) **Renamed:** - None. **Deleted:** - None. ## Breaking Changes - [x] No breaking changes (schema `0.8` is additive) The 7 platform verbs removed from `STANDARD_VERBS` are a behavior tightening, not a contract break. CLIs that relied on them being built-ins were exploiting PR #76's overly broad vocabulary; the remediation path is `.anc.toml [p6] domain_verbs` (documented). ## Deployment Notes - [x] No special deployment steps required ## Checklist - [x] Code follows project conventions and style guidelines - [x] Commit messages follow Conventional Commits - [x] Self-review of code completed - [x] Tests added/updated and passing - [x] No new warnings or errors introduced - [x] Changes are backward compatible
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Carried CI workflows and branch protection rulesets from
devtomain. This is a non-release infrastructure PR that establishes the CI/CD pipeline and branch governance for the repository.Type of Change
ci: CI/CD configuration changesFiles Modified
Created:
.github/workflows/ci.yml.github/workflows/finalize-release.yml.github/workflows/guard-main-docs.yml.github/workflows/guard-main-provenance.yml.github/workflows/release.yml.github/rulesets/protect-dev.json.github/rulesets/protect-main.jsonChecklist