epic: runtime plugin architecture — dynamic grammar, extraction and resolution packages #75

Open
opened 2026-08-26 12:30:04 +02:00 by buildagent · 4 comments
Member

Decision

code-index will become a runtime-extensible indexing platform. A format is supported by installing a plugin package, not by linking another Rust crate and publishing another code-index release.

This deliberately reverses the v1 non-goal “Dynamic plugin loading from disk” in ARCHITECTURE.md and _prdoc/vision/00-mission.md. The implementation must update those decisions and record the new trust model before the first plugin is enabled.

Definition of pluggable

This epic is complete only when an already-released code-index binary can:

  1. install a previously unknown plugin package;
  2. load its grammar dynamically;
  3. run its declarative or executable extractor;
  4. index symbols, references and imports;
  5. apply only explicitly approved resolver semantics and cross-language bridges;
  6. upgrade, disable, roll back and remove the plugin without stale rows;
  7. survive malformed, adversarial and non-terminating plugin code without wedging the daemon; and
  8. disclose exactly which package generation produced every result.

A runtime rule file over a grammar compiled into the binary does not meet this definition. Grammar loading is foundational, not a deferred enhancement.

Governing architecture

plugin package
  manifest + content digest + ABI requirements
  grammar.wasm
  declarative queries/rules
  optional extractor.wasm
  language resolution profile
  explicit cross-language bridge declarations
  fixtures and conformance expectations
          |
          v
supervised plugin-host process
  no project filesystem or network access
  bounded CPU, memory, output and parse depth
  hard deadline; kill and restart on timeout
          |
          v
validated extraction-fact protocol
  symbols, refs, imports, diagnostics
  no database ids and no asserted target edges
          |
          v
core indexer
  row-level producer/generation provenance
  capability-gated candidate pools
  core-owned resolution
  generation-safe activation and rollback

The core resolver remains the only component allowed to declare an edge. A plugin may emit a reference site and evidence, never a target symbol id. That rule is necessary but not sufficient: candidate-pool membership is itself authority and is therefore capability-gated by #77.

Package model

A package is content-addressed and immutable. Its identity covers every byte and every interpretation input:

  • package id and semantic version;
  • package-format and host ABI versions;
  • tree-sitter grammar artifact and grammar ABI;
  • declarative extraction rules and optional extractor component;
  • language profile and bridge declarations;
  • ordered path claims and precedence;
  • declared resource budgets;
  • conformance fixtures;
  • rule-engine and plugin-host compatibility requirements.

An edited package is a new digest even when its author forgot to bump the version. Projects activate an exact digest. Mutable “latest” identifiers may be resolved by an install command but are never stored as the active identity.

Project checkouts are untrusted. Merely cloning a repository must never execute a grammar or extractor. Packages are installed into a user-controlled registry and explicitly approved for a project. Project configuration may request a package digest but cannot silently grant executable or resolver capabilities.

Two extractor tiers

A real ecosystem needs both:

  1. Declarative extraction for formats whose semantics can be expressed as bounded tree queries, captures, literal transforms and container rules.
  2. Optional extractor WASM for stateful semantics such as scope tracking, visibility sections, binding inference, macro/token handling and framework conventions.

The second tier is required because the existing plugins demonstrate that node-kind tables alone are not expressive enough. Both tiers emit the same validated fact protocol and receive the same budgets. Executable extractors do not gain database, filesystem, network or resolver access.

Compiled plugins may remain as a trusted fast path during migration, but the external package path must be able to express and pass the corpus of at least one complete existing language plugin. “Markup only” is not acceptance for this epic.

Language and bridge model

Every package-defined language has its own stable namespaced language id. It must not borrow csharp, rust or another host language merely to enter existing pools.

Resolution-relevant behavior moves out of hardcoded language allowlists into a validated language profile, including:

  • case and identifier normalization;
  • qualified-name separator and canonicalization;
  • symbol kinds eligible as types, members and containers;
  • import/module normalization and relative-import behavior;
  • visibility defaults;
  • type-position and member-only role semantics;
  • permitted proximity and reachability rules.

Cross-language behavior is explicit. XAML to C#, Razor to C#, Vue to TypeScript and similar relationships are bridge capabilities with direction, source ref classes, destination symbol classes and scope constraints. A bridge is never inferred from two languages sharing a string.

Default capability is searchable-but-inert: plugin-produced symbols appear in search and outlines but participate in no cross-file candidate pool. Capabilities are promoted deliberately and are visible in payloads.

Isolation decision

The current tree-sitter WASM integration cannot be the production security boundary: an infinite lexer is not interruptible through its public API, stores may share memory/table state across grammars, and store lifetime conflicts with the current rayon parse path.

