Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
3.7 KiB
This step is ADDITIVE. It never halts the wave, never fails the phase, and never rewrites SUMMARY.md. If you cannot look, say so and finish.
<required_reading>
- {phase_dir}/{phase_num}-PLAN.md (the wave's tasks and their acceptance criteria)
- {phase_dir}/{phase_num}-UI-SPEC.md if it exists (the design contract, when the phase has one) </required_reading>
<browser_surface>
You carry exactly two browser MCP families: mcp__chrome-devtools__* and
mcp__claude-in-chrome__*. Use whichever responds. Do not assume they expose the same
tool names — probe, then use what is there. Do not paper over differences between them.
You do NOT carry the Playwright MCP family. That path belongs to the orchestrator's own verification step and is not yours. </browser_surface>
<profile_lock>
chrome-devtools-mcp holds an exclusive lock on its browser profile
($HOME/.cache/chrome-devtools-mcp/chrome-profile). A second concurrent instance fails with:
The browser is already running for <dir>. Use --isolated to run multiple browser instances.
If you see that, or any equivalent lock error:
- Record
outcome: could_not_lookandreason: profile_locked. - Name
--isolatedin the notes, so the operator knows the remedy is a flag on THEIR MCP server registration. - Stop. Do not retry, do not loop, do not wait for the lock. MSD cannot pass
--isolated— it is not MSD's flag — and a retry loop here just holds up the wave.
Parallel execution waves sharing one profile WILL hit this. It is an expected condition, not a defect, and it is not a reason to fail anything. </profile_lock>
For each UI acceptance criterion you can identify in the wave's plan:- Resolve its target URL. If no dev server or target is reachable, that criterion is
could_not_look/target_unreachable— not a failure. - Open it with the browser family that responded.
- Observe the DOM for the specific, stated condition. Assert on structure and content — an element's presence, its text, its attributes, its computed state.
- Record
passedwhen the stated condition is observably true,needs_reviewwhen it is ambiguous or requires human judgement (subjective aesthetics, content accuracy).
Scope limit for this version: DOM observation against stated criteria only. No screenshot
diffing, no accessibility audit, no performance tracing. If a criterion needs one of those,
mark it needs_review and say which.
Never invent a criterion. If the plan states no UI acceptance criteria, that is
outcome: nothing_to_report / reason: no_criteria, and it is a perfectly good result.
Frontmatter carries scalars only, so a reader can get the verdict without parsing prose:
---
schema_version: 1
wave: {wave_number}
outcome: verified | nothing_to_report | could_not_look
reason: ok | no_criteria | no_browser_mcp | profile_locked | target_unreachable
checked: <integer>
passed: <integer>
needs_review: <integer>
---
Then a short body: one line per criterion with its verdict, and — when outcome is
could_not_look — exactly what stopped you and what the operator would change.
nothing_to_report and could_not_look are different outcomes and must never be
conflated. "There were no UI criteria in this wave" and "there were criteria but I had no
browser" look identical in a summary that collapses them, and that ambiguity is the reported
problem this capability exists to remove.