gate: generation-aware corpus baselines and reasoned semantic ratchets #45
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Depends on
Reference
h-dv/code-index#45
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Current state
The deterministic tier-1 structural ratchet shipped:
Timing was deliberately excluded from exact ratchets because shared-runner wall time varied by more than 3x. Scale jobs record it and enforce generous no-hang/RSS ceilings instead.
This issue remains open for the missing populations and for runtime-plugin generation baselines.
Goal
Every semantic engine change produces a reviewable, generation-identified diff. Deterministic structural/work metrics gate exactly; noisy operational metrics use ceilings and recorded trends; raw resolution percentage never gates.
Remaining scope
Tier-3 structural baselines
Add deterministic baselines for the weekly rust-analyzer/django corpora:
Counts alone are insufficient once package generations coexist; baselines identify active semantic generation.
Runtime-plugin baselines
For #75 reference packages record:
A package/rule change without semantic-version bump still produces a baseline diff because identity is content-derived.
Enforcement tiers
Hard deterministic gates:
Reason-required ratchets:
Recorded/ceiling metrics:
Diagnostics only:
Anti-gaming
Blessing requires:
A baseline cannot be blessed from a degraded resolver or failed plugin generation without an explicit allowlisted reason/state.
Relationships
Acceptance
buildagent referenced this issue2026-07-28 22:06:08 +02:00
Landed (partially) —
tests/corpus/baseline.json+crates/indexer/tests/corpus_ratchet.rsExact structural counts pinned per tier-1 repo, gated on any drift, wired into the nightly
corpusjob.Why this suite exists
Every other corpus suite asks "is this index self-consistent?" — deterministic, coherent with a cold index, free of phantom rebinds. None of them notices a change that is self-consistent but unintended. A resolver tweak that quietly moves 4,000 refs passes all of them. #52 is the worked example: it changed resolution on real repos and no test could see it.
resolvedis the dimension this exists for.Counts only — timings deliberately excluded
The issue's Tier B listed wall clock, RSS, DB bytes and tokens-per-answer as ratchet candidates. I implemented only the count dimensions, and I think the rest should stay out:
corpus_determinism), so exact equality is a legitimate gate;That is not theoretical: while implementing this,
resolver.rs's existing 10sbounded_workassertion failed under load ~45 and then passed 3/3 in isolation on the same binary. Filed as #55. Timings stay recorded-only intarget/corpus/scale-*.json.Anti-gaming
COSI_CORPUS_BLESS=1requiresCOSI_CORPUS_BLESS_REASON(rejected below 8 chars) and writes the reason into the committed baseline, so an intentional change shows up in review. This project has already shipped a metric that "passed" because a derived value was mutated rather than measured (I030b'simpact_batches), so the escape hatch is deliberately noisy.Verified non-vacuous, not assumed
rust-ripgrep: resolved 13760 -> 13759 (-1)COSI_CORPUS_BLESS=1without a reason → refusedA finding the baseline surfaced immediately
js-expresshasimports: 0. Express is CommonJS and the javascript plugin does not capturerequire()as an import. Recorded in the baseline as a known oddity rather than hidden — adjacent to #31 item 2. If it ever becomes non-zero that is an improvement, and blessing it should cite the cause.Still open on this issue
precision_gate, determinism/coherence incorpus_metamorphic, ceilings incorpus_scale) rather than centralised here — arguably fine, but not what the issue described.resolution_gapshistogram as a diagnostic artifact) is not implemented; it belongs with #47's gap miner.feat: corpus baselines + ratchets — metrics JSON per repo, regression fails, --bless carries a reasonto gate: generation-aware corpus baselines and reasoned semantic ratchetsCOSI_CORPUS_REQUIRE=1has a floor of one, not of nine — 8 of 9 repos can skip and the run passes green #108The decision, first: the pinned corpus stays BUILTIN-ONLY — and that stops being a comment and becomes a gate
This issue's remaining scope reads, literally, as install a package into the pinned corpus so baselines can carry generation identity. That was considered and is refused, for three reasons, each measured against the tree rather than argued.
1. It would move a baseline that must not move. A package claiming a file changes what is indexed, so
tests/corpus/baseline.json— the CONTENT ratchet — would have to be blessed for a COVERAGE reason.corpus_cost.rsalready refused exactly this for the cost half, in those words, and the argument transfers unchanged. Its md5 is534084b856c22566c48e386bc41ed67eand has not moved across #77, #78 or this change.2. The separate package-bearing corpus leg ALREADY EXISTS — #84 phase 4 shipped it.
ruby_package_parity.rsruns a real external package (tests/packages/ruby,de.h-dv.ruby) over a real tier-1 corpus repo (ruby-sinatra), asserts the producer of every file out offile_contributions → extraction_components → plugin_packages, asserts all six pool capabilities GRANTED incomponent_capabilities, and compares the resolved projection row for row against the builtin leg with every delta carrying a named mechanism.ruby_package_cost.rsratchets what that leg costs on its own third baseline with its own third switch. So "add a package-bearing corpus leg beside the builtin control" — option 2 — is done. What was missing was a gate that the other corpus stays package-free.3. So that assertion becomes a measurement. "The pinned corpus installs no packages" appeared three times in
ci.ymland in two module docs and nothing anywhere measured it.corpus_stage.rsnow compares every pinned repo's recordedplugin_generations.activation_digestagainst a liveactivation::builtins_only_digest(), before it looks at any count. All seven repos recordsha256:e675fb55…. The day someone installs a package into a pinned repo, that fails by name; it can no longer arrive as unexplained drift inbaseline.json.Why the activation digest is not a ratcheted dimension (a correction to this issue's text)
The issue asks for "package digest, ABI/engine version" in the baselines. The digest cannot go there:
HostIdentity::host_versionisenv!("CARGO_PKG_VERSION")and is hashed into every activation digest — it is the only carrier of the compiled-in builtin plugin set, so it cannot be dropped. A committed digest would go red on every release for no semantic reason and would be blessed reflexively until it meant nothing.The split taken instead:
builtins_only_digest(), so both sides move together on a release and the comparison stays about packages;activationblock:fact_abi 1.0,engine wasmtime 36.0.14,rule_engine_version 0,embedded_dispatch_version 0;identity_before/identity_after, which is where a point-in-time value belongs and is exactly what the anti-gaming list asks for.What actually shipped:
tests/corpus/stage-baseline.json+crates/indexer/tests/corpus_stage.rsThe fourth corpus artifact, with the fourth bless switch (
COSI_STAGE_BLESS/COSI_STAGE_BLESS_REASON). Four artifacts, four switches, four reasons.rule.refs.resolved_by, named throughindex::resolved_by::ALLstage.resolver_health,stage_budget.*influence.refs.influence, named throughinfluence::nameproducer.file_contributions → extraction_components → plugin_packagesrule.is the dimension this exists for, and it closes acceptance 4.corpus_ratchetis structurally blind to a change that moves binds between resolver rules: every ref still resolves, to the same target, with the same kind, so all thirteen of its dimensions are byte-identical. That is themember_accessre-kind of v0.10.2 one level down. It is also the only way to get a stage-specific diff —corpus_cost's whole-repovm_stepband can only say "SQLite did 8% more work somewhere".stage.answersm0032_resolver_health's own instruction. That migration's doc says: "resolver_healthis compared by NO test anywhere … Anything added to this table is guarded by review and by nothing else. Write the test." A stage over budget leaves refs unresolved deliberately; on the corpus that reads as a small negative inresolved, indistinguishable from a precision fix.Coverage, measured: 18 of the 19 codes in
resolved_by::ALLfire in this baseline. The one that does not isbridge(rule 90) — a DECLARED cross-language bridge, which only exists where a package does. The decision above, seen from the resolver's side; it is graded on the package leg instead.Anti-gaming: 1/5 → 5/5, and ENFORCED rather than advised
corpus_ratchetrequires a non-empty reason of ≥8 characters and leaves the other four to discipline.corpus_stage::bless_verdictrefuses a bless that:identity_beforeis read out of the previous bless'sidentity_after, never recomputed);COSI_STAGE_BLESS_DEGRADEDnames each degraded stage — a blanket1is refused.Requirement 3 doubles as "for target movement, a reviewed id-independent target diff, not only counts": a rule code is id-independent by construction, and the file is written one key per line so
git diffshows precisely which rule moved.The mutation that is the whole argument (RUN, both suites, same mutated binary)
resolved_by::TIER1B_SAME_DIRECTORY: 12 → 11— the same-directory arm's binds get tagged as the qualified-bypass arm. Not one ref changes its target.4,968 binds re-attributed across seven real repos, and the content ratchet is green. Restoring
index.rs(verified by md5) returns both to green.Second corpus mutation, on the
stage.half, viaCODE_INDEX_TIER3_WORK_BUDGET=0— how the recall cliff actually happens:Ten further mutations were run against the unit arms (each bless refusal, the reader/writer round trip, the key-set diff, the digest gate, the identity chain) plus one against
ci.ymlproving the suite's CI registration is graded bycorpus_require_floor— all RED, all restored by md5.Cost: 12.3 s, executed=7, 14 positive controls, registered on the per-push
corpusjob.What is deliberately NOT done, with the reason
Tier-3 structural baselines. A suite cannot span tiers under
COSI_CORPUS_REQUIRE=1: the per-pushcorpusjob fetches tier 1 only, so a tier-3 repo would skip asSkipClass::Unavailableand #108's floor would fail the job — the same constraintci.ymlalready states forupgrade_equivalence. A tier-3 stage baseline is therefore a second file in the nightlycorpus-scalejob, not a widening of this one. It is wherestage.would pay most, because tier 3 is the scale at which a budget can actually trip. Confirmed by audit:corpus_scale.rsasserts only a wall-clock ceiling and an RSS ceiling and records the counts.The identity half of "cold == incremental == watcher-converged". The gap is real but it is a FIXTURE property, not a product one, and the fixture's own doc says so.
generation_equivalence'scoldandordinary_passcallindex_path_with_packages(…, None, …)andwatcher_legstarts aWatcherwith nopackagesin itsWatchOpts— so all three legs are builtins-only and no leg but the promoted one can carry a package identity. Production is not in that state:watcher.rspassesex.packages(). Closing it needs the fixture to hand a realPackageSetto all three legs — which is #41 acceptance 7 territory and should be built with it, not bolted on here. What exists today is honest and should not be described as more:the_activation_identity_moves_even_when_no_row_doesasserts the digest moves per transition, and the triangle is a projection triangle over five id-independent projections.A per-SITE rule projection in a committed baseline. These are grouped counts, so two refs swapping rules is invisible — the same limitation
corpus::project_influence's doc records about its own former histogram form. The per-site form exists and is used where it belongs (corpus::project_rules, inside the equivalence triangle andupgrade_equivalence). A committed baseline cannot hold 340,000 rows; a triangle does not need to be committed.Recommendation
Everything under "Runtime-plugin baselines" in this issue that means put a package in the pinned corpus should be closed as not worth doing, with the named replacements:
ruby_package_parity+ruby_package_cost(#84 phase 4) for the package leg, andcorpus_stage's digest gate for the guarantee that the control corpus stayed a control. What remains open is the tier-3 second file and #41's acceptance 7.#45.2: the stale blessing is worse than stale — the field that records it grades nothing
Verified with the index (
search_textscoped to the file,read_codeon the spans), not grep.Measured
tests/corpus/ruby-package-cost.jsonreason)tests/packages/ruby/plugin.toml)The defect, which is not the staleness
code_index_indexer::migrations::CURRENT_VERSIONoccurs exactly once incrates/indexer/tests/ruby_package_cost.rs— at line 236, inside the bless writer:It is written when you bless and never read when you assert.
the_package_leg_cost_stays_within_the_blessed_bandmeasures both legs and compares them to the recorded band without ever asking whether the band was recorded under the schema now running.So
_blessed.schemalooks like a guard and is decoration. This is the house failure mode exactly — two states rendering identically: "this band was measured under the schema you are running" and "this band was measured two schema versions ago" produce the same green.The package version is worse off still: it is not a field at all, only prose inside
reason, so nothing could compare it even in principle.Why it matters here specifically
This file's own header argues the wall-clock ratio is "the only bound in this repository that can see a slow wasm guest". A bound with that job, comparing against a band measured under different conditions and unable to say so, is asserting less than it appears to. Three schema versions and a package minor have gone by since the record was taken.
The repair, in the order that keeps it honest
_blessed.schemamust equalCURRENT_VERSIONat assert time, and the failure must say re-measure, not re-bless. Add the package version as a real field beside it and assert it the same way.Do not make this pass by widening the band or by re-blessing before step 1 exists — that reproduces the same blind spot one version later.
Mutation that must be run for the fix to count
Set the recorded schema to
CURRENT_VERSION - 1and confirm RED naming the schema. If it is green, the assertion is not reading the file it claims to.🤖 Generated with Claude Code
https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
#45.2, the migrated-full-language half: MET. The XAML half: not started, named below.
Follow-up to the diagnosis two comments up. The repair was taken in the order that comment prescribed, and step 2's RED is real rather than predicted.
Step 1 — assert the recorded conditions
_blessed.schemaand a new_blessed.package_versionare now fields that are read, before any band is compared, as their own assert rather than a line infailures— because the band's message ends "Bless with …" and a stale record is the one case where blessing is the wrong move. The failure says re-measure.The version does not come from re-parsing the checked-in
plugin.toml. It comes off theStoredPackagethe install itself parsed (ruby_parity_fixture::Installed { digest, version }): a caller recording a version beside a number it measured must record the version of the bytes that produced the number.A corpus-free arm (
the_recorded_conditions_verdict_is_graded) grades the verdict function and the committed record's shape, because the arm that reads it needs 156 staged files and a wasm worker — and a check that only runs where the corpus is staged is a check that skips green everywhere else.Step 2 — the RED, as promised
Step 3 — re-blessed from a measurement, and what the measurement says
The SQLite dimensions are the measurement and they barely moved. Six survey passes: package
vm_step104.51M 104.73M 107.31M 107.80M 108.35M 108.44M — a 3.8 % spread matching the 3.3 % non-determinism this file already records — median 107.55M against the old 106.65M, +0.85 %. Two schema versions moved the package leg's SQLite work by under one percent.The wall ratio fell 350 → 316 and the cause is the DENOMINATOR, not a faster guest. The builtin leg's
vm_stepis 14.822–14.829M here (stable to 0.05 %) against the 13.1M recorded in the superseded reason — +13 % of SQLite work in the control leg across schema 58 → 60. A ratio is a fraction and this one grew a bigger bottom. Nothing in these numbers says the guest got quicker, and this suite would not have been able to tell the two apart before the builtin figure was written down beside it.The measurement was NOT isolated, and the record says so in the record
The box never fell below load average 5 in twenty minutes of waiting; the accepted pass ran at 39. So:
300..360was registered as an acceptance window from the six survey passes, before any blessing pass ran; four passes outside it (262, 219, 231, 394) were re-run from a reset record rather than recorded; the 262 was taken while a sibling lane started acargo build, with both legs three times slower in wall clock (builtin 2168 ms against 705–850 ms). All of that is in_blessed.reason, together with the honest reading: this band can see a 1.5× guest slowdown and not a 1.1× one.The reason prose was completed by hand after the accepted pass, because the rejection count is not knowable before the loop runs. No number in the file was hand-edited —
package_legandwall_ratio_pctare exactly what the accepted pass measured and wrote, and_superseded_reasonscarries the two original entries and no retries.Mutations — RUN, restores md5-verified
_blessed.schema60 → 59 (the one this issue demanded)schema: the band was measured under 59 and this binary is schema 60"package_version"from the recordmust carry the package version as a FIELDstale_conditionsreturns an emptyVecThe schema mutation names the schema alone. That is the second half of the proof: the package-version arm was satisfied at the same moment, so the two conditions grade independently rather than as one lump.
And one the test found in itself. The absent-field case was built from
Baseline::default(), whoseschemais0, while the reader writes-1. It graded a valueload_baselinecan never produce and printed "the band was measured under 0" — a sentence about a schema nobody has ever run. It now reads a fieldless record through the real reader and asserts the-1first.Final:
ruby_package_cost3 passed, 0 failed, exit 0 with the corpus.tests/corpus/baseline.jsonuntouched, md5534084b856c22566c48e386bc41ed67e.What is NOT done on this criterion
Acceptance 2 reads "XAML and migrated-full-language packages have exact generation-aware baselines". Only the migrated-full-language half is closed.
de.h-dv.xamlstill has no committed projection baseline of any kind.package_fixture::live_host_xamlinstalls the real shipped package with its capability and its two declared bridges, andtests/packages/xaml/fixturespluspackage_fixture::xaml_doccan supply a corpus — so the leg is buildable and needs no pinned repo. It is also whereresolved_by::bridge(rule 90) would become a committed non-zero for the first time:stage-baseline.json's own_commentrecords that as the one code of nineteen that never fires, because the pinned corpus is package-free.That work was scoped together with criterion 3 as one artifact — a
tests/corpus/package-baseline.jsonwith a row per(package, lifecycle state)— and the lane building it was killed by a session rate limit before writing a line. Nothing of it exists. Reported as not started, not as partial.🤖 Generated with Claude Code
https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
#45.6 and #45.7 — and the vacuity that would have let a straggler through
Criterion 6 — PARTIAL, residual named
Before this: of six registered bless switches exactly one enforced the clause set.
COSI_CORPUS_BLESSaccepted an 8-character reason, an EMPTY categorised diff, no identities, no control roster and a degraded resolver;COSI_COST_BLESSaccepted the same and recorded only the SQLite engine version, which it never refused on;COSI_RUBY_PKG_COST_BLESSasked for a non-empty reason and nothing else. Six copies of six refusals is six places for the bar to differ, and it did.Every path now routes through
projection::bless_verdict. Its parameter list became aBlessClaimstruct rather than eight positional arguments, which also removed three#[allow(clippy::too_many_arguments)]— and the reason is written into the struct's doc:unavailableanddegradedare both&[String]and adjacent, so transposing them compiles, and the refusal that fires then names the wrong rule.Three defects in
corpus_ratchet's bless path, found by reading and fixed with their own mutations:rewrite_baselinebuilt its rows fromobservedalone with head/tail splices and never merged the previous baseline, and a skipped repo never entersobserved. (corpus_cost's writer did merge. That asymmetry was the tell.)COSI_CORPUS_REQUIRE=1the file was already on disk when the failure fired.driftwas computed and the bless branch never consulted it.The finding, and it is why this is a commit rather than a tidy delegation
bless_registry's routing test askedsrc.contains("bless_verdict"). Every compliant owner defines a local wrapper of exactly that name, so the string is present whether or not the body delegates. Measured, not argued: replacing the tier-3 wrapper's body with a weaker inline bar that reached the shared verdict only with a claim it fabricated left the whole tree green —COSI_TIER3_BLESSwould have accepted a one-character reason, an empty diff, no controls, a shrunken universe and a degraded resolver, and nothing in the workspace would have said so.Two fixes, both generic:
delegates_to_the_shared_verdictstrips comments and requires a reference to the shared item (projection::bless_verdictor itsuse … asalias), which a local definition cannot produce; andcorpus_tier3_ratchetgains a behavioural arm that varies one field per refusal and asserts its own switch is named. A lexical gate cannot see whether a body calls what its file imported; that is the brace to this belt.After the fix, weakening a shared refusal reddens all four suites:
The registry itself grades both directions (an unregistered switch, and a registered switch whose artifact or verdict is gone), plus stale waivers, plus its own scan reporting that it walked zero files rather than passing over them. It exists because a grep cannot enumerate these:
COSI_TIER3_BLESS_REASONis built withformat!("{SWITCH}_REASON")and is spelled nowhere, andCOSI_BLESS_RUBY_EXPECTdoes not match a*_BLESSshape at all.Residual, named:
COSI_RUBY_PKG_COST_BLESSis the sixth switch and it still enforces a non-empty reason only. Its waiver is written, graded, and self-clearing — it goes red the moment that suite callsbless_verdict.COSI_BLESS_RUBY_EXPECTand the switchlesstests/bench/ratchet.jsoncarry argued exemptions, not gaps; the latter is stronger than compliance, sincethe_benchmark_never_writes_its_own_expectationsmakes its bless path structurally unreachable.Criterion 7 — MET, with the lexical half's blind spots stated rather than implied
The tree was in the state "nobody asserts it today", not "nobody can". Those are different states and this criterion asks for the second.
resolution_percentage_stanceis a closed stance set —Recorded,FixtureAsserted,PayloadDisclosure— with no arm that permits gating, and that is itself graded (Stance::PayloadDisclosure => truereddens it). 16 files and 96 occurrences are registered in both directions. The one asserted ratio in the whole tree (resolver.rs,ratio >= 0.15, inside an#[ignore]d probe) is declared and pinned by exact text, so it cannot be quietly raised into a ratchet.The motivating mutation:
and the anti-vacuity one:
What the lexical half structurally cannot see, said out loud so it is not oversold — a gate oversold is worse than one that is small and true:
resolver.rsdoes exactly this today (let ratio = …;thenassert!(ratio >= 0.15)). Only the registry row catches it.assert—if rate < 0.2 { failures.push(…) }panics by another road..rsundercrates/only.Constructis a closed set of five; aresolution_ratioorbind_pctis invisible until someone adds it — which is whythe_detector_can_failexists and was mutated.Two defects in the new tests, found by running their own recipes
Gates
fmt0 ·clippy --workspace --all-targets -D warnings0 ·RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --document-private-items0 ·precision_gate7/7,phantoms=0on every language, exit 0 · no-corpus suitesbless_registry5,resolution_percentage_stance7,corpus_ratchet6,corpus_cost5,corpus_stage10,corpus_tier3_ratchet4 · corpus legs ofcorpus_costandcorpus_ratchetgreen.Nothing under
tests/corpus/was blessed or re-recorded by this work.baseline.jsonmd5534084b856c22566c48e386bc41ed67e.One red that is NOT this lane's, reported rather than absorbed
On the rebased tree
corpus_stagefails — and it is the merged resolver work surfacing exactly where that suite was built to show it:2f16e22gave tier 3 the package-origin gate tier 1b has carried since I046. #165 reports that gate is defective for hyphenated crate names (kebab-case directory tail against a snake_case import module, 58 % of cross-crate binds lost) and a lane is fixing it now, so these numbers will move again. Not blessed, on the same reasoning: re-recording over a fix in flight is how a ratchet becomes a habit instead of an event.Criterion 3 — NOT STARTED
"Inert package, activation, rollback and removal states are ratcheted." All four states are asserted —
resolver_containment::enable_then_disable_returns_to_the_searchable_only_index,capability_isolation::an_inert_row_reaches_no_aggregate,influence_classification::an_inert_package_leaves_every_reference_builtin_only,generation_promotion::a_rollback_restores_the_old_projection_with_the_producer_gone,generation_collect, and theremove_package_contributionrow ingeneration_fixture. None has a committed artifact that would produce agit diff— every comparison is against a projection computed in the same process.It was scoped with criterion 2's XAML half as one artifact (
tests/corpus/package-baseline.json, a row per(package, lifecycle state), reusingprojection::stage_rowandbless_verdict), because inert is the state whereproducer.<package>/… > 0andinfluence.builtin_only == every refhold simultaneously — two facts no current artifact can hold together — and because XAML active is whereresolved_by::bridge(rule 90) would become a committed non-zero for the first time. The lane building it was killed by a session rate limit before writing a line. Nothing of it exists, and it is reported as not started rather than partial.🤖 Generated with Claude Code
https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
page_name_fallback: 0with no shape-excluded sibling #173COSI_RUBY_PKG_COST_BLESSis outside the bless contract, no test blesses through an env switch and inspects the bytes, andbless_registry's own doc and floor have drifted below their population #177Criterion 3, scoped against the tree at
552e3a2: still NOT STARTED, and the previous comment's estimate is optimistic in one specific place and pessimistic in anotherNo code was written for this. What follows is the scoping the last comment could not do, because the lane that would have done it was killed before it read the template. Every path below was read; the two load-bearing negatives were checked by hand as well.
The previous comment's plan, re-graded
Its plan was: one artifact,
tests/corpus/package-baseline.json, a row per(package, lifecycle state), reusingprojection::stage_rowandbless_verdict.Where it is RIGHT, and better than it claims. Its stated motivation — that inert needs
producer.<pkg>/… > 0andinfluence.builtin_only == every refto hold simultaneously, and no current artifact can hold both — is satisfied byprojection::stage_rowunmodified. All four of its prefixes already carry the package-side values:producer.*(crates/indexer/tests/corpus/projection.rs:294-319) is exactly thefile_contributions → extraction_components → plugin_packages → plugin_generationsjoin, filteredg.state = 'active';KEY GONE, and rollback is it reverting — both fall out with no new observation code;influence.*already knowsdynamic_endpoint/dynamic_influenced;rule.bridge(code 90) is already inresolved_by::ALL, which is whystage-baseline.json's own_commentcan say 18 of 19 codes fire because the pinned corpus is package-free.So three of the four lifecycle states need no new projection at all.
Where it is WRONG, in the two places that carry the cost.
Nothing in
crates/indexer/tests/drives a generation lifecycle with a real package. Every one of the six sites the last comment cites —resolver_containment.rs:1614,capability_isolation.rs:1020,influence_classification.rs:879,generation_promotion.rs:792,generation_collect.rs:587, and theremove_package_contributionrow atgeneration_fixture/mod.rs:526— runs on a compiled-inFixturePlugin, not an installed.cip.generation_fixture::cold/ordinary_passpasspackages: None;build_generationusesExtractors::builtin. None of them can emit aproducer.de.h-dv.xaml/…row or a package-bearing activation digest. Build → promote → rollback → collect under a realPackageHostis new code (~120-180 lines, closest modelcrates/cli/src/activation.rs:790-830). This is what "reusingstage_rowandbless_verdict" hides.resolved_by::bridgeneeds a C# corpus that does not exist. Verified by hand:Both declared bridges are
scope = "paired_file", whichcrates/indexer/src/bridges.rs:294-308implements asc.dir = r.dir AND c.pair_key = r.pair_key—MainWindow.xamlbesideMainWindow.xaml.cs.package_fixture::xaml_docemits no C# either. The paired code-behind text does exist (XAML_CODE_BEHIND/XAML_GENERATED,crates/daemon/tests/common/plugin_pkg.rs:865and:891) — in the daemon crate's test tree, reachable fromcrates/indexer/tests/only by a cross-crate#[path]include (precedent:resolver_containment.rs:208).Also:
package_fixture::live_host_xaml(package_fixture/mod.rs:938-1001) does the whole real install path — pack, sign,store.install, grantbridge_source+ both bridges,PackageHost::from_set— butLive::_tmpis private and nothing writes a project tree to disk and indexes it.plugin_path_cost.rs:1119callshost.extract(...)directly. So the leg needs either a new field onLiveor its own installer.The rest of what a new suite must supply
projection::bless_verdicttakes aBlessClaim<'a>(projection.rs:624-642) with eight required fields, no defaults —reason(≥REASON_MIN= 24 chars), a non-emptydiff,controls(≥repos_graded),unavailable(any entry refuses),degraded+degraded_allowlist, and anidentity_afterthat is neither empty nor"(none)". It returns aBlessProofwhose only field is module-private, sorender/blessed_blockcannot be called without one: writing before refusing does not fail review, it fails to compile.A new switch must also (a) get an
Entryinbless_registry.rs:101-171and bump itsREGISTRY.len() >= 7, (b) pointartifact/ownerat paths that exist, and (c) genuinely delegate —delegates_to_the_shared_verdictstrips comments and requires a reference to the shared item, because a local wrapper of the same name was measured to leave the whole tree green over a weaker bar.And one requirement the previous comment does not mention: if the suite touches
corpus::repo_path/tierat all,corpus_require_floor.rs:897auto-detects it by transitivemod-alias reachability and it must be registered in aci.ymljob that setsCOSI_CORPUS_REQUIRE, or that test goes red.Estimate
crates/indexer/tests/corpus_package_lifecycle.rscorpus_tier3_ratchet.rsis 662 for the easiest possible third driver — same corpus, row builder reused unchanged, 4 unit armscrates/indexer/tests/xaml_parity_fixture/mod.rsruby_parity_fixture/mod.rsis 388 and is a near line-for-line analogue, plus the XAML+C# tree writer that does not existtests/corpus/package-baseline.jsonstage-baseline.json267,tier3-baseline.json176corpus/projection.rspackage_row(db)stage_rowstructurally cannot see:plugin_generations.state,generation_packages.{package_digest, extraction_identity, capability_grants}, per-state contribution counts, refused/quarantined countsbless_registry.rs,ci.yml,package_fixture/mod.rs~1,400-2,050 new lines, 3 new files + 3-4 edited. Calibration:
6f75e58("tier-3 gets the ratchet tier-1 had") was 1,892 insertions across 8 files, and it had its corpus already staged, its row builder already written, and no package host.Four decisions that must be taken BEFORE the first line, not during
Coverage/SkipClass/require_verdictare keyed onRepoSpec;BlessClaim.unavailableandrepos_gradedare repo words. A(package, lifecycle state)universe has noRepoSpecfor a state. Either one corpus repo × N states (then the corpus floor and ci.yml registration are mandatory) or a synthetic tree (thenCoverageis dead weight and the anti-vacuity story must be rebuilt from scratch).corpus_stage(host_version=CARGO_PKG_VERSIONis hashed in). But the honest package-bearing identity,plugin_packages.package_digest, moves on every byte undertests/packages/xaml/— thatplugin.toml's own header says so. So it needsruby_package_cost-style asserted recorded conditions (_blessed.schema+_blessed.package_version, read at assert time, failing with re-measure not re-bless). That machinery exists atruby_package_cost.rs:259-311and is not in the sharedprojection.rs; it has to be lifted or re-implemented.stage_row's closed-set guards PANIC on an unknownresolved_byorinfluencecode. A package-bearing index that mints one aborts the suite rather than diffing it. Correct as designed — but it means the first run over XAML is a live probe, not a formality.corpus_stageis 12.3 s over 7 repos with no host. A package leg spawns the plugin-host subprocess and precompiles the extractor once per lifecycle state.ruby_package_parity/ruby_package_costare already on the per-pushcorpusjob (ci.yml:935-936), which is precedent and also existing load.Adjacent, and it will be tripped by whoever does this
COSI_RUBY_PKG_COST_BLESSis still the sixth switch and still enforces a non-empty reason only (bless_registry.rs:129-146). Its waiver is self-clearing — it goes red the moment that suite callsbless_verdict— and it is criterion 6's named residual. A lane in this area is one edit away from closing it.Verdict
Criterion 3 is NOT STARTED and stays so. Criterion 2's XAML half is NOT STARTED and is blocked on the same two things. The remaining work is not "wire a fourth artifact to the third one's template"; it is a package-driven generation lifecycle harness that does not exist in this crate, plus corpus bytes for a bridge that has no C# to bridge to. Everything downstream of those two is genuinely the template.
🤖 Generated with Claude Code
https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
plugin enableindexes nothing on a newly-claimed extension, andpackage_fixture::XAML_BRIDGESholds a bridge key the production door refuses while its doc claims the guides show it #207