Dynamic grammars and extractor components therefore run in a supervised helper process defined by #79. The parent reads the already-eligible file and sends bytes plus bounded metadata; the plugin receives no path-based filesystem capability. A timeout kills the worker, not a rayon thread or the daemon. One plugin package never shares a WASM store with another package.

An in-process trusted fast path may be considered later, but it is not the correctness path and cannot change observable extraction output.

Data ownership and provenance

Provenance is row-level, not file-level. Embedded-language files can contain facts from the wrapper package, a host-language extractor and bridge rules simultaneously. Symbols, refs, imports and diagnostics must identify package digest, activation generation, language id and extraction component.

The existing files.lang value may remain as a compatibility summary, but it cannot be the source of truth for mixed-language resolution. #77 defines the schema and pool changes.

Activation and recovery

Plugin changes are index inputs just like source bytes. #78 replaces extension-only invalidation with generation-based activation:

  • build a pending generation without exposing partial results;
  • re-evaluate every file in the package claim domain, including formerly text-only and newly ineligible files;
  • validate extraction and resolver gates;
  • atomically promote the generation;
  • retain the previous generation for rollback;
  • recover deterministically after kill -9 at every transition;
  • garbage-collect inactive generations only after promotion is durable.

Cold indexing and incremental activation must converge to the same active database projection.

Child issues and order

  1. #76 — immutable package format, versioned host ABI, language profile schema and validated extraction-fact protocol.
  2. #79 — supervised, killable runtime grammar and extractor host. Depends on #76.
  3. #77 — row-level provenance, dynamic language identities, capability-gated resolver participation and explicit bridges. Depends on #76; integrates with #79.
  4. #78 — content-digest detection, claim-domain re-evaluation, pending/active generations, atomic promotion and rollback. Depends on #76, #77 and #79.
  5. #80 — package conformance kit, hostile-plugin tests, disclosures, operational CLI and end-to-end production proof. Depends on all preceding issues.

#81 remains independent and should ship immediately: honest disclosure of symbol-blind extensions is required before and after runtime plugins exist.

Reference implementation

XAML/C# is the first end-to-end bridge because it exercises all hard parts:

  • a dynamically loaded XML-family grammar;
  • XAML symbols and event/type references;
  • code-behind C# as a distinct destination language;
  • searchable but initially inert x:Name declarations;
  • explicit Click and x:Class bridge rules;
  • ambiguous Binding paths remaining unresolved unless a declared, graded rule proves a destination;
  • generated Designer.cs coverage measured separately rather than hidden by the plugin system.

A second acceptance package must migrate one complete existing compiled language plugin and produce an equivalent fact projection on its fixtures and corpus probes. This prevents an architecture that is only nominally generic.

Production gates

The epic does not waive existing production blockers or scale contracts. Plugin activation must degrade with disclosure rather than hang, OOM or poison the active index.

Required end-to-end mutations include:

  • infinite grammar lexer;
  • infinite extractor loop;
  • memory and output bombs;
  • malformed spans, parent ordering, kinds and UTF-8;
  • package edit without version bump;
  • claim precedence reorder;
  • text to code and code to text transitions;
  • package removal and rollback;
  • daemon crash during every activation phase;
  • mixed-language file with multiple producers;
  • attempted candidate-pool contamination;
  • undeclared cross-language bridge;
  • old client/new daemon and new client/old daemon skew.

Final acceptance

Using only released binaries and plugin-management commands:

  1. install an unknown package by pinned digest;
  2. activate it for a project;
  3. index its new grammar with useful symbols, refs and imports;
  4. resolve only explicitly permitted edges;
  5. observe package/generation/capability provenance;
  6. upgrade it and prove cold equals incremental;
  7. kill the daemon during upgrade and recover the old or new complete generation, never a mixture;
  8. roll back and remove it with no surviving rows;
  9. demonstrate that a hanging or malicious package is terminated while the daemon and other projects remain usable.

Anything less is extensible configuration, not a pluggable indexer.

