resolver: row-level provenance, capability-gated pools and explicit language bridges #77

Closed
opened 2026-08-26 12:31:56 +02:00 by buildagent · 2 comments
Member

Child of #75. Depends on #76 and on resolving #65's unbounded resolver path; integrates with #79.

Problem

The resolver currently treats producer identity as an unstated invariant: a row whose language is csharp is assumed to have been emitted by the compiled C# plugin and therefore to obey that plugin’s semantic guarantees.

Dynamic plugins invalidate that premise. Candidate pools are keyed primarily by name, language and pool class. Injecting one plausible symbol can create false edges, destroy correct edges by changing uniqueness, alter reachability anchors and corrupt resolution-gap diagnostics.

The previous proposal added files.producer. That is insufficient. An embedded-language file can contain:

  • wrapper-language facts;
  • one or more host-language regions;
  • bridge-generated references;
  • declarations emitted by different package components.

Provenance and language identity must be row-level.

Outcome

Dynamic facts are searchable without affecting existing bindings by default. Resolver participation is an explicit, project-approved capability. Cross-language resolution occurs only through declared bridges. Every edge and every failed binding can disclose whether dynamic evidence influenced the decision.

Schema model

Introduce normalized package/generation/component identity and attach it to every extraction contribution.

Conceptual tables:

plugin_packages(
  digest, package_id, version, abi, installed_metadata...
)

plugin_generations(
  id, project_root_identity, package_digest,
  state, capability_grant_digest, engine_version...
)

extraction_components(
  generation_id, component_id, language_id, claim_id...
)

file_contributions(
  id, file_id, component_id, source_region, status, diagnostics...
)

Symbols, refs and imports reference file_contributions or carry an equivalent non-null producer key. Diagnostics do as well.

Requirements:

  • compiled plugins receive explicit producer identities such as builtin:@;
  • text-only indexing has its own producer identity;
  • no magic producer string parsing in SQL;
  • package digest, generation and component are queryable without joining through mutable manifest text;
  • removal of one contribution cannot delete another component’s facts from the same file;
  • cascade behavior is tested for upgrade, removal and mixed-language files.

files.lang may remain a compatibility summary, but symbols, refs and imports use their own source language. The primary file language cannot determine mixed-region resolver behavior.

Dynamic language identity

A package-defined language uses a stable namespaced id, for example org.example.xaml/xaml. It may not stamp csharp merely to enter C# pools.

Language identity and resolution domain are separate:

  • language id identifies the source syntax/semantics;
  • resolution profile defines same-language rules;
  • bridge capability defines permitted cross-language matching.

This removes the prior “own-lang is inert, host-lang contaminates” false choice.

Language profiles

Move every resolution-relevant hardcoded language branch into either:

  1. a core invariant that applies to all languages; or
  2. a validated profile field.

Inventory includes at least:

  • identifier case-folding/collision rules;
  • qualified-name separators and normalization;
  • type, member, container and module kind classes;
  • positional type-kind demotion;
  • relative import extensions and normalization;
  • same-directory/proximity eligibility;
  • reachability/file-key construction;
  • data-member-only semantics;
  • extension-method eligibility;
  • rename collision comparison;
  • namespace/package behavior.

A profile cannot provide SQL or arbitrary predicates. It selects bounded algorithms and data from a closed core-owned registry. Unknown algorithms fail package activation.

Capability model

The package manifest requests capabilities; installation and project activation grant a subset. Facts carry capability classes validated against that grant.

Default grant:

  • searchable symbols;
  • file outline;
  • text/source navigation;
  • unresolved reference visibility;
  • no candidate-pool participation;
  • no reachability anchors;
  • no cross-file or cross-language binding.

Separate capability axes include:

  • same-file definition candidates;
  • same-language exported candidates;
  • reachability/import anchors;
  • qualified-name candidates;
  • type-position candidates;
  • member candidates;
  • cross-language bridge source;
  • cross-language bridge destination.

