# Agent tools Reginald gets **few, fat tools** so a small local model (gemma-4b class) can survive with one thing to reach for. One read tool, three write tools. All I/O is compact JSON; ticket data is fetched through tools, never copied into hot memory ([PLAN.md](./PLAN.md) memory layers). Model routing (per PLAN.md): the read tool + prose/standup run on the small local model; `capture_work` decomposition and `record_directive` negotiation route to the big model. ## `query_project` (read) The single read tool. A `view` enum selects the shape; `filters` narrows it. The scheduler's deterministic output backs every forecast field — the model never computes, it reports. ```jsonc query_project({ view: "focus" | "issue" | "milestone" | "board" | "runway" | "calibration" | "directives" | "standup" | "search", filters?: { issueId?: number, milestoneId?: number, state?: "diagnosis" | "triage" | "steeping" | "in_review" | "done", assignee?: string, // gitea username label?: string, query?: string, // free text, for view:"search" limit?: number // default 20, max 100 } }) ``` View payloads (compact; forecasts always ranges, never point dates): - **focus** — `{ now, next[], later[] }`, each `{ issue, title, rationale }`. - **issue** — intent (title, description, comments, assignee, labels) + derived (lifecycle timeline, per-issue forecast range, dependency ids, provenance). - **milestone** — `{ due, hard, stats, cone: {p10,p50,p80 dates}, issues[] }`. - **board** — issues grouped by the five lifecycle columns. - **runway** — per-milestone `{ due, band: {p50,p80}, slack }` + capacity list. - **calibration** — `{ n, coldStart, globalMultiplier, byLabelBias[], byPersonBias[] }`. - **directives** — pending consequence diff + recent ledger entries. - **standup** — drift report, per-person plan, stale blockers. - **search** — issues matching `query`. ## `capture_work` (write — big model) Braindump → interview → proposed issue set. Returns a **proposal**, never files directly; the Capture screen's review tray edits it before `apply_changes` files it. Decomposition + estimate negotiation is the one place the big model earns its keep. ```jsonc capture_work({ braindump: string, answers?: { question: string, answer: string }[] // interview turns so far }) // → { needsMoreInfo?: string[], // follow-up questions; present as chips // proposal?: { issues: [{ title, body, estimate: EstimateLabel, // priority?: PriorityLabel, deps?: number[], // milestone?: number }], // consequence: string } } // one-line schedule impact ``` ## `apply_changes` (write — unified mutation) Every mutation funnels here: filing captured issues, label/estimate/priority/ milestone/dependency edits. Additive ops apply directly; **destructive ops require `approved: true`** (the caller obtains approval via the consequence diff / Dialog first — see [decisions.md](./decisions.md) D1). Batched so one call = one coherent change with one consequence. ```jsonc apply_changes({ ops: [ { op: "create_issue", title, body, labels?, milestone?, deps? }, { op: "set_estimate", issue, estimate: EstimateLabel }, { op: "set_priority", issue, priority: PriorityLabel }, { op: "set_milestone", issue, milestone: number | null }, { op: "set_deadline_hard", milestone: number, hard: boolean }, { op: "add_dep", issue, dependsOn: number }, { op: "remove_dep", issue, dependsOn: number }, // destructive { op: "close_issue", issue }, // destructive { op: "remove_label", issue, label } // destructive ], approved?: boolean // required iff any op is destructive }) // → { applied: number, consequence: string, rejected?: {op, reason}[] } ``` Only touches labels in the CommiTea namespaces (`est/*`, `p/*`, `deadline/hard`) plus native issue fields — never invents labels, comments, or synthetic issues (zero-pollution goal). ## `record_directive` (write — big model) Appends to the directive log ([pm-state.md](./pm-state.md)), triggers a scheduler re-run, and returns the consequence diff for propose-approve. Does **not** mutate gitea itself — a directive is intent; its effects land through `apply_changes` after approval. ```jsonc record_directive({ kind: "reprioritize" | "reestimate" | "set-deadline" | "scope" | "capacity" | "note", quote: string, // verbatim PM words, stored in the ledger target?: { issue?: number, milestone?: number, member?: string }, params?: object, // structured effect, e.g. { priority: 1 } rationale?: string }) // → { directiveId, consequence: { before, after }[], summary: string } ``` ## Not tools Reads that are pure UI state (theme, current view, back-stack) never go through tools. The scheduler, Monte Carlo, calibration fit, and lifecycle inference are **code**, invoked by the runtime around these tools — the model requests a view or proposes a change; deterministic code produces every number.