## Decision code-index will become a runtime-extensible indexing platform. A format is supported by installing a plugin package, not by linking another Rust crate and publishing another code-index release. This deliberately reverses the v1 non-goal “Dynamic plugin loading from disk” in ARCHITECTURE.md and _prdoc/vision/00-mission.md. The implementation must update those decisions and record the new trust model before the first plugin is enabled. ## Definition of pluggable This epic is complete only when an already-released code-index binary can: 1. install a previously unknown plugin package; 2. load its grammar dynamically; 3. run its declarative or executable extractor; 4. index symbols, references and imports; 5. apply only explicitly approved resolver semantics and cross-language bridges; 6. upgrade, disable, roll back and remove the plugin without stale rows; 7. survive malformed, adversarial and non-terminating plugin code without wedging the daemon; and 8. disclose exactly which package generation produced every result. A runtime rule file over a grammar compiled into the binary does not meet this definition. Grammar loading is foundational, not a deferred enhancement. ## Governing architecture plugin package manifest + content digest + ABI requirements grammar.wasm declarative queries/rules optional extractor.wasm language resolution profile explicit cross-language bridge declarations fixtures and conformance expectations | v supervised plugin-host process no project filesystem or network access bounded CPU, memory, output and parse depth hard deadline; kill and restart on timeout | v validated extraction-fact protocol symbols, refs, imports, diagnostics no database ids and no asserted target edges | v core indexer row-level producer/generation provenance capability-gated candidate pools core-owned resolution generation-safe activation and rollback The core resolver remains the only component allowed to declare an edge. A plugin may emit a reference site and evidence, never a target symbol id. That rule is necessary but not sufficient: candidate-pool membership is itself authority and is therefore capability-gated by #77. ## Package model A package is content-addressed and immutable. Its identity covers every byte and every interpretation input: - package id and semantic version; - package-format and host ABI versions; - tree-sitter grammar artifact and grammar ABI; - declarative extraction rules and optional extractor component; - language profile and bridge declarations; - ordered path claims and precedence; - declared resource budgets; - conformance fixtures; - rule-engine and plugin-host compatibility requirements. An edited package is a new digest even when its author forgot to bump the version. Projects activate an exact digest. Mutable “latest” identifiers may be resolved by an install command but are never stored as the active identity. Project checkouts are untrusted. Merely cloning a repository must never execute a grammar or extractor. Packages are installed into a user-controlled registry and explicitly approved for a project. Project configuration may request a package digest but cannot silently grant executable or resolver capabilities. ## Two extractor tiers A real ecosystem needs both: 1. Declarative extraction for formats whose semantics can be expressed as bounded tree queries, captures, literal transforms and container rules. 2. Optional extractor WASM for stateful semantics such as scope tracking, visibility sections, binding inference, macro/token handling and framework conventions. The second tier is required because the existing plugins demonstrate that node-kind tables alone are not expressive enough. Both tiers emit the same validated fact protocol and receive the same budgets. Executable extractors do not gain database, filesystem, network or resolver access. Compiled plugins may remain as a trusted fast path during migration, but the external package path must be able to express and pass the corpus of at least one complete existing language plugin. “Markup only” is not acceptance for this epic. ## Language and bridge model Every package-defined language has its own stable namespaced language id. It must not borrow csharp, rust or another host language merely to enter existing pools. Resolution-relevant behavior moves out of hardcoded language allowlists into a validated language profile, including: - case and identifier normalization; - qualified-name separator and canonicalization; - symbol kinds eligible as types, members and containers; - import/module normalization and relative-import behavior; - visibility defaults; - type-position and member-only role semantics; - permitted proximity and reachability rules. Cross-language behavior is explicit. XAML to C#, Razor to C#, Vue to TypeScript and similar relationships are bridge capabilities with direction, source ref classes, destination symbol classes and scope constraints. A bridge is never inferred from two languages sharing a string. Default capability is searchable-but-inert: plugin-produced symbols appear in search and outlines but participate in no cross-file candidate pool. Capabilities are promoted deliberately and are visible in payloads. ## Isolation decision The current tree-sitter WASM integration cannot be the production security boundary: an infinite lexer is not interruptible through its public API, stores may share memory/table state across grammars, and store lifetime conflicts with the current rayon parse path. Dynamic grammars and extractor components therefore run in a supervised helper process defined by #79. The parent reads the already-eligible file and sends bytes plus bounded metadata; the plugin receives no path-based filesystem capability. A timeout kills the worker, not a rayon thread or the daemon. One plugin package never shares a WASM store with another package. An in-process trusted fast path may be considered later, but it is not the correctness path and cannot change observable extraction output. ## Data ownership and provenance Provenance is row-level, not file-level. Embedded-language files can contain facts from the wrapper package, a host-language extractor and bridge rules simultaneously. Symbols, refs, imports and diagnostics must identify package digest, activation generation, language id and extraction component. The existing files.lang value may remain as a compatibility summary, but it cannot be the source of truth for mixed-language resolution. #77 defines the schema and pool changes. ## Activation and recovery Plugin changes are index inputs just like source bytes. #78 replaces extension-only invalidation with generation-based activation: - build a pending generation without exposing partial results; - re-evaluate every file in the package claim domain, including formerly text-only and newly ineligible files; - validate extraction and resolver gates; - atomically promote the generation; - retain the previous generation for rollback; - recover deterministically after kill -9 at every transition; - garbage-collect inactive generations only after promotion is durable. Cold indexing and incremental activation must converge to the same active database projection. ## Child issues and order 1. #76 — immutable package format, versioned host ABI, language profile schema and validated extraction-fact protocol. 2. #79 — supervised, killable runtime grammar and extractor host. Depends on #76. 3. #77 — row-level provenance, dynamic language identities, capability-gated resolver participation and explicit bridges. Depends on #76; integrates with #79. 4. #78 — content-digest detection, claim-domain re-evaluation, pending/active generations, atomic promotion and rollback. Depends on #76, #77 and #79. 5. #80 — package conformance kit, hostile-plugin tests, disclosures, operational CLI and end-to-end production proof. Depends on all preceding issues. #81 remains independent and should ship immediately: honest disclosure of symbol-blind extensions is required before and after runtime plugins exist. ## Reference implementation XAML/C# is the first end-to-end bridge because it exercises all hard parts: - a dynamically loaded XML-family grammar; - XAML symbols and event/type references; - code-behind C# as a distinct destination language; - searchable but initially inert x:Name declarations; - explicit Click and x:Class bridge rules; - ambiguous Binding paths remaining unresolved unless a declared, graded rule proves a destination; - generated Designer.cs coverage measured separately rather than hidden by the plugin system. A second acceptance package must migrate one complete existing compiled language plugin and produce an equivalent fact projection on its fixtures and corpus probes. This prevents an architecture that is only nominally generic. ## Production gates The epic does not waive existing production blockers or scale contracts. Plugin activation must degrade with disclosure rather than hang, OOM or poison the active index. Required end-to-end mutations include: - infinite grammar lexer; - infinite extractor loop; - memory and output bombs; - malformed spans, parent ordering, kinds and UTF-8; - package edit without version bump; - claim precedence reorder; - text to code and code to text transitions; - package removal and rollback; - daemon crash during every activation phase; - mixed-language file with multiple producers; - attempted candidate-pool contamination; - undeclared cross-language bridge; - old client/new daemon and new client/old daemon skew. ## Final acceptance Using only released binaries and plugin-management commands: 1. install an unknown package by pinned digest; 2. activate it for a project; 3. index its new grammar with useful symbols, refs and imports; 4. resolve only explicitly permitted edges; 5. observe package/generation/capability provenance; 6. upgrade it and prove cold equals incremental; 7. kill the daemon during upgrade and recover the old or new complete generation, never a mixture; 8. roll back and remove it with no surviving rows; 9. demonstrate that a hanging or malicious package is terminated while the daemon and other projects remain usable. Anything less is extensible configuration, not a pluggable indexer.
buildagent changed title from epic: runtime-extensible format support — dialects as data, not as binaries to epic: runtime plugin architecture — dynamic grammar, extraction and resolution packages 2026-08-26 13:30:16 +02:00
dhoyer referenced this issue from a commit 2026-09-02 08:54:45 +02:00
Author
Member

