Re-granting derived_names on an already-enabled package does not reindex, so the files stay at 0 symbols #104

Closed
opened 2026-09-04 15:01:11 +02:00 by buildagent · 1 comment
Member

Found and left open deliberately by the #103 fix, under "keep this change small". It is a real gap and it is the immediate follow-up.

The defect

derived_names is not a field on dirty::PackageActivation. So when an operator changes only that answer on a package that is already enabled, dirty_domain sees no change, the domain comes back empty, and nothing is reparsed.

Concretely:

plugin enable <digest>                     # no grant — rails.rb refused, 0 symbols
plugin check  <digest> --derived-names     # verdict now covers the grant
plugin enable <digest> --derived-names     # grant RECORDED …
                                           # … and the files stay at 0 symbols

The operator has answered yes, the record says yes, the runtime policy says yes — and the index still shows the refusal until something unrelated touches the file.

Why the acceptance path did not catch it

First-time enable is fine, and that is the path #103's e2e drives: the containers move, which yields Cause::ExtractionIdentity, which reparses. Only the change of an existing answer is silent. This is the ordinary shape of a state-transition gap — the fixture starts from nothing, so the transition that matters is never exercised.

The fix

  • add derived_names to dirty::PackageActivation;
  • a new Cause at Work::Reparse — not Work::Reresolve. Cause::Capability is a Reresolve and would not re-run the extractor, and the extractor is exactly what has to run again: the facts themselves change, not their resolution.
  • ~12 struct-literal fixes across four crates follow from the new field.

The test that must go red

Not "enable with the grant and see symbols" — that passes today via ExtractionIdentity. It must be the transition: enable without, assert the refusal is recorded, re-enable with, and assert the file carries symbols without any file having been touched. The mutation is dropping the new field from PackageActivation — the test must go red on that alone.

Follow-up to #103. Blocks nothing in #84 (whose acceptance is a first-time enable), but it is an operator-visible correctness defect in a shipped path.

Found and left open deliberately by the #103 fix, under "keep this change small". It is a real gap and it is the immediate follow-up. ## The defect `derived_names` is **not** a field on `dirty::PackageActivation`. So when an operator changes only that answer on a package that is already enabled, `dirty_domain` sees no change, the domain comes back empty, and nothing is reparsed. Concretely: ``` plugin enable <digest> # no grant — rails.rb refused, 0 symbols plugin check <digest> --derived-names # verdict now covers the grant plugin enable <digest> --derived-names # grant RECORDED … # … and the files stay at 0 symbols ``` The operator has answered yes, the record says yes, the runtime policy says yes — and the index still shows the refusal until something unrelated touches the file. ## Why the acceptance path did not catch it First-time enable **is** fine, and that is the path #103's e2e drives: the containers move, which yields `Cause::ExtractionIdentity`, which reparses. Only the *change* of an existing answer is silent. This is the ordinary shape of a state-transition gap — the fixture starts from nothing, so the transition that matters is never exercised. ## The fix - add `derived_names` to `dirty::PackageActivation`; - a new `Cause` at `Work::Reparse` — **not** `Work::Reresolve`. `Cause::Capability` is a Reresolve and would not re-run the extractor, and the extractor is exactly what has to run again: the facts themselves change, not their resolution. - ~12 struct-literal fixes across four crates follow from the new field. ## The test that must go red Not "enable with the grant and see symbols" — that passes today via `ExtractionIdentity`. It must be the **transition**: enable without, assert the refusal is recorded, re-enable with, and assert the file carries symbols *without any file having been touched*. The mutation is dropping the new field from `PackageActivation` — the test must go red on that alone. ## Related Follow-up to #103. Blocks nothing in #84 (whose acceptance is a first-time enable), but it is an operator-visible correctness defect in a shipped path.
Author
Member

Triage 2026-09-06: CLOSING. The grant is a compared fact on the dirty domain, it raises Reparse rather than Reresolve, and the transition test exists with its mutation ledger written out.

Verified against master and by re-running the suite myself.

Against the stated criterion

  • A field, not a derivation: pub derived_names: bool on PackageActivation — crates/indexer/src/dirty.rs:314 (struct at :236), with a doc block naming this issue and explaining the Reparse-vs-Reresolve axis.
  • The right work kind: Cause::DerivedNames — dirty.rs:901; module doc at :104-118 ("AND WHY #104's derived_names GRANT IS NEITHER"). A Reresolve would have left the files at 0 symbols, which is the defect.
  • Compared, not recomputed: KeyFacts::derived_names_answer_moved — dirty.rs:731, called at :1135.
  • Both constructors read approval::effective_grant — crates/indexer/src/approval.rs:3205-3212.

The mutation that matters was run

crates/indexer/tests/dirty_domain.rs:149-215 records M19: delete the key_facts insert and the exact #104 defect is restored — RED. Plus M20/M22/M23/M24 with a deliberate two-mutation control. That is the difference between "a test exists" and "a test that can fail".

Runs (exit 0)

$ export CARGO_INCREMENTAL=0
$ cargo test -p code-index-indexer --test dirty_domain
test result: ok. 22 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.20s

incl. re_answering_derived_names_on_a_standing_container_reparses_only_its_files.

$ cargo test -p code-index-daemon --test ruby_package_e2e re_granting_derived_names
re_granting_derived_names_on_an_enabled_package_reindexes_without_touching_a_file → 1 passed

The second is the one that grades the issue's actual scenario end to end: without touching a file, which is the clause that makes it a real regression test rather than a reindex-everything smoke.

Residual

None.

🤖 Triage lane, 2026-09-06, master 45cf6e4

## Triage 2026-09-06: CLOSING. The grant is a compared fact on the dirty domain, it raises `Reparse` rather than `Reresolve`, and the transition test exists with its mutation ledger written out. Verified against master and by re-running the suite myself. ### Against the stated criterion - **A field, not a derivation**: `pub derived_names: bool` on `PackageActivation` — `crates/indexer/src/dirty.rs:314` (struct at `:236`), with a doc block naming this issue and explaining the Reparse-vs-Reresolve axis. - **The right work kind**: `Cause::DerivedNames` — `dirty.rs:901`; module doc at `:104-118` (*"AND WHY #104's `derived_names` GRANT IS NEITHER"*). A `Reresolve` would have left the files at 0 symbols, which is the defect. - **Compared, not recomputed**: `KeyFacts::derived_names_answer_moved` — `dirty.rs:731`, called at `:1135`. - Both constructors read `approval::effective_grant` — `crates/indexer/src/approval.rs:3205-3212`. ### The mutation that matters was run `crates/indexer/tests/dirty_domain.rs:149-215` records **M19**: delete the `key_facts` insert and the exact #104 defect is restored — RED. Plus M20/M22/M23/M24 with a deliberate two-mutation control. That is the difference between "a test exists" and "a test that can fail". ### Runs (exit 0) ``` $ export CARGO_INCREMENTAL=0 $ cargo test -p code-index-indexer --test dirty_domain test result: ok. 22 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 10.20s ``` incl. `re_answering_derived_names_on_a_standing_container_reparses_only_its_files`. ``` $ cargo test -p code-index-daemon --test ruby_package_e2e re_granting_derived_names re_granting_derived_names_on_an_enabled_package_reindexes_without_touching_a_file → 1 passed ``` The second is the one that grades the issue's actual scenario end to end: **without touching a file**, which is the clause that makes it a real regression test rather than a reindex-everything smoke. ### Residual None. 🤖 Triage lane, 2026-09-06, master `45cf6e4`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index#104
No description provided.