Skip to content

Prove the M0 legacy/canonical isolation boundary (REQ-MIGRATION-004) - #43

Merged
drevendev merged 2 commits into
masterfrom
claude/issue-41-migration-004-isolation
Sep 4, 2026
Merged

drevendev merged 2 commits into
masterfrom
claude/issue-41-migration-004-isolation

Conversation

@drevendev

@drevendev drevendev commented Sep 4, 2026 •

Copy link
Copy Markdown
Owner

Closes #41

Achieved outcome

The M0 legacy/canonical isolation boundary (REQ-MIGRATION-004) now has automated
evidence, not just inspection. A static cross-runtime reference scanner audits every
.ts file under src/ and every .cs file under TradeCraftSimulation/ for textual
evidence that one runtime names the other's implementation, and a regression test
proves the real repository currently has zero such references. Three negative-control
tests prove the scanner actually rejects a genuine prohibited reference rather than
passing vacuously, including one that drives a similarly-named-but-distinct fixture
through the full auditRepositoryIsolationBoundary traversal — closing a real
exclusion-bypass defect that operator QA and the ACCEPTOR independently reproduced
against this PR's first revision (bfe8969). The boundary, the check's coverage and
its explicit limits are documented in docs/spec/MIGRATION_BOUNDARY.md.