Status as of v0.26.1 — measured, not remembered

The epic has SHIPPED to master and is live. The working note that said "one release at #80's gate, nothing ships until then" is superseded: v0.25.0 and v0.26.0 both contain the plugin system, and this repository's own index currently reports a runtime package active —

plugin_activation: { active_generation: 1,
                     active_activation_digest: "sha256:1821018a6d183eedcc6a8842583ff015732ec847249a6d38ce9d780f96c27b1d" }
symbol_blind_extensions.structurally_indexed:
    { extension: "de.h-dv.xaml/xaml", files: 2, state: "claimed_and_structurally_indexed" }

That is the reference implementation loading a dynamic grammar and producing structural rows, in production, on the dogfood repo.

Phases

phase issues state
0 #65 done — resolver fan-out bounded, per-stage budgets, live progress + cancellation
1 #76, #79 done — fact ABI, .cip format, supervised killable worker
2 #77 done — schema v42→v48: producer identity, row provenance, capability-gated pools, directional bridges
3 #78 done — schema v49→v54: 8-state durable machine, atomic promotion + rollback, kill -9 at all 12 transitions, reader epoch
4 #80 in progress
— #81 closed

#76/#77/#78/#79 remain open as tracker bookkeeping; their code is landed and shipping.

The three production-wiring gaps from #80 S15 are now ALL CLOSED

They were the reason "all six conformance layers implemented" was not the same as production-ready. Re-measured just now with this project's own find_callers, the same instrument that found them:

