ADR 0023: Static goal-contract + run-plan emitter
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-06-01 |
| Deciders | Project maintainers |
| Supersedes | — |
| Superseded by | — |
| Related ADRs | ADR-0021 (task-quality-score), ADR-0014 (ProjectContext) |
Summary
@aidokit/core gains buildGoalContract(briefMarkdown), a pure, deterministic
function that extracts a goal contract from a task brief: an objective, the
set of allowed files, the runnable validation commands, and a fixed set of stop
rules. It returns a markdown string and makes zero model calls.
The CLI gains aidokit goal draft <id|path> (writes
agent-artifacts/goals/<id>-goal.md) and aidokit goal plan <id|path> (writes
agent-artifacts/goals/<id>-plan.sh, a commented run-plan shell script). The
plan script is documentation: it enumerates the steps (draft goal → run YOUR
adapter manually → aidokit eval → aidokit verify → human review) and never
invokes a model itself.
Context
Two pain points (#1 goal drift, #2 unbounded scope) share a root cause: the agent starts work without a written, checkable contract for what "done" means and which files it may touch. Teams want a single artifact that pins the objective, bounds the blast radius, and lists the exact commands that decide pass/fail — produced before any model runs, so it can be reviewed up front.
aidokit already has the raw material:
- Task briefs carry a
## Acceptance criteriasection whose- [ ] > <cmd>linesaidokit evalruns verbatim (the validation commands). - Briefs commonly carry a
## Scope/## In-scope filessection (the allowed files). - The first heading is the objective.
What was missing was a deterministic step that consolidates these into one
contract artifact and a documented, model-free run plan around it. Per the
permanent non-goal (CLAUDE.md §11, ROADMAP §Non-goals), aidokit must not call a
model — so the "run the adapter" step is a manual placeholder, not automation.
Decision
1. buildGoalContract(briefMarkdown): string in @aidokit/core
A pure function (and a structured extractGoalContract companion returning
GoalContract) that statically extracts:
- objective — text of the first
#/##heading, else the first non-empty paragraph. - allowed files — bullet entries under a case-insensitive
Scope/In-scope files/Allowed filesheading, preferring an inline-code span when present. - validation commands — the
- [ ] > <cmd>lines inside the## Acceptance criteriasection (same conventionaidokit evalruns). - stop rules — fixed boilerplate: stop on first red validation; no scope expansion beyond allowed files; a human approves the merge.
No regexless parsing, no model, no I/O. Missing sections render guidance placeholders rather than failing.
2. aidokit goal draft <id|path>
Resolves the brief like aidokit eval / aidokit task score (bare id →
agent-artifacts/task-briefs/<id>.md; path-looking value relative to root;
--brief <path> override), builds the contract, and writes
agent-artifacts/goals/<id>-goal.md.
3. aidokit goal plan <id|path>
Writes agent-artifacts/goals/<id>-plan.sh — a commented bash script that
documents the workflow steps and ends with an echo. Every actionable line is a
comment the human un-comments and runs. The adapter step is explicitly manual:
the script never calls a model.
Consequences
Positive
- Up-front, reviewable contract. Objective + scope + pass/fail commands in one artifact before any model runs.
- Reuses existing conventions. Validation commands are exactly the lines
aidokit evalalready runs; scope is the brief's existing section. - Deterministic, zero model calls. Honours the permanent non-goal.
Negative
- Extraction is heuristic. A brief without a Scope section yields an unconstrained contract (flagged with a placeholder, not an error).
Neutral
- Output lives under
agent-artifacts/goals/, a new sibling oftask-briefs/.
Alternatives considered
- Generate the contract with a model. Rejected — violates the zero-LLM-call non-goal.
- Make
goal planexecute the steps. Rejected — running the adapter means calling a model; the plan stays documentation.
Validation
This decision is validated when:
- [ ]
buildGoalContractextracts objective, allowed files, and validation commands from a representative brief. - [ ]
aidokit goal draft <id>writesagent-artifacts/goals/<id>-goal.md. - [ ]
aidokit goal plan <id>writes a commented<id>-plan.shthat contains no model invocation.
References
- ADR-0021 — task-quality-score (precedent for an advisory brief-derived command).
aidokit eval— acceptance-criteria convention reused here.- CLAUDE.md §11 / ROADMAP §Non-goals — zero LLM calls from
aidokititself.