Capabilities are intentionally finer than “participates in tier N.” Resolver tiers combine multiple evidence classes, and a package should not receive unrelated authority because one use case needs a single class.

Dangerous capabilities such as module/file-key anchors, extension-method definitions and partial-class scope require explicit host support and stronger conformance gates. They are never inferred from emitted kinds.

Cross-language bridges

A bridge declaration is directional and bounded:

  • source language/profile;
  • source ref kind/role/capture class;
  • destination language;
  • destination symbol kind/capability class;
  • scope relationship: same file, paired file, same package/directory, import-reachable, or another closed algorithm;
  • name/qualifier mapping from a closed normalization registry;
  • ambiguity policy, always uniqueness-based and core-owned;
  • evidence grade and disclosure text.

Examples:

  • XAML Click value to a C# method in the paired code-behind class;
  • XAML x:Class value to a C# class;
  • Razor embedded C# regions processed as C# contributions rather than pretending Razor is C#.

Binding paths whose destination type is unproven remain unresolved. A package cannot assert that a string names a target.

Undeclared language pairs never share candidate pools even when normalized names match.

Pool construction

All candidate and anchor CTEs/tables must be audited. A dynamic row reaches a pool only when:

  • its contribution is in the active generation;
  • its language/profile matches the rule;
  • its fact capability class is valid;
  • the project grant permits that class;
  • for cross-language matching, an approved bridge admits the exact source/destination classes.

This applies to symbol_buckets, exported pools, directory buckets, qualified candidates/anchors, file keys, extension candidates, C# partial scope and diagnostic pool CTEs. There must be no “one forgotten CTE” escape hatch.

Default-inert dynamic rows must not alter:

  • any existing target_id;
  • candidate counts used to decide uniqueness;
  • no_candidate/internal_missed/ambiguous classification;
  • ref_count or symbol_edges for builtin evidence.

Evidence provenance

resolved_by continues to identify the core resolver mechanism. Do not overload it with producer identity.

Add computed resolution influence:

  • builtin_only: deciding candidate/anchor sets contained no dynamic contribution;
  • dynamic_endpoint: source or selected target is dynamic, but no excluded competing dynamic evidence affected uniqueness;
  • dynamic_influenced: dynamic evidence changed candidate count, reachability or a bridge decision, including a ref that remains unresolved because a dynamic candidate introduced ambiguity.

The exact representation may be an enum plus deciding generation/component ids. It is recomputed by the resolver and never asserted by the plugin.

An absent field from an old daemon is “not reported,” never builtin_only.

Search and tool behavior

  • search_symbols and file_outline include producer/generation/language metadata for dynamic rows.
  • find_references/callers/callees expose influence on resolved rows.
  • resolution_gaps classifies against the capability-filtered active pools and discloses dynamic influence.
  • safe_delete/check_rename include dynamic text/fact evidence and capability limitations.
  • project_overview summarizes active languages, packages, grants and dynamically influenced edge counts through #80.

Response formats need concise shapes that do not turn every symbol row into a manifest dump; stable package handles can reference a package block once per response.

Migration and builtins

Backfill every existing row with a builtin/text contribution identity without changing resolved targets.

Migration equivalence requires:

  • symbols/refs/imports identical;
  • target_id and resolved_by identical;
  • ref_count and symbol_edges identical;
  • project_overview counts identical except additive provenance fields;
  • cold index equals upgraded index.

Compiled and package versions of the migration language can run in shadow databases and compare canonical fact projections.

Tests

Structural gates:

  • every resolver candidate/anchor definition contains an active-generation and capability predicate;
  • every extraction table row has valid contribution provenance;
  • no language-specific allowlist remains without registry coverage;
  • bridge algorithms are closed and enumerated.

Mutation gates:

  • one dynamic same-name symbol cannot capture builtin refs;
  • one dynamic collision cannot destroy builtin bindings;
  • a dynamic module cannot create file-key reachability without the capability;
  • a dynamic C#-named row cannot enter partial-class scope by language-string spoofing;
  • a qualified-name collision cannot suppress builtin TYPE_FQN resolution;
  • diagnostic pools remain unchanged for inert rows;
  • granting exactly one capability changes only its declared population;
  • undeclared bridge produces zero cross-language edges;
  • mixed-language file removal deletes only the selected contribution.

