Editing records via the agent (the resolution layer)¶
T1150, the last child task of the T1143 EPIC (the record-mutation API, D050's write side),
builds the seam between "tell the agent what you want changed, in plain language" and the typed
Mutate<Class> calls mcp-tools.md documents. This page explains the
contract and its current integration status honestly — read the "What's wired up today" section
before assuming a command below already exists.
The contract¶
plain-language request
--[a Planner resolves it]--> a Plan: zero or more typed Calls, shown for confirmation
--[you confirm, or decline]--
--[Execute dispatches each confirmed call]--> the record's rendered text, reported back
- The agent never opens a record's markdown file in an editor. Every change goes through
the same typed
Mutate<Class>functions the CLI verbs and MCP tools (mcp-tools.md) already call — there is no separate, parallel write path for agent-driven edits. - You always see the plan before anything happens. A Plan renders as a one-line summary plus
one line per call (e.g.
task 42: set status="paused", priority_score=8) — nothing executes until that plan is confirmed. - An unresolvable request refuses cleanly, with a reason — "I couldn't figure out what you meant" is a normal outcome, not a crash or a guess.
- One call failing does not abort the rest of the plan. If a request resolves to several edits and one of them fails (an unknown id, a validation error), the others still run; you get a per-call result.
What's wired up today¶
This task landed the resolution layer itself — gald3r_project/board/resolve — as a Go package:
a Planner interface (the seam any real language-understanding step plugs into), a small
deterministic reference planner covering a narrow, fixed set of request shapes (see below), and
the Plan -> confirm -> Execute -> rendered result orchestration, wired against the real
ops.MutateTask/ops.MutateBug functions.
It is not yet wired into gald3r chat or any other end-user command. That integration —
along with the actual language-model-backed planner (see "Design boundary" below) — is expected
to land on top of the always-on project-agent runtime (gald3r.ai/swarm/projectagent, tracked as
a separate, larger epic) once that runtime reaches main. Until then, this page documents the
package contract for whoever wires that integration up, and the narrow reference planner you can
already exercise directly against the package.
The reference planner's recognized request shapes¶
resolve.ReferencePlanner is a small, fully deterministic, zero-model implementation that
recognizes exactly these English sentence shapes (case-insensitive):
| Request | Effect |
|---|---|
set task <id> status to <status> |
Sets the task's status |
set task <id> priority score to <n> |
Sets the task's priority_score |
pause task <id> |
Shorthand for status: paused |
set bug <id> status to <status> |
Sets the bug's status |
set bug <id> severity to <severity> |
Sets the bug's severity |
resolve bug <id> as <resolution> |
Sets the bug's status: done and resolution |
Anything outside these shapes returns an unresolved Plan with a Reason explaining that
nothing matched — it never guesses.
Design boundary: this package does not ship an LLM integration¶
T1150's own description imagines a human telling "the AI what needed to be done" and the agent
resolving that into calls — i.e. a language model doing the actual understanding. Which model to
call, what prompt/context to give it, how much of the board to feed it, and how ambiguity gets
surfaced back to the human are all real product decisions with no ratified answer on record as
of this task. Wiring a live model call is also outside gald3r_project/'s territory (this task's
scope): gald3r_llm and the always-on project-agent runtime are the natural home for a real
Planner implementation once that design is ratified.
Nothing about the package changes when that planner arrives — it is just another Planner:
type Planner interface {
Plan(ctx context.Context, request string, board BoardContext) (Plan, error)
}
Any implementation — the reference planner above, a test's fake model, or a future LLM-backed
one — plugs into the exact same resolve.ResolveAndExecute orchestration.
Supported record classes today¶
Only task and bug ship a built-in dispatcher out of the box (resolve.ClassTask /
resolve.ClassBug), routed to ops.MutateTask / ops.MutateBug — the two record classes with
the richest, fully DB-first mutation API. The other ten classes T1143 named (ADR, TO-DO, idea,
decision, PRD, feature, subsystem, release, constraint, document) already have their own
Mutate<Class> functions (see mcp-tools.md); wiring one into the resolution
layer is a resolve.RegisterClass("adr", myDispatchFunc) call away, not a package change.
Guard: a "child process" cannot bypass the API with a direct write¶
Every write this package ever performs goes through a registered Mutate<Class> function —
Execute has no write path of its own to fall back to. While building this task, an audit found
that ops.MutateTask/MutateBug/MutateTodo/MutateIdea (unlike every other Mutate<Class>
function in the codebase) carried no reviewsandbox.Guard check at all: a sandboxed
reviewer-turn child (GALD3R_REVIEW_SANDBOX=1) or a T1141 single-writer-board child
(GALD3R_SINGLE_WRITER_BOARD=1) could call them directly and mutate the board anyway. This task
closes that gap in gald3r_project/board/ops (mutate.go/mutate2.go) — both markers now
refuse with the same named errors every other guarded write path in this codebase already uses —
and proves the fix holds all the way through this package's own Execute step
(resolve_reviewsandbox_test.go), not just when Mutate<Class> is called directly
(mutate_reviewsandbox_test.go).