summaryrefslogtreecommitdiffhomepage
path: root/backend-handoff.md
diff options
context:
space:
mode:
Diffstat (limited to 'backend-handoff.md')
-rw-r--r--backend-handoff.md91
1 files changed, 87 insertions, 4 deletions
diff --git a/backend-handoff.md b/backend-handoff.md
index 6d14ad4..7f4ddca 100644
--- a/backend-handoff.md
+++ b/backend-handoff.md
@@ -5,10 +5,10 @@
> **From:** dispatch-web orchestrator · **To:** `../dispatch-backend` orchestrator · **Courier:** the user.
> `lsp` does NOT span the repos (AGENTS.md § Backend seam) — every cross-repo ask flows through here.
-_Last updated: 2026-06-25 (§2f ADDED — Heartbeat feature shipped: workspace autonomous-agent config + run
-history + live run-chat modal; new `src/features/heartbeat/` feature library + `watchConversation`/`unwatchConversation`
-on the store; 837/837 tests green, typecheck 0/0, biome clean, build OK. The heartbeat API is a plain REST surface —
-NOT a transport-contract type — so the FE owns the types locally; see §2f for the contract-swap note)._
+_Last updated: 2026-06-26 (§2g ADDED — Heartbeat follow-up UI: prompt editor modal (reuses `GET /system-prompt/variables`;
+opens 1 backend ask CR-HB-1: confirm/implement `[type:name]` variable resolution in the heartbeat system/task prompts)
++ hours/minutes interval timer (FE-only conversion, no backend change). typecheck 0/0, tests green, biome clean, build OK).
+§2f (the initial heartbeat slice) is unchanged._
**FE is current on `[email protected]` / `[email protected]` / `[email protected]`.** Open asks: **CR-9**
(`system:os` should detect WSL + include Linux distro — backend behavior change, no contract bump). The SSH-divergence
(§2d) is RESOLVED.
@@ -504,6 +504,89 @@ later, the heartbeat view kind renames in lockstep with the rest.
---
+## 2g. Heartbeat follow-up UI — prompt editor modal + hours/minutes timer → **FE BUILT; 1 BACKEND ASK**
+
+A follow-up to §2f (branch `feature/heartbeat`, uncommitted-to-this-handoff commit). Two UI changes on the heartbeat
+config panel, each analyzed below for backend impact. **One needs a backend change (variable resolution in the
+heartbeat prompts); the other needs none.**
+
+### Change A — Prompt editor modal (replaces the two textareas) → **1 BACKEND ASK (CR-HB-1)**
+
+The config panel's two inline textareas (system prompt + task prompt) are replaced by a single "Edit prompts" button
+that opens a full-width modal (`src/features/heartbeat/ui/PromptEditor.svelte`):
+- LEFT side: two stacked text editors (top = system prompt, bottom = task prompt).
+- RIGHT side: the variable palette (grouped by type, same `[type:name]` tag insertion as the global system-prompt
+ builder). Clicking a variable inserts `[type:name]` at the cursor of whichever textarea is focused.
+- Save persists both prompts via the existing `PUT /workspaces/:id/heartbeat` with `{ systemPrompt, taskPrompt }`
+ (a partial patch — no new endpoint, no data-shape change).
+
+**What the FE reuses (NO new endpoint needed):**
+- The variable palette is sourced from the EXISTING global `GET /system-prompt/variables` endpoint — the SAME one the
+ global System Prompt builder uses. The heartbeat feature imports the pure helpers (`buildTag`, `groupVariables`,
+ `insertTag`, `isDynamicVariable`) from `features/system-prompt` (its public `index.ts` — `isDynamicVariable` was added
+ to that export in this slice, an additive cross-unit seam change). So there is **no new variable source or endpoint**;
+ the heartbeat prompt editor offers the exact same variable tags the global system prompt does.
+- The persisted strings carry literal `[type:name]` placeholders (plain text), exactly like the global template.
+
+**CR-HB-1 — Resolve `[type:name]` variables in the heartbeat system/task prompts → ASK (needs backend confirmation + likely implementation):**
+
+The global system-prompt template is resolved by the backend: `[type:name]` placeholders (e.g. `[system:os]`,
+`[system:date]`, `[file:path]`, `[if system:wsl]…`) are substituted with their resolved values at construction time
+(once per conversation at first turn, then persisted for prompt-cache safety). **The critical question: does the
+backend apply this SAME variable resolution to the heartbeat's `systemPrompt` and `taskPrompt` fields?**
+
+- If YES (the heartbeat prompts already flow through the same resolver as the global template): **no backend change
+ needed** — the FE just inserts the same `[type:name]` tags and the backend resolves them. Confirm + close.
+- If NO (the heartbeat prompts are inserted into the model turn as raw text, so `[system:os]` would reach the model
+ literally as the string `[system:os]` rather than the resolved OS string): **the backend should resolve `[type:name]`
+ placeholders in the heartbeat `systemPrompt` + `taskPrompt` using the SAME resolver/variable set as the global
+ system prompt** (so a variable inserted in either place resolves identically). This is the expected gap — the heartbeat
+ prompts are a NEW surface that predates the variable system, so they likely bypass the resolver.
+
+ - Resolution timing: mirror the global template's behavior — resolve once when a heartbeat run's turn is constructed
+ (NOT on every interval tick, to stay prompt-cache-safe; a stable resolved prompt keeps the cache warm across runs).
+ If the heartbeat re-resolves per-run anyway (intervals may want fresh `[system:time]`), that's a backend product
+ decision — the FE doesn't care WHEN it resolves, only THAT `[type:name]` becomes its value.
+ - Variable set: the SAME catalog `GET /system-prompt/variables` returns (system/file/prompt/git groups). No
+ heartbeat-specific variables are required for this slice.
+ - No wire/transport-contract/ui-contract change needed — this is a backend behavior change (the prompt strings are
+ still `string`; the FE is unaffected once the backend resolves them).
+
+**Optional future enhancement (NOT required now):** heartbeat-specific variables (e.g. `[heartbeat:runCount]`,
+`[heartbeat:lastResult]`, `[heartbeat:elapsed]`). The FE's prompt editor would offer these if `GET /system-prompt/variables`
+(or a new `GET /workspaces/:id/heartbeat/variables`) returned them. Defer until there's a product need.
+
+### Change B — Hours + minutes interval timer → **NO backend change needed**
+
+The interval input is split into two fields: an HOURS input (left) + a MINUTES input (right, 0–59). The conversion is
+entirely FE-side:
+- On load: the backend's single `intervalMinutes` is split into `{ hours, minutes }` via the pure `splitInterval`
+ helper (`Math.floor(total/60)` hours, remainder minutes).
+- On save: the FE recombines via `joinInterval(hours, minutes)` → `hours*60 + minutes` (clamped to the 1–1440 range
+ = 1 min–24 h) and sends a SINGLE `intervalMinutes` integer to `PUT /workspaces/:id/heartbeat`.
+
+So **the backend's `intervalMinutes` field is UNCHANGED** — still one integer of total minutes. The hours/minutes split
+is pure FE presentation. No endpoint, data-shape, or behavior change. (The existing clamp to 1–1440 also stands; a
+0h0m entry clamps to 1 minute — the FE guards this, and the backend's own validation should too, but that's pre-existing.)
+
+### FE summary (this slice)
+- `src/features/heartbeat/logic/view-model.ts`: `HeartbeatFormState` now carries `intervalHours` + `intervalMinutes`
+ (0–59); new pure `splitInterval`/`joinInterval` round-trip helpers; `formFromConfig`/`patchFromForm`/`formDiffers`
+ updated to split/recombine. +4 tests (split/join round-trip, form split, differs-after-edit).
+- `src/features/heartbeat/ui/PromptEditor.svelte` (new): two-pane modal (system+task editors left, variable palette
+ right), focus-aware variable insertion, Save/Reset. Reuses `features/system-prompt`'s pure helpers.
+- `src/features/heartbeat/ui/HeartbeatView.svelte`: two textareas → "Edit prompts" button + modal; interval → hours+minutes inputs.
+- `src/features/system-prompt/index.ts`: added `isDynamicVariable` to the public exports (additive — needed by the
+ heartbeat editor's dynamic `file:<path>` row).
+- `src/app/App.svelte`: passes `loadVariables={loadSystemPromptVariablesPrompt}` to `HeartbeatView`.
+
+**Verification:** typecheck 0/0, tests green, biome clean, build OK (see the commit). The variable-resolution behavior
+(CR-HB-1) can only be confirmed end-to-end against a running backend with variable-laden heartbeat prompts — a human
+should set `[system:os]` in a heartbeat prompt via the new editor, trigger a run, and confirm the resolved value (not the
+literal `[system:os]`) appears in the model's context / run transcript.
+
+---
+
## 3. Likely NEXT backend asks (heads-up, not yet requested)
- **Model max context-window LIMIT** → **CONSUMED ✅** — `GET /models` now returns