Corpus gate:

Index the same corpus with no package, inert package and progressively granted package. The no-package and inert projections of all pre-existing bindings and diagnostics must be identical. Every movement after a grant must fall inside the bridge/capability’s declared source population, with a positive control proving the capability did useful work.

Acceptance

  1. Dynamic facts are visible in search while completely inert to resolver pools by default.
  2. A mixed-language file carries multiple producers/languages without lying through files.lang.
  3. XAML to C# edges exist only through explicit bridge capabilities.
  4. Every dynamically influenced resolution or ambiguity is identifiable.
  5. Removing/rolling back one component cannot leave rows or affect another contribution.
  6. Builtin migration is byte/semantics-equivalent apart from additive provenance.
  7. No package can gain resolver authority merely by choosing a language string, symbol kind or qualified name.
Child of #75. Depends on #76 and on resolving #65's unbounded resolver path; integrates with #79. ## Problem The resolver currently treats producer identity as an unstated invariant: a row whose language is csharp is assumed to have been emitted by the compiled C# plugin and therefore to obey that plugin’s semantic guarantees. Dynamic plugins invalidate that premise. Candidate pools are keyed primarily by name, language and pool class. Injecting one plausible symbol can create false edges, destroy correct edges by changing uniqueness, alter reachability anchors and corrupt resolution-gap diagnostics. The previous proposal added files.producer. That is insufficient. An embedded-language file can contain: - wrapper-language facts; - one or more host-language regions; - bridge-generated references; - declarations emitted by different package components. Provenance and language identity must be row-level. ## Outcome Dynamic facts are searchable without affecting existing bindings by default. Resolver participation is an explicit, project-approved capability. Cross-language resolution occurs only through declared bridges. Every edge and every failed binding can disclose whether dynamic evidence influenced the decision. ## Schema model Introduce normalized package/generation/component identity and attach it to every extraction contribution. Conceptual tables: plugin_packages( digest, package_id, version, abi, installed_metadata... ) plugin_generations( id, project_root_identity, package_digest, state, capability_grant_digest, engine_version... ) extraction_components( generation_id, component_id, language_id, claim_id... ) file_contributions( id, file_id, component_id, source_region, status, diagnostics... ) Symbols, refs and imports reference file_contributions or carry an equivalent non-null producer key. Diagnostics do as well. Requirements: - compiled plugins receive explicit producer identities such as builtin:<id>@<engine-version>; - text-only indexing has its own producer identity; - no magic producer string parsing in SQL; - package digest, generation and component are queryable without joining through mutable manifest text; - removal of one contribution cannot delete another component’s facts from the same file; - cascade behavior is tested for upgrade, removal and mixed-language files. files.lang may remain a compatibility summary, but symbols, refs and imports use their own source language. The primary file language cannot determine mixed-region resolver behavior. ## Dynamic language identity A package-defined language uses a stable namespaced id, for example org.example.xaml/xaml. It may not stamp csharp merely to enter C# pools. Language identity and resolution domain are separate: - language id identifies the source syntax/semantics; - resolution profile defines same-language rules; - bridge capability defines permitted cross-language matching. This removes the prior “own-lang is inert, host-lang contaminates” false choice. ## Language profiles Move every resolution-relevant hardcoded language branch into either: 1. a core invariant that applies to all languages; or 2. a validated profile field. Inventory includes at least: - identifier case-folding/collision rules; - qualified-name separators and normalization; - type, member, container and module kind classes; - positional type-kind demotion; - relative import extensions and normalization; - same-directory/proximity eligibility; - reachability/file-key construction; - data-member-only semantics; - extension-method eligibility; - rename collision comparison; - namespace/package behavior. A profile cannot provide SQL or arbitrary predicates. It selects bounded algorithms and data from a closed core-owned registry. Unknown algorithms fail package activation. ## Capability model The package manifest requests capabilities; installation and project activation grant a subset. Facts carry capability classes validated against that grant. Default grant: - searchable symbols; - file outline; - text/source navigation; - unresolved reference visibility; - no candidate-pool participation; - no reachability anchors; - no cross-file or cross-language binding. Separate capability axes include: - same-file definition candidates; - same-language exported candidates; - reachability/import anchors; - qualified-name candidates; - type-position candidates; - member candidates; - cross-language bridge source; - cross-language bridge destination. Capabilities are intentionally finer than “participates in tier N.” Resolver tiers combine multiple evidence classes, and a package should not receive unrelated authority because one use case needs a single class. Dangerous capabilities such as module/file-key anchors, extension-method definitions and partial-class scope require explicit host support and stronger conformance gates. They are never inferred from emitted kinds. ## Cross-language bridges A bridge declaration is directional and bounded: - source language/profile; - source ref kind/role/capture class; - destination language; - destination symbol kind/capability class; - scope relationship: same file, paired file, same package/directory, import-reachable, or another closed algorithm; - name/qualifier mapping from a closed normalization registry; - ambiguity policy, always uniqueness-based and core-owned; - evidence grade and disclosure text. Examples: - XAML Click value to a C# method in the paired code-behind class; - XAML x:Class value to a C# class; - Razor embedded C# regions processed as C# contributions rather than pretending Razor is C#. Binding paths whose destination type is unproven remain unresolved. A package cannot assert that a string names a target. Undeclared language pairs never share candidate pools even when normalized names match. ## Pool construction All candidate and anchor CTEs/tables must be audited. A dynamic row reaches a pool only when: - its contribution is in the active generation; - its language/profile matches the rule; - its fact capability class is valid; - the project grant permits that class; - for cross-language matching, an approved bridge admits the exact source/destination classes. This applies to symbol_buckets, exported pools, directory buckets, qualified candidates/anchors, file keys, extension candidates, C# partial scope and diagnostic pool CTEs. There must be no “one forgotten CTE” escape hatch. Default-inert dynamic rows must not alter: - any existing target_id; - candidate counts used to decide uniqueness; - no_candidate/internal_missed/ambiguous classification; - ref_count or symbol_edges for builtin evidence. ## Evidence provenance resolved_by continues to identify the core resolver mechanism. Do not overload it with producer identity. Add computed resolution influence: - builtin_only: deciding candidate/anchor sets contained no dynamic contribution; - dynamic_endpoint: source or selected target is dynamic, but no excluded competing dynamic evidence affected uniqueness; - dynamic_influenced: dynamic evidence changed candidate count, reachability or a bridge decision, including a ref that remains unresolved because a dynamic candidate introduced ambiguity. The exact representation may be an enum plus deciding generation/component ids. It is recomputed by the resolver and never asserted by the plugin. An absent field from an old daemon is “not reported,” never builtin_only. ## Search and tool behavior - search_symbols and file_outline include producer/generation/language metadata for dynamic rows. - find_references/callers/callees expose influence on resolved rows. - resolution_gaps classifies against the capability-filtered active pools and discloses dynamic influence. - safe_delete/check_rename include dynamic text/fact evidence and capability limitations. - project_overview summarizes active languages, packages, grants and dynamically influenced edge counts through #80. Response formats need concise shapes that do not turn every symbol row into a manifest dump; stable package handles can reference a package block once per response. ## Migration and builtins Backfill every existing row with a builtin/text contribution identity without changing resolved targets. Migration equivalence requires: - symbols/refs/imports identical; - target_id and resolved_by identical; - ref_count and symbol_edges identical; - project_overview counts identical except additive provenance fields; - cold index equals upgraded index. Compiled and package versions of the migration language can run in shadow databases and compare canonical fact projections. ## Tests Structural gates: - every resolver candidate/anchor definition contains an active-generation and capability predicate; - every extraction table row has valid contribution provenance; - no language-specific allowlist remains without registry coverage; - bridge algorithms are closed and enumerated. Mutation gates: - one dynamic same-name symbol cannot capture builtin refs; - one dynamic collision cannot destroy builtin bindings; - a dynamic module cannot create file-key reachability without the capability; - a dynamic C#-named row cannot enter partial-class scope by language-string spoofing; - a qualified-name collision cannot suppress builtin TYPE_FQN resolution; - diagnostic pools remain unchanged for inert rows; - granting exactly one capability changes only its declared population; - undeclared bridge produces zero cross-language edges; - mixed-language file removal deletes only the selected contribution. Corpus gate: Index the same corpus with no package, inert package and progressively granted package. The no-package and inert projections of all pre-existing bindings and diagnostics must be identical. Every movement after a grant must fall inside the bridge/capability’s declared source population, with a positive control proving the capability did useful work. ## Acceptance 1. Dynamic facts are visible in search while completely inert to resolver pools by default. 2. A mixed-language file carries multiple producers/languages without lying through files.lang. 3. XAML to C# edges exist only through explicit bridge capabilities. 4. Every dynamically influenced resolution or ambiguity is identifiable. 5. Removing/rolling back one component cannot leave rows or affect another contribution. 6. Builtin migration is byte/semantics-equivalent apart from additive provenance. 7. No package can gain resolver authority merely by choosing a language string, symbol kind or qualified name.
buildagent changed title from resolver: candidate pools have no producer axis — runtime-defined symbols are indistinguishable from plugin symbols to resolver: row-level provenance, capability-gated pools and explicit language bridges 2026-08-26 13:32:13 +02:00
Author
Member

