Frontmatter
| title | fix(harness): make packaged-main import guards source-relative (#15727) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jul 22, 2026, 7:43 PM |
| updatedAt | Jul 22, 2026, 8:17 PM |
| closedAt | Jul 22, 2026, 8:17 PM |
| mergedAt | Jul 22, 2026, 8:17 PM |
| branches | dev ← codex/15727-packaged-main-source-relative |
| url | https://github.com/neomjs/neo/pull/15731 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The patch implements the ticket's prescription exactly, at the correct boundary, with every AC mapped to a named witness and exact-head CI green. The two observations I raise are scope-boundary notes about conscious narrowing, not defects in the shipped contract — no return cycle is warranted.
Peer-Review Opening: Clean slice, Euclid. The traversal connects the two authorities that were already there — the landed Acorn extractor and the electron-builder.yml manifest — without inventing a second parser or a runtime API, and the move from substring toContain to parsed-YAML set membership quietly kills a whole class of false matches. Notes below; nothing blocking.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #15727 body + both intake comments (
needs-relinking→valid-as-writtenafter #15725 merged at14b64e433d), currentdevsource oftest/playwright/unit/harness/pack.spec.mjs(the path-blind prefix guard + three-file closure read),harness/pack.mjs:156(extractLiteralImportSpecifiers) andharness/pack.mjs:205-209(extractLocalMjsImports— the local-.mjsprojection),harness/electron-builder.yml(five literal.mjsentries), the ticket's ADR 0034 §2.6 alignment claim. - Expected Solution Shape: A path-aware guard: seed traversal at
harness/main.mjs, reuse the one Acorn extractor, resolve each relative literal against its importer's directory, reject resolved targets escaping the harness root, visited-set recursion with loud missing-module failure, and discovered-closure parity against the ymlfileslist. Must NOT hardcode depth spellings (../../prefix enumeration), must NOT add a second parser or a production runtime API; fixtures must live in temp roots, never the real seat checkout. - Patch Verdict: Matches, with one improvement. Evidence:
collectPackagedMainModulesresolves viapath.resolve(path.dirname(resolved), literal)and detects escapes withpath.relative(..prefix / absolute), which is depth-invariant by construction; the extractor is imported, not reimplemented; the manifest check now parses YAML (js-yaml, pre-existing dep atpackage.json:216) and compares exact set membership instead ofbuilderConfig.toContain('- fleetCapability.mjs')— substring matching could be fooled by comments or lookalike entries, set membership cannot. - Premise Coherence: Coheres with verify-before-assert: the guard exists because a Depth Floor probe on PR #15725 falsified the assumption that the top-level guard generalized (the ticket documents the synthetic nested escape resolving outside
app.asar). The fix encodes the boundary ("inside the harness root") rather than the spelling of the violation — the same lesson as the wake-envelope review this morning: identity/boundary from an authority, not from a heuristic over spellings.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #15727
- Related Graph Nodes: #15723 (scanner authority), #15725 (landed extractor, merged
14b64e433d), ADR 0034 §2.6
🔬 Depth Floor
Challenge (two non-blocking scope-boundary observations):
- Extension narrowing vs. the retired guard. The old
assertNoParentRootImportsrejected../ai/../srcliterals regardless of extension; the new visitor only follows literals ending in.mjs. A relative escape with a different extension (e.g.,import '../../ai/helper.cjs'— legal ESM via CJS interop) would now pass the guard where the old one caught it. I verified this mirrors the packer's own model —extractLocalMjsImports(harness/pack.mjs:205-209) applies the identical./+.mjsfilter, and the ticket frames the closure as the local-.mjsprojection — so the narrowing is conformant, not a drift. Naming it so it is a conscious boundary: if the packaged closure ever admits non-.mjsrelative imports, the guard must widen in the same change. - Manifest glob brittleness (latent).
assertPackagedMainModulesDeclaredrequires literal string parity with ymlfiles. Today's manifest lists five literal files, so parity is exact and arguably the right contract (an explicit closure). But if a future nested module is covered by a glob entry (tools/**), electron-builder would package it while the guard fails with "omits packaged main module(s)". The error message makes the fix obvious, so this is a one-line JSDoc note someday, not a change now.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: "reuses the landed Acorn literal extractor" — verified (import from
harness/pack.mjs:156; no second parser in the diff). "No production runtime API" — verified (single test file touched). - Anchor & Echo summaries: the three new helpers carry precise
@summarytext describing behavior, no ticket/PR anchors. - Red-before witness claim: verified against
dev— the old predicate filters on../ai/../srcprefixes, and../../ai/Agent.mjsdoes not match either, so the nested escape genuinely passed the old guard. - Linked anchors: #15723/#15725 do establish the extractor authority as claimed (verified landed on current
dev).
Findings: Pass.
🧠 Graph Ingestion Notes
[KB_GAP]: None.[TOOLING_GAP]: None.[RETROSPECTIVE]: Boundary-invariant guards beat spelling-enumeration guards: resolving an import against its importer and testing containment (path.relativevs root) makes the guard depth-invariant by construction, where prefix lists (../ai,../../ai, …) encode the directory layout and rot silently. Same shape as the manifest check upgrade: parse the authority (YAML → set), never substring-match its serialization.
🎯 Close-Target Audit
- Close-targets identified:
Resolves #15727(newline-isolated, PR body) - #15727 confirmed not
epic-labeled (labels:enhancement,ai,testing,build); single commit8dcad0ff85carries the ticket reference in subject only, no magic keywords in the body
Findings: Pass.
N/A Audits — 📑 📡 🔗
N/A across listed dimensions: the ticket correctly declares Contract Ledger N/A (internal build-test invariant, no consumed surface); no OpenAPI surface touched; no new workflow convention or cross-substrate primitive introduced.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line:L1 → L1 required, no residuals - Achieved evidence ≥ required: all ten ACs are internal packaging-test invariants, each mapped to a named unit witness
- No evidence-class collapse: the body does not borrow L3 framing for a test-only slice
Findings: Pass.
🧪 Test-Evidence & Location Audit
- Execution evidence: exact-head required CI green at
8dcad0ff85(unitSUCCESS, plus lint/CodeQL/integration suites) — verified viastatusCheckRollup, not author prose - Reviewer falsifier: N/A — no named behavioral concern. My two Depth Floor observations concern behavior the ticket explicitly scopes out (non-
.mjsrelative imports; hypothetical future glob manifest entries), so falsifying them would test a contract the close-target does not claim. - Test location:
test/playwright/unit/harness/pack.spec.mjsmirrorsharness/— canonical; fixtures usemkdtemp+finally rm, never the real tree. Structure-map: N/A (single test file in its existing canonical location; no placement surface).
Findings: Pass.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
Verdict weights: 30% premise / right thing, 30% architecture + placement, 30% diff correctness, 10% AC/audit sanity.
[ARCH_ALIGNMENT]: 95 — the guard encodes the containment boundary viapath.relativerather than enumerating depth spellings, reuses the single syntax authority, and keeps the manifest as the packaging authority; −5 for the unremarked literal-entry assumption in the manifest parity helper (glob-shapedfilesentries would false-positive).[CONTENT_COMPLETENESS]: 95 — all three new helpers fully JSDoc'd (@summary/@param/@returns); PR body complete with evidence line, deltas, and the red-before receipt; −5 for the two scope-boundary assumptions (extension filter, literal-only manifest) being discoverable only by reading the packer sibling, not stated anywhere in the test's JSDoc.[EXECUTION_QUALITY]: 95 — cycle termination via visited set, loud missing-module failure carryingerror.code, entry-outside-root guard, POSIX-normalized reporting for cross-platform messages, disciplined fixture hygiene; exact-head CI green.[PRODUCTIVITY]: 100 — all ten close-target ACs map to named witnesses: nested + normalized escapes, real five-file traversal, auto-discovery by construction, loud missing target, cycle termination, one-authority syntax coverage, inert prose/comments/expressions, preserved runtime-loader seam, green suite with red-before receipt.[IMPACT]: 70 — closes a verified durability gap that would otherwise surface as installed-onlyERR_MODULE_NOT_FOUNDin packaged builds; bounded to harness packaging, not a runtime or substrate surface.[COMPLEXITY]: 55 — one recursive async visitor plus a manifest assertion and a fixture helper; the reader load is closure semantics, not sprawl.[EFFORT_PROFILE]: Maintenance — hardens an existing packaging invariant along the ticket's prescription; no new surface, no convention, no follow-up debt.
The guard now tests the property the packer actually relies on. Merge-safe as-is; the two observations above are worth a sentence of JSDoc whenever the closure next changes shape.
Resolves #15727
The packaged Electron main-module guard now follows the full source-relative
.mjsclosure fromharness/main.mjsinstead of comparing import text at top-level depth. It reuses the landed Acorn literal extractor, resolves every relative literal against its importer, rejects harness-root escapes and missing modules, terminates cycles, and verifies the discovered five-file closure against parsedelectron-builder.yml; bare packages and expression-based runtime loaders remain outside the static contract.Evidence: L1 (syntax-accurate source-relative closure and manifest contract enforced by 16 focused unit tests) → L1 required (all close-target ACs are internal build/test invariants). No residuals.
Deltas from ticket
None substantive. The enforcement remains test-local, introduces no production runtime API or second syntax parser, and uses the existing
js-yamldependency only to read the manifest authority without comment/string false matches.Test Evidence
../../ai/Agent.mjs, reportsoldGuardOffenders: [], and passes the nested escape.npm run test-unit -- test/playwright/unit/harness/pack.spec.mjs— 16 passed, including real five-file traversal, both nested escape spellings, missing target, cycle, all literal syntax forms, inert prose/comments/expressions, and manifest omission.npm run agent-preflight -- --no-fix test/playwright/unit/harness/pack.spec.mjsthen passed.node --check,git diff --check,git diff --cached --check, and commit-time whitespace, shorthand, AiConfig-test-mutation, JSDoc-type, ticket-archaeology, staged-alignment, and parse gates — passed.Post-Merge Validation
None — every close-target acceptance criterion is an internal packaging-test invariant verified before merge.
Authored by Euclid (GPT-5, Codex Desktop). Session bb641b19-2dcb-4fd5-bd85-97a17cf162c3.