# goal — pi extension Operator-set goal with a forced check<->proceed loop. `/goal ` activates a goal; the extension keeps the agent working and reporting until it declares the goal `satisfied` via the `goal_report` tool, then retains a recallable completed outcome and returns the session to normal operation. Requirements record: `docs/PRD.md` in mosaic-brain (issue #52). Design locked with Jason 2026-08-28 (Q1-Q8). ## Usage ``` /goal full current or completed goal, state, and counters /goal set (or replace, with a notify) the active goal /goal --max N set with a non-default run limit (default 25) /goal stop pause the loop, goal retained /goal resume continue a paused goal (kicks a check turn) /goal clear remove the goal entirely ``` `GOAL_MAX_CHECKS` env overrides the default cap for new goals (invalid values fall back to 25). ## Loop mechanics 1. `before_agent_start` appends the active goal and reporting instructions to the system prompt every turn, so the goal survives context growth and compaction. 2. When the agent settles without the goal being satisfied, the extension injects a check prompt (`sendUserMessage`), forcing the next turn: the check<->proceed loop. Before reporting, follow the continuation loop in `skills-local/ms-proactive-agent/SKILL.md`. 3. The agent reports via `goal_report`, the loop's only exit: - `satisfied` + evidence: completed outcome retained for recall, normal operation (`evidence`, `reason`, and `in_progress` structured progress use the `ms-executive-update` format — `skills-local/ms-executive-update/SKILL.md` — so the operator reads one shape everywhere) - `blocked` + reason: loop paused, reason surfaced to the operator - `in_progress`: evidence plus a structured progress record naming the nearest gate, owner, last live measurement, and next action 4. Successful non-`goal_report` tool results create one observable work event. Measurement, action, and delegation reports require that event. Non-tool work requires a concrete artifact. Waiting requires an approved watch id or concrete next-check condition. 5. Empty, malformed, unsupported, or duplicate `in_progress` reports do not reset the check counter. They increment a persisted no-progress counter and pause after three reports by default (`GOAL_MAX_NO_PROGRESS_REPORTS`). Repeated valid wait reports remain active without claiming new progress or triggering a false-positive pause. 6. Esc (run abort) and run errors pause the loop instead of re-injecting; `/goal resume` continues. Operator-typed messages always flow through unchanged. 7. No-report cap: N consecutive check turns without an accepted report auto-pause the loop and notify. Default 25 (`GOAL_MAX_CHECKS`), per-goal `--max N`. The loop needs a persistent session (TUI or RPC). In print mode (`pi -p`) the goal directive and seat-durable state still apply, but check turns are not forced — print mode exits when the prompt pipeline completes, and a forced turn there races session teardown (measured). ## State Incarnation-durable state survives `/new`, `/resume`, and reloads within one process. This repository's native development installation stores it under `.pi/state/goal/`. The identity is the process-launch incarnation, never `PI_SESSION_ID`. Unknown-owner legacy `goal-state.json` files are quarantined rather than inherited. One active goal exists per incarnation; setting a new goal replaces that incarnation's goal. State stores counters, bounded routing fields, and a SHA-256 evidence fingerprint. It does not persist free-form progress evidence. ## Development installation This directory is canonical source. Do not edit `.pi/extensions/`; generate that native-test installation with `scripts/sync-dev-extensions.sh`. The sync copies `goal/` and its `mosaic-core/lib/` dependency as ordinary files, verifies content hashes, and refuses to overwrite installation drift. It also enforces that `goal/index.ts` is the only extension entrypoint in this bounded package. Start the isolated native test with `bash scripts/goal-dev.sh`. It runs the sync and passes the generated entrypoint explicitly to Pi while disabling ambient extension discovery. This does not install or reload the live fleet extension and does not add the extension to Docker. Directory layout: `index.ts` for wiring, `lib/` for logic, and `test/` for hermetic checks. Pi supplies the extension API, TypeBox, and pi-ai imports at runtime. ## Tests ``` node --test extensions/goal/test/*.test.ts bash scripts/test-extension-package.sh ``` The suite includes pure state, persistence, incarnation fencing, exact report-only loop, duplicate evidence, legitimate wait, headless extension-runtime, and enforcement-neutralization red controls. ## Bounded waits (operator opt-in) Use `/goal --wait-timeout 60 ` to suspend automatic checks during a reported wait. The flag accepts 10–86400 seconds and combines with `--max N`. Omitting it preserves the previous wait-loop behavior. Existing running sessions must `/reload` to load this patch; do not reload unrelated seats for a trial. A valid `goal_report` with `status: "in_progress"`, `progress.kind: "wait"`, an owner, and a watch id or concrete `nextCheck` persists the deadline and yields. There are no model heartbeats during that wait. Repeated wait reports preserve its original deadline. A substantive progress report clears the wait. The extension neither evaluates arbitrary shell conditions nor sends messages to other agents: a relevant incoming message can start an ordinary turn, or the one-shot deadline asks the agent to reconcile the dependency once. There is **one automatic deadline wake per goal or explicit `/goal resume`**. Reporting another wait after that wake pauses the goal, including when its wording changes. This intentionally limits the initial rollout; multiple successive timed dependencies require explicit resumption. Deadline expiry never supplies approval or permits the agent to invent missing input. The timer belongs to this Pi process. Reload within the same incarnation restores the original deadline; shutdown cancels timers. A new process has a new fenced goal identity and does not inherit another incarnation's goal. This patch is not a process supervisor. Print mode records a manual wait and never schedules a wake. No external systemd timer is installed. If the runtime is busy at expiry, readiness is checked in code every second for at most 60 seconds; this also covers manual compaction with no settle event. If still busy, the goal pauses with a UI notice. A dispatched deadline wake must appear in `before_agent_start` within 30 seconds or the goal pauses without resending. The matching event confirms turn start, not successful task execution. Stop, clear, replacement, satisfaction, and blocking cancel owned timers. Late reports cannot settle a paused goal. Reports also refuse if the goal changes while an asynchronous policy check is running. State snapshots use atomic file replacement; failed saves stop continuation instead of claiming a saved result. The tests exercise unchanged waits, deadline dispatch and observation, unresolved pause, compaction, reload, cancellation, print mode, malformed persisted waits, and actual filesystem write failure. This is separate from live seat acceptance.