Closing with residuals. Shipped in v0.23.0 — not v0.25.0, and the "one release at #80's gate" policy this stayed open under was retired days ago.

Audited criterion by criterion against the tree, with the test that grades each one named. 6 of 7 met.

# criterion verdict evidence
1 dynamic facts searchable, inert by default MET resolver_containment.rs::step_one_equals_step_two_for_every_pre_existing_row; capability_isolation.rs::an_inert_row_reaches_no_aggregate
2 mixed-language file carries multiple producers NOT EXERCISABLE see below
3 XAML→C# only through explicit bridges MET bridge_isolation.rs, 15 tests incl. an_undeclared_language_pair_resolves_nothing
4 every dynamically influenced resolution identifiable MET influence_classification.rs, 12 tests incl. influence_is_recomputed_and_never_latched
5 removing one component leaves no rows, touches no other MET provenance_invariants.rs::deleting_one_contribution_takes_only_its_own_rows
6 builtin migration equivalent apart from additive provenance MET activation_identity.rs::the_backfill_is_what_a_default_project_recomputes; upgrade_equivalence.rs::a_v42_corpus_index_migrates_to_the_same_projection_a_fresh_one_builds
7 no package gains authority via language string / kind / qualified name MET capability_isolation.rs ×5, plus the structural gate pool_capability_registry.rs (1811 lines)

