From c16bfde8d8e7383bb0af0d986bb4824edf610403 Mon Sep 17 00:00:00 2001 From: Jake Ruesink Date: Sun, 9 Aug 2026 20:45:35 -0500 Subject: [PATCH 1/2] feat(audit): upstream-authored rules are unscored, not just undated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Answering the ask: exempt from rule-quality scoring too, but scoped per check rather than removed from the scoring surface — and the signal is stated, not dropped. postiz-app: CRITICAL -> NEEDS WORK. openclaw stays NEEDS WORK. arbor and every repo without an upstream remote are untouched, because the check never fires there. Two parts. Low-Yield now exempts upstream content alongside pointers. A finding about the quality of a vendor's documentation is not actionable: acting on it means rewriting someone else's docs to satisfy our audit. Same justification as the date exemption, so the same treatment. Scoped to Low-Yield DELIBERATELY, not to the whole scoring surface. Upstream's AGENTS.md really is loaded into our sessions, so it still counts toward Context Load Pressure, redundancy and conflict — postiz still reports 187 always-on lines. Dropping it from everything would understate load we actually pay, which is the mistake the pointer exclusion in #41 nearly made. The exclusion is named in the detail rather than silent — "0/0 scoring files miss Why or Examples (excludes 2 pointer/upstream docs)" — so a reader still learns that upstream ships rules we judged low-yield, without a blocking finding we have no standing to act on. That is the informational-but-unscored option, reached by reusing the mechanism already there. The rename is also fixed, and n=1 does not argue against it the way it argued against the basename discriminator. That check was a PROXY for renames and could collide; this one is content identity. Git's blob hash IS the content, so a file whose blob appears anywhere in the upstream tree contains zero bytes of ours no matter what it is called, and one edited character diverges the hash and drops the exemption. The bound comes from construction rather than sample size. Verified on the real case: HEAD:AGENTS.md and upstream/main:CLAUDE.md are both blob 2704017990140945f94d2ab68ef641f2a0fdb8a8. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/audit.ts | 30 ++++++++- scripts/lib/upstream-authorship.test.ts | 83 +++++++++++++++++++++++++ scripts/lib/upstream-authorship.ts | 47 ++++++++++++++ 3 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 scripts/lib/upstream-authorship.test.ts diff --git a/scripts/audit.ts b/scripts/audit.ts index 15de220..9c08cec 100644 --- a/scripts/audit.ts +++ b/scripts/audit.ts @@ -856,6 +856,22 @@ function requiresExplicitTier(ruleFile: RuleFile): boolean { * Pattern docs and bootstrap templates ship as reference material, so they were * never part of the loaded surface. */ +/** + * Trailing " (excludes N pointer/upstream doc(s))" when any were dropped. + * + * The signal is not lost, only unscored: a reader still sees that upstream ships + * rules we judged low-yield, without a blocking finding we have no standing to act on. + */ +function describeLowYieldExemptions( + scoringRuleFiles: RuleFile[], + candidates: RuleFile[], +): string { + const dropped = scoringRuleFiles.length - candidates.length; + return dropped > 0 + ? ` (excludes ${dropped} pointer/upstream doc${dropped === 1 ? "" : "s"})` + : ""; +} + /** Trailing " (+N chain-loaded, +N path-scoped, +N mirror twin)" when non-zero. */ function describeLazyLoad(load: { chainLoadedLines: number; @@ -2182,8 +2198,18 @@ export function assessStageD( // document to score. Note this is scoped to Low-Yield deliberately: the same // file still counts toward Context Load Pressure, because Claude really does // load the shim in addition to what it imports. + // Upstream's rules join pointers here: a finding about the quality of a + // vendor's documentation is not actionable, because acting on it means + // rewriting a vendor's docs to satisfy our audit. Same justification as the + // date exemption — we did not write it. + // + // Scoped to low-yield deliberately, and NOT to the whole scoring surface. + // Upstream's AGENTS.md really is loaded into our sessions, so it still counts + // toward Context Load Pressure, redundancy and conflict. Dropping it from + // everything would understate load we actually pay, which is the mistake the + // pointer exclusion in #41 nearly made. const lowYieldCandidates = scoringRuleFiles.filter( - (ruleFile) => !ruleFile.importsRootMirror, + (ruleFile) => !ruleFile.importsRootMirror && !ruleFile.isUpstreamAuthored, ); const lowYieldRules = lowYieldCandidates.filter( (ruleFile) => !ruleFile.hasWhySection || !ruleFile.hasExamplesSection, @@ -2263,7 +2289,7 @@ export function assessStageD( status: lowYieldStatus, detail: toolNativeAdvisoryLowYield ? `${lowYieldRules}/${lowYieldCandidates.length || 0} scoring files miss Why or Examples; tool-native-first surface keeps this advisory while duplication/conflict/load stay healthy` - : `${lowYieldRules}/${lowYieldCandidates.length || 0} scoring files miss Why or Examples`, + : `${lowYieldRules}/${lowYieldCandidates.length || 0} scoring files miss Why or Examples${describeLowYieldExemptions(scoringRuleFiles, lowYieldCandidates)}`, }, ]; diff --git a/scripts/lib/upstream-authorship.test.ts b/scripts/lib/upstream-authorship.test.ts new file mode 100644 index 0000000..7870673 --- /dev/null +++ b/scripts/lib/upstream-authorship.test.ts @@ -0,0 +1,83 @@ +import { afterAll, expect, test } from "bun:test"; +import { writeFileSync } from "node:fs"; +import { join } from "node:path"; + +import { + resolveUpstreamRef, + upstreamAuthoredFiles, +} from "./upstream-authorship.ts"; +import { + cleanupFixtures, + commitFile, + gitOrThrow, + makeRepo, + makeTempDir, +} from "../__tests__/git-fixtures.ts"; + +afterAll(() => { + cleanupFixtures(); +}); + +/** A fork with an `upstream` remote pointing at a local bare repo. */ +function makeFork(): { fork: string } { + const upstream = makeTempDir("anvil-upstream-"); + gitOrThrow(upstream, ["init", "--bare", "--initial-branch", "main", "-q"]); + + const seed = makeRepo(); + commitFile(seed, "CLAUDE.md", "# Upstream rules\n\nTheirs.\n", "docs: rules"); + gitOrThrow(seed, ["remote", "add", "origin", upstream]); + gitOrThrow(seed, ["push", "-q", "-u", "origin", "main"]); + + const fork = makeTempDir("anvil-fork-"); + gitOrThrow(fork, ["clone", "-q", upstream, "."]); + gitOrThrow(fork, ["config", "user.name", "Anvil Fixture"]); + gitOrThrow(fork, ["config", "user.email", "fixture@example.invalid"]); + gitOrThrow(fork, ["remote", "add", "upstream", upstream]); + gitOrThrow(fork, ["fetch", "-q", "upstream"]); + return { fork }; +} + +test("no upstream remote means the check never fires", () => { + const plain = makeRepo(); + + expect(resolveUpstreamRef(plain)).toBeNull(); + expect(upstreamAuthoredFiles(plain).size).toBe(0); +}); + +test("an untouched upstream file is upstream-authored", () => { + const { fork } = makeFork(); + + expect(upstreamAuthoredFiles(fork).has("CLAUDE.md")).toBe(true); +}); + +test("a file we renamed but did not write is still upstream's", () => { + // Our own contract: rename CLAUDE.md -> AGENTS.md on every fork so Codex can + // read it. That gives the file a local commit and no counterpart at its own + // path upstream, so path-and-history alone calls upstream's document ours. + const { fork } = makeFork(); + gitOrThrow(fork, ["mv", "CLAUDE.md", "AGENTS.md"]); + gitOrThrow(fork, ["commit", "-q", "-m", "chore: make AGENTS.md canonical"]); + + const authored = upstreamAuthoredFiles(fork); + + expect(authored.has("AGENTS.md")).toBe(true); +}); + +test("editing one character drops the exemption", () => { + // The bound comes from construction: git's blob hash IS the content, so any + // edit diverges the hash and the file becomes ours to answer for. + const { fork } = makeFork(); + gitOrThrow(fork, ["mv", "CLAUDE.md", "AGENTS.md"]); + writeFileSync(join(fork, "AGENTS.md"), "# Upstream rules\n\nOurs now.\n"); + gitOrThrow(fork, ["add", "--", "AGENTS.md"]); + gitOrThrow(fork, ["commit", "-q", "-m", "docs: our own guidance"]); + + expect(upstreamAuthoredFiles(fork).has("AGENTS.md")).toBe(false); +}); + +test("a file we authored ourselves is never exempt", () => { + const { fork } = makeFork(); + commitFile(fork, "OURS.md", "# Ours\n", "docs: ours"); + + expect(upstreamAuthoredFiles(fork).has("OURS.md")).toBe(false); +}); diff --git a/scripts/lib/upstream-authorship.ts b/scripts/lib/upstream-authorship.ts index f8af028..6b1e45e 100644 --- a/scripts/lib/upstream-authorship.ts +++ b/scripts/lib/upstream-authorship.ts @@ -78,5 +78,52 @@ export function upstreamAuthoredFiles(repoRoot: string): Set { authored.add(trimmed); } } + + for (const path of renamedUpstreamFiles(repoRoot, ref)) { + authored.add(path); + } return authored; } + +/** + * Files we renamed but did not write. + * + * We rename `CLAUDE.md` to `AGENTS.md` on every fork so Codex can read it, which + * gives the file a local commit and no counterpart at its own path upstream — + * so path-and-history alone calls upstream's document ours. + * + * The test is content identity, not a guess about renames: git's blob hash IS + * the content, so a file whose blob appears anywhere in the upstream tree + * contains zero bytes of ours no matter what it is called. Edit one character + * and the hash diverges and the exemption drops. That bound comes from + * construction rather than from sample size, which is what separates this from + * the basename discriminator that had to be abandoned. + */ +function renamedUpstreamFiles(repoRoot: string, ref: string): string[] { + const upstreamBlobs = new Set(); + for (const line of (git(repoRoot, ["ls-tree", "-r", ref]) ?? "").split( + "\n", + )) { + // ` blob \t` + const sha = line.split(/\s+/)[2]; + if (sha) { + upstreamBlobs.add(sha); + } + } + if (upstreamBlobs.size === 0) { + return []; + } + + const renamed: string[] = []; + for (const line of (git(repoRoot, ["ls-tree", "-r", "HEAD"]) ?? "").split( + "\n", + )) { + const parts = line.split("\t"); + const sha = (parts[0] ?? "").split(/\s+/)[2]; + const path = (parts[1] ?? "").trim(); + if (sha && path && upstreamBlobs.has(sha)) { + renamed.push(path); + } + } + return renamed; +} From 7e7d0920fe06bc2be992e9decc572faa5f66166a Mon Sep 17 00:00:00 2001 From: Jake Ruesink Date: Sun, 9 Aug 2026 20:46:21 -0500 Subject: [PATCH 2/2] chore: bump 0.1.0-alpha.18 Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 8 ++++---- docs-site/public/llms-full.txt | 2 +- docs-site/src/content/docs/reference/cli.md | 2 +- docs/byok-trust-model.md | 2 +- docs/first-user-proof-packet.md | 6 +++--- docs/first-user-proof.md | 4 ++-- docs/getting-started.md | 6 +++--- docs/proofs/current-outside-tester-send-packet.md | 14 +++++++------- package.json | 2 +- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index cf2719b..48e0b70 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Relative `--target` paths resolve from your current shell cwd. If you are alread Choose the lane that matches your setup before your first run. -If you are collecting the outside-user proof, stay on the exact pinned version and launcher from [First User Proof](docs/first-user-proof.md) instead of switching to the unpinned examples in this README. The current pinned `0.1.0-alpha.17` proof packet uses one repo-root `bunx` command with `--ci --output ./anvil-audit.md` so the saved report comes back from the first run. +If you are collecting the outside-user proof, stay on the exact pinned version and launcher from [First User Proof](docs/first-user-proof.md) instead of switching to the unpinned examples in this README. The current pinned `0.1.0-alpha.18` proof packet uses one repo-root `bunx` command with `--ci --output ./anvil-audit.md` so the saved report comes back from the first run. ### Local-only first pass (no provider required) @@ -151,7 +151,7 @@ bun run ./bin/anvil.ts --version Verified on the current alpha packet: - `--help` prints the four shipped entry commands: `audit`, `drift`, `bootstrap`, `mine-pr` -- `--version` prints `0.1.0-alpha.17` +- `--version` prints `0.1.0-alpha.18` Why you might choose this lane: @@ -173,10 +173,10 @@ For first-run setup and CI/lint guidance, see: Lambda Curry maintains this project with internal automation behind it, but that machinery is secondary to the public product path above. -- **Status:** Report as Decision Tool shipped; current charter follow-through is to collect outside-Lambda-Curry first-run proof on pinned `0.1.0-alpha.17` +- **Status:** Report as Decision Tool shipped; current charter follow-through is to collect outside-Lambda-Curry first-run proof on pinned `0.1.0-alpha.18` - **Verification posture:** CI artifact (audit report) + downstream observed impact in rule quality - **Current checked-in self-audit:** `docs/audits/anvil-audit-2026-08-08.md` reports `98/100` Structural Lint, `35/35` Guardrail Readiness, `0` issues, and `0` remediation tasks on current `main` -- **Current proof packet:** `docs/proofs/current-outside-tester-send-packet.md` keeps the external proof lane on one canonical repo-root command that saves `./anvil-audit.md`; the pinned packet stays on `@lambdacurry/anvil@0.1.0-alpha.17` +- **Current proof packet:** `docs/proofs/current-outside-tester-send-packet.md` keeps the external proof lane on one canonical repo-root command that saves `./anvil-audit.md`; the pinned packet stays on `@lambdacurry/anvil@0.1.0-alpha.18` Anvil is not primarily a UI project. Its real proof surface is whether downstream outputs and consumers reflect the intended rule behavior correctly. diff --git a/docs-site/public/llms-full.txt b/docs-site/public/llms-full.txt index 576c06d..fc19156 100644 --- a/docs-site/public/llms-full.txt +++ b/docs-site/public/llms-full.txt @@ -745,7 +745,7 @@ anvil audit --target ./my-repo [options] Relative `--target` paths resolve from your current shell cwd. -If you arrived here from the external first-user proof docs, use the exact pinned command from that packet. The current `0.1.0-alpha.17` packet uses the public `--ci` spelling; `--no-ai` remains only as a deprecated compatibility alias. +If you arrived here from the external first-user proof docs, use the exact pinned command from that packet. The current `0.1.0-alpha.18` packet uses the public `--ci` spelling; `--no-ai` remains only as a deprecated compatibility alias. ## `anvil drift` diff --git a/docs-site/src/content/docs/reference/cli.md b/docs-site/src/content/docs/reference/cli.md index 45499fc..944aeab 100644 --- a/docs-site/src/content/docs/reference/cli.md +++ b/docs-site/src/content/docs/reference/cli.md @@ -32,7 +32,7 @@ anvil audit --target ./my-repo [options] Relative `--target` paths resolve from your current shell cwd. -If you arrived here from the external first-user proof docs, use the exact pinned command from that packet. The current `0.1.0-alpha.17` packet uses the public `--ci` spelling; `--no-ai` remains only as a deprecated compatibility alias. +If you arrived here from the external first-user proof docs, use the exact pinned command from that packet. The current `0.1.0-alpha.18` packet uses the public `--ci` spelling; `--no-ai` remains only as a deprecated compatibility alias. ## `anvil drift` diff --git a/docs/byok-trust-model.md b/docs/byok-trust-model.md index 44a4b87..a3249fe 100644 --- a/docs/byok-trust-model.md +++ b/docs/byok-trust-model.md @@ -18,7 +18,7 @@ By default, Anvil scans your repo locally, then expects a working AI provider fo If you want the privacy-first path, run: -> **Current alpha note:** The published `0.1.0-alpha.17` proof packet uses one canonical repo-root `bunx` command with `--ci --output ./anvil-audit.md`. Packaged relative `--target` and `--output` paths still resolve from your shell cwd, so normal repo-relative first-run commands are honest when you use the unpinned command (it tracks the latest published build). +> **Current alpha note:** The published `0.1.0-alpha.18` proof packet uses one canonical repo-root `bunx` command with `--ci --output ./anvil-audit.md`. Packaged relative `--target` and `--output` paths still resolve from your shell cwd, so normal repo-relative first-run commands are honest when you use the unpinned command (it tracks the latest published build). ```bash # zero-install diff --git a/docs/first-user-proof-packet.md b/docs/first-user-proof-packet.md index dbbe84d..40e8e71 100644 --- a/docs/first-user-proof-packet.md +++ b/docs/first-user-proof-packet.md @@ -20,7 +20,7 @@ Send back whether it worked first try, the first useful fix the report pointed t bunx @lambdacurry/anvil@ audit --target . --ci --output ./anvil-audit.md ``` -Replace `` with the specific published build you want validated. The current `0.1.0-alpha.17` proof packet sends only the repo-root saved-report command above so the artifact comes back from the same first run without asking the tester to choose between layouts. +Replace `` with the specific published build you want validated. The current `0.1.0-alpha.18` proof packet sends only the repo-root saved-report command above so the artifact comes back from the same first run without asking the tester to choose between layouts. Helpful docs: - Getting started: https://lambda-curry.github.io/anvil/getting-started/first-audit @@ -54,7 +54,7 @@ Before sending the note above, make sure: ## 3. Exact command blocks to send -Pick one install path and one shell layout, then send only that exact command so the tester is not choosing between multiple moving parts. For the current `0.1.0-alpha.17` packet, the canonical layout is Bun zero-install from the target repo root. +Pick one install path and one shell layout, then send only that exact command so the tester is not choosing between multiple moving parts. For the current `0.1.0-alpha.18` packet, the canonical layout is Bun zero-install from the target repo root. Replace `` before you send anything. Do not use the floating `@alpha` tag in the external proof packet. @@ -128,7 +128,7 @@ bun run verify:first-user-proof -- docs/proofs/YYYY-MM-DD--first-user-pr ``` The validator returns a deterministic `counts` / `does-not-count` result and names the missing proof fields or contract mismatches directly. -For the current pinned `0.1.0-alpha.17` proof lane, that includes checking that the retained audit command keeps the packet's `--ci` spelling. +For the current pinned `0.1.0-alpha.18` proof lane, that includes checking that the retained audit command keeps the packet's `--ci` spelling. When the packet keeps a local report artifact, it also requires `Saved report path or screenshot link` to match the retained audit command's `--output` path. Save one small packet with these fields: diff --git a/docs/first-user-proof.md b/docs/first-user-proof.md index 822d0c1..a58098a 100644 --- a/docs/first-user-proof.md +++ b/docs/first-user-proof.md @@ -15,7 +15,7 @@ Capture one real outside-Lambda-Curry run that proves: Do this only after the exact published version you want to validate is live, and before Milestone 3 is called complete. -Do not send this packet with the floating `@alpha` tag. Replace `` in the command below with the specific published build you are validating, for example `0.1.0-alpha.17`. +Do not send this packet with the floating `@alpha` tag. Replace `` in the command below with the specific published build you are validating, for example `0.1.0-alpha.18`. ## Suggested tester profile @@ -115,7 +115,7 @@ bun run verify:first-user-proof -- docs/proofs/YYYY-MM-DD--first-user-pr ``` That validator checks the outside-tester status, pinned CLI version, first-try success, returned artifact, and other minimum packet fields, then returns `counts` or `does-not-count` with explicit reasons. -For the current pinned `0.1.0-alpha.17` proof lane, it requires the retained audit command to keep the exact `--ci` spelling from the packet. +For the current pinned `0.1.0-alpha.18` proof lane, it requires the retained audit command to keep the exact `--ci` spelling from the packet. ## Done signal for Milestone 3 gate diff --git a/docs/getting-started.md b/docs/getting-started.md index 3d4caa2..fd80a1b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -64,7 +64,7 @@ anvil --version What you should see in the current alpha: - `--help` lists the four shipped commands: `audit`, `drift`, `bootstrap`, `mine-pr` -- `--version` prints `0.1.0-alpha.17` +- `--version` prints `0.1.0-alpha.18` If you are validating Anvil from a cloned checkout instead of a global install, run: @@ -125,7 +125,7 @@ Top 5 improvements: ## Save the report to a file -> **Current alpha note:** The published `0.1.0-alpha.17` proof packet uses one canonical repo-root `bunx` command with `--ci --output ./anvil-audit.md`, while the packaged CLI still resolves relative `--target` and `--output` paths from your shell cwd on `bunx`, `npx`, and Bun global install. Normal relative-path examples are honest when you use the unpinned command (it tracks the latest published build). +> **Current alpha note:** The published `0.1.0-alpha.18` proof packet uses one canonical repo-root `bunx` command with `--ci --output ./anvil-audit.md`, while the packaged CLI still resolves relative `--target` and `--output` paths from your shell cwd on `bunx`, `npx`, and Bun global install. Normal relative-path examples are honest when you use the unpinned command (it tracks the latest published build). ```bash # zero-install with bunx @@ -235,7 +235,7 @@ anvil audit \ `--ci` keeps discovery, drift detection, coverage scoring, and markdown output local. The report headline becomes `Structural Lint Score`, and the improvement section is generated from repo-local heuristics instead of a provider. -`--no-ai` still works as a deprecated compatibility alias for the same mode. The current external first-user proof packet stays pinned to `0.1.0-alpha.17` and uses `--ci` for the local-only lane. +`--no-ai` still works as a deprecated compatibility alias for the same mode. The current external first-user proof packet stays pinned to `0.1.0-alpha.18` and uses `--ci` for the local-only lane. Privacy-first example artifact from the same example target: diff --git a/docs/proofs/current-outside-tester-send-packet.md b/docs/proofs/current-outside-tester-send-packet.md index 983cc19..214a31b 100644 --- a/docs/proofs/current-outside-tester-send-packet.md +++ b/docs/proofs/current-outside-tester-send-packet.md @@ -2,7 +2,7 @@ Use this packet to route one outside-Lambda-Curry tester through Anvil's remaining Milestone 3 proof lane. -This packet stays pinned to `@lambdacurry/anvil@0.1.0-alpha.17`. Do not swap the tester onto the floating `@alpha` tag. +This packet stays pinned to `@lambdacurry/anvil@0.1.0-alpha.18`. Do not swap the tester onto the floating `@alpha` tag. ## Three-line opener @@ -15,7 +15,7 @@ Send back whether it worked first try, the first useful fix the report pointed t ## Exact command to send ```bash -bunx @lambdacurry/anvil@0.1.0-alpha.17 audit --target . --ci --output ./anvil-audit.md +bunx @lambdacurry/anvil@0.1.0-alpha.18 audit --target . --ci --output ./anvil-audit.md ``` Send this as the only command. It assumes the tester is already in the target repo root, guarantees the saved report path, and keeps the local-only flag aligned with current public docs. @@ -27,7 +27,7 @@ Could you try one first-run Anvil audit on a real repo of yours? Paste the single command below from that repo's root; it saves `./anvil-audit.md`, stays local, and does not require an AI provider. ```bash -bunx @lambdacurry/anvil@0.1.0-alpha.17 audit --target . --ci --output ./anvil-audit.md +bunx @lambdacurry/anvil@0.1.0-alpha.18 audit --target . --ci --output ./anvil-audit.md ``` Helpful docs: @@ -39,7 +39,7 @@ What I'd love back: 1. Whether the exact command worked on the first try 2. If it did not, what failed first 3. If you changed the launcher or command, what you used instead - - If you switched to global `anvil`, keep both the pinned `bun add -g @lambdacurry/anvil@0.1.0-alpha.17` line and the `anvil audit ...` line together in `Exact command`. + - If you switched to global `anvil`, keep both the pinned `bun add -g @lambdacurry/anvil@0.1.0-alpha.18` line and the `anvil audit ...` line together in `Exact command`. 4. Whether you ran it from the repo root or somewhere else 5. The first useful fix the report pointed to, if any 6. Anything that felt confusing, too internal, or too hand-wavy @@ -47,7 +47,7 @@ What I'd love back: - If you send back the saved report path itself, keep `./anvil-audit.md`, the exact path the retained command wrote with `--output`. If you want one extra cross-check, this should print the same pinned version: -`bunx @lambdacurry/anvil@0.1.0-alpha.17 --version` +`bunx @lambdacurry/anvil@0.1.0-alpha.18 --version` If you changed launchers before the successful run, use the matching `--version` command from that same install path instead of mixing launchers in the saved packet. Do not append `anvil --version` to a `bunx` or `npx` proof packet. @@ -57,7 +57,7 @@ Count this as Milestone 3 proof only if all of these are true: - the tester is outside Lambda Curry - the tester completes a successful first run on a real repo -- the retained audit command keeps the pinned `0.1.0-alpha.17` local-only `--ci` spelling +- the retained audit command keeps the pinned `0.1.0-alpha.18` local-only `--ci` spelling - the exact command and returned artifact are retained in a saved proof packet - any rough edge found is captured as follow-up work @@ -79,7 +79,7 @@ bun run verify:first-user-proof -- docs/proofs/YYYY-MM-DD--first-user-pr ``` Run that verifier from an Anvil repo checkout or an unpacked published Anvil package root; the verifier now ships with the same proof-doc bundle. -It keys validation off the saved packet's `Pinned CLI version`, so this retained `0.1.0-alpha.17` packet can still be checked after current `main` advances to a later package version. +It keys validation off the saved packet's `Pinned CLI version`, so this retained `0.1.0-alpha.18` packet can still be checked after current `main` advances to a later package version. Historical note: the original dated retained packet for this same pinned proof lane remains at `docs/proofs/2026-05-23-alpha4-outside-tester-send-packet.md`. diff --git a/package.json b/package.json index f95b03b..b53b8d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lambdacurry/anvil", - "version": "0.1.0-alpha.17", + "version": "0.1.0-alpha.18", "description": "AI rules + engineering guardrails audit engine for AI-assisted codebases", "keywords": [ "agents",