[CLI] META-195 — populate generated.fileIndex and generated.frameworkManifest - #11
Conversation
fileIndex was emitted as `{}` unconditionally and frameworkManifest as bare
AGENTS.md tokens at a hardcoded confidence 0.5. Both now derive from repository
evidence, deterministically.
fileIndex is the tracked file inventory, keyed by repository-root-relative
POSIX path, entries empty. Keys are the load-bearing part: the consumer adapter
extracted under META-248 joined by key presence and never read a value, so an
empty index made every such join return zero rows. The per-file values the
schema names are behavioral and only derivable from git history, which
META-195's experimental boundary keeps out of the stable contract pending the
VR-526 ruling.
frameworkManifest now emits only frameworks a declared manifest dependency
corroborates, at 0.9. The schema documents this field as "Detected frameworks
(confidence >= 0.7)", so every entry the producer emitted before sat under its
own contract's floor and a consumer filtering at the threshold read an empty
manifest. Corroboration matches the fallback branch frameworkDrift already
uses, so the emitter and the rule that audits it agree.
Both fields sit inside the material projection, so both are explicitly sorted
with a locale-independent comparator and neither touches RepoState.gitHistory,
which is a moving 30-day window whose no-git fallback is "every file".
Updates the ratified parity baseline: artifact-equivalence and `generate
--dry-run` were already expected differences from META-203, but their recorded
reason no longer described the full diff.
…ETA-195)
Substring containment awarded 0.9 confidence on lexical overlap. `vite` is a
substring of `vitest`, so a repository installing only vitest — an ordinary
setup, not an edge case — published `vite` at the confidence that tells a
consumer to trust it. That inverts the field's whole point and contradicts this
change's own "absent, not wrong" policy.
Corroboration is now exact case-insensitive dependency equality, and tokens are
normalized before deduping so `React` and `react` cannot both survive.
Adds negative regressions (vite/vitest, rest/interest, next.js/next) plus a
positive control so the guard cannot pass by rejecting everything. The
tailwind -> tailwindcss expectation codified the old behavior as desirable; it
now asserts the omission, alongside the corroborating `tailwindcss` token.
Corrects two claims that were wrong rather than merely imprecise:
- the comment said this matched `frameworkDrift`. It does not: that rule
looks a token up in the internal variant map and skips it when unmapped,
so it never matches on a raw token.
- a conformance test was named for wall-clock movement it never performed.
Git runs in a subprocess, so no in-process clock fake reaches it. The
immunity is structural — neither builder is passed RepoState.gitHistory —
and the test is renamed to what it actually establishes.
|
Fixed in c8505ad. The substring defect was real and worse than the
Changed
Two corrections where the review was right that I was wrong, not merely imprecise
The byte-stability unit test now compares reversed input rather than calling the same function twice. Scope held as advised: no cross-repo change. Exporting Verification after the fix The new negative regression was verified to bite: restoring substring matching turns 2 of the framework tests red. The parity baseline note was updated to record that matching is exact and why, since it now describes a narrower set of entries than before. |
Closes META-195. All four of its blockers (META-236, META-240, META-203, META-200) are Done.
Before this, the producer emitted:
fileIndex— the tracked inventory, and nothing moreKeys are repository-root-relative POSIX paths from
git ls-files; entries are{}.Keys alone are the load-bearing part. The consumer adapter extracted under META-248 joined by
hasOwnProperty(fileIndex, key)and never read a value — sofileIndex: {}is precisely why that join silently returned zero rows.Entries are deliberately empty. The per-file values the schema names —
fragility,aiModificationCount,humanModificationCount— are all behavioral, and the only evidence available for them is git-derived. META-195's experimental boundary keeps that harness-side pending VR-526, which is open because a weak git-scan fragility formula diverges from live scores. Publishing that number into the four-path stable contract is the disputed act, so this emits keys and claims nothing about them.frameworkManifest— corroborated detection onlyThe published schema describes this field as "Detected frameworks (confidence >= 0.7). Read by Buildomator." Every entry the producer emitted was a bare AGENTS.md token at
0.5— under its own contract's floor, universally. A consumer filtering at the documented threshold read an empty manifest. AJV only enforces0..1numerically, so this validated while contradicting the field's stated semantics.Entries are now those a declared manifest dependency corroborates by exact case-insensitive name equality, at
0.9. Tokens are normalized to lowercase before deduping, soReactandreactcannot both survive.Why exact, not containment
An earlier revision of this PR matched by substring. That is categorically unsound against the vocabulary it matches, not merely edge-casey — these are all real
KNOWN_FRAMEWORKStokens sitting inside real dependency names:vitevitestnestnestjs,@nestjs/coreredisiorediszodzod-to-json-schemagraphqlgraphql-tagrestrestify,interestSome of those collisions are outright false positives and others accidentally approximate a legitimate alias, and containment cannot distinguish the two. Publishing either at
0.9tells a consumer to trust a claim the repository does not support. Recovering aliases safely requires a curated map, not a string test.It therefore under-reports, deliberately.
@workspacejson/ruleskeepsFRAMEWORK_MANIFEST_MAPandKNOWN_FRAMEWORKSinternal — neither is exported. Re-typing that table here would fork standard-owned knowledge, which META-195's own contract-owner clause forbids and which is the split-brain META-200 just spent a week reconciling. So tokens whose dependency is published under a different name (next.js→next,nestjs→@nestjs/core,drizzle→drizzle-orm,tailwind→tailwindcss) are omitted rather than guessed. Absent, not wrong.Exporting that map from
workspacejson/standardis the real fix and is deliberately left to a follow-up, so the DataHub-criticalfileIndexwork is not coupled to a cross-repository contract change.This is not the test
frameworkDriftperforms, and does not claim to be. That rule looks a token up in the internal variant map,continues when unmapped, and only then compares — so it never matches on a raw token the way this must.Determinism — the actual hazard was not the one the issue named
Both fields sit inside the material projection (
generatedProjectionexcludes onlygeneratedAtandby), so any churn rewrites the artifact every run and breaksgenerate --checkas a CI gate.The issue warned about map iteration order, absolute paths, timestamps and float churn. The real landmine is
RepoState.gitHistory:filesChangedLast30Daysisgit log --since=30 days ago— a moving wall-clock window that changes with zero commits as history ages out;[...Set]in git-log order, so unsorted;getTrackedFilesdegrades from sortedgit ls-filesto fast-glob.Independence from it is structural: neither builder is passed
gitHistoryat all. Their signatures take onlyfilesand(tokens, manifests), so there is nothing time-varying to read. No test here simulates wall-clock movement — git runs in a subprocess, so no in-process clock fake would reach it, and a test claiming otherwise would be theatre.Both builders sort explicitly with the default comparator, never
localeCompare, which is locale-dependent and would make the written bytes vary by host.Verification
Both guards were verified to bite rather than pass vacuously:
fileIndex: {}/0.5emission turns 3 of the 6 new conformance tests red;Framework coverage is guarded in both directions — negative regressions (
vite/vitest,rest/interest,next.js/next), a positive exact-match control so the guard cannot pass by rejecting everything, case-variant dedupe, and the deliberatetailwind/tailwindcssomission.Real output on this repository: 76 files indexed, 4.3 KB artifact.
frameworkManifestis[]here — honestly so: this repo's AGENTS.md contains no framework tokens at all.Parity baseline
artifact-equivalenceandagents-audit generate --dry-runwere already ratified expected differences from META-203, so the gate still passes mechanically. But that file records why each difference is authorized, and its own header says an entry is a claim a human made. The META-203 block asserted the verified diffs were onlyspecVersionand+ conventions, which is no longer true. Updated to describe both fields, cite META-195, record that matching is exact and why — including thatframeworkManifest's entry count can now drop against the frozen source, since an unbacked token is omitted rather than published below its contract's floor.Noted, not fixed
types/ambient.d.tshand-declaresnode:child_processwithstdio?: 'pipe'only, sostdio: 'ignore'fails typecheck. This is the same shadowing-of-Node-builtins defectOWNERSHIP.mdflags as a follow-up after META-248 — which turns out to cover more thannode:fs. Worked within it rather than expanding this PR's scope.Boundary
@workspacejson/specand@workspacejson/rulesconsumed as released packages, unmodified.agents-auditcompatibility preserved — both binaries still route through one implementation (META-247).