1. "A running daemon never observes an approval change" — CLOSED. crates/daemon/src/reactivate.rs polls the approval surface (DEFAULT_POLL_INTERVAL 5 s, chosen against the daemon's other periodic jobs at 60 s / 90 s / 30 min). Sentinel::moved() is true at most once per real move — the witness is adopted before returning, so a failed re-arm does not re-report forever. An unreadable surface answers false and adopts nothing, because a transient read error must not read as "every package was just disabled".

2. "A grant-only activation change has no trigger at all" — CLOSED. The witness includes the capability grant, so a regrant that moves no file's claim is still a move. Its test says so in as many words, and its mutation — dropping the caps= term — is recorded as "that is gap 2's only trigger".

3. "build::build and promotion::promote have no production caller" — CLOSED. It was 14 callers, 14 excluded as tests, 0 production. Now:

find_callers(promotion::promote, exclude_tests=true)
  -> 4 non-test callers, 2 of them in crates/cli/src/activation.rs
     excluded_test_refs: 19

The generation pipeline is reachable from the operator surface.

What still stands between here and final acceptance

  • #84 — the genericity proof. This epic explicitly requires that "a second acceptance package must migrate one complete existing compiled language plugin", and states that markup-only is not acceptance. Ruby is the candidate. Deferred by decision in favour of finishing core infrastructure first.
  • #86 gap 3 (is_extension) — C# cannot migrate. Left open deliberately: admitting the bit is the authorisation into the C# extension-method binding pool, so it is a capability decision, not an ABI addition.
  • #87's other half — a package still cannot replace a builtin.
  • #85 — package extraction is serialised to one thread across all packages; the measured blocker for "every language ships as a plugin".
  • C6 scale — this epic asks for 20k–100k-file claim domains; the largest package-fed corpus is nine files. Still the weakest layer.

Carried forward, measured and named

  • promotion holds the writer lock ~7 s at 100k files (2.5–3.0 µs/row, linear, no knee). The fix direction is rows read through a generation rather than carried between them.
  • cursors carry no active-generation fingerprint; needs an epoch on the paginated reply envelope.
  • a timing ratchet over target/corpus/scale-*.json is still owed.

tests/corpus/baseline.json has not moved once across the whole of #77 and #78 and has never been blessed — that remains the evidence that the capability design is inert by default.

## Status as of v0.26.1 — measured, not remembered **The epic has SHIPPED to master and is live.** The working note that said "one release at #80's gate, nothing ships until then" is superseded: v0.25.0 and v0.26.0 both contain the plugin system, and this repository's own index currently reports a runtime package active — ``` plugin_activation: { active_generation: 1, active_activation_digest: "sha256:1821018a6d183eedcc6a8842583ff015732ec847249a6d38ce9d780f96c27b1d" } symbol_blind_extensions.structurally_indexed: { extension: "de.h-dv.xaml/xaml", files: 2, state: "claimed_and_structurally_indexed" } ``` That is the reference implementation loading a dynamic grammar and producing structural rows, in production, on the dogfood repo. ## Phases | phase | issues | state | |---|---|---| | 0 | #65 | done — resolver fan-out bounded, per-stage budgets, live progress + cancellation | | 1 | #76, #79 | done — fact ABI, `.cip` format, supervised killable worker | | 2 | #77 | done — schema v42→v48: producer identity, row provenance, capability-gated pools, directional bridges | | 3 | #78 | done — schema v49→v54: 8-state durable machine, atomic promotion + rollback, `kill -9` at all 12 transitions, reader epoch | | 4 | #80 | **in progress** | | — | #81 | **closed** | #76/#77/#78/#79 remain open as tracker bookkeeping; their code is landed and shipping. ## The three production-wiring gaps from #80 S15 are now ALL CLOSED They were the reason "all six conformance layers implemented" was not the same as production-ready. Re-measured just now with this project's own `find_callers`, the same instrument that found them: **1. "A running daemon never observes an approval change" — CLOSED.** `crates/daemon/src/reactivate.rs` polls the approval surface (`DEFAULT_POLL_INTERVAL` 5 s, chosen against the daemon's other periodic jobs at 60 s / 90 s / 30 min). `Sentinel::moved()` is true **at most once per real move** — the witness is adopted before returning, so a failed re-arm does not re-report forever. An *unreadable* surface answers `false` and adopts nothing, because a transient read error must not read as "every package was just disabled". **2. "A grant-only activation change has no trigger at all" — CLOSED.** The witness includes the capability grant, so a regrant that moves **no file's claim** is still a move. Its test says so in as many words, and its mutation — dropping the `caps=` term — is recorded as *"that is gap 2's only trigger"*. **3. "`build::build` and `promotion::promote` have no production caller" — CLOSED.** It was 14 callers, 14 excluded as tests, 0 production. Now: ``` find_callers(promotion::promote, exclude_tests=true) -> 4 non-test callers, 2 of them in crates/cli/src/activation.rs excluded_test_refs: 19 ``` The generation pipeline is reachable from the operator surface. ## What still stands between here and final acceptance - **#84 — the genericity proof.** This epic explicitly requires that "a second acceptance package must migrate one complete existing compiled language plugin", and states that markup-only is not acceptance. Ruby is the candidate. **Deferred by decision** in favour of finishing core infrastructure first. - **#86 gap 3 (`is_extension`)** — C# cannot migrate. Left open deliberately: admitting the bit *is* the authorisation into the C# extension-method binding pool, so it is a capability decision, not an ABI addition. - **#87's other half** — a package still cannot replace a builtin. - **#85** — package extraction is serialised to one thread across all packages; the measured blocker for "every language ships as a plugin". - **C6 scale** — this epic asks for 20k–100k-file claim domains; the largest package-fed corpus is nine files. Still the weakest layer. ## Carried forward, measured and named - promotion holds the writer lock **~7 s at 100k files** (2.5–3.0 µs/row, linear, no knee). The fix direction is rows read *through* a generation rather than carried between them. - cursors carry no active-generation fingerprint; needs an epoch on the paginated reply envelope. - a timing ratchet over `target/corpus/scale-*.json` is still owed. `tests/corpus/baseline.json` has **not moved once** across the whole of #77 and #78 and has never been blessed — that remains the evidence that the capability design is inert by default.
Author
Member

Close-out sweep: the epic's true remaining count is 63 open issues, and the phase chain is DONE

Close-out lane, master 552e3a2. Six lanes landed work in the last few hours and closed almost nothing, so the open list overstated the remaining work — twice causing this epic's release gate to be under-scoped. This comment makes the list true. Full evidence lives in the closing comment on each issue; the companion comment on #80 carries the gate-side detail.

Count method, because it has been wrong three times in two days: the Forgejo issues API caps at 50 rows per page and silently ignores a larger limit. Paged until a short page — 50 + 13 + 0 = 63 open issues.


The epic's own structure

phase state
#65 resolver fan-out budgets closed (v0.23.0)
#76 static package validation closed
#77 bridges / resolution packages closed
#78 lifecycle, generations, rollback closed
#79 isolated runtime validation closed
#80 conformance / containment / disclosure gate OPEN
#84 full-language migration proof (#80 step 13) OPEN
#86 three ABI gaps blocking any language but Ruby/PHP OPEN

Phases 0–4 are all closed. What remains structurally is #80, #84, #86 — plus the three of #80's nine formally-linked adjacent blockers that are still open: #41 (scale ceilings), #45 (generation-aware ratchets), #51 (agent-task benchmark). The other six — #65, #71, #72, #73, #81, #82 — are closed.

Closed by this sweep (7)

#110 #164 (bridge tier budget — duplicates of each other), #169 (masked #51 benchmark, verified by dispatch), #170 (parity arm split), #171 (check_rename roster), #172 (C# generic ref names), #176 (__future__ imports + a grammar-derived import-kind registry).

Kept open with corrected text (10)

#93 #125 #168 #173 #174 #175 #177 #178 #179 #180. Each was reported as fixed or refused by its lane; each still has a live, reproducible residual. Seven had their titles rewritten because the filed diagnosis was measured and found wrong — notably #175 (the cause is the method_call → POOL_METHOD pool rule plus python.rs minting no module symbol, not package directories) and #168 (both proposed directions cost ~1 300 correct binds and did not fix the two phantoms; they relocated them into the stem-anchored arm).


Two facts that bear directly on this epic's readiness

1. Master has never been through CI. Local master 552e3a2 is 7 commits ahead of origin/master (a9ba058) and unpushed. Every "gates green" claim in the last few hours' lane reports is a local green.

2. The Windows job is red on the last two dispatches (run 610 / job 33546; run 612 / job 33560), failing ci_disk_preflight and schedule_liveness. The fix is local at 8d9ac8e and unpushed, so it too is unverified by dispatch. windows-gate gates releases, and #80 step 12 requires the runtime gate on every shipped platform.


The 63, grouped

Epic structure (4): 75, 80, 84, 86
#80's remaining formally-linked blockers (3): 41, 45, 51
Roadmap / backlog under this epic (18): 24, 27, 28, 31, 32, 34, 35, 42, 46, 47, 48, 49, 50, 53, 59, 68, 69, 70
Findings, mostly from the last few days (38): 93, 98, 111, 119, 120, 124, 125, 128, 133, 137, 149, 153, 154, 158, 159, 160, 162, 166, 168, 173, 174, 175, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192

Read the last group carefully before sizing anything from it: #188 records that precision_gate's 7/7 with phantom_count == 0 covers ~47 probes over 54 fixture files and never indexes a corpus repo, and #191 records that the pinned corpus is not reachable through our own tools at all. Both are cited routinely as if they were corpus-scale evidence. They are not.

## Close-out sweep: the epic's true remaining count is **63 open issues**, and the phase chain is DONE Close-out lane, master `552e3a2`. Six lanes landed work in the last few hours and closed almost nothing, so the open list overstated the remaining work — twice causing this epic's release gate to be under-scoped. This comment makes the list true. Full evidence lives in the closing comment on each issue; the companion comment on **#80** carries the gate-side detail. **Count method, because it has been wrong three times in two days:** the Forgejo issues API caps at 50 rows per page and silently ignores a larger `limit`. Paged until a short page — 50 + 13 + 0 = **63 open issues**. --- ### The epic's own structure | phase | state | |---|---| | **#65** resolver fan-out budgets | **closed** (v0.23.0) | | **#76** static package validation | **closed** | | **#77** bridges / resolution packages | **closed** | | **#78** lifecycle, generations, rollback | **closed** | | **#79** isolated runtime validation | **closed** | | **#80** conformance / containment / disclosure gate | **OPEN** | | **#84** full-language migration proof (#80 step 13) | **OPEN** | | **#86** three ABI gaps blocking any language but Ruby/PHP | **OPEN** | **Phases 0–4 are all closed.** What remains structurally is #80, #84, #86 — plus the three of #80's nine formally-linked adjacent blockers that are still open: **#41** (scale ceilings), **#45** (generation-aware ratchets), **#51** (agent-task benchmark). The other six — #65, #71, #72, #73, #81, #82 — are closed. ### Closed by this sweep (7) **#110 #164** (bridge tier budget — duplicates of each other), **#169** (masked #51 benchmark, verified by dispatch), **#170** (parity arm split), **#171** (`check_rename` roster), **#172** (C# generic ref names), **#176** (`__future__` imports + a grammar-derived import-kind registry). ### Kept open with corrected text (10) **#93 #125 #168 #173 #174 #175 #177 #178 #179 #180.** Each was reported as fixed or refused by its lane; each still has a live, reproducible residual. Seven had their titles rewritten because the filed diagnosis was measured and found wrong — notably **#175** (the cause is the `method_call → POOL_METHOD` pool rule plus `python.rs` minting no module symbol, not package directories) and **#168** (both proposed directions cost ~1 300 correct binds and did *not* fix the two phantoms; they relocated them into the stem-anchored arm). --- ### Two facts that bear directly on this epic's readiness **1. Master has never been through CI.** Local master `552e3a2` is **7 commits ahead of `origin/master` (`a9ba058`)** and unpushed. Every "gates green" claim in the last few hours' lane reports is a *local* green. **2. The Windows job is red on the last two dispatches** (run 610 / job 33546; run 612 / job 33560), failing `ci_disk_preflight` and `schedule_liveness`. The fix is local at `8d9ac8e` and unpushed, so it too is unverified by dispatch. `windows-gate` gates releases, and #80 step 12 requires the runtime gate on every shipped platform. --- ### The 63, grouped **Epic structure (4):** 75, 80, 84, 86 **#80's remaining formally-linked blockers (3):** 41, 45, 51 **Roadmap / backlog under this epic (18):** 24, 27, 28, 31, 32, 34, 35, 42, 46, 47, 48, 49, 50, 53, 59, 68, 69, 70 **Findings, mostly from the last few days (38):** 93, 98, 111, 119, 120, 124, 125, 128, 133, 137, 149, 153, 154, 158, 159, 160, 162, 166, 168, 173, 174, 175, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192 Read the last group carefully before sizing anything from it: **#188** records that `precision_gate`'s 7/7 with `phantom_count == 0` covers ~47 probes over 54 fixture files and never indexes a corpus repo, and **#191** records that the pinned corpus is not reachable through our own tools at all. Both are cited routinely as if they were corpus-scale evidence. They are not.
Author
Member

Close-out round 2 — the true open count, 2026-09-06, master fc329a8

Four lanes merged since the last close-out and closed nothing. This lane verified their claims against the tree and settled ten issues. Posting the epic-level state so the next planner does not have to re-derive it.

Repository-wide: 55 open issues (was 61 before this round). Counted by paging the Forgejo issues API to a short page — it caps at 50 rows and silently ignores a larger limit, which has produced a wrong count three times in two days.

This epic's children

child state
#76 — package format, host ABI, profile schema, fact protocol closed
#79 — supervised killable grammar/extractor host closed
#77 — provenance, dynamic identities, capability-gated resolution closed
#78 — content-digest detection, generations, promotion, rollback closed
#80 — conformance, hostile containment, disclosure, E2E gate OPEN
#81 — symbol-blind coverage disclosure (independent) closed

One child remains: #80. Phases 0–3 are done; #80 is the whole of what is left, plus the two issues that hang off it: #84 (full-language migration proof — #80 step 13) and #86 (the three plugin-ABI gaps that block migrating any language other than Ruby or PHP).

Closed in this round

#188 precision_gate reports and ratchets its own population (47 probes, 23 declared decoy sites, 87 files, 0 corpus repos; JavaScript's forbid_sites is 1) — floor mutation run to real red by the close-out lane. #189 the receiver locality gate was inverted, now three-valued recv_proof; cs-dapper −183, php-guzzle −206, seven of nine repos byte-identical, ~9:1 phantoms to correct. #111 five payload categories on the wire, summing to the measured total. #98 / #133 reconciled and closed separately, neither as the other's duplicate. #120 the "10x less context" claim corrected at five sites with an inverted pinning test.

Left open, with corrected residuals

#149 (both fields shipped; dynamic_influence.semantics has no gate — deleting its initializer compiles and ships green), #158 (part 2's coverage half: no blessed absolute read-path vm_step over the corpus), #160 (items 1 and 3; tool descriptions are 38,636 chars, +1,293 above the figure the issue was filed on), #162 (implemented and unit-graded, but never executed — zero release.yml runs since the step landed).

One thing this epic should know

Master's own CI is red at fc329a8. The OSS corpus (tier 1) job failed on run #4981 while every other Linux job passed, and it was green on 87a3fc8 one commit earlier. The Windows job fmt + clippy + build + test (windows) has failed on both 87a3fc8 and fc329a8. Per the release workflow that Windows job gates releases. #75 is not complete until [#80's] gate is green — and right now the tree it would run on is not.

