From 850a4d442d5d135f22c3f717a259040f40a5341b Mon Sep 17 00:00:00 2001 From: Qwynn Marcelle Date: Tue, 11 Aug 2026 10:11:53 -0400 Subject: [PATCH 1/2] feat(conformance): enforce canonical pair ordering, and report unmeasured properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `docs/conformance.md` stated that canonical pair ordering was enforced by the candidate-producer conformance suite. The suite examined `coChange[].files` not at all, so a producer emitting reversed endpoints passed a gate that advertised the check. Found by review on #26. The gate is added rather than the claim softened. Two checks, both scoped to producers: - every emitted observation-form pair is ordered by ascending UTF-8 bytes. A real byte comparison, not `<` — a bare string comparison is UTF-16 code unit order, and the two disagree on supplementary-plane characters, so `<` would accept an ordering the rule forbids; - no emitted entry stores a derived `rate`. Readers are untouched: pairs remain unordered with set semantics, reversed documents stay valid, and joins are by membership. This is a producer obligation only. A candidate that emits no observation-form entries is recorded as NOT MEASURED and counted SEPARATELY from passes. A property that could not be exercised has not been demonstrated, and folding it into the pass total would inflate the denominator with a check that measured nothing — the failure this suite exists to refuse. Watched-red: injecting a candidate output carrying a reversed pair and a stored rate turns the suite red (29 passed, 1 failed of 30, naming the ordering check); reverting returns 28 passed, 0 failed, 1 not measured. The current CLI candidate reports NOT MEASURED because the conformance fixture carries no git history and `generate` does not mine by default — exercising it needs a fixture with history and a public mining flag, which is a later increment. Gates: spec 264/264, rules 173/173, examples 11/11 + 12/12, typecheck, build, architecture, schema, corpus, docs, adr, packed all green. --- docs/conformance.md | 21 ++++++--- scripts/check-producer-conformance.mjs | 59 ++++++++++++++++++++++++++ scripts/producer-conformance-lib.mjs | 37 +++++++++++----- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/docs/conformance.md b/docs/conformance.md index 787f2f8..91bd2aa 100644 --- a/docs/conformance.md +++ b/docs/conformance.md @@ -409,11 +409,22 @@ Stated plainly, because a conformance document that hides them is misleading: locale collation, not case-folded, and with no Unicode normalization applied, since each of those is a different total order and ADR-006 forbids rewriting a stored key. The property this buys is that endpoint reversal produces - identical producer bytes, so a regenerated artifact is stable. Enforcement - belongs to the candidate-producer conformance suite that gates observation-form - emission — recorded in ADR-003 A-009, and out of scope for this repository; - what is pinned here by test is the rule itself and the fact that applying it - changes nothing about what readers accept. + identical producer bytes, so a regenerated artifact is stable. + + **Enforcement now lives in this repository's candidate-producer conformance + suite**, which checks that every emitted observation-form pair is ordered by + ascending UTF-8 bytes — a real byte comparison, not `<`, since a bare string + comparison is UTF-16 code unit order and the two disagree on + supplementary-plane characters. The suite also checks that no emitted entry + stores a derived `rate`. This paragraph previously said enforcement was out of + scope for this repository while the suite examined `coChange[].files` not at + all, so a producer emitting reversed endpoints passed a gate that advertised + the check. The gate was added rather than the claim softened. + + **A candidate that emits no observation-form entries is recorded as + `NOT MEASURED`, counted separately from passes.** A property that could not be + exercised has not been demonstrated, and folding it into the pass total would + inflate the denominator with a check that measured nothing. **`cochange-observations-v0.4.json` stores its pair as `["src/session.ts", "src/auth.ts"]`, which is *not* ascending UTF-8 order. diff --git a/scripts/check-producer-conformance.mjs b/scripts/check-producer-conformance.mjs index 696701d..954e092 100644 --- a/scripts/check-producer-conformance.mjs +++ b/scripts/check-producer-conformance.mjs @@ -192,6 +192,60 @@ section('5. Producer identity, determinism, and mediation'); check('output validates as v0.4 when it declares specVersion 0.4', doc.generated?.specVersion !== '0.4' || validateV4(doc) === true); + // Canonical pair ordering — the producer-profile obligation from ADR-003 + // A-009, enforced here rather than merely described. + // + // `docs/conformance.md` previously said this suite enforced it while nothing + // in the suite looked at `coChange[].files` at all, so a producer emitting + // reversed endpoints passed a gate that advertised the check. That is fixed + // here rather than by softening the claim. + // + // Scope, stated so the result is not over-read: readers stay unordered and a + // reversed document remains VALID. This is a producer obligation only, and it + // applies to what a candidate emits, never to what a consumer must accept. + const coChange = Array.isArray(doc.generated?.coChange) ? doc.generated.coChange : undefined; + const observationEntries = (coChange ?? []).filter((entry) => entry?.support !== undefined); + + if (observationEntries.length === 0) { + // Absence is reported as absence. A producer that emits no observation-form + // pairs has not demonstrated this property, and recording it as a pass + // would be the "green gate that measured nothing" failure this file exists + // to avoid. + check.notMeasured('canonical pair ordering — candidate emitted no observation-form coChange entries, so this property was not exercised'); + } else { + // UTF-8 BYTE order, not `<`. A bare string comparison is UTF-16 code unit + // order and the two disagree on supplementary-plane characters, so `<` + // would accept an ordering the rule forbids. + const compareUtf8 = (a, b) => Buffer.compare(Buffer.from(a, 'utf8'), Buffer.from(b, 'utf8')); + + // Malformed pairs are FAILED, not skipped. + // + // An earlier version folded the well-formedness test into the ordering + // filter, so an entry whose `files` was not a two-string array could not be + // counted as misordered and silently passed the ordering check. A + // structurally invalid observation must not be able to buy itself an + // exemption from the rule it cannot be evaluated against — that is the same + // "green gate that measured nothing" failure the NOT MEASURED channel above + // exists to prevent, arriving one level down. + const wellFormed = (entry) => + Array.isArray(entry.files) && entry.files.length === 2 && entry.files.every((p) => typeof p === 'string'); + const malformed = observationEntries.filter((entry) => !wellFormed(entry)); + check('every emitted coChange entry carries a two-string files pair', + malformed.length === 0, + `${malformed.length} of ${observationEntries.length} entr(ies) malformed, e.g. ${JSON.stringify(malformed[0]?.files)}`); + + const misordered = observationEntries.filter( + (entry) => wellFormed(entry) && compareUtf8(entry.files[0], entry.files[1]) > 0, + ); + check('every emitted coChange pair is ordered by ascending UTF-8 bytes', + misordered.length === 0, + `${misordered.length} of ${observationEntries.length} pair(s) reversed, e.g. ${JSON.stringify(misordered[0]?.files)}`); + + check('no emitted coChange entry stores a derived rate', + observationEntries.every((entry) => !('rate' in entry)), + 'a new observation producer must emit support + occurrences and must not emit rate'); + } + // Determinism: same repository, same producer version, byte-identical. const first = readArtifactRaw(repo); const second = runDirect(candidate, repo); @@ -241,6 +295,11 @@ section('5. Producer identity, determinism, and mediation'); // --------------------------------------------------------------------------- console.log(`\n${'='.repeat(70)}`); console.log(` RESULT: ${state.pass} passed, ${state.fail} failed (total ${state.pass + state.fail})`); +if (state.notMeasured.length > 0) { + // Reported separately and never folded into the pass count: a property the + // candidate gave no way to exercise has not been demonstrated. + console.log(` NOT MEASURED: ${state.notMeasured.length} — ${state.notMeasured.join('; ')}`); +} if (state.fail) console.log(` FAILED: ${state.failures.join(', ')}`); console.log('='.repeat(70)); process.exit(state.fail ? 1 : 0); diff --git a/scripts/producer-conformance-lib.mjs b/scripts/producer-conformance-lib.mjs index f9c26d8..641b57f 100644 --- a/scripts/producer-conformance-lib.mjs +++ b/scripts/producer-conformance-lib.mjs @@ -200,19 +200,34 @@ export function runMediated(candidate, repo) { // --------------------------------------------------------------------------- export function createReporter() { - const state = { pass: 0, fail: 0, failures: [] }; + const state = { pass: 0, fail: 0, failures: [], notMeasured: [] }; + const check = (label, condition, detail = '') => { + if (condition) { + console.log(` PASS ${label}`); + state.pass += 1; + } else { + console.log(` FAIL ${label}${detail ? `\n ${detail}` : ''}`); + state.fail += 1; + state.failures.push(label); + } + }; + + /** + * Record a property this candidate gave the suite no way to measure. + * + * Counted separately from `pass` on purpose. A property that could not be + * exercised has not been demonstrated, and folding it into the pass count + * would inflate the denominator with checks that measured nothing — the + * failure this suite refuses to skip for elsewhere. + */ + check.notMeasured = (label) => { + console.log(` N/A ${label}`); + state.notMeasured.push(label); + }; + return { state, - check(label, condition, detail = '') { - if (condition) { - console.log(` PASS ${label}`); - state.pass += 1; - } else { - console.log(` FAIL ${label}${detail ? `\n ${detail}` : ''}`); - state.fail += 1; - state.failures.push(label); - } - }, + check, section(title) { console.log(`\n${'='.repeat(70)}\n ${title}\n${'='.repeat(70)}`); }, From 49ae90de39e5d96832dadfb1f09a5b1cb4362f3c Mon Sep 17 00:00:00 2001 From: Qwynn Marcelle Date: Tue, 11 Aug 2026 10:13:17 -0400 Subject: [PATCH 2/2] feat(evidence): commit the first artifact carrying commit-history co-change evidence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first `workspace.json` anywhere in this lineage whose `generated.coChange` was produced by reading the commit graph. Every prior artifact was a working-tree scan; `generated.coChange` was specified by the schema and emitted by nothing. Candidate interoperability, NOT published-package interoperability. The published @workspacejson/spec@0.4.4 and @workspacejson/rules@0.4.4 REJECT this artifact — their schema predates ADR-003 A-009 and still requires `rate` while forbidding `support`. It was produced and validated against candidate builds packed from pinned source revisions, and it is reproducible from those revisions rather than from the registry. Producer candidate: workspacejson/cli @ 44d374b4dedfa7c61b14f06512d30dd751b3f508 Standard candidate: workspacejson/standard @ 8e08c8c5cd110e7f95bbd52246ea295c22b072e3 Both packed from clean detached worktrees at those revisions, so the build inputs are exactly the committed source. The install graph was verified to carry no registry substitution: every @workspacejson entry resolves `file:`, zero registry URLs, one spec copy. Version strings could not have distinguished candidate from registry, so the suite asserts the installed schema's SHAPE instead — including that the validator the producer calls accepts the observation form. History completeness: not shallow; 90 first-parent transitions available against a 500-transition window, so the window did not bind and the full first-parent history was analyzed. Artifact: sha256 9fff32e0c015a7ffc3411342afa4374e5fc63db3cd1c53c8618233b8cf92c81b basisRevision 8e08c8c5cd110e7f95bbd52246ea295c22b072e3 entries 50 (threshold support >= 3, ranked, capped at 50; min emitted 4) validation valid, 0 errors History-block sha256: 7012352617df37f442a627b8dfc334ed17d63dd2a69bb2d875f759bfddcc7b4f That block digest is the receipt that matters. The whole-file digest includes `generatedAt`, which records the generation run rather than the evidence; `basisRevision` is the authoritative freshness and provenance pin. All 50 entries carry exactly `files`, `occurrences`, `support` — no derived value, and no `generated` classification flag, because this producer implements no deterministic classifier and A-010 defines absence as unclassified. Every pair is ordered by ascending UTF-8 bytes; `support <= occurrences` throughout. Two runs are byte-identical. The second-ranked pair is packages/spec/schema/v1.json and packages/spec/src/schema.ts — the two schema mirrors. There is no import edge between them; neither file imports the other. The measured claim is the counts: of the 11 qualifying commits touching either file, 10 touched both. Whether that generalises beyond this repository is not established here. `.agents/RECEIPT.md` records the full provenance, verification counts, watched-red results and standing limits. No publication is authorized by this artifact, and no outreach may cite it beyond what is measured. --- .agents/RECEIPT.md | 185 ++++++++++++ .agents/workspace.json | 637 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 822 insertions(+) create mode 100644 .agents/RECEIPT.md create mode 100644 .agents/workspace.json diff --git a/.agents/RECEIPT.md b/.agents/RECEIPT.md new file mode 100644 index 0000000..a5944ed --- /dev/null +++ b/.agents/RECEIPT.md @@ -0,0 +1,185 @@ +# Artifact receipt — first commit-history evidence + +This records how `.agents/workspace.json` in this repository was produced, and +what may and may not be concluded from it. + +## What this is + +The first `workspace.json` artifact anywhere carrying **commit-history +co-change evidence** from the neutral producer. Every prior artifact in this +lineage was a working-tree scan; `generated.coChange` was specified and emitted +by nothing. + +## What this is NOT + +**This is candidate interoperability, not published-package interoperability.** + +The producer and the schema it was validated against are both *candidate builds +from pinned source revisions*, not registry releases. The published +`@workspacejson/spec@0.4.4` and `@workspacejson/rules@0.4.4` **reject** this +artifact: their schema predates ADR-003 amendment A-009 and still requires +`rate` while forbidding `support`. Publication remains frozen, so nobody can +reproduce this from the registry today. They can reproduce it from the source +revisions named below. + +## Producer + +| | | +| -- | -- | +| Producer | `workspacejson/cli` @ `031c3504a0977b8d90ac518c82a39a2f4ec741a9` — **merged to `main`** (PR #20) | +| Evidence basis | `workspacejson/standard` @ `8e08c8c5cd110e7f95bbd52246ea295c22b072e3` | + +Both were packed from **clean detached worktrees at those revisions**, not from +working trees, so the build inputs are exactly the committed source. + +**The producer identity was refreshed; the evidence basis was not.** An earlier +build of this artifact used producer `44d374b`, which carried a known defect: an +explicitly requested history refresh that could not complete fell back to the +previously recorded block *silently*, so a caller could not tell a refused +refresh from a completed one. That is fixed in `031c350`, and the artifact was +regenerated with the corrected producer **against the same `8e08c8c` evidence +basis**. + +Refreshing the evidence to a newer `standard` revision was deliberately NOT +done. `basisRevision` means "these observations were computed from this +revision"; advancing it because `main` moved would claim a measurement that was +never taken. A pin that lags `main` reads as *stale*, which is accurate and is +the pin doing its job. + +This doubles as a perturbation test, and it is recorded because a negative +result would have been a finding. The fix changes only refusal *signalling* on +the unsuccessful mining path, so the successful path must produce identical +output. It does: + +| | Before (`44d374b`) | After (`031c350`) | +| -- | -- | -- | +| `basisRevision` | `8e08c8c…` | **unchanged** | +| History-block `sha256` | `7012352617df…` | **unchanged** | +| `coChange` entries | 50 | 50, **byte-identical** | +| Fields that moved | — | `generatedAt` and `hygiene.scannedAt` only, both wall-clock | + +Had the history block moved, that would have meant the refusal fix altered the +successful mining path, and this receipt would not have been written. + +## Packed candidate identities + +| Package | `sha256` of tarball | +| -- | -- | +| `workspacejson-spec-0.4.4.tgz` | `2e0c326e7d8b50d3e3fa801944659803cd95d13dc253e65e1ace8dfccf949111` | +| `workspacejson-rules-0.4.4.tgz` | `548dd788725899ccaded6568121a271eeb593f143c581ec7e4714b50d9e5dbb7` | +| `workspacejson-cli-0.5.2.tgz` | `aa0ab7526a8f8fc6316f8b809d2ee5cdd04c80c5483a29d39e8dfbcc2e15ad18` | + +**These digests are pack-specific, not content identities.** `npm pack` produces +a gzip stream carrying file mtimes, so repacking the same source yields a +different digest. They pin *these* artifacts; the durable identity is the pair +of source revisions above. Recorded this way rather than presented as a +reproducible hash, because a digest that silently changes on every pack would be +a false anchor. + +The install graph was verified to contain **no registry substitution**: every +`@workspacejson/*` entry in the lockfile resolves `file:`, there are zero +registry URLs for them, and exactly one `@workspacejson/spec` copy exists in the +tree. Version strings could not have distinguished candidate from registry — +all three carry the same numbers as the published packages — so the candidate +suite additionally asserts the *shape* of the installed schema, including that +the validator the producer actually calls accepts the observation form. + +## History completeness + +| | | +| -- | -- | +| Repository | `workspacejson/standard` | +| Shallow | **no** (`git rev-parse --is-shallow-repository` = `false`) | +| First-parent transitions available | 90 | +| Analysis window | 500 transitions | +| Window bound the result | **no** — 90 < 500, so the full first-parent history was analyzed | + +This is complete history, not a truncated view. No artifact produced from a +shallow clone or otherwise incomplete history may be used as evidence. + +## The artifact + +| | | +| -- | -- | +| Path | `.agents/workspace.json` | +| Bytes | 20,417 | +| `sha256` | `9fff32e0c015a7ffc3411342afa4374e5fc63db3cd1c53c8618233b8cf92c81b` | +| `generated.specVersion` | `0.4` | +| `generated.basisRevision` | `8e08c8c5cd110e7f95bbd52246ea295c22b072e3` | +| `coChange` entries | 50 | +| Selection | threshold `support >= 3`, ranked, capped at 50; minimum emitted support 4 | +| Validation | **valid, 0 errors**, through `WorkspaceJsonValidator` from the candidate `rules` build | + +**History-block `sha256`: `7012352617df37f442a627b8dfc334ed17d63dd2a69bb2d875f759bfddcc7b4f`** + +That digest covers `basisRevision` + `coChange` under canonical key ordering, and +it is the receipt that actually matters. The whole-file digest includes +`generatedAt`, which records the generation run rather than the evidence. +**`basisRevision` is the authoritative freshness and provenance pin for the +co-change observations** — comparing `generatedAt` to the repository's current +revision says nothing about whether this block is stale. + +Two consecutive runs produced byte-identical output, including `generatedAt`, +because the producer detected no material change and did not rewrite the file. + +## Shape audit — all 50 entries + +- keys are exactly `files`, `occurrences`, `support`; +- **no `rate`** — no derived probability, lift, confidence or ranking is stored; +- **no `generated` flag** — this producer implements no deterministic + tooling-coupling classifier, and under A-010 absence means *unclassified* + rather than `false`; +- every pair ordered by ascending UTF-8 bytes; +- `support <= occurrences` throughout. + +## Highest-ranked observations + +| Pair | support | occurrences | +| -- | -- | -- | +| `packages/rules/package.json` ↔ `packages/spec/package.json` | 12 | 24 | +| `packages/spec/schema/v1.json` ↔ `packages/spec/src/schema.ts` | 10 | 11 | +| `packages/spec/src/index.test.ts` ↔ `packages/spec/src/schema.ts` | 10 | 15 | + +The second pair is the two schema mirrors. **There is no import edge between +them** — neither file imports the other, and no static analysis of this +repository relates them. The measured claim is what the counts say: of the 11 +qualifying commits that touched either file, 10 touched both. Whether that +generalises beyond this repository is not established here. + +## Verification at these revisions + +| Suite | Result | +| -- | -- | +| Candidate-contract suite, packed environment | **22/22** | +| Producer repo-native (`mining-core` / `cli` / `agents-audit-compat`) | 97 / 72 / 44 = **213/213** | +| Standard (`spec` / `rules`) | 264 / 173 | +| Standard examples | 11/11 positive, 12/12 negative | +| Producer conformance | 28 passed, 0 failed, **1 not measured** | +| Candidate-contract suite, rerun from the merged producer `031c350` | **22/22** | + +**Watched-red** — each stated guarantee was deliberately broken in the producer +source to confirm the suite detects it, then restored. A test that passes is +weak evidence; a test that fails when the behaviour it names is removed is +strong evidence. Each mutation below was rebuilt, repacked and clean-installed +before measurement: + +| Mutation | Result | +| -- | -- | +| Remove carry-forward — destroys existing evidence | 7 failed / 12 passed | +| Advance `basisRevision` without recomputing | 6 failed / 13 passed | +| Recompute history during ordinary generation | 1 failed / 18 passed | +| Drop the refresh-outcome field (silent fallback) | 2 failed / 20 passed | +| Report `mined: true` unconditionally | 1 failed / 21 passed | +| Restored | **22/22** | + +The conformance suite reports **1 not measured**: canonical pair ordering was +not exercised, because its fixture carries no git history and `generate` does +not mine by default. That is recorded as absence rather than counted as a pass. + +## Standing limits + +- No package publication is authorized by this artifact. +- No outreach or external target ranking may cite it beyond what is measured + here. +- Regenerating it requires the two source revisions above; the registry cannot + reproduce it while the freeze holds. diff --git a/.agents/workspace.json b/.agents/workspace.json new file mode 100644 index 0000000..fa463a1 --- /dev/null +++ b/.agents/workspace.json @@ -0,0 +1,637 @@ +{ + "manual": {}, + "generated": { + "specVersion": "0.4", + "generatedAt": "2026-08-11T14:09:41.759Z", + "by": { + "name": "@workspacejson/cli", + "version": "0.5.2" + }, + "frameworkManifest": [], + "conventions": [], + "fileIndex": { + ".changeset/README.md": {}, + ".changeset/config.json": {}, + ".changeset/lucky-pugs-invent.md": {}, + ".changeset/olive-crabs-observe.md": {}, + ".changeset/olive-hosts-settle.md": {}, + ".changeset/olive-keys-report.md": {}, + ".changeset/olive-moons-listen.md": {}, + ".changeset/tall-otters-classify.md": {}, + ".editorconfig": {}, + ".gitattributes": {}, + ".github/CODEOWNERS": {}, + ".github/ISSUE_TEMPLATE/bug_report.yml": {}, + ".github/ISSUE_TEMPLATE/config.yml": {}, + ".github/ISSUE_TEMPLATE/feature_request.yml": {}, + ".github/RELEASE-AUTHORITY.md": {}, + ".github/copilot-instructions.md": {}, + ".github/dependabot.yml": {}, + ".github/instructions/ci-release.instructions.md": {}, + ".github/instructions/conformance.instructions.md": {}, + ".github/instructions/governance-docs.instructions.md": {}, + ".github/instructions/repo-tooling.instructions.md": {}, + ".github/instructions/rules-engine.instructions.md": {}, + ".github/instructions/schema-contract.instructions.md": {}, + ".github/pull_request_template.md": {}, + ".github/workflows/adr-006-evidence.yml": {}, + ".github/workflows/ci.yml": {}, + ".gitignore": {}, + ".npmrc": {}, + "AGENTS.md": {}, + "CHANGELOG.md": {}, + "CODE_OF_CONDUCT.md": {}, + "CONTRIBUTING.md": {}, + "GOVERNANCE.md": {}, + "LICENSE": {}, + "MAINTAINERS.md": {}, + "OWNERSHIP.md": {}, + "README.md": {}, + "SECURITY.md": {}, + "SUPPORT.md": {}, + "assets/README.md": {}, + "assets/workspace-json-lockup-dark.png": {}, + "assets/workspace-json-lockup-light.png": {}, + "conformance/path-identity/README.md": {}, + "conformance/path-identity/baseline-normalize.mjs": {}, + "conformance/path-identity/corpus.json": {}, + "conformance/path-identity/receipt-baseline.json": {}, + "conformance/path-identity/run-baseline.mjs": {}, + "docs/adr/001-canonical-artifact-path.md": {}, + "docs/adr/002-bounded-enrichment-program.md": {}, + "docs/adr/003-field-lifecycle-and-admission.md": {}, + "docs/adr/004-root-version-compatibility.md": {}, + "docs/adr/005-schema-identity.md": {}, + "docs/adr/006-canonical-path-identity.md": {}, + "docs/adr/README.md": {}, + "docs/adr/experiments/006-path-identity/receipts-darwin.json": {}, + "docs/adr/experiments/006-path-identity/receipts-linux.json": {}, + "docs/adr/experiments/006-path-identity/run.mjs": {}, + "docs/adr/index.json": {}, + "docs/conformance.md": {}, + "docs/glossary.md": {}, + "docs/repository-settings.md": {}, + "docs/troubleshooting.md": {}, + "docs/versioning.md": {}, + "migration/PROVENANCE.md": {}, + "migration/architecture-red-tests.txt": {}, + "migration/commit-map.txt": {}, + "migration/parity-packed.mjs": {}, + "migration/parity-packed.txt": {}, + "migration/parity-runtime.mjs": {}, + "migration/parity-runtime.txt": {}, + "package.json": {}, + "packages/rules/CHANGELOG.md": {}, + "packages/rules/LICENSE": {}, + "packages/rules/README.md": {}, + "packages/rules/package.json": {}, + "packages/rules/src/engine/__tests__/finding-graph.test.ts": {}, + "packages/rules/src/engine/__tests__/hygiene-score.invariants.test.ts": {}, + "packages/rules/src/engine/__tests__/incremental-cache.test.ts": {}, + "packages/rules/src/engine/__tests__/rule-dependency-graph.test.ts": {}, + "packages/rules/src/engine/__tests__/rule-engine.test.ts": {}, + "packages/rules/src/engine/__tests__/temporal-decay.test.ts": {}, + "packages/rules/src/engine/finding-graph.ts": {}, + "packages/rules/src/engine/hygiene-score.ts": {}, + "packages/rules/src/engine/incremental-cache.ts": {}, + "packages/rules/src/engine/rule-dependency-graph.ts": {}, + "packages/rules/src/engine/rule-engine.ts": {}, + "packages/rules/src/engine/temporal-decay.ts": {}, + "packages/rules/src/index.ts": {}, + "packages/rules/src/parser/__tests__/agents-md-parser.test.ts": {}, + "packages/rules/src/parser/agents-md-parser.ts": {}, + "packages/rules/src/plugin.ts": {}, + "packages/rules/src/presets/index.ts": {}, + "packages/rules/src/rules/consistency/__tests__/convention-mismatch.test.ts": {}, + "packages/rules/src/rules/consistency/convention-mismatch.ts": {}, + "packages/rules/src/rules/drift/__tests__/framework-drift.test.ts": {}, + "packages/rules/src/rules/drift/framework-drift.ts": {}, + "packages/rules/src/rules/fragility/__tests__/blast-radius.test.ts": {}, + "packages/rules/src/rules/fragility/__tests__/churn-fragility.test.ts": {}, + "packages/rules/src/rules/fragility/blast-radius.ts": {}, + "packages/rules/src/rules/fragility/churn-fragility.ts": {}, + "packages/rules/src/rules/integrity/__tests__/missing-file-reference.test.ts": {}, + "packages/rules/src/rules/integrity/__tests__/pattern-zero-match.test.ts": {}, + "packages/rules/src/rules/integrity/missing-file-reference.ts": {}, + "packages/rules/src/rules/integrity/pattern-zero-match.ts": {}, + "packages/rules/src/rules/intelligence/__tests__/review-time-anomaly.test.ts": {}, + "packages/rules/src/rules/intelligence/review-time-anomaly.ts": {}, + "packages/rules/src/rules/meta/__tests__/rule-coverage-gap.test.ts": {}, + "packages/rules/src/rules/meta/rule-coverage-gap.ts": {}, + "packages/rules/src/rules/staleness/__tests__/section-staleness.test.ts": {}, + "packages/rules/src/rules/staleness/section-staleness.ts": {}, + "packages/rules/src/scanner/__tests__/repo-scanner.test.ts": {}, + "packages/rules/src/scanner/repo-scanner.ts": {}, + "packages/rules/src/testing/__tests__/real-repos.integration.test.ts": {}, + "packages/rules/src/testing/__tests__/rule-tester.test.ts": {}, + "packages/rules/src/testing/fixtures/agents-md/colocated-tests.md": {}, + "packages/rules/src/testing/fixtures/agents-md/dunder-tests.md": {}, + "packages/rules/src/testing/fixtures/agents-md/framework-heavy.md": {}, + "packages/rules/src/testing/fixtures/agents-md/minimal.md": {}, + "packages/rules/src/testing/fixtures/agents-md/missing-paths.md": {}, + "packages/rules/src/testing/fixtures/agents-md/python-package.md": {}, + "packages/rules/src/testing/fixtures/agents-md/stale-conventions.md": {}, + "packages/rules/src/testing/fixtures/agents-md/typescript-monorepo.md": {}, + "packages/rules/src/testing/fixtures/repos/clean-repo/package.json": {}, + "packages/rules/src/testing/fixtures/repos/clean-repo/src/index.test.ts": {}, + "packages/rules/src/testing/fixtures/repos/clean-repo/src/index.ts": {}, + "packages/rules/src/testing/fixtures/repos/python-package/pyproject.toml": {}, + "packages/rules/src/testing/fixtures/repos/python-package/src/main.py": {}, + "packages/rules/src/testing/fixtures/repos/ts-monorepo/package.json": {}, + "packages/rules/src/testing/fixtures/repos/ts-monorepo/packages/app/AGENTS.md": {}, + "packages/rules/src/testing/fixtures/repos/ts-monorepo/packages/app/package.json": {}, + "packages/rules/src/testing/fixtures/repos/ts-monorepo/packages/app/src/index.test.ts": {}, + "packages/rules/src/testing/fixtures/repos/ts-monorepo/packages/app/src/index.ts": {}, + "packages/rules/src/testing/rule-tester.ts": {}, + "packages/rules/src/types.ts": {}, + "packages/rules/src/validator/__tests__/workspace-json-validator.test.ts": {}, + "packages/rules/src/validator/workspace-json-validator.ts": {}, + "packages/rules/tsconfig.json": {}, + "packages/spec/CHANGELOG.md": {}, + "packages/spec/LICENSE": {}, + "packages/spec/README.md": {}, + "packages/spec/examples/cochange-absent-v0.4.json": {}, + "packages/spec/examples/cochange-empty-pinned-v0.4.json": {}, + "packages/spec/examples/cochange-empty-unpinned-v0.4.json": {}, + "packages/spec/examples/cochange-legacy-head-basis-v0.4.json": {}, + "packages/spec/examples/cochange-legacy-rate-v0.4.json": {}, + "packages/spec/examples/cochange-observations-v0.4.json": {}, + "packages/spec/examples/cochange-unclassified-v0.4.json": {}, + "packages/spec/examples/invalid/cochange-abbreviated-basis-revision.json": {}, + "packages/spec/examples/invalid/cochange-both-forms-zero-occurrences.json": {}, + "packages/spec/examples/invalid/cochange-both-representations.json": {}, + "packages/spec/examples/invalid/cochange-legacy-missing-generated.json": {}, + "packages/spec/examples/invalid/cochange-missing-basis-revision.json": {}, + "packages/spec/examples/invalid/cochange-mixed-forms-disguised.json": {}, + "packages/spec/examples/invalid/cochange-mixed-forms.json": {}, + "packages/spec/examples/invalid/cochange-negative-support.json": {}, + "packages/spec/examples/invalid/cochange-neither-representation.json": {}, + "packages/spec/examples/invalid/cochange-non-integer-occurrences.json": {}, + "packages/spec/examples/invalid/cochange-support-exceeds-occurrences.json": {}, + "packages/spec/examples/invalid/cochange-zero-denominator.json": {}, + "packages/spec/examples/minimal-v0.3.json": {}, + "packages/spec/examples/populated-v0.3.json": {}, + "packages/spec/examples/populated-v0.4.json": {}, + "packages/spec/examples/with-manual-block-v0.3.json": {}, + "packages/spec/package.json": {}, + "packages/spec/schema/v1.json": {}, + "packages/spec/src/cli.test.ts": {}, + "packages/spec/src/cli.ts": {}, + "packages/spec/src/index.test.ts": {}, + "packages/spec/src/index.ts": {}, + "packages/spec/src/path-identity.test.ts": {}, + "packages/spec/src/path-identity.ts": {}, + "packages/spec/src/schema.ts": {}, + "packages/spec/src/stored-key-inspection.test.ts": {}, + "packages/spec/src/stored-key-inspection.ts": {}, + "packages/spec/src/type-invariants.ts": {}, + "packages/spec/src/types.ts": {}, + "packages/spec/src/validator.ts": {}, + "packages/spec/tsconfig.json": {}, + "pnpm-lock.yaml": {}, + "pnpm-workspace.yaml": {}, + "scripts/adr-index.mjs": {}, + "scripts/adr-index.test.mjs": {}, + "scripts/check-architecture.mjs": {}, + "scripts/check-architecture.test.mjs": {}, + "scripts/check-corpus.mjs": {}, + "scripts/check-docs.mjs": {}, + "scripts/check-packed-path-identity.mjs": {}, + "scripts/check-producer-conformance.mjs": {}, + "scripts/check-producer-conformance.test.mjs": {}, + "scripts/producer-conformance-lib.mjs": {}, + "scripts/validate-examples.mjs": {}, + "scripts/verify-npm-publish-access.mjs": {}, + "scripts/verify-package-tarball.mjs": {}, + "scripts/verify-published.mjs": {}, + "scripts/verify-schema-provenance.mjs": {}, + "tsconfig.base.json": {}, + "types/ambient.d.ts": {} + }, + "topology": { + "packageCount": 5, + "type": "monorepo", + "ciProvider": "github-actions", + "agentFiles": { + "agentsMd": "AGENTS.md", + "workspaceJson": ".agents/workspace.json" + } + }, + "hygiene": { + "score": 76, + "grade": "C", + "failCount": 0, + "warnCount": 8, + "scannedAt": "2026-08-11T14:09:41.759Z" + }, + "basisRevision": "8e08c8c5cd110e7f95bbd52246ea295c22b072e3", + "coChange": [ + { + "files": [ + "packages/rules/package.json", + "packages/spec/package.json" + ], + "support": 12, + "occurrences": 24 + }, + { + "files": [ + "packages/spec/schema/v1.json", + "packages/spec/src/schema.ts" + ], + "support": 10, + "occurrences": 11 + }, + { + "files": [ + "packages/spec/src/index.test.ts", + "packages/spec/src/schema.ts" + ], + "support": 10, + "occurrences": 15 + }, + { + "files": [ + "packages/spec/schema/v1.json", + "packages/spec/src/index.test.ts" + ], + "support": 9, + "occurrences": 15 + }, + { + "files": [ + "packages/spec/schema/v1.json", + "packages/spec/src/types.ts" + ], + "support": 8, + "occurrences": 10 + }, + { + "files": [ + "packages/spec/src/schema.ts", + "packages/spec/src/types.ts" + ], + "support": 8, + "occurrences": 11 + }, + { + "files": [ + "packages/spec/CHANGELOG.md", + "packages/spec/package.json" + ], + "support": 8, + "occurrences": 23 + }, + { + "files": [ + ".github/workflows/ci.yml", + "package.json" + ], + "support": 7, + "occurrences": 11 + }, + { + "files": [ + "packages/spec/src/index.test.ts", + "packages/spec/src/types.ts" + ], + "support": 7, + "occurrences": 15 + }, + { + "files": [ + "packages/spec/src/index.test.ts", + "packages/spec/src/index.ts" + ], + "support": 7, + "occurrences": 16 + }, + { + "files": [ + "packages/spec/CHANGELOG.md", + "packages/spec/src/index.test.ts" + ], + "support": 7, + "occurrences": 20 + }, + { + "files": [ + "packages/spec/README.md", + "packages/spec/src/index.test.ts" + ], + "support": 7, + "occurrences": 21 + }, + { + "files": [ + "packages/spec/package.json", + "packages/spec/src/index.test.ts" + ], + "support": 7, + "occurrences": 25 + }, + { + "files": [ + "packages/spec/src/index.ts", + "packages/spec/src/types.ts" + ], + "support": 6, + "occurrences": 11 + }, + { + "files": [ + "packages/spec/schema/v1.json", + "packages/spec/src/index.ts" + ], + "support": 6, + "occurrences": 13 + }, + { + "files": [ + "packages/spec/src/index.ts", + "packages/spec/src/schema.ts" + ], + "support": 6, + "occurrences": 14 + }, + { + "files": [ + "packages/rules/README.md", + "packages/spec/README.md" + ], + "support": 6, + "occurrences": 15 + }, + { + "files": [ + "packages/rules/CHANGELOG.md", + "packages/rules/package.json" + ], + "support": 6, + "occurrences": 19 + }, + { + "files": [ + "packages/spec/package.json", + "packages/spec/src/index.ts" + ], + "support": 6, + "occurrences": 21 + }, + { + "files": [ + "CONTRIBUTING.md", + "SECURITY.md" + ], + "support": 5, + "occurrences": 7 + }, + { + "files": [ + "docs/versioning.md", + "packages/spec/src/index.test.ts" + ], + "support": 5, + "occurrences": 14 + }, + { + "files": [ + "packages/rules/CHANGELOG.md", + "packages/spec/CHANGELOG.md" + ], + "support": 5, + "occurrences": 15 + }, + { + "files": [ + "docs/conformance.md", + "packages/spec/src/index.test.ts" + ], + "support": 5, + "occurrences": 16 + }, + { + "files": [ + "packages/spec/CHANGELOG.md", + "packages/spec/src/index.ts" + ], + "support": 5, + "occurrences": 17 + }, + { + "files": [ + ".github/workflows/ci.yml", + "packages/spec/README.md" + ], + "support": 5, + "occurrences": 19 + }, + { + "files": [ + "packages/spec/README.md", + "packages/spec/schema/v1.json" + ], + "support": 5, + "occurrences": 19 + }, + { + "files": [ + "packages/spec/README.md", + "packages/spec/src/schema.ts" + ], + "support": 5, + "occurrences": 20 + }, + { + "files": [ + "packages/spec/CHANGELOG.md", + "packages/spec/README.md" + ], + "support": 5, + "occurrences": 22 + }, + { + "files": [ + "packages/spec/package.json", + "packages/spec/schema/v1.json" + ], + "support": 5, + "occurrences": 23 + }, + { + "files": [ + "packages/spec/package.json", + "packages/spec/src/schema.ts" + ], + "support": 5, + "occurrences": 24 + }, + { + "files": [ + "packages/spec/README.md", + "packages/spec/package.json" + ], + "support": 5, + "occurrences": 27 + }, + { + "files": [ + "docs/adr/README.md", + "scripts/check-docs.mjs" + ], + "support": 4, + "occurrences": 8 + }, + { + "files": [ + "docs/conformance.md", + "docs/versioning.md" + ], + "support": 4, + "occurrences": 8 + }, + { + "files": [ + "docs/versioning.md", + "packages/spec/schema/v1.json" + ], + "support": 4, + "occurrences": 11 + }, + { + "files": [ + ".github/workflows/ci.yml", + "CONTRIBUTING.md" + ], + "support": 4, + "occurrences": 12 + }, + { + "files": [ + ".github/workflows/ci.yml", + "SECURITY.md" + ], + "support": 4, + "occurrences": 12 + }, + { + "files": [ + "docs/versioning.md", + "packages/spec/src/schema.ts" + ], + "support": 4, + "occurrences": 12 + }, + { + "files": [ + ".github/workflows/ci.yml", + "packages/rules/README.md" + ], + "support": 4, + "occurrences": 13 + }, + { + "files": [ + ".github/workflows/ci.yml", + "types/ambient.d.ts" + ], + "support": 4, + "occurrences": 13 + }, + { + "files": [ + "docs/versioning.md", + "packages/spec/README.md" + ], + "support": 4, + "occurrences": 15 + }, + { + "files": [ + "docs/conformance.md", + "packages/spec/README.md" + ], + "support": 4, + "occurrences": 17 + }, + { + "files": [ + "packages/spec/README.md", + "types/ambient.d.ts" + ], + "support": 4, + "occurrences": 17 + }, + { + "files": [ + "packages/spec/src/index.test.ts", + "types/ambient.d.ts" + ], + "support": 4, + "occurrences": 17 + }, + { + "files": [ + "packages/rules/package.json", + "scripts/verify-package-tarball.mjs" + ], + "support": 4, + "occurrences": 18 + }, + { + "files": [ + "packages/spec/package.json", + "scripts/verify-package-tarball.mjs" + ], + "support": 4, + "occurrences": 18 + }, + { + "files": [ + "packages/spec/CHANGELOG.md", + "packages/spec/schema/v1.json" + ], + "support": 4, + "occurrences": 19 + }, + { + "files": [ + "packages/spec/README.md", + "packages/spec/src/index.ts" + ], + "support": 4, + "occurrences": 19 + }, + { + "files": [ + ".github/workflows/release.yml", + "packages/rules/package.json" + ], + "support": 4, + "occurrences": 20 + }, + { + "files": [ + "packages/spec/CHANGELOG.md", + "packages/spec/src/schema.ts" + ], + "support": 4, + "occurrences": 20 + }, + { + "files": [ + "packages/rules/CHANGELOG.md", + "packages/spec/package.json" + ], + "support": 4, + "occurrences": 21 + } + ] + }, + "agents": {}, + "health": { + "intelligenceState": "INSUFFICIENT_DATA", + "observationCount": 0, + "confidence": 0 + } +}