This PR also carries the AUTHOR_RUNBOOK.md section 1 reconciliation: the
REQ-MIGRATION-002 row in docs/spec/IMPLEMENTATION_STATUS.md referenced Issue #39
and a "pending PR"; that PR (#40) has since merged, so the row now records its merge
commit and proving test and moves to IMPLEMENTED.

Fix for review round 1 (operator QA + ACCEPTOR, both CHANGES_REQUESTED)

At bfe8969, excludingGuardModule filtered files with
file.path.includes("isolationBoundary") — a substring match on the whole path, not
an exact match on the guard's own two files. Both operator QA
(#43 (comment)) and
the ACCEPTOR independently reproduced a real false negative: a file such as
src/domain/isolationBoundaryBridge.ts, containing a genuine prohibited
TradeCraftSimulation reference, silently escaped the real-tree audit because its
path merely contains the text isolationBoundary.

Fixed at e814eca by:

  • narrowing excludingGuardModule to an exact, normalized repository-relative path
    match against diagnostics/isolationBoundary.ts and
    diagnostics/isolationBoundary.test.ts only (GUARD_MODULE_RELATIVE_PATHS);
  • adding a negative-control test that builds a similarly-named-but-distinct fixture
    file in a temporary directory and runs it through the real
    auditRepositoryIsolationBoundary traversal (not just the pure
    findCrossRuntimeReferences scan the original two negative controls used) to prove
    the bypass is closed;
  • correcting the secondary defect flagged in the same review:
    docs/spec/MIGRATION_BOUNDARY.md said the build-and-test required check runs
    npm test; per .github/workflows/ci.yml that's actually the typescript job
    (build-and-test is the legacy dotnet job). Also expanded the doc's description
    of the exclusion to state it is an exact-path match and to name the new test.

Tested revision

e814eca5317d39d1c79d82fd90346262605a8290

Changed artifacts

  • src/diagnostics/isolationBoundary.ts — collectSourceFiles (recursive file
    walk), findCrossRuntimeReferences (pure scan over in-memory SourceFile[]), and
    auditRepositoryIsolationBoundary (wires the two marker lists —
    CANONICAL_INTO_LEGACY_MARKERS, LEGACY_INTO_CANONICAL_MARKERS — against the real
    src/ and TradeCraftSimulation/ trees). The guard's own module/test files are now
    excluded from the real-tree audit by exact repository-relative path, not substring.
  • src/diagnostics/isolationBoundary.test.ts — the real-tree audit assertion, three
    negative-control tests (two pure-scan fixtures, one full-traversal
    similarly-named-file fixture via a temporary directory), and one "does not flag an
    unrelated marker" sanity test.
  • docs/spec/MIGRATION_BOUNDARY.md — the two entry points and their authoritative
    stocks as they exist today, what the guard checks, the exact-path exclusion and what
    the negative-control tests (including the new one) prove, the corrected required
    check name, and an explicit "limits of this evidence" section.
  • docs/spec/IMPLEMENTATION_STATUS.md — flips REQ-MIGRATION-002 to IMPLEMENTED
    with PR Pin the expected seed-7/30-turn legacy baseline hash (REQ-MIGRATION-002) #40's merge commit and proving test; REQ-MIGRATION-004 row stays
    IN_PROGRESS, now naming this PR (Prove the M0 legacy/canonical isolation boundary (REQ-MIGRATION-004) #43) and the third negative-control test.
  • package.json / package-lock.json — adds @types/node (devDependency only) so
    isolationBoundary.ts can import node:fs/node:path/node:os under strict
    typecheck.
  • tsconfig.json — adds "node" to the types array alongside the existing
    "vitest/globals".

Acceptance criteria

  • Record observed evidence for both runtime entry points and the absence of a
    shared authoritative stock write/bridge in the current M0 implementation — see
    the "two entry points" table in docs/spec/MIGRATION_BOUNDARY.md.
  • Add a named automated regression check for the actual isolation boundary, with a
    negative control proving the check rejects an intentionally introduced
    prohibited bridge/dependency, using a contained test fixture rather than a live
    bridge — three tests in src/diagnostics/isolationBoundary.test.ts:
    rejects a canonical file that imports the legacy tree,
    rejects a legacy file that reads canonical module output, and (new)
    auditRepositoryIsolationBoundary rejects a similarly-named-but-distinct file that the guard-module exclusion must not swallow, the last of which exercises
    the real traversal path where the original bypass lived.
  • Explain precisely what the test enforces and what remains a review/invariant
    obligation in later milestones — "Limits of this evidence" in
    docs/spec/MIGRATION_BOUNDARY.md is explicit that this is a source-level guard,
    not a runtime/CI-wiring proof, and that the marker lists may need extending once
    canonical stocks exist from M1 onward.
  • No existing runtime behavior or invariant test is weakened; all existing tests
    remain green — 11/11 npm test (6 files; 1 new negative control added this
    round), 45/45 TradeCraftSimulation.Tests, unchanged.
  • Both runtime verification suites and all required current-head CI checks pass —
    see Checks below.
  • Coverage row remains truthful before merge — REQ-MIGRATION-004 is recorded as
    IN_PROGRESS, referencing this Issue and "pending PR Prove the M0 legacy/canonical isolation boundary (REQ-MIGRATION-004) #43", not IMPLEMENTED,
    until this PR merges and a future run records the merge commit.

Checks

Check Outcome Evidence
npm ci passed clean install at e814eca
npm run typecheck passed no errors
npm test passed 6 files / 11 tests passed
npm run build passed dist/canonical.js built (unchanged — the guard is not part of the public entry point)
dotnet restore passed clean restore, both projects
dotnet build --configuration Release passed 0 warnings, 0 errors
dotnet test --configuration Release passed 45/45 passed, 0 failed, 0 skipped — unchanged
python3 scripts/policy_guard.py --base origin/master passed 7 changed files, no product/policy mix, no secret-shaped strings
python3 -m unittest discover -s scripts/tests passed 40/40 (unrelated automation scripts, run as a broader sanity check)

Not checked

Nothing required by this Issue's Verification section was skipped. The guard is not
wired as a CI job distinct from npm test — it already gates every PR as part of the
existing typescript required check (corrected from the earlier, wrong
build-and-test attribution), but a reviewer looking for a separately named
"isolation boundary" job in CI will not find one; this is noted explicitly in
docs/spec/MIGRATION_BOUNDARY.md rather than left implicit.

Assumptions and unknowns

  • The scan is textual/line-based, not an AST or module-resolution analysis. It cannot
    be defeated by a straightforward literal reference (an import path, a File.Read
    call, a namespace name), which is the realistic shape a prohibited bridge would take
    at M0 given the two runtimes cannot share memory in-process, but it could in
    principle be defeated by deliberate obfuscation (string concatenation, indirection
    through a config value). Given no untrusted contributor path exists in this
    repository's authoring model, that is judged out of scope for M0 rather than a gap
    to close now.
  • @types/node is pinned to ^22.20.1, matching the existing engines.node: ">=22"
    constraint; it is a type-only devDependency and does not change runtime behavior or
    the build output.

Highest-risk area for review

The same file as before, now fixed and independently verified by re-running the
reviewer's own reproduction shape as a permanent test:
src/diagnostics/isolationBoundary.ts's excludingGuardModule. It now matches only
the two exact, normalized repository-relative paths of the guard's own module and
test file, and a temporary-directory-backed negative-control test proves a
similarly-named-but-distinct file is not swept up by the exclusion. A reviewer should
confirm the exact-path set really is limited to those two files and that the new test
genuinely exercises auditRepositoryIsolationBoundary (the real traversal entry
point) rather than only the pure scan function.

Remaining gate

None for this Issue's scope. This PR does not merge itself — it is handed off again
for independent ACCEPTOR review per AGENTS.md.

Adds a static cross-runtime reference scanner (src/diagnostics/isolationBoundary.ts)
that audits src/**/*.ts and TradeCraftSimulation/**/*.cs for textual evidence one
runtime names the other's implementation, with fixture-based negative-control tests
proving the scanner rejects a genuine prohibited reference. Documents scope and
limits in docs/spec/MIGRATION_BOUNDARY.md.

Also reconciles the REQ-MIGRATION-002 row in IMPLEMENTATION_STATUS.md now that PR #40
(closing Issue #39) has merged at 6063ed9, per
AUTHOR_RUNBOOK.md section 1.

Closes #41

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
@abogun-product

Copy link
Copy Markdown
Collaborator

Operator QA: REQUEST_CHANGES

Head bfe8969. This is an operator QA finding with a reproduced false negative, not a substitute for the independent ACCEPTOR merge decision.

  1. excludingGuardModule in src/diagnostics/isolationBoundary.ts filters with file.path.includes("isolationBoundary"). I executed the actual module against a temporary source tree containing src/domain/isolationBoundaryBridge.ts with import { readLegacyStorage } from "../../TradeCraftSimulation/Storage";. findCrossRuntimeReferences reports the forbidden import, but auditRepositoryIsolationBoundary returns both violation lists empty. This is a real traversal/exclusion bypass of exactly the violation the guard claims to detect. Limit exclusions to exact normalized repository-relative paths for the guard/test files, and add a negative control through the full audit/traversal path proving similarly named files/directories remain checked. Keep the ordinary real-tree audit and pure scanner tests.

  2. docs/spec/MIGRATION_BOUNDARY.md incorrectly says required check build-and-test runs npm test; .github/workflows/ci.yml runs it in typescript. Correct the check name and document the exact exclusions and textual scanner limitations truthfully.

Please repair within #41, update the current-head handoff, and rerun all checks. No workflow permission, economic change, or broad scanner redesign is requested. Temporary local reproducer files were removed; no PR branch content was changed by this review.

@drevendev

Copy link
Copy Markdown
Owner Author

ACCEPTOR verdict: REQUEST_CHANGES

(Posted as a comment: GitHub refuses a same-account review — "Review Can not
request changes on your own pull request".)

Head bfe896999a06365b355ec8c3857bca19e9887cad.

I independently reproduced the defect the operator QA comment above flagged
(#43 (comment)),
rather than taking it on trust:

Defect — exclusion bypass in the isolation-boundary guard itself
(src/diagnostics/isolationBoundary.ts, excludingGuardModule):

const GUARD_MODULE_BASENAME = "isolationBoundary";
function excludingGuardModule(files: SourceFile[]): SourceFile[] {
  return files.filter((file) => !file.path.includes(GUARD_MODULE_BASENAME));
}

This is a substring match on the whole relative path, not an exact match on the
guard's own two files. I checked out the head revision in a git worktree, added
src/domain/isolationBoundaryBridge.ts containing
import { readLegacyStorage } from "../../TradeCraftSimulation/Storage";, and ran
auditRepositoryIsolationBoundary against the real tree via a temporary vitest
test. Result: { "canonicalViolations": [], "legacyViolations": [] } — the guard
reports zero violations even though a genuine prohibited cross-runtime reference is
present in a plain, non-adversarial filename. I removed the reproduction file and
test afterward and confirmed git status clean in the worktree; the PR branch
itself was never modified.

This fails the Issue's own acceptance criterion: "Add a named automated regression
check for the actual isolation boundary, with a negative control proving the check
rejects an intentionally introduced prohibited bridge/dependency." The check does
not reliably reject such a bridge — any file whose path merely contains the
substring isolationBoundary anywhere (not just the two guard files this PR adds)
silently escapes the real-tree audit entirely. The two negative-control tests in
isolationBoundary.test.ts only exercise findCrossRuntimeReferences directly on
in-memory fixtures, so they never exercise excludingGuardModule or the
auditRepositoryIsolationBoundary traversal path where the bypass lives — which is
why they pass despite the bug.

What would satisfy it: narrow the exclusion to an exact, normalized
repository-relative path match against the guard's actual two files (e.g.
src/diagnostics/isolationBoundary.ts and
src/diagnostics/isolationBoundary.test.ts), and add a negative-control test that
goes through auditRepositoryIsolationBoundary (not just
findCrossRuntimeReferences) with a similarly-named-but-distinct fixture file to
prove the bypass is closed.

Secondary, lower-severity: docs/spec/MIGRATION_BOUNDARY.md names the wrong
required check (says build-and-test runs npm test; per .github/workflows/ci.yml
that's the typescript job). Please correct this in the same pass.

Per docs/zendev/ACCEPTOR_RUNBOOK.md section 4: setting the Issue back to
status:in-progress. This is an ordinary code-correctness refusal — PR #43 does not
touch .github/workflows/**, AGENTS.md, or docs/zendev/**, so no
status:needs-decision gate applies.

@abogun-product abogun-product left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed at bfe8969: the substring-based exclusion in excludingGuardModule silently drops src/domain/isolationBoundaryBridge.ts from the repository audit despite its prohibited legacy import. Both operator QA and ACCEPTOR independently reproduced the false negative. Please implement exact normalized path exclusions, a full-audit negative control for similarly named files/directories, and the CI check-name correction documented in #43 (comment) . All existing checks must be rerun at the corrected head. This formal review restates the same defect, not a new scope.

excludingGuardModule matched any path containing the substring
"isolationBoundary", so a genuine prohibited cross-runtime reference in a
similarly-named file (e.g. src/domain/isolationBoundaryBridge.ts) silently
escaped the real-tree audit. Both operator QA and the ACCEPTOR independently
reproduced this against PR #43's head. Narrow the exclusion to exact,
normalized repository-relative paths for the guard's own two files, and add
a negative-control test that drives the fixture through the full
auditRepositoryIsolationBoundary traversal (not just the pure scan) to prove
the bypass is closed. Also correct MIGRATION_BOUNDARY.md, which named
build-and-test as the check running npm test; per ci.yml that's the
typescript job.

Closes #41

@abogun-product abogun-product left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operator QA finding resolved at e814eca. Inspected the exact normalized two-path exclusion and full traversal negative-control test; reproduced the original false negative earlier, and the new full-audit regression now passes on Windows. Independently ran clean npm ci, typecheck, all 11 TypeScript tests, Vite build and policy_guard (passed). All three required CI checks are SUCCESS at this head in run 33872690685, including legacy .NET tests. The CI job-name documentation correction is present. This resolves my previous formal change request; final independent acceptance/merge remains with the ACCEPTOR workflow.

@drevendev

Copy link
Copy Markdown
Owner Author

ACCEPTOR verdict: ACCEPT

Head e814eca5317d39d1c79d82fd90346262605a8290.

  1. All required checks measured green at this head. gh pr checks 43 shows build-and-test, policy-guard, typescript all pass in run 33872690685, at this exact revision.
  2. All acceptance criteria met, with evidence I observed myself (not taken from the PR body):
    • Checked out the head in a worktree, ran npm ci, npm run typecheck (clean), npm test (6 files / 11 tests passed, matching the claim), npm run build (dist/canonical.js built).
    • Ran dotnet restore, dotnet build --configuration Release (0 warnings, 0 errors), dotnet test --configuration Release (45/45 passed) — legacy build stays green per REQ-MIGRATION-003's standing requirement.
    • Ran python3 scripts/policy_guard.py --base origin/master: passed over 7 changed files.
    • Re-read src/diagnostics/isolationBoundary.ts: excludingGuardModule now filters by exact membership in GUARD_MODULE_RELATIVE_PATHS (diagnostics/isolationBoundary.ts, diagnostics/isolationBoundary.test.ts), not the prior substring match. Ran the new negative-control test directly (npx vitest run src/diagnostics/isolationBoundary.test.ts --reporter=verbose): the "rejects a similarly-named-but-distinct file" test, which drives src/domain/isolationBoundaryBridge.ts through the real auditRepositoryIsolationBoundary traversal, passes — the exact bypass I reproduced against bfe8969 in the prior round is closed at this head. All 5 tests in the file pass.
    • docs/spec/MIGRATION_BOUNDARY.md now correctly attributes npm test to the typescript required check, not build-and-test.
  3. Diff confined to declared scope: the isolation guard + test + doc, and a truthful REQ-MIGRATION-004 row in IMPLEMENTATION_STATUS.md, plus the devDependency (@types/node) and tsconfig.json types addition needed for the guard to compile under strict typecheck. The bundled REQ-MIGRATION-002 row reconciliation (recording PR Pin the expected seed-7/30-turn legacy baseline hash (REQ-MIGRATION-002) #40's already-merged commit) touches only docs/spec/IMPLEMENTATION_STATUS.md, a file AGENTS.md grants free write access to, and doesn't touch product code — accepted as a truthful-status correction, not scope creep.
  4. No invariant or test weakened: all prior tests remain green; one new regression test added.
  5. No secret, credential, or personal data present: confirmed by policy-guard and my own read of the diff.
  6. Handoff record complete: outcome, Issue + tested revision, changed artifacts, acceptance criteria with evidence, check outcomes, not-checked section, assumptions/unknowns, highest-risk area, remaining gate — all present in the PR body.

No changes to .github/workflows/**, AGENTS.md, or docs/zendev/** — no policy-widening gate applies. Merging via squash.

@drevendev
drevendev merged commit 33683dd into master Sep 4, 2026
3 checks passed
@drevendev
drevendev deleted the claude/issue-41-migration-004-isolation branch September 4, 2026 12:30
drevendev added a commit that referenced this pull request Sep 4, 2026
…IZATION-003) (#45)

* Publish the M0 Milestone Preview on existing GitHub Pages (REQ-VISUALIZATION-003)

Adds a "Migration status" panel to docs/index.html that reads a static
one-way JSON diagnostic (docs/m0-preview.json) showing the M0 milestone
label, the frozen legacy baseline seed/turns/hash, and the canonical
TypeScript scaffolding status. A Vitest regression test keeps the JSON
honest against docs/spec/LEGACY_BASELINE.md and src/index.ts's real
toolchainStatus(). Also carries the AUTHOR_RUNBOOK section 1
reconciliation: REQ-MIGRATION-004 flips to IMPLEMENTED now that PR #43
has merged.

* Fix M0 preview panel CSS grid overflow at narrow viewports

The two-column CSS grid dl layout blew out at narrow widths (each hash
character on its own line, horizontal overflow) — verified with a
headless-Chromium screenshot at 360px. Replaced it with a simple
stacked label/value layout, re-verified at 360px and 1280px.

* Normalize CRLF before parsing LEGACY_BASELINE.md in m0Preview test

An LF-only fence regex silently failed to find the expected-hash on a
Windows checkout, where docs/spec/LEGACY_BASELINE.md is checked out
with CRLF line endings (npm test 14/15 on Windows, per PR #45 review
at 23a58b4). Extract the parsing logic into a pure function that
normalizes \r\n to \n first, and add a regression covering both LF and
CRLF input directly, without depending on the checkout's own line
endings.

Addresses changes requested on PR #45 (#42).

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
drevendev added a commit that referenced this pull request Sep 4, 2026
)

GitHub refuses a same-account formal review, so ACCEPTOR sometimes posts
its REQUEST_CHANGES/ACCEPT verdict as a PR comment instead of a formal
review. AUTHOR_RUNBOOK section 2 item 1 only described the formal-review
path, so a real change request could go unaddressed while a later run
reported green with nothing fixed (Issue #44, reproduced from PR #43 /
Issue #41).

Add exact detection steps for both verdict forms, tied to the pull
request's current head so a superseded comment can't cause repeat work
or an endless retry, and tighten item 6's trigger so a run that finds
nothing eligible after 1-5 always leaves a durable record instead of
exiting silently.

Closes #44

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prove the M0 legacy/canonical isolation boundary (REQ-MIGRATION-004)

2 participants