Context
Exact-head review of PR #15725 closed the syntax-scanning defect in #15723 and left the current packaged-main closure green. A Depth Floor probe then tested the future nested-module case instead of assuming the top-level guard generalized.
At c72d3ddc983d3d87610ed9e10d9f58cdbad9aab2:
- the recursive local closure rooted at
harness/main.mjs is currently five files: main.mjs, appLifecycle.mjs, brain.mjs, contentPolicy.mjs, and fleetCapability.mjs;
- none of those files currently escapes the packaged harness root; and
- a synthetic
harness/tools/x.mjs containing import '../../ai/Agent.mjs' resolves outside app.asar, while the current literal-only guard does not reject it.
This is therefore a verified durability gap, not a claim that the approved PR ships a current regression.
The Problem
The guard in test/playwright/unit/harness/pack.spec.mjs:22-59 receives source text but no source-file path. It normalizes the literal itself and only rejects the exact ../ai / ../src prefix forms. That is correct for top-level files, but relative depth changes the spelling without changing the escape:
harness/tools/x.mjs + ../../ai/Agent.mjs
→ <repo>/ai/Agent.mjs
→ outside the packaged harness module closure
The same test reads only brain.mjs, fleetCapability.mjs, and main.mjs into the guard. The current closure also contains appLifecycle.mjs and contentPolicy.mjs. A later nested local module can therefore create an installed-only ERR_MODULE_NOT_FOUND failure while the packaging test remains green.
The Architectural Reality
ADR 0034 §2.6 requires one source graph with an explicit packaged boundary; it does not authorize static main-process ESM edges to escape app.asar.
harness/pack.mjs:199-209 already provides syntax-accurate literal import extraction and a local-.mjs projection. harness/electron-builder.yml remains the authority for files shipped inside app.asar. The guard should connect those two existing authorities with source-relative resolution.
The runtime loadFleetRuntimeContracts(organismRoot) seam is different by design: it loads the staged organism through an explicit runtime root. A static-closure guard must not turn that intentional expression-based loader into a false positive.
The Fix
Harden the existing packaging-test seam so it is source-aware and closure-complete:
- Seed traversal at
harness/main.mjs.
- Reuse
extractLiteralImportSpecifiers(); do not add a regex or a second parser.
- Resolve each relative literal against the importing module's directory.
- Reject any resolved static/literal-dynamic module target outside the harness root.
- Recursively visit local
.mjs dependencies with a visited set and fail loudly on a missing local module.
- Assert that every discovered packaged-main module is present in the
electron-builder.yml files closure.
- Preserve expression-based runtime loaders and bare-package imports as separate contracts.
Keep this as packaging enforcement. Do not introduce a production runtime API solely to make the test convenient.
Contract Ledger Matrix
Not applicable: this hardens an internal build-test invariant and introduces no human-, agent-, or external-system-facing API or configuration surface.
Decision Record impact
aligned-with ADR 0034 §2.6 — the ticket makes the existing explicit packaged-source boundary depth-invariant; it does not amend the decision.
Acceptance Criteria
Out of Scope
- Reopening or blocking PR
#15725; its current closure is green.
- Bundling all of
ai/ or src/ into app.asar.
- Replacing
electron-builder.yml as the packaged-main manifest authority.
- Reworking renderer content-policy enforcement.
- Resolving arbitrary expression-based dynamic imports.
Avoided Traps
- Prefix accumulation: adding
../../ai, ../../../ai, and deeper regex branches would encode directory depth instead of the boundary.
- Whole-repository import ban: the staged organism has intentional root-aware runtime loaders; only the packaged static main-module closure is in scope.
- Second scanner: the syntax authority repaired by
#15723 remains the one extractor.
Duplicate Sweep
Immediately before creation at 2026-07-22T16:19:08.684Z, the latest 20 open issues, the last 30 all-state A2A messages, three targeted all-state GitHub issue searches, and raw-memory plus summary retrieval were rechecked. #15723 is the scanner-authority predecessor and explicitly leaves the local dependency model unchanged; no ticket covers source-relative nested closure enforcement.
Related
Related: #15723, #15725
Origin Session ID: bb641b19-2dcb-4fd5-bd85-97a17cf162c3
Retrieval Hint: "packaged-main nested harness module source-relative import closure guard"
Context
Exact-head review of PR
#15725closed the syntax-scanning defect in#15723and left the current packaged-main closure green. A Depth Floor probe then tested the future nested-module case instead of assuming the top-level guard generalized.At
c72d3ddc983d3d87610ed9e10d9f58cdbad9aab2:harness/main.mjsis currently five files:main.mjs,appLifecycle.mjs,brain.mjs,contentPolicy.mjs, andfleetCapability.mjs;harness/tools/x.mjscontainingimport '../../ai/Agent.mjs'resolves outsideapp.asar, while the current literal-only guard does not reject it.This is therefore a verified durability gap, not a claim that the approved PR ships a current regression.
The Problem
The guard in
test/playwright/unit/harness/pack.spec.mjs:22-59receives source text but no source-file path. It normalizes the literal itself and only rejects the exact../ai/../srcprefix forms. That is correct for top-level files, but relative depth changes the spelling without changing the escape:The same test reads only
brain.mjs,fleetCapability.mjs, andmain.mjsinto the guard. The current closure also containsappLifecycle.mjsandcontentPolicy.mjs. A later nested local module can therefore create an installed-onlyERR_MODULE_NOT_FOUNDfailure while the packaging test remains green.The Architectural Reality
ADR 0034 §2.6 requires one source graph with an explicit packaged boundary; it does not authorize static main-process ESM edges to escape
app.asar.harness/pack.mjs:199-209already provides syntax-accurate literal import extraction and a local-.mjsprojection.harness/electron-builder.ymlremains the authority for files shipped insideapp.asar. The guard should connect those two existing authorities with source-relative resolution.The runtime
loadFleetRuntimeContracts(organismRoot)seam is different by design: it loads the staged organism through an explicit runtime root. A static-closure guard must not turn that intentional expression-based loader into a false positive.The Fix
Harden the existing packaging-test seam so it is source-aware and closure-complete:
harness/main.mjs.extractLiteralImportSpecifiers(); do not add a regex or a second parser..mjsdependencies with a visited set and fail loudly on a missing local module.electron-builder.ymlfilesclosure.Keep this as packaging enforcement. Do not introduce a production runtime API solely to make the test convenient.
Contract Ledger Matrix
Not applicable: this hardens an internal build-test invariant and introduces no human-, agent-, or external-system-facing API or configuration surface.
Decision Record impact
aligned-with ADR 0034 §2.6— the ticket makes the existing explicit packaged-source boundary depth-invariant; it does not amend the decision.Acceptance Criteria
harness/tools/x.mjsimporting../../ai/Agent.mjsfails the packaged-main guard.../middle/../../src/Neo.mjs, fail relative to the importing source path..mjsedge underharness/automatically brings that module into the checked closure..mjstarget fails loudly instead of silently shrinking the closure.loadFleetRuntimeContracts(organismRoot)runtime seam remains allowed.Out of Scope
#15725; its current closure is green.ai/orsrc/intoapp.asar.electron-builder.ymlas the packaged-main manifest authority.Avoided Traps
../../ai,../../../ai, and deeper regex branches would encode directory depth instead of the boundary.#15723remains the one extractor.Duplicate Sweep
Immediately before creation at 2026-07-22T16:19:08.684Z, the latest 20 open issues, the last 30 all-state A2A messages, three targeted all-state GitHub issue searches, and raw-memory plus summary retrieval were rechecked.
#15723is the scanner-authority predecessor and explicitly leaves the local dependency model unchanged; no ticket covers source-relative nested closure enforcement.Related
Related: #15723, #15725
Origin Session ID: bb641b19-2dcb-4fd5-bd85-97a17cf162c3
Retrieval Hint: "packaged-main nested harness module source-relative import closure guard"