diff --git a/extensions/signals-dashboard/extension.mjs b/extensions/signals-dashboard/extension.mjs index 8647e05f..e15e0ded 100644 --- a/extensions/signals-dashboard/extension.mjs +++ b/extensions/signals-dashboard/extension.mjs @@ -649,6 +649,7 @@ function renderDashboard(signals, stashed) { async function startServer(instanceId, workshopDir) { const server = createServer(async (req, res) => { + try { const url = new URL(req.url, `http://${req.headers.host}`); if (req.method === "POST" && url.pathname.startsWith("/api/") && isCrossSiteRequest(req)) { @@ -708,6 +709,18 @@ async function startServer(instanceId, workshopDir) { const stashed = await readStash(workshopDir); res.setHeader("Content-Type", "text/html; charset=utf-8"); res.end(renderDashboard(signals, stashed)); + } catch (err) { + // Top-level boundary: never leave a request hanging or let a + // rejection become an unhandled crash — e.g. malformed %-encoding + // in the path, a read-only workshop on a stash write, or a scan + // failure. Return a controlled error instead. + if (!res.headersSent) { + res.writeHead(500, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ ok: false, error: "internal_error" })); + } else { + try { res.end(); } catch {} + } + } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const address = server.address(); diff --git a/skills/workshop-create/SKILL.md b/skills/workshop-create/SKILL.md index 0f12c8cb..f3e18b88 100644 --- a/skills/workshop-create/SKILL.md +++ b/skills/workshop-create/SKILL.md @@ -21,9 +21,11 @@ The operator already has a folder they want to use. Maybe it's a repo they cloned, maybe it's a local project folder. 1. **Confirm the path exists.** If not, ask the operator for a valid path. -2. **Check if it's already a workshop.** Look for `desks/` or `classroom/` - folders, a `workshop.md`, `CAIRN.md`, or `hands-up.md`. If any exist, - it's already a workshop — just use it. +2. **Detect existing workshop markers.** Look for `desks/` or `classroom/` + folders, a `workshop.md`, `CAIRN.md`, or `hands-up.md`. Finding any of + these tells you this is an existing workshop — but this is detection + only, not a stopping point. Continue to the next step and add whatever + is missing; never overwrite what is already there. 3. **Scaffold the workshop structure** (only what's missing): ``` /