Criterion 2, stated honestly rather than resolved by wording

EMBEDDED_DISPATCH_SEMANTICS_VERSION = 0 — routing is whole-file, single-owner (dirty.rs:181-191). A mixed-language file with two producers is not merely untested; there is no configuration of this build in which it could be true.

The half this issue owns is delivered: m0044_row_language made language a row-level fact, and file_contributions carries region_start/region_end keyed per generation — the storage for two contributions in one file exists and is graded. What is missing is the dispatcher, which belongs to the routing layer.

I am deliberately not calling that "met because the schema supports it." That move is the one this project keeps paying for: #84's axis C graded zero and passed; #109's weekly jobs skip on every scheduled run; #116's ceiling cannot fail for either regression its own comment names. So criterion 2 is split out as #119 with its own acceptance, rather than absorbed into this close. If you read C2 literally, this issue stays open — I would not argue hard against that, and #119 exists so the requirement is owned either way.

The body requirement that is genuinely incomplete

This issue says: "Move every resolution-relevant hardcoded language branch into a core invariant or a validated profile field," naming qualified-name separators, positional type-kind demotion, and same-directory eligibility.

Three survive, and each measurably costs a packaged language real output — verified in source and independently confirmed by the #84 Ruby port, which hit all three as pinned deltas:

  • writer.rs:848 qualified_name_separator(lang) → every packaged language gets qualified_name = NULL
  • core/kinds.rs:236 POSITION_TYPE_KINDS_BY_LANG → packaged Ruby loses the type-position exemption; 19 refs demoted to member_access
  • index.rs:4405 st.lang IN ('php','ruby','csharp') → packaged Ruby never gets TIER1B_SAME_DIRECTORY; 53 refs, every one builtin resolved_by == 12 and package-unresolved

