Adopt the Agent Plugins v1.0.0 namespaced composition model from #2546. Bundle the reusable daily-focus-board canvas into Ember only and remove its obsolete standalone catalog manifest.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 52be9c67-3ae4-4610-93d0-fe0b7ab95ccb
An array is typeof "object", so progress:[] (or a top-level array state file) was kept as the container; JSON.stringify then drops the non-index props (counters, t, day...), the API returns progress:[] and the canvas crashes reading state.counters. Guard both with !Array.isArray, matching looksLikeBoard. Headless regression tests added (48/48).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fd1eae93-cc9f-4777-812c-a2a9872e1c2b
A state file with a non-string txt (e.g. {"txt":1}) previously loaded, then esc(n.txt) in the canvas threw (numbers have no .replace) and the whole board failed to render. Filter day/brain entries to require a string txt, mirroring the existing task-notes filter. Verified with a headless regression test (45/45).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fd1eae93-cc9f-4777-812c-a2a9872e1c2b
Resolves the Copilot review on board-core.mjs: task ids like `toString` /
`constructor` passed validId and, used as object keys, could resolve to
Object.prototype members. Now validId also rejects any inherited name (`s in {}`),
and the progress maps (counters, t) are null-prototype so a task id can never
resolve to an inherited member. (`__proto__` was already blocked by the id grammar.)
Headless test 43/43 (adds inherited-name rejection, drop-on-normalize, and an
Object.prototype-intact check).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Resolves two more Copilot review comments on board-core.mjs:
- Mutations no longer report false success: every op returns an explicit error
for unknown task ids, wrong task kinds, and empty/no-op inputs, and mutate
propagates it (so set_status on a bad id fails instead of claiming { ok: true }).
- The default state filename is now date-scoped (focus-board-<YYYY-MM-DD>.json),
so reopening the board tomorrow starts a fresh day instead of silently reusing
today's tasks/progress. Explicit stateFile paths are used as-is; input doc updated.
Headless test 39/39 (adds op-error + date-scope cases).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Resolves the remaining Copilot review comments.
Canvas extension (board-core.mjs, board.html):
- DNS-rebinding: pin the Host header to the exact 127.0.0.1:<port> authority and
require a per-server capability token (minted at startup, embedded in the served
page, sent as x-board-token) on ALL /api/* routes -- so GET /api/state can't leak
task data and POSTs can't be forged. Mirrors extensions/signals-dashboard.
- Destructive write: loadDoc only synthesizes a fresh board for ENOENT and now
propagates I/O + JSON parse errors, so a transient/malformed state file is never
overwritten by a later mutation.
- XSS: escape emoji (from the seed / add_task action) at render, like title/unit.
Skill (board.template.html, sample-board.html):
- a11y: each task card gets role=group + aria-label so screen readers get task context.
- counters: step=1 on the goal/update number inputs to match the positive-integer contract.
Verified headless (35/35): token gates reads+writes, CSRF + foreign-Host refused,
malformed file left intact. Repo plugin + skill validation green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Replaces the generated placeholder with an actual rendered board (cropped to the
visible cards). Closes the last gap flagged in the review.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Addresses the Copilot review on board-core.mjs: an externally supplied
stateFile pointing at an existing non-board file was silently normalized to an
empty board and overwritten on first mutation. ensureStateFile now refuses an
existing file unless it parses as JSON and looks like a board (a
'daily-focus-board' marker or the board schema); the marker is stamped on every
write. Also reserve 'day'/'brain' as task ids so a task can't collide with the
momentum/brain-dump feed delete routing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308
Adds a canvas version of the daily-focus-board alongside the skill, addressing
@aaronpowell's review suggestion. The canvas renders the board in the Copilot
app and is backed by a JSON state file the assistant reads and writes, so you
can mark tasks done, add tasks, log progress, and recap your day from chat --
the file-backed "close the loop" upgrade over the localStorage skill.
The skill stays as the zero-install universal fallback for anyone not in the
Copilot app (same both-not-either pattern as the-workshop's signals-dashboard).
- extensions/daily-focus-board/: extension.mjs (canvas + session wiring) +
board-core.mjs (loopback server, JSON state file, mutations, recap) +
assets/board.html (file-backed UI) + preview.png + manifests.
- plugins/ember: register via x-awesome-copilot.extensions, bump 1.1.0 -> 1.2.0,
add a Components row.
Note: assets/preview.png is a placeholder pending a real board screenshot.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb356aa8-0af2-48f3-b3c6-8086c69d5308