dynamic-goal1.0.0
Dynamic replanning extension for the Pi coding agent: persistent goal, evidence-driven plan revision, phase-scoped tools, ADaPT-style as-needed decomposition, staleness detection, and isolated completion verification.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
goal-mode — dynamic replanning for the Pi coding agent
A Pi extension for goal-directed execution with dynamic replanning: it keeps a persistent goal, tracks a revisable plan with provenance, classifies evidence, enforces approval boundaries per feedback mode, and survives session restarts.
It is not a TODO manager, a static plan mode, or a blind retry loop:
- The goal is the stable reference point. It is never silently redefined and its mandatory acceptance criteria are never silently weakened.
- The plan is a first-class, revisable artifact. Every revision records its trigger, evidence, affected tasks, and approval status.
- Replanning is evidence-driven and bounded: revisions must cite evidence; repeated/oscillating plans and exhausted budgets are rejected, not looped.
Requirements
- Pi coding agent v1.1.0 (verified against
@earendil-works/pi-coding-agent@1.1.0extension APIs). Minimum supported: 1.1.0. - Node.js ≥ 22.18 (type-stripping for local TS, no build step needed).
Install
From npm (published package):
pi install npm:dynamic-goal
Or from git:
pi install git:github.com/neoguri7/dynamic-goal@v1.0.0
# or unversioned: pi install https://github.com/neoguri7/dynamic-goal
Or locally during development:
git clone https://github.com/neoguri7/dynamic-goal
cd dynamic-goal && npm install --no-audit --no-fund
pi install ./dynamic-goal # as a package
# or: pi --extension ./dynamic-goal/src/index.ts # one-off
# or: node scripts/install.mjs # junction into ~/.pi/agent/extensions/
Host-provided imports (@earendil-works/*, typebox) are peerDependencies
resolved by Pi itself — no bundled copies, no duplicate registries.
The extension registers its tools inactive; they activate when a run starts
(/goal, or the model calling goal_define) and deactivate on completion or
cancellation, so normal conversations are untouched.
Quick start
/goal Add an export button to the settings page, wired to the new API
The agent then researches (normal tools), records evidence (goal_evidence),
plans (goal_plan with assumptions), executes tasks, updates statuses with
verification evidence (goal_task_update), and finally calls goal_complete,
which independently checks that every non-superseded task is completed and every
mandatory acceptance criterion has supporting evidence.
User commands:
| Command | Purpose |
|---|---|
/goal <text> |
Start a run (also shows current goal when bare) |
/goal-plan |
Inspect plan + task statuses |
/goal-constraints <text> |
Add acceptance criterion (optional: prefix → preference) |
/goal-questions |
Review/answer open questions |
/goal-mode |
Switch feedback mode + question policy |
/goal-approve, /goal-reject |
Decide a pending plan revision |
/goal-pause, /goal-resume |
Pause/resume |
/goal-cancel |
Cancel (history retained via /goal-history) |
/goal-history |
Revision history with triggers + approval status |
/goal-report |
Final status + verification report |
Model tools (active only during a run): goal_define, goal_plan,
goal_state, goal_evidence, goal_task_update, goal_question,
goal_answer, goal_assumption, goal_propose, goal_decompose, goal_complete.
Task decomposition (function-split refinement)
Once the plan is confirmed — feedback rounds settled, revision approved,
evidence covering the tasks — top-level tasks can be split into concrete
subtasks (t1 → t1-1, t1-2, …), recursively up to depth 3
(t1-1-1), like function decomposition.
- Confidence gate.
goal_decomposeis allowed only when a deterministic plan-confidence score ≥decomposeThreshold(default 0.7): 0.25 approved/auto-applied latest revision + 0.25 no open high-impact questions + 0.25 resolved-assumption ratio + 0.25 evidence coverage of top-level tasks. A pending proposal is a hard zero./goal-decompose(no args) shows the current score and its reasons. - Forcing.
/goal-decompose <taskId>bypasses the gate once (consumed on the nextgoal_decomposecall) and asks the agent to decompose immediately. - Same approval pipeline. A decomposition is a revision like any other
(
trigger: decompose):humanmode queues it for/goal-approve,aiauto-applies within boundaries,hybridauto-applies (subtasks default to low impact) unless a subtask is high impact. - Stable ids + inheritance. Subtask ids are stable across revisions;
subtasks inherit the parent's
dependsOn,acceptance,verification, and assumptions unless overridden — the model typically chains them (t1-2 dependsOn [t1-1]). - Guards. No double-decomposition (refine via
goal_proposeinstead), completed/superseded parents refuse, and a parent cannot be completed viagoal_task_updatewhile subtasks remain open — completion of every leaf is verified first.
/goal-decompose → plan confidence: 0.50 (threshold 0.7)
assumptions resolved: 0% …
/goal-decompose t1 → gate bypassed once; agent decomposes t1 now
Feedback modes
| Mode | Behavior |
|---|---|
human |
Every plan revision requires explicit approval (/goal-approve, /goal-reject). Questions are prompted at tool-call time (never mid-stream). |
ai |
Revisions auto-apply within mandatory boundaries: goal changes, mandatory-acceptance changes, and mandatory-constraint changes always require approval regardless of mode. Retries and low-impact revisions proceed autonomously. |
hybrid (default) |
Low/medium-impact revisions auto-apply; high-impact revisions queue for approval. |
Question policies (/goal-mode):
| Policy | Behavior |
|---|---|
off |
Never interrupt; make conservative assumptions and record them. |
on-demand (default) |
Prompt only for high-impact blocking decisions. |
checkpoint |
Prompt at high-impact ambiguity, always for high impact. |
proactive |
Also prompt for medium-impact opportunities. |
Mode changes preserve goal, plan, history, and progress.
Token efficiency
A goal-mode run never duplicates the plan into every request:
- Compact per-turn context.
before_agent_startinjects ~8 lines (goal, progress counters, in-progress + next-ready tasks ≤5, pending proposal notice) instead of the full plan. The full render lives behindgoal_state, called on demand. - Delta tool results.
goal_task_update,goal_propose,goal_decomposereturn the change + compact summary, not the whole document — the model just authored the change, it doesn't need it echoed. - Phase-scoped tools. 10 of 11 tools are
defaultActive: false; onlygoal_define(a few lines) is declared before a run starts. During a run the model sees planning tools (goal_plan…) or executing tools (goal_task_update,goal_propose,goal_decompose,goal_complete…), never both sets, never off-phase schemas. Cancel/complete removes them all. - Bounded full renders.
goal_state//goal-plancap at 40 tasks, 10 questions, 10 assumptions (with+N moremarkers); completed tasks collapse to one line (no acceptance text). - No persistence overhead. State snapshots are session entries, which are excluded from LLM context by design — they cost disk, not tokens.
Result: per-request cost stays roughly constant as plans grow (O(next-ready) instead of O(plan size)).
Research-grounded mechanisms (2025–26 literature)
Grounded in docs/research-feedback.md (ALAS, ADaPT, AdaPlan-H, SyncPlan,
Reflexion, CART/BacktrackAgent):
- ADaPT as-needed decomposition. When a task exhausts its retry budget
and can still be decomposed,
decideRetryroutes to decompose instead of blind escalation, and the failure unlocks the confidence gate once — failure itself is the evidence the task was too coarse. - AdaPlan-H complexity trigger.
goal_task_updateresults name high-impact / previously-failed undecomposed tasks as decomposition candidates (decompositionCandidates). - ALAS isolated verification.
goal_completeperforms a fresh-context check: goal + criteria + evidence summaries in a bounded prompt via a nested provider-neutral model call — never the transcript — so the run is not graded by the same context that produced the work. Falls back to the deterministic evidence check when no model/auth is available. - SyncPlan staleness detection. Every render surfaces
PLAN STALE:when an invalidated assumption touches a ready/in-progress/failed task (deterministic, zero extra tokens). - Reflexion-style reflections. Failures accept a one-line
reflection(what to try differently); recent ones surface in the compact render and persist across sessions. - CART proposal parking.
/goal-rejectparks the rejected proposal (bounded, last 5);/goal-revert <rev>re-promotes it with a fresh revision number and re-keyed fingerprint (oscillation-safe trajectory resume).
Interoperability (agents, skills, MCP, classifiers)
goal-mode is deliberately composable instead of self-contained:
- Optional classifier-assisted confidence (e.g. Jev). By default the
decomposition gate uses the deterministic score above. To blend in a
structured classifier — such as a Jev system-one model registered by another
extension — set
confidenceClassifier: { provider, model }on the policy (persisted with the goal state). The classifier scores plan settledness viactx.modelRegistry.classify(); the gate uses0.5·deterministic + 0.5·classifier, and falls back to deterministic-only when the model is unregistered or the call fails. The classifier never touches approval decisions — those stay deterministic for safety. - Research-first questions. The tools instruct the agent to resolve
uncertainties itself before interrupting the user: code inspection, tests,
available skills, web research, MCP tools — findings recorded via
goal_evidence(kind=research), answers viagoal_answer(source=research). - Compaction-safe. State lives in session entries and is re-injected
fresh each turn (
before_agent_start), so compaction extensions (e.g.pi-blackhole) can compress old turns freely — goal-mode rebuilds its context from the persisted snapshot, not from conversation history. - Cross-extension hook point.
pi.eventsremains available for other extensions to observe goal-mode activity; goal-mode itself registers no MCP servers and adds no network dependencies.
State + persistence design
Schema version 3 (older data migrates up automatically: v2 added
parentTaskId+decomposeThreshold, v3 added failurereflection+parkedrejected proposals; all optional with defaults).In-memory
StateStorewith an enforced lifecycle:draft → planning → (awaiting_approval)? → executing → paused/blocked → completed/cancelled. Transitions are validated in code; invalid transitions throw.Persistence via
pi.appendEntry("goal-mode-state", <JSON snapshot>)— Pi session entries, branch-sensitive. Onsession_start/session_treethe extension restores the last state entry on the active branch. Snapshots are validated (src/validate.ts) before loading: corrupt state, dependency cycles, unknown deps, or a newer schema version are rejected safely and the run starts fresh with a warning instead of corrupting.Schema is versioned (
schemaVersion: 3); older versions migrate up, newer ones are refused. No secrets, env vars, or raw conversation are persisted.Model context injection is per-run (
before_agent_start) and deduplicated incontext: the model always sees the current goal/plan snapshot, and stale snapshots from earlier turns are stripped.
Evidence-driven replanning — worked example
- Goal.
/goal Ship the CSV export featurewith criterion "tests + lint pass" (mandatory). - Research. The agent records
goal_evidence:kind=research, verdict=invalidates, assumptionId=a1("we assumed a CSV writer exists in the repo"). Assumptiona1flips toinvalidated. - Proposal. The agent proposes via
goal_propose(trigger=research-invalidated-assumption): add a taskt4 write minimal CSV serializer, cite evidencee1, keep completed unrelated tasks untouched. Inhybridmode a low/medium-impact revision auto-applies; a revision touching high-impact tasks queues for approval. - User review.
/goal-plan, then/goal-approve(or/goal-reject— the current plan stands, and the rejection is recorded in history). - Implementation. Task executed; verification attempt fails a test →
goal_task_update status=failed classification=transient reason="...". Retry is allowed undermaxRetriesPerTask(default 3); repeated failures escalate instead of looping.classification=invalid-assumptionnever retries: it asks for a plan revision instead. - Targeted replan. New proposal cites the failing-test evidence;
completed-but-unaffected tasks stay completed. An upstream change to a
completed task reopens only its transitive dependents
(
invalidateDownstream). - Completion.
goal_completeindependently verifies: open tasks? Mandatory criteria without supporting evidence? Pending proposal? Only a clean state completes — completing the checklist is not enough.
Safeguards
- No shell command runs just because a plan mentions it; normal Pi tools and permission hooks still gate every call.
- Replans are bounded (
maxReplans, default 12) and oscillation-guarded (recent proposal fingerprints cannot repeat). - Question flow uses supported dialogs at tool-call time (
ctx.hasUIguarded) with a text fallback (goal_answerfrom chat). The extension never aborts an in-flight model request; checkpoints happen between tool calls, which is the boundary Pi supports. - Cancel/pause are pure state transitions — always consistent.
Tests
npm install
npm run typecheck # tsc --noEmit
npm test # 26 deterministic unit tests, no model required
Covered: topological planning, readiness derivation, downstream invalidation, approval boundaries per mode, mandatory-criteria/goal protection, oscillation and budget guards, bounded retries, persistence round-trips, corrupt/legacy schema rejection, cycle/unknown-dep rejection, lifecycle enforcement, and resume-from-persisted-state.
Known limitations
- Question prompting happens at tool-call boundaries, not mid-stream (Pi extension API boundary; documented behavior, not a bug).
humanmode relies on the user running/goal-approve//goal-reject; execution of unaffected tasks may continue meanwhile.- One goal run per session (by design; start a new session or
/goal-cancelfor a fresh goal). - AI question routing to
web_search/research tools is the model's choice via prompt guidance; the extension records the answer source for auditability.