@@ -0,0 +1,129 @@
|
||||
# goal — pi extension
|
||||
|
||||
Operator-set goal with a forced check<->proceed loop. `/goal <text>` 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 <text> set (or replace, with a notify) the active goal
|
||||
/goal --max N <text> 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 <goal text>` 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.
|
||||
Reference in New Issue
Block a user