summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-24 00:13:43 +0900
committerAdam Malczewski <[email protected]>2026-06-24 00:13:43 +0900
commitb9abc1f8d8815f57e7b958b871ad85db205a9257 (patch)
tree2fa7b7629d33ed067a00f6e5f31bfb2b7f9d5c0f
parentebcaf59180bda1d04e6353ccd3ea78e45bed7088 (diff)
downloaddispatch-b9abc1f8d8815f57e7b958b871ad85db205a9257.tar.gz
dispatch-b9abc1f8d8815f57e7b958b871ad85db205a9257.zip
docs: system-prompt builder FE courier handoff + tasks.md update
-rw-r--r--frontend-system-prompt-handoff.md123
-rw-r--r--tasks.md30
2 files changed, 152 insertions, 1 deletions
diff --git a/frontend-system-prompt-handoff.md b/frontend-system-prompt-handoff.md
new file mode 100644
index 0000000..c0739b3
--- /dev/null
+++ b/frontend-system-prompt-handoff.md
@@ -0,0 +1,123 @@
+# FE Courier Handoff: System Prompt Builder
+
+> Backend→FE courier. The user couriers this to `../dispatch-web` (FE agent `ffe3`).
+> `@dispatch/transport-contract` bumped to `0.18.0` (additive types).
+
+## Overview
+
+A template-based system prompt builder. The user defines a template with variable
+placeholders (`[type:name]`) and conditionals (`[if]`/`[else]`/`[endif]`). Variables are
+resolved at construction time (once per conversation, persisted for cache safety —
+reconstructed only on compaction).
+
+## API endpoints
+
+### `GET /system-prompt` → `SystemPromptTemplateResponse`
+```ts
+{ template: string }
+```
+Returns the current global template. When no template is stored (or the service is
+unavailable), returns the built-in `DEFAULT_TEMPLATE`.
+
+### `PUT /system-prompt` ← `SetSystemPromptTemplateRequest`
+```ts
+// Request body:
+{ template: string }
+// Response:
+{ template: string } // echoed back
+```
+- `template` can be empty (means "no system prompt").
+- 400 if `template` is missing or not a string.
+- 503 if the system-prompt service is unavailable.
+
+### `GET /system-prompt/variables` → `SystemPromptVariablesResponse`
+```ts
+{
+ variables: readonly SystemPromptVariable[]
+}
+// SystemPromptVariable:
+{
+ type: string; // "system", "file", "prompt", "git"
+ name: string; // "time", "date", "os", "cwd", etc.
+ description: string; // human-readable
+ dynamic?: boolean; // true for file: (any path is valid)
+}
+```
+Static catalog — always available (no service dependency). Use this to render the
+variable selector buttons in the builder UI.
+
+## Template format
+
+### Variable insertion
+```
+[type:name]
+```
+Resolves the variable at construction time. Unknown type → blank string. Non-existent
+variable (e.g. file not found) → blank string.
+
+### Conditional blocks
+```
+[if type:name]
+ ...content if variable exists...
+[else]
+ ...content if variable does NOT exist...
+[endif]
+```
+Negated condition:
+```
+[if !type:name]
+ ...content if variable does NOT exist...
+[endif]
+```
+- Nested `[if]` blocks: supported.
+- Multi-line content: supported.
+- Unmatched `[if]`/`[endif]`: treated as literal text.
+
+## Available variables
+
+| Type:Name | Description | Dynamic? |
+|---|---|---|
+| `system:time` | Current time (ISO 8601) | No |
+| `system:date` | Current date (YYYY-MM-DD) | No |
+| `system:os` | Operating system | No |
+| `system:hostname` | Machine hostname | No |
+| `prompt:cwd` | Working directory | No |
+| `prompt:model` | Current model name | No |
+| `prompt:conversation_id` | Conversation ID | No |
+| `git:branch` | Current git branch | No |
+| `git:status` | Short git status | No |
+| `file:<path>` | File contents (relative to cwd, or absolute if starts `/`) | **Yes** |
+
+For `file:<path>`, the FE should allow free-text input for the path. Any filename is
+valid — the backend resolves it relative to the conversation's cwd (or absolute if it
+starts with `/`).
+
+## Caching behavior (important for FE)
+
+The system prompt is **constructed once** (on the first turn of a new conversation) and
+**persisted**. It is reused on all subsequent turns (no reconstruction — this preserves
+the prompt cache). It is only **reconstructed on compaction** (fresh variable resolution).
+
+Changing the template (via `PUT /system-prompt`) does NOT affect existing conversations
+until they are compacted. New conversations use the new template on their first turn.
+
+## Default template
+
+When no template is stored, the backend uses:
+```
+You are a helpful coding assistant.
+
+[if file:AGENTS.md]
+[file:AGENTS.md]
+[endif]
+
+The current working directory is [prompt:cwd].
+```
+
+## FE UI suggestion
+
+The builder is a full-page modal split into two:
+1. **Text editor** (left/main): the template text.
+2. **Variable selectors** (right/side): buttons grouped by type. Clicking a variable
+ inserts `[type:name]` at the cursor position. For `file:` (dynamic), show a text
+ input for the path + an "insert" button.
diff --git a/tasks.md b/tasks.md
index 4e2794d..911b018 100644
--- a/tasks.md
+++ b/tasks.md
@@ -5,7 +5,7 @@
> Keep this lean and current; do not let it re-accrete a step-by-step changelog.
## Status (current)
-`tsc -b` EXIT 0 · biome clean · **1332 vitest** green.
+`tsc -b` EXIT 0 · biome clean · **1396 vitest** green.
## LSP cwd resolution — server-default fallthrough + workspace assignment (DONE)
Bug: `GET /conversations/:id/lsp` called `getEffectiveCwd` directly, which falls through
@@ -757,3 +757,31 @@ sealed → the FE spinner spun forever AND the conversation was bricked (next `c
- [ ] **Live-verify** (needs a fresh `bin/up` — the dev stack is currently wedged, the very symptom
of this bug): start a hanging tool (`run_shell` sleep/grandchild), Stop, then send a NEW message →
it must be ACCEPTED (conversation not bricked) and the spinner clears.
+
+## System prompt builder — template-based system context (DONE)
+Design: `notes/system-prompt-design.md`. FE courier: `frontend-system-prompt-handoff.md`.
+Problem: no system prompt was sent to the provider for regular turns (the messages array
+started with the user message; `providerOpts.systemPrompt` was never set). This adds a
+template-based system prompt builder with variable placeholders (`[type:name]`) and
+conditionals (`[if]`/`[else]`/`[endif]`).
+- **Cache constraint (critical):** the system prompt is constructed ONCE (first turn of
+ a new conversation) and persisted. Reused on all subsequent turns (no reconstruction —
+ cache-safe). Reconstructed only on **compaction** (fresh variable resolution + compaction
+ instructions appended).
+- **Variable types:** `system:time/date/os/hostname`, `prompt:cwd/model/conversation_id`,
+ `git:branch/status`, `file:<path>` (dynamic — any path).
+- **Wave 0 (orchestrator, contracts):** `@dispatch/transport-contract` `0.17.0→0.18.0` —
+ `SystemPromptTemplateResponse`, `SetSystemPromptTemplateRequest`, `SystemPromptVariable`,
+ `SystemPromptVariablesResponse`.
+- **Wave 1 — `system-prompt` (NEW ext):** pure parser (29 tests) + variable resolver
+ (injected adapters, 12 tests) + catalog (3 tests) + service handle (`construct` +
+ `get` + `getTemplate` + `setTemplate`, 8 tests). 52 tests total. Default template:
+ persona + AGENTS.md if exists + cwd.
+- **Wave 2 (parallel):** `session-orchestrator` (wire service: construct on first turn,
+ get on subsequent, construct+append on compaction; 12 tests) + `transport-http`
+ (GET/PUT `/system-prompt`, GET `/system-prompt/variables`; 6 tests).
+- **Wave 3 — host-bin:** registered `system-prompt` in `CORE_EXTENSIONS`.
+- [x] Verified: `tsc -b` EXIT 0, biome clean, **1396 vitest** pass.
+- [ ] Live-verify (boot smoke: extension activates, `GET /system-prompt` returns default
+ template, `GET /system-prompt/variables` returns catalog).
+- [x] **FE courier** sent to FE agent `ffe3`: `frontend-system-prompt-handoff.md`.