fix(design): skill resolvers learn the daemon's BOARD_URL output

The five skills that invoke $D compare --serve (design-shotgun,
design-consultation, plan-design-review, office-hours, design-review)
parsed `SERVE_STARTED: port=N` from stderr and then POSTed to
`/api/reload` at that port during regenerate cycles. The new daemon
hosts boards under `/boards/<id>/` so the reload endpoint moved to
`<BOARD_URL>api/reload` — without this update, the regenerate phase
of every skill invocation would silently fail against daemon mode.

Updated scripts/resolvers/design.ts to parse `BOARD_URL:` instead of
the port, and to POST reloads against the per-board URL. Regenerated
the four SKILL.md files via bun run gen:skill-docs.

Legacy `--no-daemon` invocations continue to emit `SERVE_STARTED:` and
serve at `/api/reload` — the resolver instructions note both.

Surfaced by the maintainability specialist during /ship review (the
"stale comment" finding was actually a behavior bug pointing at five
downstream consumers). Codex's plan-review pass flagged the migration
story as incomplete but I dismissed the concern — Codex was right.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-05-25 19:43:41 -07:00
parent 77b5ad2fa1
commit 8d534cd95c
5 changed files with 78 additions and 24 deletions
+17 -5
View File
@@ -1307,8 +1307,12 @@ This command generates the board HTML, starts an HTTP server on a random port,
and opens it in the user's default browser. **Run it in the background** with `&` and opens it in the user's default browser. **Run it in the background** with `&`
because the server needs to stay running while the user interacts with the board. because the server needs to stay running while the user interacts with the board.
Parse the port from stderr output: `SERVE_STARTED: port=XXXXX`. You need this Parse the board URL from stderr output. Default daemon path:
for the board URL and for reloading during regeneration cycles. `BOARD_URL: http://127.0.0.1:N/boards/<id>/` (already includes the per-board
path; use this for the AskUserQuestion URL AND as the base for the reload
endpoint). Legacy `--no-daemon` path emits `SERVE_STARTED: port=XXXXX` and
serves a single board at `/`, with reload at `/api/reload` — only relevant
when an external caller explicitly passes `--no-daemon`.
**PRIMARY WAIT: AskUserQuestion with board URL** **PRIMARY WAIT: AskUserQuestion with board URL**
@@ -1316,11 +1320,14 @@ After the board is serving, use AskUserQuestion to wait for the user. Include th
board URL so they can click it if they lost the browser tab: board URL so they can click it if they lost the browser tab:
"I've opened a comparison board with the design variants: "I've opened a comparison board with the design variants:
http://127.0.0.1:<PORT>/ — Rate them, leave comments, remix <BOARD_URL> — Rate them, leave comments, remix
elements you like, and click Submit when you're done. Let me know when you've elements you like, and click Submit when you're done. Let me know when you've
submitted your feedback (or paste your preferences here). If you clicked submitted your feedback (or paste your preferences here). If you clicked
Regenerate or Remix on the board, tell me and I'll generate new variants." Regenerate or Remix on the board, tell me and I'll generate new variants."
Substitute `<BOARD_URL>` with the URL parsed from stderr (the daemon path
emits `BOARD_URL: http://127.0.0.1:N/boards/<id>/`).
**Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison **Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison
board IS the chooser. AskUserQuestion is just the blocking wait mechanism. board IS the chooser. AskUserQuestion is just the blocking wait mechanism.
@@ -1364,8 +1371,13 @@ the approved variant.
2. If `regenerateAction` is `"remix"`, read `remixSpec` (e.g. `{"layout":"A","colors":"B"}`) 2. If `regenerateAction` is `"remix"`, read `remixSpec` (e.g. `{"layout":"A","colors":"B"}`)
3. Generate new variants with `$D iterate` or `$D variants` using updated brief 3. Generate new variants with `$D iterate` or `$D variants` using updated brief
4. Create new board: `$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"` 4. Create new board: `$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"`
5. Reload the board in the user's browser (same tab): 5. Reload the board in the user's browser (same tab) — the URL is per-board
`curl -s -X POST http://127.0.0.1:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'` under daemon mode, so use `<BOARD_URL>` (from the `BOARD_URL:` stderr
line) as the base:
`curl -s -X POST "${BOARD_URL}api/reload" -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'`
Under `--no-daemon` the reload endpoint is `/api/reload` at the legacy
port; this path only matters if the caller explicitly opted out of the
daemon.
6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to 6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to
wait for the next round of feedback. Repeat until `feedback.json` appears. wait for the next round of feedback. Repeat until `feedback.json` appears.
+17 -5
View File
@@ -1193,8 +1193,12 @@ This command generates the board HTML, starts an HTTP server on a random port,
and opens it in the user's default browser. **Run it in the background** with `&` and opens it in the user's default browser. **Run it in the background** with `&`
because the server needs to stay running while the user interacts with the board. because the server needs to stay running while the user interacts with the board.
Parse the port from stderr output: `SERVE_STARTED: port=XXXXX`. You need this Parse the board URL from stderr output. Default daemon path:
for the board URL and for reloading during regeneration cycles. `BOARD_URL: http://127.0.0.1:N/boards/<id>/` (already includes the per-board
path; use this for the AskUserQuestion URL AND as the base for the reload
endpoint). Legacy `--no-daemon` path emits `SERVE_STARTED: port=XXXXX` and
serves a single board at `/`, with reload at `/api/reload` — only relevant
when an external caller explicitly passes `--no-daemon`.
**PRIMARY WAIT: AskUserQuestion with board URL** **PRIMARY WAIT: AskUserQuestion with board URL**
@@ -1202,11 +1206,14 @@ After the board is serving, use AskUserQuestion to wait for the user. Include th
board URL so they can click it if they lost the browser tab: board URL so they can click it if they lost the browser tab:
"I've opened a comparison board with the design variants: "I've opened a comparison board with the design variants:
http://127.0.0.1:<PORT>/ — Rate them, leave comments, remix <BOARD_URL> — Rate them, leave comments, remix
elements you like, and click Submit when you're done. Let me know when you've elements you like, and click Submit when you're done. Let me know when you've
submitted your feedback (or paste your preferences here). If you clicked submitted your feedback (or paste your preferences here). If you clicked
Regenerate or Remix on the board, tell me and I'll generate new variants." Regenerate or Remix on the board, tell me and I'll generate new variants."
Substitute `<BOARD_URL>` with the URL parsed from stderr (the daemon path
emits `BOARD_URL: http://127.0.0.1:N/boards/<id>/`).
**Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison **Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison
board IS the chooser. AskUserQuestion is just the blocking wait mechanism. board IS the chooser. AskUserQuestion is just the blocking wait mechanism.
@@ -1250,8 +1257,13 @@ the approved variant.
2. If `regenerateAction` is `"remix"`, read `remixSpec` (e.g. `{"layout":"A","colors":"B"}`) 2. If `regenerateAction` is `"remix"`, read `remixSpec` (e.g. `{"layout":"A","colors":"B"}`)
3. Generate new variants with `$D iterate` or `$D variants` using updated brief 3. Generate new variants with `$D iterate` or `$D variants` using updated brief
4. Create new board: `$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"` 4. Create new board: `$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"`
5. Reload the board in the user's browser (same tab): 5. Reload the board in the user's browser (same tab) — the URL is per-board
`curl -s -X POST http://127.0.0.1:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'` under daemon mode, so use `<BOARD_URL>` (from the `BOARD_URL:` stderr
line) as the base:
`curl -s -X POST "${BOARD_URL}api/reload" -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'`
Under `--no-daemon` the reload endpoint is `/api/reload` at the legacy
port; this path only matters if the caller explicitly opted out of the
daemon.
6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to 6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to
wait for the next round of feedback. Repeat until `feedback.json` appears. wait for the next round of feedback. Repeat until `feedback.json` appears.
+5 -2
View File
@@ -1414,8 +1414,11 @@ If the JSON contains `"regenerated": true`:
1. Read `regenerateAction` (or `remixSpec` for remix requests) 1. Read `regenerateAction` (or `remixSpec` for remix requests)
2. Generate new variants with `$D iterate` or `$D variants` using updated brief 2. Generate new variants with `$D iterate` or `$D variants` using updated brief
3. Create new board with `$D compare` 3. Create new board with `$D compare`
4. POST the new HTML to the running server via `curl -X POST http://localhost:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'` 4. POST the new HTML to the running board. Parse the board URL from stderr
(parse the port from stderr: look for `SERVE_STARTED: port=XXXXX`) (`BOARD_URL: http://127.0.0.1:N/boards/<id>/` — the daemon path) or fall
back to the legacy port (`SERVE_STARTED: port=N` — only emitted under
`--no-daemon`, hits `/api/reload` root). Daemon path:
`curl -X POST "${BOARD_URL}api/reload" -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'`
5. Board auto-refreshes in the same tab 5. Board auto-refreshes in the same tab
If `"regenerated": false`: proceed with the approved variant. If `"regenerated": false`: proceed with the approved variant.
+17 -5
View File
@@ -1131,8 +1131,12 @@ This command generates the board HTML, starts an HTTP server on a random port,
and opens it in the user's default browser. **Run it in the background** with `&` and opens it in the user's default browser. **Run it in the background** with `&`
because the server needs to stay running while the user interacts with the board. because the server needs to stay running while the user interacts with the board.
Parse the port from stderr output: `SERVE_STARTED: port=XXXXX`. You need this Parse the board URL from stderr output. Default daemon path:
for the board URL and for reloading during regeneration cycles. `BOARD_URL: http://127.0.0.1:N/boards/<id>/` (already includes the per-board
path; use this for the AskUserQuestion URL AND as the base for the reload
endpoint). Legacy `--no-daemon` path emits `SERVE_STARTED: port=XXXXX` and
serves a single board at `/`, with reload at `/api/reload` — only relevant
when an external caller explicitly passes `--no-daemon`.
**PRIMARY WAIT: AskUserQuestion with board URL** **PRIMARY WAIT: AskUserQuestion with board URL**
@@ -1140,11 +1144,14 @@ After the board is serving, use AskUserQuestion to wait for the user. Include th
board URL so they can click it if they lost the browser tab: board URL so they can click it if they lost the browser tab:
"I've opened a comparison board with the design variants: "I've opened a comparison board with the design variants:
http://127.0.0.1:<PORT>/ — Rate them, leave comments, remix <BOARD_URL> — Rate them, leave comments, remix
elements you like, and click Submit when you're done. Let me know when you've elements you like, and click Submit when you're done. Let me know when you've
submitted your feedback (or paste your preferences here). If you clicked submitted your feedback (or paste your preferences here). If you clicked
Regenerate or Remix on the board, tell me and I'll generate new variants." Regenerate or Remix on the board, tell me and I'll generate new variants."
Substitute `<BOARD_URL>` with the URL parsed from stderr (the daemon path
emits `BOARD_URL: http://127.0.0.1:N/boards/<id>/`).
**Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison **Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison
board IS the chooser. AskUserQuestion is just the blocking wait mechanism. board IS the chooser. AskUserQuestion is just the blocking wait mechanism.
@@ -1188,8 +1195,13 @@ the approved variant.
2. If `regenerateAction` is `"remix"`, read `remixSpec` (e.g. `{"layout":"A","colors":"B"}`) 2. If `regenerateAction` is `"remix"`, read `remixSpec` (e.g. `{"layout":"A","colors":"B"}`)
3. Generate new variants with `$D iterate` or `$D variants` using updated brief 3. Generate new variants with `$D iterate` or `$D variants` using updated brief
4. Create new board: `$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"` 4. Create new board: `$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"`
5. Reload the board in the user's browser (same tab): 5. Reload the board in the user's browser (same tab) — the URL is per-board
`curl -s -X POST http://127.0.0.1:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'` under daemon mode, so use `<BOARD_URL>` (from the `BOARD_URL:` stderr
line) as the base:
`curl -s -X POST "${BOARD_URL}api/reload" -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'`
Under `--no-daemon` the reload endpoint is `/api/reload` at the legacy
port; this path only matters if the caller explicitly opted out of the
daemon.
6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to 6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to
wait for the next round of feedback. Repeat until `feedback.json` appears. wait for the next round of feedback. Repeat until `feedback.json` appears.
+22 -7
View File
@@ -891,8 +891,11 @@ If the JSON contains \`"regenerated": true\`:
1. Read \`regenerateAction\` (or \`remixSpec\` for remix requests) 1. Read \`regenerateAction\` (or \`remixSpec\` for remix requests)
2. Generate new variants with \`$D iterate\` or \`$D variants\` using updated brief 2. Generate new variants with \`$D iterate\` or \`$D variants\` using updated brief
3. Create new board with \`$D compare\` 3. Create new board with \`$D compare\`
4. POST the new HTML to the running server via \`curl -X POST http://localhost:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'\` 4. POST the new HTML to the running board. Parse the board URL from stderr
(parse the port from stderr: look for \`SERVE_STARTED: port=XXXXX\`) (\`BOARD_URL: http://127.0.0.1:N/boards/<id>/\` — the daemon path) or fall
back to the legacy port (\`SERVE_STARTED: port=N\` — only emitted under
\`--no-daemon\`, hits \`/api/reload\` root). Daemon path:
\`curl -X POST "\${BOARD_URL}api/reload" -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'\`
5. Board auto-refreshes in the same tab 5. Board auto-refreshes in the same tab
If \`"regenerated": false\`: proceed with the approved variant. If \`"regenerated": false\`: proceed with the approved variant.
@@ -919,8 +922,12 @@ This command generates the board HTML, starts an HTTP server on a random port,
and opens it in the user's default browser. **Run it in the background** with \`&\` and opens it in the user's default browser. **Run it in the background** with \`&\`
because the server needs to stay running while the user interacts with the board. because the server needs to stay running while the user interacts with the board.
Parse the port from stderr output: \`SERVE_STARTED: port=XXXXX\`. You need this Parse the board URL from stderr output. Default daemon path:
for the board URL and for reloading during regeneration cycles. \`BOARD_URL: http://127.0.0.1:N/boards/<id>/\` (already includes the per-board
path; use this for the AskUserQuestion URL AND as the base for the reload
endpoint). Legacy \`--no-daemon\` path emits \`SERVE_STARTED: port=XXXXX\` and
serves a single board at \`/\`, with reload at \`/api/reload\` — only relevant
when an external caller explicitly passes \`--no-daemon\`.
**PRIMARY WAIT: AskUserQuestion with board URL** **PRIMARY WAIT: AskUserQuestion with board URL**
@@ -928,11 +935,14 @@ After the board is serving, use AskUserQuestion to wait for the user. Include th
board URL so they can click it if they lost the browser tab: board URL so they can click it if they lost the browser tab:
"I've opened a comparison board with the design variants: "I've opened a comparison board with the design variants:
http://127.0.0.1:<PORT>/ — Rate them, leave comments, remix <BOARD_URL> Rate them, leave comments, remix
elements you like, and click Submit when you're done. Let me know when you've elements you like, and click Submit when you're done. Let me know when you've
submitted your feedback (or paste your preferences here). If you clicked submitted your feedback (or paste your preferences here). If you clicked
Regenerate or Remix on the board, tell me and I'll generate new variants." Regenerate or Remix on the board, tell me and I'll generate new variants."
Substitute \`<BOARD_URL>\` with the URL parsed from stderr (the daemon path
emits \`BOARD_URL: http://127.0.0.1:N/boards/<id>/\`).
**Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison **Do NOT use AskUserQuestion to ask which variant the user prefers.** The comparison
board IS the chooser. AskUserQuestion is just the blocking wait mechanism. board IS the chooser. AskUserQuestion is just the blocking wait mechanism.
@@ -976,8 +986,13 @@ the approved variant.
2. If \`regenerateAction\` is \`"remix"\`, read \`remixSpec\` (e.g. \`{"layout":"A","colors":"B"}\`) 2. If \`regenerateAction\` is \`"remix"\`, read \`remixSpec\` (e.g. \`{"layout":"A","colors":"B"}\`)
3. Generate new variants with \`$D iterate\` or \`$D variants\` using updated brief 3. Generate new variants with \`$D iterate\` or \`$D variants\` using updated brief
4. Create new board: \`$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"\` 4. Create new board: \`$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"\`
5. Reload the board in the user's browser (same tab): 5. Reload the board in the user's browser (same tab) the URL is per-board
\`curl -s -X POST http://127.0.0.1:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'\` under daemon mode, so use \`<BOARD_URL>\` (from the \`BOARD_URL:\` stderr
line) as the base:
\`curl -s -X POST "\${BOARD_URL}api/reload" -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'\`
Under \`--no-daemon\` the reload endpoint is \`/api/reload\` at the legacy
port; this path only matters if the caller explicitly opted out of the
daemon.
6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to 6. The board auto-refreshes. **AskUserQuestion again** with the same board URL to
wait for the next round of feedback. Repeat until \`feedback.json\` appears. wait for the next round of feedback. Repeat until \`feedback.json\` appears.