## Close-out round 2 — the true open count, 2026-09-06, master `fc329a8` Four lanes merged since the last close-out and closed nothing. This lane verified their claims against the tree and settled ten issues. Posting the epic-level state so the next planner does not have to re-derive it. **Repository-wide: 55 open issues** (was 61 before this round). Counted by paging the Forgejo issues API to a short page — it caps at 50 rows and **silently ignores a larger `limit`**, which has produced a wrong count three times in two days. ### This epic's children | child | state | |---|---| | #76 — package format, host ABI, profile schema, fact protocol | **closed** | | #79 — supervised killable grammar/extractor host | **closed** | | #77 — provenance, dynamic identities, capability-gated resolution | **closed** | | #78 — content-digest detection, generations, promotion, rollback | **closed** | | #80 — conformance, hostile containment, disclosure, E2E gate | **OPEN** | | #81 — symbol-blind coverage disclosure (independent) | **closed** | **One child remains: #80.** Phases 0–3 are done; #80 is the whole of what is left, plus the two issues that hang off it: **#84** (full-language migration proof — #80 step 13) and **#86** (the three plugin-ABI gaps that block migrating any language other than Ruby or PHP). ### Closed in this round **#188** `precision_gate` reports and ratchets its own population (47 probes, 23 declared decoy sites, 87 files, 0 corpus repos; JavaScript's `forbid_sites` is **1**) — floor mutation run to real red by the close-out lane. **#189** the receiver locality gate was inverted, now three-valued `recv_proof`; cs-dapper −183, php-guzzle −206, seven of nine repos byte-identical, ~9:1 phantoms to correct. **#111** five payload categories on the wire, summing to the measured total. **#98 / #133** reconciled and closed separately, neither as the other's duplicate. **#120** the "10x less context" claim corrected at five sites with an inverted pinning test. ### Left open, with corrected residuals **#149** (both fields shipped; `dynamic_influence.semantics` has no gate — deleting its initializer compiles and ships green), **#158** (part 2's coverage half: no blessed absolute read-path `vm_step` over the corpus), **#160** (items 1 and 3; tool descriptions are **38,636 chars, +1,293 above the figure the issue was filed on**), **#162** (implemented and unit-graded, but **never executed** — zero `release.yml` runs since the step landed). ### One thing this epic should know **Master's own CI is red at `fc329a8`.** The `OSS corpus (tier 1)` job failed on run #4981 while every other Linux job passed, and it was **green on `87a3fc8`** one commit earlier. The Windows job `fmt + clippy + build + test (windows)` has failed on both `87a3fc8` and `fc329a8`. Per the release workflow that Windows job **gates releases**. `#75 is not complete until [#80's] gate is green` — and right now the tree it would run on is not.
Author
Member

Next stage of this epic: moving the seven builtin languages into first-party packages is now tracked in #309. That covers the plan, the operator's decisions (reserved plain ids, opt-in enablement, the 10%/20% performance budget, a one-release fallback, and the port order Ruby → PHP → JS → TS → C# → Rust → Python) and progress.

Phase 0 (#306) and Phase 1 (#308) are merged. Master is at 4ecf930.

Next stage of this epic: moving the seven builtin languages into first-party packages is now tracked in #309. That covers the plan, the operator's decisions (reserved plain ids, opt-in enablement, the 10%/20% performance budget, a one-release fallback, and the port order Ruby → PHP → JS → TS → C# → Rust → Python) and progress. Phase 0 (#306) and Phase 1 (#308) are merged. Master is at `4ecf930`.
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.

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