refactor(engine): converge remaining gap-fix artifacts - #234
Merged
Conversation
A historical gap-fix bundle left seven executable engine modules that no production entrypoint could reach. They survived because they imported each other and because tests imported them — a closed loop that reads as "covered" in a coverage report and as `status: active` in an L9_META header, while the runtime never touched a line of it. Removed, with the canonical owner that already held each responsibility: - engine/compliance/audit_persistence.py — kept its own module-global `_POOL`, set only by configure_audit_pool(), which nothing calls, and INSERTed into a 5-column `audit_log` table. The real path is boot.py -> init_dependencies (db_pool) -> EngineState -> ComplianceEngine.flush_audit -> AuditLogger.flush_to_store, writing 16 typed columns to `packet_audit_log`. Two competing audit schemas, one owner. - engine/graph_return_channel.py — a per-tenant queue whose docstring names its consumer as convergence_controller.run_convergence_loop(). Neither the module nor the function exists in this repository. Every producer was itself an orphan, so nothing could enqueue; the consumer was absent, so nothing could drain. - engine/graph/community_export.py — instructed the reader to attach it to a "GDSScheduler post-job completion hook". GDSScheduler has no hook mechanism, so it was not merely unwired but unwireable. Redundant regardless: _run_louvain already writes the label into the graph via gds.louvain.write (writeProperty: community_id in the plasticos spec) and engine/scoring/assembler.py reads it straight back off the node. - engine/convergence_controller_patch.py — told the operator to call patch_convergence_controller(), a function defined neither here nor anywhere else, to patch a convergence_controller.py that does not exist. Its four symbols had no importers, not even a test. Its schema-proposal path imported chassis.events, which also does not exist. - engine/graph/graph_sync_client_fix.py — a "drop this over GraphSyncClient in graph/sync/client.py" replacement for a package and call site that do not exist. Its write shape was incompatible with the canonical one: it MERGEd a labelless node on a hardcoded `entity_id` and set `tenant`, where SyncGenerator MERGEs on the domain-declared idproperty and sets `_tenant`. Had it run it would have built a parallel, unqueryable node keyspace. - engine/contract_enforcement.py — a second PacketEnvelope architecture beside engine/packet/: a private frozenset of packet-type strings against PacketType(StrEnum), a parallel required-fields table, its own content hash. Not one of its packet-type strings appears in the canonical enum. Once the three modules above go, every remaining importer is a test. (Unrelated to docs/L9_Contract_Enforcement_System.md, which specifies the static 24-contract scanner. Name similarity only.) - engine/startup_wiring.py — could not execute even once: its first statement imports `shared.audit_persistence`, and no `shared` package exists, so the call raised ModuleNotFoundError before applying any fix. Two later imports name an absent top-level `graph` package, and it calls a GDSScheduler hook method that does not exist. Its real hazard was documentary: "Add these calls to your application lifespan / startup handler" is a standing instruction to activate the six modules above. engine/boot.py is the startup owner and already creates the Postgres pool this recipe claimed to wire. Tests whose sole purpose was keeping the implementations alive go with them (gap1, gap2, gap5). tests/gap_fixes/test_gap9_inference_authority.py is kept and strengthened rather than dropped: its guard against reintroducing the undeclared spec.kb / load_domain_rules recipe used to read one file's source text, so it now scans the whole engine tree and no longer depends on that file existing. No production behavior changes. No cross-repo consumer exists: a GitHub code search across org:Quantum-L9 for every module path returns exactly one hit, a Cursor-Governance plan document, not a consumer. Verified at base 5868bc4 vs this change: 1984 -> 1981 tests, 0 failures and 0 errors at both. The -3 is exactly accounted for (11 deleted, 8 added).
The removed island survived three years of review because "unreachable" was only ever visible to someone willing to trace imports by hand. Make it mechanical instead. tests/invariants/test_module_reachability.py adds a static AST import-graph analyzer that models absolute, relative, deferred and importlib-string imports plus Python's ancestor-package execution semantics, then answers "can production reach this module?" from chassis ingress and the lifecycle hook. On top of it, six invariants: the removed paths stay absent, no engine or chassis module imports the removed surfaces or symbols, no module ships a gap-fix activation recipe, the five canonical owners stay present, and each is provably production-reachable. A seventh test guards the analyzer itself against silently parsing nothing, which would make the rest vacuous. Scoped deliberately narrower than a full-tree reachability gate. The analyzer reports 59 further unreachable engine modules across nine unaudited subsystems — health, intake, personas, hoprag, kge, arbitration, outcomes, replay, shadow, and notably gates/registry.py plus gates/types/all_gates.py, whose decorator-registered gate classes are never imported because both gates/__init__.py and gates/compiler.py bypass GateRegistry entirely. Gating on the full tree today would need either a 59-entry permanent exemption list or a 59-module classification sweep. Both are out of scope, and the first is the rubber-stamp baseline that makes such gates worthless. Recorded as DEF-001 instead, with the analyzer already built so acting on it is a scope decision rather than new machinery. Also lands the audit evidence under docs/audits/2026-08-23-gap-fix-artifact-convergence/: the artifact inventory, the reachability classification with per-artifact proof and cross-repo consumer analysis, and the implementation filetree. The classification records one finding worth reading on its own: the Cursor-Governance plan docs/plans/BUILT/wire_gap-fix_modules_7d4d9028.plan.md has every todo marked completed and is filed under BUILT, but not one of its outputs exists in this repository — not the relocated modules, not boot_gap_wiring.py, not the relocated tests, not the __init__ exports, not the boot.py call site. Its planned deletions were never performed either, which is why the island was still here. Plan completion text is not evidence about a tree.
ADR-DEC-001 cited engine/graph/graph_sync_client_fix.py:113 as the place where the ungoverned `entity_id` candidate-identity property is "client-supplied at sync time". That module had no caller anywhere in the repository and was removed as an unwired gap-fix artifact, so the cited Cypher never executed. The correction widens the divergence this ADR records rather than narrowing it. The live sync path is handlers.py::handle_sync -> sync/generator.py:: SyncGenerator, which MERGEs on the domain-declared idproperty — facility_id, code, form_id, opportunity_id, demand_id in the plasticos spec. No domain spec declares `idproperty: entity_id`, so no canonical writer of `entity_id` exists at all, while handlers.py:509,616,1497 still read it through silent fallbacks. Evidence pointers only. The decision (OPTION-B: identity is the namespaced entity_ref), the options considered, and the residual reconciliation task are unchanged, and the residual risk stands.
…parsing Two follow-ups from an adversarial re-read of the new invariant module. The removed-symbol guard blocks generic names — ContractViolationError and enforce_packet_envelope in particular — that a future canonical owner could plausibly want. Left unexplained, the cheapest way past a red build is to delete the test, which is exactly the outcome it exists to prevent. It now says where each responsibility lives and what a legitimate reintroduction looks like: add the name to the canonical owner and drop it from REMOVED_SYMBOLS in the same commit. Also flattens the importlib-string branch of the AST walker, which nested an isinstance check inside a conditional expression to reach the same result. Verified the guards are not vacuous: a probe module importing a removed surface trips exactly three invariants (removed-surface import, removed symbol, gap-fix activation recipe); the suite is green with the probe gone.
|
📋 Best Practices for Large Changes
✅ This PR passes the blocking limit but is larger than recommended. |
L9 Audit Harness Report
Step Results
Architecture Audit Findings
See Spec Coverage
See Next StepsAll checks passed. Safe to merge. |
…e policy
Two CI gates, two root causes.
**Baseline ratchet** (Quarantined Debt, Ratchet Verdict, Pre-commit Hooks, and
the CI Gate rollup) failed with:
ledger entry 'packet-envelope/engine-graph-graph-sync-client-fix-py-string-annotation'
no longer matches any observed finding; the debt is resolved —
remove the entry to ratchet the baseline down
Deleting engine/graph/graph_sync_client_fix.py resolved the PacketEnvelope debt
recorded against it, and the ledger header is explicit that migrating a file
must delete its entries in the same PR. Removing the resolved entry is the gate
working as designed, not an accommodation of it: the baseline ratchets from 19
entries to 18. No other entry is touched, and no threshold is relaxed.
**Enforce PR Policies** failed on reviewable size: +1025 additions against a
block threshold of 1000 (additions-only). The audit evidence was 717 of those
lines, and most of its bulk was prose that the PR description already carries in
narrative form. GAP_FIX_REACHABILITY_CLASSIFICATION.yaml goes 411 -> 292 and
IMPLEMENTATION_FILETREE.yaml 125 -> 95 by keeping every fact — classification,
confidence, callers, canonical owner, proof, action — and dropping the essay
around them. No finding, artifact, or evidence item was removed. Now +876 across
16 files, inside both the 1000-addition and 50-file limits.
Also records .l9/baselines/packet-envelope.yml in IMPLEMENTATION_FILETREE.yaml,
since the Phase 7 gate requires every changed file to appear there with the
finding it serves.
Re-validated after the edits: all three audit YAMLs parse, and
`make agent-check-unit` passes all 8 gates with the audit harness green.
Evidence-only repair of four factual defects in the audit record. The seven-module cleanup, its classifications, and all runtime code are unchanged. 1. Predecessor attribution. The record identified the inference-ownership closure contract as having merged as PR #232. False: PR #232 is "fix(inference): remove ghost bridge and unowned KB rule loading" (merge commit 5868bc4, this PR's base). It deliberately RETAINED engine/inference_rule_registry.py and explicitly recorded that the module has no verified production edge. The ownership-closure contract is a separate, unexecuted program. All "predecessor" wording now names PR #232 and its actual scope. 2. Inference registry classification. HISTORICAL_REFERENCE was wrong — the module is executable, current, and reachable only from tests/gap_fixes/test_gap3_inference_registry.py and test_gap9. Reclassified TEST_ONLY_IMPLEMENTATION (CONFIRMED, runtime_callers: [], production_reachability: NONE_VERIFIED). KEEP stands, restated as what it is: a scope decision deferring inference ownership, not a canonicality claim. 3. GateRegistry mechanics. The record said all_gates.py holds "decorator-registered gate classes". There is no decorator registration anywhere in engine/gates/ — GateRegistry._REGISTRY is a static dictionary mapping GateType values to classes imported from all_gates.py. 4. Gate finding upgraded from speculation to GATE-001. "May be a real defect because GateCompiler bypasses it" understated the evidence. Recorded facts: GateCompiler is the production-reachable compiler with its own per-GateType handlers and composite recursion (_compile_composite); GateRegistry + all_gates form a production-unreachable ALTERNATE implementation surface in which CompositeGate recurses via GateRegistry.get_gate_class; tests (test_boot_and_registry.py::TestGateRegistry) and the active gate-development skill both maintain that alternate surface. Whether it carries semantics that must be preserved before consolidation is UNKNOWN — deferred to a dedicated gate-by-gate parity audit. No conclusion (wire it / delete it / keep both) is authorized by this record, and engine/gates/** is untouched. DEF-001's full-tree reachability-gate deferral, the 59-module UNKNOWN classification, the no-large-baseline decision, and every deletion conclusion are preserved verbatim.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Mission
Audit the remaining historical CEG gap-fix artifact cluster at production
reachability depth, resolve the canonical owner of every responsibility, and
remove only what is proven stale, duplicated, displaced, unreachable, or
superseded. No filename was trusted, and no artifact was deleted for containing
"patch" or "fix" in its name.
Seven executable engine modules turned out to be reachable from nothing. They
survived because they imported each other and because tests imported them — a
closed loop that reads as "covered" in a coverage report and as
status: activein an L9_META header, while the runtime never touched a line of it.
Production behavior is unchanged. Every responsibility the island claimed was
already owned, and still working, elsewhere.
Base SHA
5868bc49865eba0afd6154a0746ac06111cf1ccf—origin/main, the merge commit ofPR #232, "fix(inference): remove ghost bridge and unowned KB rule loading",
confirmed merged before this audit began. PR #232 deliberately retained
engine/inference_rule_registry.pyand explicitly recorded that it has noverified production edge (test-reachable only). The later inference-ownership
closure contract is a separate, unexecuted program — it did not merge as
PR #232, and nothing in this PR relies on it. Open PR #233 was checked for overlap: governance/CI seed
files only, no
engine/ortests/paths. The pre-push overlap gate re-confirmedthis against current main (
PASS: no non-generated file overlap with open PRs).Artifact Classification Matrix
engine/compliance/audit_persistence.pyengine/graph_return_channel.pyengine/graph/community_export.pyengine/convergence_controller_patch.pyengine/graph/graph_sync_client_fix.pyengine/contract_enforcement.py†engine/startup_wiring.py†engine/inference_rule_registry.pytests/contracts/test_known_gaps.py† Not in the contract's mandatory set. Both were discovered in Phase 1 as members
of the same island —
contract_enforcementis its shared dependency, andstartup_wiringis the only module that presents the island as installablewiring. Full per-artifact proof in
GAP_FIX_REACHABILITY_CLASSIFICATION.yaml.Deleted Artifacts
engine/compliance/audit_persistence.py— kept its own module-global_POOL,set only by
configure_audit_pool(), which nothing calls, and INSERTed into a5-column
audit_logtable. The live path isboot.py→init_dependencies(db_pool)→
EngineState→ComplianceEngine.flush_audit()→AuditLogger.flush_to_store(),writing 16 typed columns to
packet_audit_log. Two competing audit schemas, one owner.engine/graph_return_channel.py— a per-tenant queue whose own docstring namesits consumer as
convergence_controller.run_convergence_loop(). Neither exists inthis repository. Every producer was itself an orphan, so nothing could enqueue; the
consumer was absent, so nothing could drain.
engine/graph/community_export.py— instructed the reader to attach it to a"GDSScheduler post-job completion hook".
GDSSchedulerhas no hook mechanism atall, so it was not merely unwired but unwireable. Redundant regardless:
_run_louvainalready writes the label into the graph viagds.louvain.write(
writeproperty: community_idin the plasticos spec) andengine/scoring/assembler.pyreads it straight back off the node. The graph isthe transport.
engine/convergence_controller_patch.py— told the operator to callpatch_convergence_controller(), a function defined neither in this file noranywhere else, to patch a
convergence_controller.pythat does not exist. Its foursymbols had no importers, not even a test. Its schema-proposal path imported
chassis.events, which also does not exist, so that branch could only ever takeits own except-and-warn path.
engine/graph/graph_sync_client_fix.py— a "drop this overGraphSyncClientin
graph/sync/client.py" replacement for a package and a call site that do notexist. Its write shape was incompatible with the canonical one:
(
SANITIZED_TARGETNODE/SANITIZED_IDPROPERTYstand for the domain-declared,sanitize_label()-checked values the canonical generator interpolates.)A labelless MERGE on a hardcoded
entity_id, writingtenantrather than_tenant. Had it ever run it would have built a parallel, unqueryable nodekeyspace beside the real one.
engine/contract_enforcement.py— a second PacketEnvelope architecture besideengine/packet/: a private frozenset of packet-type strings against the canonicalPacketType(StrEnum), a parallel required-fields table, and its own content hash.Not one of its packet-type strings (
graph_inference_result,schema_proposal,community_export, …) appears in the canonical enum. Once the three modules abovego, every remaining importer is a test. This is Contract 05 (Redefine
PacketEnvelope) exactly.
Checked for a name collision:
docs/L9_Contract_Enforcement_System.mdspecifies thestatic 24-contract scanner (
tools/contract_scanner.py,tools/verify_contracts.py,pre-commit, CI gates). It never references this module. Similar name, unrelated
system.
engine/startup_wiring.py— could not execute even once. Its first statement isfrom shared.audit_persistence import configure_audit_pool, and there is nosharedpackage in this repository, so the call raised
ModuleNotFoundErrorbefore applyingany fix. Two later imports name an absent top-level
graphpackage, one of themoutside the
tryblock that would have caught it, and it then callsGDSScheduler.register_post_job_hook, which does not exist.Its real hazard was documentary rather than executable: "Add these calls to your
application lifespan / startup handler in order." That is a standing instruction to
a future operator or agent to activate the six modules above. Deleting them while
keeping this file would have left the exact resurrection vector this audit exists to
close.
Retained Artifacts and Why
engine/inference_rule_registry.py— TEST_ONLY_IMPLEMENTATION: zero runtimecallers, no verified production reachability, imported only by
tests/gap_fixes/test_gap3_inference_registry.pyandtest_gap9. PR fix(inference): remove ghost bridge and unowned KB rule loading #232retained it (removing only the raw-KB loaders) and recorded exactly this state.
KEEP here is a scope decision — resolving inference ownership (wire, relocate,
or remove) is outside this contract and deferred. It is not a claim that the
registry is canonical production architecture, and test imports do not make it
one.
tests/contracts/test_known_gaps.py— matched the Phase 1 filename filter on"gap" only. It is xfail placeholders for absent contract YAML files. Untouched.
engine/boot.py— needs no edit. It never called the removed recipe, and italready does the one thing the recipe claimed to wire (creating the optional
Postgres pool). No new boot wiring was created.
Canonical Owners
Each reclaimed responsibility returns to a single owner that already had it:
engine/compliance/audit.py::AuditLogger.flush_to_storeengine/packet/packet_envelope.py::PacketEnvelopeengine/sync/generator.py::SyncGeneratorengine/gds/scheduler.py::GDSScheduler._run_louvainengine/boot.py::GraphLifecycleNo relocation was performed. Relocating a module requires a proven canonical
destination and a live behavior to preserve; neither held for any artifact here.
No new adapter, subsystem, or wiring layer was created to save old code.
Runtime Reachability Evidence
Traced from every verified production entrypoint:
chassis/handler_registration.py→
engine.handlers.ACTION_HANDLERS,chassis/actions.py::execute_action,chassis/chassis_app.py(theL9_LIFECYCLE_HOOKimportlib path),GraphLifecycle.startup/shutdown/execute, the compliance flush loop, the 8 actionhandlers,
GDSScheduler,ConvergenceLoop.on_outcome_recorded, and the packagedpublic API (
engine/__init__.py;pyprojectshipspackages = [{include = "engine"}]).Result for all seven: NO_VERIFIED_PRODUCTION_PATH. None appears in
engine/__init__.py's__all__, in any subpackage__init__.py(
engine/graph/exports onlyGraphDriver;engine/compliance/only the fourcanonical classes;
engine/packet/onlyPacketEnvelope+ the two chassisfunctions), in
tools/, inMakefile, in.github/, or in any dynamic import.The only
importlibstring literals inengine/andchassis/arechassis.chassis_app's hook resolution andengine/config/explanations.py'sengine.tensorprobe.Evidence is not grep alone: this PR adds a static AST import-graph analyzer that
models absolute, relative, deferred and importlib-string imports plus Python's
ancestor-package execution semantics (importing
engine.a.b.calso executesengine.a.bandengine.a), and BFS's from those entrypoints. It is exercised bythe test suite, including a guard that it parses a known real edge — so a silently
broken parser cannot make the invariants vacuous.
Cross-Repo Consumer Evidence
GitHub code search across
org:Quantum-L9for every island module path returnedexactly one hit, and it is not a consumer:
Quantum-L9/Cursor-Governance→docs/plans/BUILT/wire_gap-fix_modules_7d4d9028.plan.mdThat plan has every todo marked
completedand is filed underBUILT/. Not oneof its outputs exists in this repository — verified individually: no
engine/packet/contract_enforcement.py, noengine/feedback/graph_return_channel.py,no
engine/feedback/enrich_helpers.py, noengine/boot_gap_wiring.py, none of itsfour relocated tests, none of its four
__init__.pyexport additions, and zerooccurrences of
apply_all_gap_fixesinboot.py. Its planned deletions were neverperformed either — which is why this island was still here.
Plan completion text is not evidence about a tree. Per the contract's authority
order,
historical_gap_fix_planssits second-from-bottom, beneath current runtimereachability, and
wire_because_historical_plan_said_completedis a prohibitedshortcut. Notably the plan's own intent — delete the non-canonical originals —
agrees with this audit; it is the "relocate them first" half that no real consumer
ever justified.
No artifact qualifies as a COMPATIBILITY_BOUNDARY.
Test Migration
Deleted only tests whose sole purpose was keeping a deleted implementation alive:
test_gap1_contract.py(5),test_gap2_return_channel.py(3),test_gap5_audit.py(3).tests/gap_fixes/test_gap9_inference_authority.pyis kept and strengthened, notdropped. Its guard against reintroducing the undeclared
spec.kb/load_domain_rulesrecipe — added by PR #232 — read one file's sourcetext, and that file is deleted here. It now scans the whole
engine/tree and addsan explicit absence assertion, so PR #232's coverage survives its subject
and no longer depends on that file existing.
Added
tests/invariants/test_module_reachability.py(7 tests): removed paths stayabsent, no engine/chassis module imports the removed surfaces or symbols, no module
ships a gap-fix activation recipe, the five canonical owners stay present and
production-reachable, and the analyzer self-check.
Test accounting is exact — 11 removed, 8 added, net −3:
Verified the guards are not vacuous: a probe module importing a removed surface
trips exactly three invariants; green once removed.
Validation
Run in a Python 3.12 Poetry env on this branch.
git diff --checkruff check .ruff format --check .mypy engine/ --config-file=pyproject.toml --ignore-missing-imports --exclude chassispytest tests/(excl. Docker suites)make agent-check-unitHARNESS PASSEDmake agent-checkmake agent-checkblocker (exact): no Docker daemon in this container, so thetestcontainers-neo4j fixture fails at setup withdocker.errors.DockerException: Error while fetching server API version: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory')).This is environmental and pre-existing, proven rather than asserted: the base
tree was extracted at
5868bc4and run with the same interpreter, and the two errorsets are byte-identical — the same 25
tests/integration/**andtests/performance/**setup errors, 0 failures on both sides. None of those suitesimport anything touched here. CI is the authority for them.
GitHub CI state on the current head: all material CEG validation contexts are
successful except
L9 Analysis, which reportsstartup_failure— a run-lessworkflow-startup failure that reproduces identically on
mainat the base SHA andon unrelated branches (verified across the last 12 runs of that workflow, every
one
startup_failure), so it is repo-wide and pre-existing, not caused or fixableby this PR's diff.
Refactoring Safety Gateisskipped. Neither is claimedas green.
Publication used the sanctioned path:
open_pr_after_gate.shafterl4_local.py begin → record-kernels → authorize-release. Both pre-push gatespassed — L4
release_authorized, andPR overlap gate: PASS. Note that CEG'sMakefile defines no
prtarget, so the underlying governance script was invokeddirectly; the checkers it exists to protect were run first and are tabulated above.
Deferred Findings
DEF-001 — 59 further engine modules are unreachable from production entrypoints.
The analyzer reports 66 unreachable modules at base; 7 are this island. The other 59
form unrelated dormant clusters:
engine/health/**,engine/intake/**,engine/personas/**,engine/hoprag/**,engine/kge/**(a documented dormantsubsystem,
kge_enabled=False),engine/arbitration/**,engine/outcomes/**,engine/replay/**,engine/shadow/**.GATE-001 — GateRegistry/all_gates is a production-unreachable alternate gate
implementation surface (state: defer to a dedicated convergence audit). The facts:
GateCompileris the production-reachable compiler, with its own per-GateTypehandlers and its own composite recursion (
_compile_composite) that never touchesGateRegistry.GateRegistry._REGISTRYis a static dictionary (no decoratorregistration exists anywhere in
engine/gates/) mappingGateTypevalues toclasses in
all_gates.py, whoseCompositeGaterecurses throughGateRegistry.get_gate_class— a second, self-contained compilation path that isunreachable from every verified production root. It is kept alive by tests
(
test_boot_and_registry.py::TestGateRegistryasserts fullGateTypecoverage)and by the active gate-development skill, which instructs agents to add every new
gate to both surfaces. This is not evidenced as "GateCompiler forgot to wire
GateRegistry"; it is two implementation surfaces for one responsibility. Whether
the alternate surface carries semantics that must be preserved before
consolidation is UNKNOWN until a gate-by-gate parity audit — so this PR neither
wires nor deletes it, and no conclusion is pre-authorized.
Why the full-tree reachability gate is not in this PR. The contract asks for one
and simultaneously forbids a
large_permanent_unreachable_baselineandadding_new_modules_to_baseline_to_make_test_green. With 59 out-of-scope unreachablemodules, going green would need either a 59-entry permanent exemption list (the
prohibited baseline, and the thing that makes such gates worthless) or a 59-module
classification sweep across nine unaudited subsystems (the prohibited unrelated
refactor). Both routes are closed, so the honest move is to ship the narrow provable
invariant plus the analyzer the full gate needs, and record the rest. Tightening the
scope later is a decision, not new machinery.
Unknowns
subsystem or a dormant defect is UNKNOWN at this evidence depth. Not guessed,
not touched.
ADR-DEC-001's residual reconciliation task is left open and now recorded moreaccurately. The ADR cited
graph_sync_client_fix.py:113as where the ungovernedentity_idis "client-supplied at sync time". That module never had a caller, sothe citation was wrong; and no domain spec declares
idproperty: entity_id(plasticos uses
facility_id,code,form_id,opportunity_id,demand_id),so no canonical writer of
entity_idexists at all, whileengine/handlers.py:509,616,1497still read it through silent fallbacks. Thecorrection widens the divergence the ADR records rather than narrowing it. The
decision (OPTION-B) and the residual task are unchanged — resolving that
contract-vs-runtime identity gap is out of scope here.
Merge: not authorized by this contract. Remediate to green; do not merge.
Audit evidence:
docs/audits/2026-08-23-gap-fix-artifact-convergence/— artifact inventory, reachability classification, implementation filetree.Generated by Claude Code