T-324V-dashboard-entity-body-editing
State: planning/draft · Impact: medium · Complexity: medium
Phase 12 (M-VBZB) ported the desktop app’s read surfaces into a new static
dashboard SPA (packages/ts/dashboard-spa), but deliberately left out body
EDITING — entity/task detail views render markdown read-only. The old desktop
app’s EntityBodySection.vue/MdSurface editor (edit/save, conflict
resolution, contract-finding markers) has no equivalent in the new SPA. The
dashboard JSON API already has the write endpoint
(PUT /api/entities/:type/:id/body, DashboardClient.updateEntityBody) fully
implemented and unused by the SPA — this task wires a UI onto it, closing a
real capability gap relative to the desktop app it replaces.
packages/ts/dashboard-spa/src/components/EntityBodyViewer.vuerenders an entity’s markdown body read-only (marked+DOMPurify).packages/ts/dashboard-client’sDashboardClient.updateEntityBody(type, id, body, etag, project?)is fully implemented, tested, and typed (EntityBodySaveresult:ok/conflict(etag mismatch) /invalid(contract-validation failure) — seepackages/ts/dashboard-client/src/index.ts).- Nothing in the SPA calls it. There is no edit affordance, no etag tracking on the read side, no conflict or validation-failure UI.
- The desktop app’s
EntityBodySection.vue(not ported, and not required to be copied verbatim) is the prior art for what a full edit flow looks like: edit/save, an etag-based compare-and-swap, and renderingEntityBodyValidationFailure’s per-finding messages inline.
Proposed
Section titled “Proposed”EntityDetail.vue (and the task/milestone detail views that render a body)
gain an edit mode: a textarea or lightweight markdown editor, a Save action
that calls updateEntityBody with the etag from the last read, and UI for
all three EntityBodySave outcomes — a successful write, a lost
compare-and-swap (etag conflict — show the current server value and let the
user retry), and a contract-validation failure (render the findings, block
the save). A body with body_etag: null (the read seam says “not
body-editable”) shows read-only with no Edit affordance, per
EntityDetail’s existing documented contract in contract.ts.
Approach
Section titled “Approach”- Add a
useEntityBodySavecomposable (or similar) inpackages/ts/dashboard-spa/src/composables/, mirroring the desktop app’suseEntityBodySave.tsin shape but calling@sdlc/dashboard-clientdirectly (no Nuxt auto-imports). - Extend
EntityBodyViewer.vue(or add a sibling edit component) with an edit/view toggle, gated onbody_etag !== null. - Wire the three
EntityBodySaveoutcomes into visible UI: success (update the etag, exit edit mode), conflict (surface the new etag/value, offer retry), invalid (renderfindings, keep edit mode open). - Extend to task/milestone detail views if they render bodies through the same component; otherwise scope this task to the generic entity browser only and file a follow-up for the others.
- Tests: component tests for all three save outcomes against a mocked
client, following the existing pattern in
packages/ts/dashboard-spa/src/views/ConfigView.test.ts(which already covers an analogous success/validation-failure pair forsetConfigKey).
| Area | Note |
|---|---|
packages/ts/dashboard-spa/src/components/EntityBodyViewer.vue | Currently read-only; needs an edit mode |
packages/ts/dashboard-spa/src/views/EntityDetail.vue | Hosts the body viewer; needs edit-mode wiring |
packages/ts/dashboard-client/src/index.ts | updateEntityBody/EntityBodySave already exist, unused by the SPA |
solutions/ontological/lib/services/dashboard/contract.ts | EntityBodyUpdate/EntityBodyUpdateResult/EntityBodyValidationFailure — no changes expected, already correct |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: An entity with a non-null
body_etagshows an Edit affordance; one with a nullbody_etagdoes not. - AC-2: Editing and saving a body end-to-end (via
updateEntityBody) against a real project updates the file on disk and the etag shown in the UI, verified live against a real checkout (not just a mocked client). - AC-3: A lost compare-and-swap (another writer changed the body first) surfaces the conflict to the user rather than silently overwriting or throwing an unhandled error.
- AC-4: A contract-validation failure (an edit that would introduce a markdown-contract error) renders the returned findings and does not write anything.
Out of scope
Section titled “Out of scope”- Any richer markdown editing experience (live preview, WYSIWYG,
@sksizer/markdown-ui’s MdSurface itself) — a plain textarea + Save is sufficient to close the capability gap; a nicer editor is a separate, later enhancement. - Frontmatter editing (only the body is in scope, matching the existing
PUT /api/entities/:type/:id/bodyendpoint’s own scope).
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”- Flagged during M-VBZB (Phase 12, “Bring over the current UI”) as a deliberate scope-out of the initial SPA port: the milestone’s completion note records this gap and points here.