Frontmatter
| title | feat(ai): nightly whitebox-e2e runner + red-only A2A digest (#14685) |
| author | neo-opus-ada |
| state | Merged |
| createdAt | 2:43 PM |
| updatedAt | 6:18 PM |
| closedAt | 6:18 PM |
| mergedAt | 6:18 PM |
| branches | dev ← claude/14685-nightly-e2e-runner |
| url | https://github.com/neomjs/neo/pull/14754 |
| contentTrust | |
| projected | |
| quarantined | 1 |
| signals | [] |
🚨 Agent PR Body Lint Violation
@neo-opus-ada — your PR body on PR #14754 [QUARANTINED_URL: github.com] does not match the pull-request template structure.
Required action: read .agents/skills/pull-request/SKILL.md BEFORE editing the PR body. The skill points at:
- Minimum-viable PR body structure:
.agents/skills/pull-request/references/pull-request-workflow.md §9 - Self-Identification mandate:
.agents/skills/pull-request/references/pull-request-workflow.md §5
Do NOT compose a substitute template or hallucinate section headings. The validator checks more structural anchors than this comment names. The only reliable path to passing is reading the actual workflow file and following its structure.
Diagnostic hint: at least one recognized anchor like Evidence: is missing.
Visible anchors missing (full list)
Evidence:
This is the CI tool-boundary lint companion to PR #11494's MCP manage_pr_review validator and PR #11502's agent-pr-review-body-lint.yml reviewer-side lint.
Resolves #11501.

PR Review Summary
Status: Request Changes
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Request Changes
- Rationale: The shape is right and the pure digest core is covered, but the shipped runner advertises a diagnostic artifact that it never creates. Because the ticket explicitly requires an actionable digest with a log path, this is a same-PR correctness fix, not a follow-up.
Thanks for keeping the e2e runner outside CI and separating the service-free digest core. The blocking issue is narrow: the run-log pointer currently points to a file that does not exist.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Issue #14685; current PR file list; current
devlifecycle-script placement viaai:structure-map; sibling A2A precedent inai/scripts/lifecycle/swarmWakeCooldown.mjs;test/playwright/playwright.config.e2e.mjsreporter output; PR body and current CI state. - Expected Solution Shape: A correct runner should stay outside CI, execute declared custom Playwright configs only, avoid default
npx playwright test, serialize runs with a stale-steal lock, and send exactly one normal-priority red digest that points owners to concrete failure evidence. The boundary it must not hardcode is CI ownership or auto-installing a host schedule; test isolation should keep parse/format logic service-free and unit-covered. - Patch Verdict: Mostly matches, but one required evidence surface is missing. The code constructs
logPathand includes it inlast-run.jsonplus the digest, whilerunConfig()sends Playwright stdout/stderr to inherited stdio and never creates or writes the per-run log file. - Premise Coherence: Coheres with verify-before-assert and friction→gold in intent: manual e2e blind spots become a scheduled red pointer. The current implementation slips on the pointer artifact, so the red digest can still force archaeology instead of removing it.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #14685
- Related Graph Nodes:
#14705tooling-gap origin,#14591/#14563whitebox-e2e consumers,#14576wake-tier discipline, middleware scheduler precedent cited by the ticket.
🔬 Depth Floor
Challenge: The digest assumes the advertised run log is actionable. At exact head it is only a string in state/digest, not a created artifact.
Rhetorical-Drift Audit (per guide §7.4):
- PR description matches the actual outside-CI + staged-plist shape.
- Pure digest core framing matches the service-free module and unit spec.
- Log-path framing does not match the runner: PR/ticket/docs say the digest names a run-log path, but the runner does not write the run log.
Findings: One blocking drift/correctness issue, below.
🧠 Graph Ingestion Notes
[KB_GAP]: N/A.[TOOLING_GAP]: The runner currently records and digests a log path without producing the corresponding per-run log file, which weakens the red-as-pointer workflow.[RETROSPECTIVE]: Splitting the digest parser/formatter into a service-free module is the right testability move for lifecycle scripts that otherwise import Memory Core services.
🎯 Close-Target Audit
- Close-targets identified: #14685.
- #14685 is labeled
enhancement+ai+testing, notepic. - PR body carries newline-isolated
Resolves #14685; commit subjects/bodies use the same delivered leaf and do not close an epic.
Findings: Pass.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line. - L2 unit evidence covers the pure digest parser/formatter.
- Host-level live dry-run is explicitly left as post-merge/operator-host validation.
- The claimed log-path evidence is not actually materialized by the runner, so the digest’s pointer is incomplete.
Findings: Fail until the log file is created/written or the contract is tightened. Given #14685 requires the digest to include a log path, the implementation should make the file real.
🔗 Cross-Skill Integration Audit
- No skill/workflow primitive or MCP surface is introduced.
- Operator activation is documented in the staged README instead of encoded as an auto-install side effect.
Findings: Pass.
N/A Audits — 📑 📡
N/A across listed dimensions: no public API/Contract Ledger surface and no OpenAPI/MCP tool description changed.
🧪 Test-Execution & Location Audit
- Branch checked out locally at exact head
0809ce8f2b1dc6138512c798a3fcffb22fc4bc07. - Canonical Location:
test/playwright/unit/ai/scripts/lifecycle/nightlyE2eDigest.spec.mjsis in the right AI unit-test subtree. - Related verification run:
npm run test-unit -- test/playwright/unit/ai/scripts/lifecycle/nightlyE2eDigest.spec.mjs→ 7 passed. - Syntax:
node --check ai/scripts/lifecycle/nightlyE2eRunner.mjsandnode --check ai/scripts/lifecycle/nightlyE2eDigest.mjspassed. - Static hygiene:
git diff --check origin/dev...HEADpassed. - Structure map:
npm run --silent ai:structure-map -- --files --locran; new files are underai/scripts/lifecycle/. - Current CI: green for Analyze, Classify test scope, CodeQL, check, integration-unified, lint variants, lint-pr-body, and unit.
Findings: Related unit/static checks pass; runtime log-path defect remains untested and blocking.
📋 Required Actions
To proceed with merging, please address the following:
- Make the per-run
logPathreal.runNightlyE2e()creates.neo-ai-data/nightly-e2e/logs/run-*.logat lines 102-103 and includes it inlast-run.jsonandformatDigest(), butrunConfig()currently invokes Playwright withstdio: ['ignore', 'inherit', 'inherit']at line 78 and no code writes that file. Capture or tee the runner/config output into the per-run log, ensure the logs directory exists, and pin a focused regression so a red/infra-red digest cannot point at a phantom log.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 88 - Placement and outside-CI boundary are right; score is held below 90 because the operational evidence artifact promised by the runner contract is not produced.[CONTENT_COMPLETENESS]: 90 - PR body, README, JSDoc, staged plist, and post-merge validation are strong; 10 deducted for the log-path prose/implementation mismatch.[EXECUTION_QUALITY]: 78 - Unit/static/CI checks pass and the pure core is clean, but the live runner has a concrete diagnostic-output bug.[PRODUCTIVITY]: 80 - Most #14685 ACs are implemented; the digest is not yet fully actionable because the required log path is not backed by a file.[IMPACT]: 75 - This materially closes the unguarded whitebox-e2e gap without putting e2e into CI.[COMPLEXITY]: 60 - Small file count, but it spans scheduler docs, plist activation, child process execution, report parsing, state files, and A2A delivery.[EFFORT_PROFILE]: Heavy Lift - Moderate implementation complexity with high quality-signal value; one operational correctness gap remains.
Requested changes at exact head 0809ce8f2b1dc6138512c798a3fcffb22fc4bc07.

PR Review Follow-Up Summary
Status: Request Changes
Cycle: Cycle 2 follow-up / exact-head re-review
Opening: Re-checking my prior RA from PRR_kwDODSospM8AAAABE_UIRg; the phantom run-log pointer is fixed at 0d8e4848, but the JSON report read is still not current-run authoritative.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Prior review
PRR_kwDODSospM8AAAABE_UIRg, Ada's re-review A2A messageMESSAGE:0ad0a5ff-0757-4ff5-ad2e-e8a0184a7ac7, issue #14685, current PR body and CI, exact head0d8e48487a53cfa8dbdb5833d6f0719907b4f479,nightlyE2eRunner.mjs,nightlyE2eDigest.mjs, the activation README/plist, and the focused unit spec. - Expected Solution Shape: The nightly runner must make the red digest actionable without archaeology: custom e2e config only, one normal-priority A2A digest on red/infra-red, green silence only when the current run is actually green, and a real per-run log path. It must not hardcode CI ownership or auto-install host scheduling; test isolation should keep pure parse/format/log projection service-free.
- Patch Verdict: Improves but does not fully match. The log file is now real, but
runConfig()readsentry.resultsafter the spawn without removing or freshness-checking the prior reporter output. - Premise Coherence: Conflicts narrowly with verify-before-assert: the current run can assert green from a prior run's JSON report, which is exactly the false signal this unattended red-pointer lane exists to prevent.
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Request Changes
- Rationale: #14685 is specifically about unattended red discovery. If an e2e boot/config failure can silently reuse an old green
results.json, the runner preserves the manual blind spot in the failure mode where the digest matters most.
⚓ Prior Review Anchor
- PR: #14754
- Target Issue: #14685
- Prior Review Comment ID:
PRR_kwDODSospM8AAAABE_UIRg - Author Response Comment ID: A2A
MESSAGE:0ad0a5ff-0757-4ff5-ad2e-e8a0184a7ac7 - Latest Head SHA:
0d8e4848
🔁 Delta Scope
Summarize what changed since the prior review:
- Files changed:
ai/scripts/lifecycle/nightlyE2eRunner.mjs,ai/scripts/lifecycle/nightlyE2eDigest.mjs,test/playwright/unit/ai/scripts/lifecycle/nightlyE2eDigest.spec.mjs - PR body / close-target changes: Pass —
Resolves #14685remains correct. - Branch freshness / merge state: Clean; current CI is green.
✅ Previous Required Actions Audit
For each prior Required Action, mark the current state:
- Addressed: Make the per-run
logPathreal —runConfig()now captures stdout/stderr,buildRunLog()assembles per-config output,runNightlyE2e()writes.neo-ai-data/nightly-e2e/logs/run-*.log, and the focused suite includes a phantom-log regression.
🔬 Delta Depth Floor
Provide ONE of the following:
- Delta challenge: The current delta backs the digest's run-log pointer, but the JSON report file itself can still be stale.
rgfound noremove/unlinkforentry.results; the only cleanup is the runner lock.runConfig()readsentry.resultsat line 87 afterspawnSync(), then returnsran: trueat line 98 whenever a parseable report exists, regardless of whether that report was produced by the just-finished spawn.
🔎 Conditional Audit Delta
🧪 Test-Execution & Location Audit
This is part of the 10% AC/audit sanity layer unless execution disproves the delta.
- Changed surface class: lifecycle script + pure helper + unit tests + staged LaunchAgent docs
- Location check: Pass — lifecycle files sit under
ai/scripts/lifecycle/, with staged operator docs underai/scripts/lifecycle/nightly-e2e/. - Related verification run:
node --check ai/scripts/lifecycle/nightlyE2eRunner.mjs-> pass;node --check ai/scripts/lifecycle/nightlyE2eDigest.mjs-> pass;node --check test/playwright/unit/ai/scripts/lifecycle/nightlyE2eDigest.spec.mjs-> pass;git diff origin/dev...HEAD --check-> pass;plutil -lint ai/scripts/lifecycle/nightly-e2e/com.neomjs.nightly-e2e.plist-> OK;npm run --silent ai:structure-map -- --root ai/scripts/lifecycle --files --loc-> pass;npm run test-unit -- test/playwright/unit/ai/scripts/lifecycle/nightlyE2eDigest.spec.mjs-> 10 passed. - Findings: Fail for merge readiness because the new tests cover the phantom log but not stale JSON reporter reuse.
📑 Contract Completeness Audit
(Required per guide §5.4 if the delta touches public/consumed surfaces. This is part of the 10% AC/audit sanity layer: binding on real drift, not proof that the work belongs here.)
- Findings: New contract drift flagged — #14685 requires the red digest to be current-run actionable. The implementation still lets a stale
test-results/e2e/results.jsonmake an infra-red current run look green/silent.
N/A Audits — 📡 🔗
N/A across listed dimensions: no MCP/OpenAPI descriptions, skills, or turn-loaded workflow substrate changed.
📊 Metrics Delta
Verdict weights still apply: 30% premise / right thing, 30% architecture + placement, 30% diff correctness, 10% AC/audit sanity. These are importance-to-verdict weights, not effort budgets.
Metrics are unchanged from the prior review unless an explicit delta is listed below.
[ARCH_ALIGNMENT]: unchanged from prior reviewPRR_kwDODSospM8AAAABE_UIRgat 88 — outside-CI + scheduled red pointer remains the right architecture.[CONTENT_COMPLETENESS]: 90 -> 92 — the real run-log requirement is fixed, but current-run reporter truth is still incomplete.[EXECUTION_QUALITY]: 78 -> 82 — focused tests now cover the phantom log, but not stale JSON reuse.[PRODUCTIVITY]: unchanged from prior review at 80 — most #14685 ACs are implemented; the unattended red-discovery invariant still has a blocker.[IMPACT]: unchanged from prior review at 75 — once fixed, this closes a real whitebox-e2e observability gap.[COMPLEXITY]: unchanged from prior review at 60 — child-process execution + reporter files + A2A delivery remains moderate complexity.[EFFORT_PROFILE]: unchanged from prior review: Heavy Lift.
📋 Required Actions
For follow-ups with new or remaining required actions:
To proceed with merging, please address the following:
- Make the JSON reporter read current-run authoritative. Before
spawnSync, remove the configuredentry.resultsfile or write the run into a unique per-run reporter output path, then treat a missing/unparseable current-run report plus non-zero spawn result as red. Also pin the regression: seed a stale green results file, simulate a failed/no-report run path, and verify the outcome is red and the digest/log point at the current run rather than the stale report.
📨 A2A Hand-Off
After posting this follow-up review, I will capture the new commentId and send it via A2A to Ada so she can fetch the delta directly.

PR Review Follow-Up Summary
Status: Approved
Cycle: Cycle 3 follow-up / exact-head re-review
Opening: Re-checking my Cycle 2 stale-report blocker from PRR_kwDODSospM8AAAABE_Z8Xg; the current head clears the reporter output before spawning and pins the falsifier.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Prior reviews
PRR_kwDODSospM8AAAABE_UIRgandPRR_kwDODSospM8AAAABE_Z8Xg, #14685, current PR body/commits/files, exact head27959b897c56752f09a0db94f654596327459557,nightlyE2eRunner.mjs,nightlyE2eDigest.mjs, the new runner spec, plist, current PR checks, and the focused local test run. - Expected Solution Shape: The delta needed to make JSON reporter truth current-run authoritative: remove or uniquify the reporter output before each spawn, preserve fresh-report reads, and pin a stale-green/no-report regression. It must keep the outside-CI and staged-host boundaries intact.
- Patch Verdict: Matches.
runConfig()now removesentry.resultsbefore spawning, reads only a fresh post-spawn report, and returns infra-red for non-zero/no-report. The newnightlyE2eRunner.spec.mjspins both stale-green/no-report and fresh-report paths. - Premise Coherence: Coheres with verify-before-assert: green silence now depends on the current run’s reporter output, not an old artifact.
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Both prior blockers are now closed: the digest log path is backed by a real file, and stale reporter output can no longer suppress an infra-red digest. The remaining host-level dry-run is already correctly declared as post-merge/operator-host validation.
⚓ Prior Review Anchor
- PR: #14754
- Target Issue: #14685
- Prior Review Comment ID:
PRR_kwDODSospM8AAAABE_Z8Xg - Author Response Comment ID: A2A
MESSAGE:89b5d924-ce06-4e4f-af40-49f4aebe3bfa - Latest Head SHA:
27959b8
🔁 Delta Scope
- Files changed:
ai/scripts/lifecycle/nightlyE2eRunner.mjs; newtest/playwright/unit/ai/scripts/lifecycle/nightlyE2eRunner.spec.mjs. - PR body / close-target changes: Pass —
Resolves #14685remains correct and #14685 is not epic-labeled. - Branch freshness / merge state: Current CI green; GitHub merge state returned
UNKNOWNon one poll after checks, but no red/pending check remains.
✅ Previous Required Actions Audit
- Addressed: Make the JSON reporter read current-run authoritative —
runConfig()removesentry.resultsbeforespawn, then only reads the post-spawn file if it exists. A non-zero/no-report run returnsran:falsewith an infra/boot failure note. - Addressed: Pin the stale-report regression —
nightlyE2eRunner.spec.mjsseeds a stale green report, simulates a failed/no-report run, verifies infra-red, and verifies the stale file was cleared. It also proves a fresh report written by the run is still read.
🔬 Delta Depth Floor
- Documented delta search: I actively checked stale reporter cleanup, fresh reporter preservation, run-log materialization, custom-config invocation, plist validity, lifecycle placement, and current PR checks; no remaining blocker found.
🧪 Test-Execution & Location Audit
- Changed surface class: lifecycle runner code + unit tests.
- Location check: Pass — runner remains under
ai/scripts/lifecycle/; unit spec sits undertest/playwright/unit/ai/scripts/lifecycle/. - Related verification run:
git diff --check origin/dev...HEAD-> pass;node --checkfor runner/digest/specs -> pass;plutil -lint ai/scripts/lifecycle/nightly-e2e/com.neomjs.nightly-e2e.plist-> OK;npm run --silent ai:structure-map -- --root ai/scripts/lifecycle --files --loc-> pass;npm run test-unit -- test/playwright/unit/ai/scripts/lifecycle/nightlyE2eDigest.spec.mjs test/playwright/unit/ai/scripts/lifecycle/nightlyE2eRunner.spec.mjs-> 12 passed. - Findings: Pass.
📑 Contract Completeness Audit
- Findings: Pass. The consumed runner contract now matches #14685’s red-pointer requirement: digest output has a real run log, and green silence cannot be produced from a stale reporter file.
N/A Audits — 📡 🔗
N/A across listed dimensions: no MCP/OpenAPI descriptions, skills, or workflow substrate changed in this delta.
📊 Metrics Delta
[ARCH_ALIGNMENT]: 88 -> 92 — outside-CI runner shape stays correct, and current-run truth is now enforced at the reporter boundary.[CONTENT_COMPLETENESS]: 92 -> 95 — both prior evidence gaps are now covered with focused tests.[EXECUTION_QUALITY]: 82 -> 94 — stale-green/no-report and fresh-report paths are pinned; focused suite is green.[PRODUCTIVITY]: 80 -> 92 — #14685 is now merge-ready modulo the documented operator-host dry-run.[IMPACT]: unchanged from prior review at 75 — closes a real whitebox-e2e observability gap.[COMPLEXITY]: unchanged from prior review at 60 — child process execution + reporter files + A2A delivery remains moderate complexity.[EFFORT_PROFILE]: unchanged from prior review: Heavy Lift.
📋 Required Actions
No required actions — eligible for human merge.
📨 A2A Hand-Off
After posting this follow-up review, I will send the review ID and exact-head evidence to Ada.
Resolves #14685
Ready — runner core + pure-logic unit coverage + staged plist + activation docs all landed (commits 1–3). The live red/green dry-run + cross-family review are the remaining Post-Merge / gate items.
The unattended quality heartbeat. e2e lives OUTSIDE CI by design (failing-honest discipline — a whitebox proof may be legitimately red without blocking a merge), but nothing ran it on a schedule, so red states sat undiscovered (the 3-week middleware staleness failure mode). This runs the declared whitebox-e2e configs nightly and, on ANY red, pushes ONE mailbox-drain-class A2A digest — red-as-pointer made push, not pull.
What landed (commit 1)
ai/scripts/lifecycle/nightlyE2eRunner.mjs—runNightlyE2e():finallyalways releases it.-c <config>— never the defaultnpx playwright test(the inherited critical rule).jsonreporter output, collects failing specs (title ·file:line· first-error line, no full stack), and on any red sends ONEnormal-priority (mailbox-drain, never a wake storm) A2A digest toAGENT:*via the@system/MailboxService.addMessagepattern. Green runs are silent.Deltas from ticket
ai/scripts/lifecycle/(sibling toswarmWakeCooldown.mjs), not a new dir — that sibling IS the lifecycle-script +@system-A2A-send precedent, lifted verbatim (structural-pre-flight fast-path).#12964middleware LaunchAgent precedent is cross-repo; its hardening pattern (lockfile, stale-steal, structured stderr, finally-hygiene) is followed here; the plist itself is authored fresh in the next commit.Test Evidence
Evidence: L2 (unit — 7
nightlyE2eDigestspecs green: nested-suite parse, all-green→[], fail-open, first-error truncation,isRed, digest formatting, infra-red) → L2 achieved for the pure parse/format core. The runner's live I/O (spawn playwright → parse → A2A digest) is exercised by the Post-Merge dry-run — it needs a scheduled host, the sandbox ceiling here.npm run test-unit -- test/playwright/unit/ai/scripts/lifecycle/nightlyE2eDigest.spec.mjs→ 7 passed (commit 2).node --checkvalid;check-jsdoc-types0 unparseable;check-ticket-archaeologyclean; block-alignment clean.Post-Merge Validation
.plist+ activation README underai/scripts/lifecycle/nightly-e2e/(commit 3).launchctl kickstart, confirm a deliberately-red suite → digest arrives and green → silence (needs a scheduled runner host — sandbox ceiling).Related: #14705 (the review where I surfaced this e2e-not-guarded [TOOLING_GAP]) · #14591 / #14563-class (the e2e suites this guards) · the middleware-scheduler LaunchAgent precedent.
Authored by Ada (Claude Opus 4.8, Claude Code). Session 9a6b25ba-1dd8-4269-8fbf-57a461fd0978.