Files
kandev/docs/specs/tasks/system-design/plan-write-append-mode-agent-text.md
T
2026-09-06 15:10:10 +01:00

133 lines
7.6 KiB
Markdown

---
status: draft
system: tasks
requirements:
- REQ-TASKS-PLAN-APPEND-006
created: 2026-09-05
owners:
- kandev
---
# Task plan append-mode agent text System Design
## Purpose and boundaries
This design owns `REQ-TASKS-PLAN-APPEND-006`: every agent-facing string that describes a
plan write, the class rule that binds them, the truncation warning's rewrite, and the
acknowledgement. It defines no composition, locking or size-limit behavior — those stay in
[the main design](plan-write-append-mode.md), which this file was split out of on
2026-09-05 when that file reached its 32,768-byte lint ceiling. No content was dropped in
the move.
Split here rather than compressed away because the alternative was deleting Build guidance
that closed earlier review findings, which would have reopened them. The companion-file
pattern follows `external-id-idempotency-operations.md` in this same directory.
## Requirement mapping
| Requirement | Design section |
| --- | --- |
| `REQ-TASKS-PLAN-APPEND-006` | [Agent-facing description and acknowledgement](#agent-facing-description-and-acknowledgement) |
## Agent-facing description and acknowledgement
`REQ-TASKS-PLAN-APPEND-006` covers the tool registration in
`internal/mcp/server/server.go` and the existing runtime warning and acknowledgement
paths. The description an agent reads is the place where the choice between modes is
explained, so it is contract, not commentary.
**`AC-TASKS-PLAN-APPEND-006.9` binds a CLASS of strings, not a list.** No agent-facing
text describing a plan write may claim that no append mode exists or prescribe
read-then-resend. Two review rounds enumerated these texts and each list came up one
short, so the criterion is stated over the class: Build sweeps `origin/main` for the
claim and fixes what it finds, rather than trusting any enumeration including this one.
On 2026-09-05 the sweep matched five strings, all of which change: the
`update_task_plan_kandev` description (`server.go:1846`) and its `content` parameter
(`:1848`); `create_task_plan_kandev`'s `content` parameter (`:1832`) **and its own tool
description** (`:1830`); and both branches of the truncation warning. Site `:1830` is the
one both earlier enumerations missed — it says the tool replaces everything "same as
update_task_plan_kandev through a different door" and tells the caller to read the plan
first, so the equivalence turns false the moment `update` gains `append` and the second
clause is exactly the prescription `006.9` removes. Treat that list as of its date.
The `update_task_plan_kandev` description asserts there is no partial update, append or
section-patch mode, and that sending only a new section "will silently delete everything
else"; left alone it argues against the mode being added. Its `content` parameter also
carries the 256 KiB cap sentence from `plan-content-size-limit`, which stays and applies
to the composed document once `append` exists (`REQ-TASKS-PLAN-APPEND-007`). Rewritten,
the description must name both modes and the default (`AC-TASKS-PLAN-APPEND-006.1`),
keep the deletion warning **scoped to `replace`** rather than dropping it (`006.2`), and
state that `append` inserts a blank line before the fragment (`006.3`), is not
idempotent so a resubmission adds it again (`006.4`), and does not require reading the
plan first (`006.5`). The `mode` parameter's own description carries the two accepted
values and the default. The deletion warning must survive because replace is still the
destructive path; softening it to make room for append would trade one silent data loss
for another.
Both `create_task_plan_kandev` strings deny the mode exists and prescribe the
read-then-resend pattern this capability removes, on the same tool
`AC-TASKS-PLAN-APPEND-005.2` makes *reject* a mode by naming
`update_task_plan_kandev`'s append. `AC-TASKS-PLAN-APPEND-006.9` fixes the replacement.
The boundary Build must hold: the `mode` property, if declared, states only that its
value is rejected, per
[the two sibling surfaces](plan-write-append-mode.md#the-two-sibling-surfaces-which-behave-differently-on-purpose),
which explains why this tool's schema declares it at all — the platform's generic
argument-schema validator would otherwise shadow `createTaskPlanHandler`'s own,
correctly-worded rejection with a generic error naming neither tool.
### The truncation warning is the most urgent of them
`planTruncationWarning` in `internal/mcp/handlers/task_plan_guard.go` renders both of
its branches around this sentence:
> Plan writes REPLACE THE ENTIRE DOCUMENT — there is no partial update or append mode.
Shipping append while leaving that in place denies the mode at the one moment an agent
has just destroyed content and would most benefit from being told it exists, and steers
it back toward the replace path that caused the loss. `AC-TASKS-PLAN-APPEND-006.7` and
`006.8` require that sentence replaced by one naming `append` as the way to add a
section without resubmitting the document.
Everything else in that warning must survive, for reasons recorded in its doc comment
rather than obvious: it deliberately does not tell the caller to "recover" the content
through an MCP tool, because no plan tool reads a past revision and
`get_task_plan_kandev` would hand back the truncated document; it names where the prior
content lives; and it tells the caller not to rewrite from memory. Naming `append`
strengthens that advice rather than competing with it — the caller is told what to do
instead of what just failed. Keep the rune counts, the percentage, the two branches on
whether the prior revision number is known, and the response field.
This is Go string content, not localized UI copy, so the i18n rules do not reach it.
`AC-TASKS-PLAN-APPEND-005.1` excludes this wording from the replace-mode freeze
precisely so the two criteria do not contradict each other.
### Acknowledgement
The acknowledgement needs **no change**, verified rather than assumed. `planWriteAck`
in `internal/mcp/server/handlers.go` already prefers the stored `content` length over
what the call sent, renders it as bytes, and already appends a line stating that plan
content is omitted and should be read back with `get_task_plan_kandev`. That is exactly
`AC-TASKS-PLAN-APPEND-006.6`, satisfied by existing behavior on both modes.
Build must not add plan content to that acknowledgement. Doing so would return the
accumulated document on every append and give back the response-leg saving this
capability exists to obtain — the ack omits content deliberately, because echoing the
stored plan to the agent that just wrote it was measured as the largest single
contributor to context growth in plan-heavy sessions.
## Test strategy
**Description tests** assert the class rule by ITERATION, not by naming strings: walk
every registered plan-tool description and parameter description plus both rendered
warning branches, and assert none carries the no-append claim or the read-then-resend
prescription (`AC-TASKS-PLAN-APPEND-006.9`). A per-string test would have passed against
a spec that missed one, which is what happened twice. This must not exempt
`update_task_plan_kandev`'s own strings from the walk — the tool whose description most
recently carried the banned claim is exactly the one a special-cased test would stop
checking. Also assert the warning names `append` while still stating where the prior
content lives and that the caller must not rewrite from memory (`006.7`, `006.8`), and
that `create_task_plan_kandev`'s texts point at `update_task_plan_kandev`, and that its
`mode` property (whether or not the schema declares one) states only that the value is
rejected, never a functioning capability. Assert on substantive clauses, not whole
strings, so they do not become punctuation change-detectors.