Filed as #112. No manifest can close any of them.

A near-miss worth recording

The audit nearly reported "no registry coverage for those three" and would have been wrong. pool_capability_registry.rs does register the C# gates (NeverDynamic("s.lang = 'csharp'") ×4, plus s.is_extension = 1) and verifies each predicate really appears in the source. Its scope is pool relations; these three are a writer function, a post-resolution re-kinding table, and a tier-1b CASE arm — structurally outside it.

That is exactly why the registry's gate is green while the gap is real, and it is the reason to prefer "audit by behaviour" over "grep for the name". Two other claims in the same audit were made from a name-grep and both were false.

Residuals

  • #119 — embedded-region dispatch (criterion 2's literal reading).
  • #112 — the three non-pool language gates.
  • #86 — the fact ABI has no qualifier text field; the Ruby port measured 876 rows lost to it, proved causally (patching the builtin to emit None took the delta 876→0). Commented there rather than filed separately, since #86 already carries the two sibling ABI gaps.
## Closing with residuals. Shipped in **v0.23.0** — not v0.25.0, and the "one release at #80's gate" policy this stayed open under was retired days ago. Audited criterion by criterion against the tree, with the test that grades each one named. **6 of 7 met.** | # | criterion | verdict | evidence | |---|---|---|---| | 1 | dynamic facts searchable, inert by default | **MET** | `resolver_containment.rs::step_one_equals_step_two_for_every_pre_existing_row`; `capability_isolation.rs::an_inert_row_reaches_no_aggregate` | | 2 | mixed-language file carries multiple producers | **NOT EXERCISABLE** | see below | | 3 | XAML→C# only through explicit bridges | **MET** | `bridge_isolation.rs`, 15 tests incl. `an_undeclared_language_pair_resolves_nothing` | | 4 | every dynamically influenced resolution identifiable | **MET** | `influence_classification.rs`, 12 tests incl. `influence_is_recomputed_and_never_latched` | | 5 | removing one component leaves no rows, touches no other | **MET** | `provenance_invariants.rs::deleting_one_contribution_takes_only_its_own_rows` | | 6 | builtin migration equivalent apart from additive provenance | **MET** | `activation_identity.rs::the_backfill_is_what_a_default_project_recomputes`; `upgrade_equivalence.rs::a_v42_corpus_index_migrates_to_the_same_projection_a_fresh_one_builds` | | 7 | no package gains authority via language string / kind / qualified name | **MET** | `capability_isolation.rs` ×5, plus the structural gate `pool_capability_registry.rs` (1811 lines) | ### Criterion 2, stated honestly rather than resolved by wording `EMBEDDED_DISPATCH_SEMANTICS_VERSION = 0` — routing is whole-file, single-owner (`dirty.rs:181-191`). A mixed-language file with two producers is **not merely untested; there is no configuration of this build in which it could be true.** The half this issue owns *is* delivered: `m0044_row_language` made language a row-level fact, and `file_contributions` carries `region_start`/`region_end` keyed per generation — the storage for two contributions in one file exists and is graded. What is missing is the **dispatcher**, which belongs to the routing layer. **I am deliberately not calling that "met because the schema supports it."** That move is the one this project keeps paying for: #84's axis C graded zero and passed; #109's weekly jobs skip on every scheduled run; #116's ceiling cannot fail for either regression its own comment names. So criterion 2 is **split out as #119** with its own acceptance, rather than absorbed into this close. If you read C2 literally, this issue stays open — I would not argue hard against that, and #119 exists so the requirement is owned either way. ### The body requirement that is genuinely incomplete This issue says: *"Move every resolution-relevant hardcoded language branch into a core invariant or a validated profile field,"* naming qualified-name separators, positional type-kind demotion, and same-directory eligibility. **Three survive, and each measurably costs a packaged language real output** — verified in source and independently confirmed by the #84 Ruby port, which hit all three as pinned deltas: - `writer.rs:848` `qualified_name_separator(lang)` → every packaged language gets `qualified_name = NULL` - `core/kinds.rs:236` `POSITION_TYPE_KINDS_BY_LANG` → packaged Ruby loses the type-position exemption; 19 refs demoted to `member_access` - `index.rs:4405` `st.lang IN ('php','ruby','csharp')` → packaged Ruby never gets `TIER1B_SAME_DIRECTORY`; 53 refs, every one builtin `resolved_by == 12` and package-unresolved Filed as **#112**. No manifest can close any of them. ### A near-miss worth recording The audit nearly reported "no registry coverage for those three" and would have been **wrong**. `pool_capability_registry.rs` *does* register the C# gates (`NeverDynamic("s.lang = 'csharp'")` ×4, plus `s.is_extension = 1`) and verifies each predicate really appears in the source. Its scope is **pool relations**; these three are a writer function, a post-resolution re-kinding table, and a tier-1b `CASE` arm — structurally outside it. That is exactly why the registry's gate is green while the gap is real, and it is the reason to prefer "audit by behaviour" over "grep for the name". Two other claims in the same audit were made from a name-grep and both were false. ### Residuals - **#119** — embedded-region dispatch (criterion 2's literal reading). - **#112** — the three non-pool language gates. - **#86** — the fact ABI has no qualifier **text** field; the Ruby port measured **876 rows** lost to it, proved causally (patching the builtin to emit `None` took the delta 876→0). Commented there rather than filed separately, since #86 already carries the two sibling ABI gaps.
Author
Member

