Context
Surfaced while answering the operator's question of whether dev was red after five merges landed inside ~15 minutes. A full unit run against dev HEAD (10a29b1a79) reported 12856 passed, 1 failed — and the one failure was an artifact of the machine, not of dev.
Live latest-open sweep at 2026-08-11T15:3xZ: checked open issues matching false.red|flake|symlink|prescription|\.env; only #16676 matched and it is a different subject (prescription routing, not the test's file handling). No A2A claim on this scope.
The Problem
deploymentPrescriptionEnvironment.spec.mjs — "the rendered file changes what the container is CREATED with" — writes ai/deploy/.env, preserving any existing content:
existed = fs.existsSync(envPath),
backup = existed ? fs.readFileSync(envPath, 'utf8') : null;
…
fs.writeFileSync(envPath, content);
The deployment-prescription workflow links that path at a per-operator store outside the repo (~/.neo-ai/deployment-prescriptions/active.env). A machine that has run it once carries the symlink whether or not the target survives.
fs.existsSync follows symlinks. So a dangling link reads as absent, which defeats the preserve-and-restore on its own terms: the test records "nothing to back up", then writes through a link that resolves nowhere and fails:
Error: ENOENT: no such file or directory, open '<repo>/ai/deploy/.env'
The failure names the repo path, so it reads as a broken suite rather than a broken link. ai/deploy/.env is untracked and gitignored (.gitignore:113), so CI never carries it — the red is unreproducible anywhere but the affected machine, which is what makes it expensive: it cost a full-suite run plus a tracked-vs-untracked check to attribute, during an incident where the operator had just merged overruling what CI appeared to be saying.
A test that cannot distinguish "this machine cannot host me" from "the code is wrong" makes CI unreadable exactly when reading it matters.
The Architectural Reality
test/playwright/unit/ai/services/memory-core/helpers/deploymentPrescriptionEnvironment.spec.mjs — owns the write and its restore.
- The file already models environment preconditions correctly one line above, via
test.skip(!composeConfigAvailable(), …). The shape exists; the symlink case is simply not covered by it.
fs.lstatSync is the distinction that resolves it: it describes the link, never its target.
The Fix
Treat a dangling prescription symlink as an environment precondition, the same class as the absent Docker CLI already handled beside it — skip with a reason that names the machine state, rather than failing and blaming the suite.
Acceptance Criteria
Out of Scope
- The prescription workflow's own symlink lifecycle, and whether a vanished store should be re-created (that is deployment-side, not test-side).
- The queued/cancelled
Tests runs on dev — separate cause, GitHub-side, not this.
Decision Record impact
none — test-environment precondition handling; no production surface, no authority boundary.
Authored by @neo-opus-ada (Ada).
Context
Surfaced while answering the operator's question of whether
devwas red after five merges landed inside ~15 minutes. A full unit run againstdevHEAD (10a29b1a79) reported 12856 passed, 1 failed — and the one failure was an artifact of the machine, not ofdev.Live latest-open sweep at 2026-08-11T15:3xZ: checked open issues matching
false.red|flake|symlink|prescription|\.env; only #16676 matched and it is a different subject (prescription routing, not the test's file handling). No A2A claim on this scope.The Problem
deploymentPrescriptionEnvironment.spec.mjs— "the rendered file changes what the container is CREATED with" — writesai/deploy/.env, preserving any existing content:existed = fs.existsSync(envPath), backup = existed ? fs.readFileSync(envPath, 'utf8') : null; … fs.writeFileSync(envPath, content);The deployment-prescription workflow links that path at a per-operator store outside the repo (
~/.neo-ai/deployment-prescriptions/active.env). A machine that has run it once carries the symlink whether or not the target survives.fs.existsSyncfollows symlinks. So a dangling link reads as absent, which defeats the preserve-and-restore on its own terms: the test records "nothing to back up", then writes through a link that resolves nowhere and fails:The failure names the repo path, so it reads as a broken suite rather than a broken link.
ai/deploy/.envis untracked and gitignored (.gitignore:113), so CI never carries it — the red is unreproducible anywhere but the affected machine, which is what makes it expensive: it cost a full-suite run plus a tracked-vs-untracked check to attribute, during an incident where the operator had just merged overruling what CI appeared to be saying.A test that cannot distinguish "this machine cannot host me" from "the code is wrong" makes CI unreadable exactly when reading it matters.
The Architectural Reality
test/playwright/unit/ai/services/memory-core/helpers/deploymentPrescriptionEnvironment.spec.mjs— owns the write and its restore.test.skip(!composeConfigAvailable(), …). The shape exists; the symlink case is simply not covered by it.fs.lstatSyncis the distinction that resolves it: it describes the link, never its target.The Fix
Treat a dangling prescription symlink as an environment precondition, the same class as the absent Docker CLI already handled beside it — skip with a reason that names the machine state, rather than failing and blaming the suite.
Acceptance Criteria
ai/deploy/.envsymlink skips the write test with a message naming the missing prescription store..envfile still runs the test and is preserved and restored byte-for-byte..envpath still runs the test — a clean CI checkout must not be skipped, or the skip would mask real regressions.Out of Scope
Testsruns ondev— separate cause, GitHub-side, not this.Decision Record impact
none— test-environment precondition handling; no production surface, no authority boundary.Authored by @neo-opus-ada (Ada).