#75's searchable-but-inert default is unreachable through the generation build when one package claims all of a project's code — gate 6 cannot tell "granted nothing, deliberately" from "a grant that failed" #206
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.
Dependencies
No dependencies set
Reference
h-dv/code-index#206
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?
Found while building #45's package-lifecycle ladder, which had to drive the real activation path rather than a fixture. Measured, not inferred.
The fact
build::build's gate 6 refuses an activation whose "pending generation's pools admit nothing".admittedcounts contributions in any resolver pool — and a package granted no capabilities is in no pool, by construction.So on a project all of whose code a single package claims, the searchable-but-inert state cannot be reached through a generation build. The build refuses before there is anything to serve.
The XAML reference package is the sharper case: it requests
bridge_sourceonly and no pool capability at all, so a project consisting of nothing but.xamlcould never activate it.Why this is a defect and not a policy
#75's design has searchable-but-inert as the default and the safe first step — install, enable with no capabilities, prove existing bindings unchanged, and only then grant. #80's C3 conformance layer is built on it: "index the probe corpus with (1) no package; (2) package active but searchable-only; …" and asserts step 1 equals step 2 for every pre-existing target.
Gate 6 is a good gate — an activation that admits nothing is usually a grant that silently failed to reach the pools, and refusing it is right. The problem is that it cannot tell that case from the deliberate one. Two different states, one rendering, which is the shape this project closes everywhere else.
Why the existing tests do not see it
Every package fixture in the tree grants at least one pool capability, or runs on a project the builtins also claim — so there is always something in a pool and gate 6 never fires on the inert rung. #45's ladder is the first thing to drive
absent → inert → active → rollback → removedon a project where the package is the only producer, which is why it surfaced here and not earlier.Shape of a fix
The gate needs to distinguish the two, not to be relaxed. The information exists at the call site: whether the activation requested any pool capability at all. An activation that requested none and admitted none is the documented inert state; an activation that requested some and admitted none is the failure gate 6 exists for, and must keep refusing.
What must NOT be done
plugin enablewrites the activation record; the generation build is the path that refuses. Whatever fix lands needs the ladder inpackage_baseline.rsto exercise it.Related
#75 (searchable-by-default is its design), #80 C3 (which is built on the inert step), #45 (where this surfaced), and the sibling finding filed alongside about an activation build deferring a newly-claimed extension with no
filesrow.Reported by the #45 lane, 2026-09-07, measured on the real activation path.
Fixed on
masterat4b332ff(mergede425320). Two corrections to this issue first — one half was already refuted by the tree.The XAML paragraph is refuted, and
package_baseline.rsalready said soThis issue argues the xaml package "requests
bridge_sourceonly and no pool capability at all". Measured:capabilities::POOL_CAPABILITIESisRESOLVER_CAPABILITIESand includesbridge_source, andadmit_selectjoins any capability.package_baseline.rs:1185-1198had already retired that half as #233's cause.The inert half stands, and that is what was fixed.
The prescribed rule is slightly too strong
This issue says: "an activation that requested some and admitted none is the failure gate 6 exists for, and must keep refusing."
That would refuse a granted package whose files were all deferred — which is #207's Ruby shape, and self-healing. The fix measures the expectation over the projection rather than over the request, which handles both.
What shipped
build::gate's sixth arm now measures the expectation beside the outcome.admissible_projectioncounts how much of the pending projection belongs to a producer this activation gave authority to — read fromgeneration_packages(the activation record) rather than fromcomponent_capabilities(the projection under test). That distinction is the whole design: an expectation read from the projection could only ever agree withpool_admit.expected > 0 && admitted == 0→ still refuses.expected == 0→ promotes and discloses onReady::admits_nothing_by_construction, whichplugin enableprints.first_activation_admits::an_ungranted_package_still_fails_the_gatebecame…activates_inert_and_the_generation_says_so, with a false-arm control beside it.A third state fell out of the same clause that neither this issue nor #207 described: a granted package whose files are all deferred yields
contributions = 0→expected = 0→ inert, not refused. It needed no case of its own.Mutations
if admitted == 0arm) → RED onan_ungranted_package_activates_inert_…and nothing else: "a generation reachedreadywithadmitted: 0and did not say why".admissible_projectionreturns 0) → SURVIVED across all six integration tests — the refusal branch is unreachable with the projection intact. Covered afterwards by a new row-level unit test where M7b is RED (left: 0 / right: 1). Reported as a survivor first.#206 and #207 did not share a root — measured
Both render "this package produced nothing" opaquely, but the mechanisms are disjoint: #207's deferral fires at gate 3 (
generation_build_files.file_id IS NULL) and never reaches the admission arm; #206's inert state fires at gate 6 withdeferred = 0. The new.wpfxe2e producesdeferred=1withadmitted > 0, so gate 6 is not involved;first_activation_admits's fixture reaches gate 6 with nothing deferred at all.Three tree gates caught real consequences on the way —
doc_citation_gate(a renamed test still cited),ref_kind_stance_registry(movingkind = 'type'into a helper made the registry's claim unverifiable; the literal is back at both call sites, deliberately), andactivation_identity::nothing_reads_the_neutralised_clock.baseline.jsonunmoved; binds unmoved by construction (nothing here is a resolver input), confirmed bycorpus_ratchetexecuted=7 unavailable=0. Closing.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