Close attempted and correctly refused — leaving it open.

change issue state err: cannot close this issue because it still has open dependencies

Dependencies are #76 (open) and #65 (closed today). So #76 is the single formal blocker, and the refusal is right rather than bureaucratic: #76 defines the ABI and manifest contract that this issue's provenance model consumes, and #76 has a criterion that is genuinely unmet (the declarative extractor tier — packages.rs:1537 refuses Tier::Declarative outright).

Leaving the tracker's judgement in place rather than unlinking the dependency to force the close I had queued. The assessment in the comment above stands unchanged: 6 of 7 criteria met, residuals filed as #119, #112 and a comment on #86. This issue closes when #76 does, and nothing here needs re-auditing at that point.

Noting it explicitly because this is the second time the dependency graph has refused a close on this epic — #79 hit the same refusal on 2026-09-03 — and both times the graph was more accurate than the close would have been.

### Close attempted and correctly refused — leaving it open. ``` change issue state err: cannot close this issue because it still has open dependencies ``` Dependencies are **#76** (open) and **#65** (closed today). So #76 is the single formal blocker, and the refusal is right rather than bureaucratic: #76 defines the ABI and manifest contract that this issue's provenance model consumes, and #76 has a criterion that is genuinely unmet (the declarative extractor tier — `packages.rs:1537` refuses `Tier::Declarative` outright). Leaving the tracker's judgement in place rather than unlinking the dependency to force the close I had queued. The assessment in the comment above stands unchanged: **6 of 7 criteria met, residuals filed as #119, #112 and a comment on #86.** This issue closes when #76 does, and nothing here needs re-auditing at that point. Noting it explicitly because this is the second time the dependency graph has refused a close on this epic — #79 hit the same refusal on 2026-09-03 — and both times the graph was more accurate than the close would have been.
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.

Blocks Depends on
Reference
h-dv/code-index#77
No description provided.