lib/goal-core.mjsstores goals, validates completion, formats summaries, redacts sensitive text, and tracks persisted tool history.
- Copilot CLI stable adapter: skill, Copilot SDK tools, and CLI hooks.
- MCP stdio adapter: local
goalSystemserver configured in Copilot CLImcp-config.json. - VS Code Chat preview adapter: custom agent, direct VS Code goal tools,
goalctlfallback, and VS Code agent hooks.
Data flow
State locations
State is written to three places:| Path | Purpose |
|---|---|
~/.copilot/session-state/goal-system/by-session/<session>.json | Same-session lookup. |
~/.copilot/session-state/<session>/goal-state.json | Session-local workspace state. |
~/.copilot/session-state/goal-system/by-cwd-session/<cwd-hash>--<session>.json | Same-directory continuation and ambiguity detection. |
~/.copilot/session-state/goal-system/compact/<session>.txt | Compact prompt snapshot written before compaction. |
~/.copilot/session-state/goal-system/compact/<session>.txt.json | Machine-readable compact snapshot metadata. |
Isolation model
The session id and cwd hash are both part of the lookup model. Multiple main sessions can run in the same directory and each will read only its own session goal during normal, cooperative operation. This is a correctness convention, not a security boundary.sessionId is supplied by the calling host and is not verified against any credential, so any process running as the same OS user can pass an arbitrary sessionId and read or modify another session’s goal state. The real security boundary is the OS user account under which Copilot and the goal-system tools run; do not rely on session or cwd isolation to separate untrusted callers on a shared account.
Same-directory continuation is conservative:
- zero open goals: do not pretend a goal exists
- one open goal: allow explicit continuation from that persisted record
- duplicate records for the same resumed goal id: treat them as one goal and pick the newest copy
- two or more open goals: refuse automatic continuation and ask for the intended session or goal id
Goal record
remaining and blockers replace by explicit update so the main session can clear queues on purpose.
discoveredIssues is additive because horizon tasks reveal work over time. If an inspection expands the task from three issues to ten, the full ten stay in the durable issue set. remaining is replaceable because it represents the current live queue, not a permanent history log.
issueResolutions records safe issue evolution. A discovered issue can be marked resolved, merged, renamed, duplicate, or superseded only when the entry names the original issue and includes evidence. Wildcard references such as “all issues” are rejected, so Copilot cannot bypass the completion gate by manufacturing literal resolved strings.
Status model
| Status | Open? | Meaning |
|---|---|---|
draft | Yes | Created from an activation prompt, still needs real inspection. |
active | Yes | Normal execution state. |
blocked without closedAt | Yes | Temporarily blocked, but still resumable. |
blocked with closedAt | No | Terminal blocker recorded. |
complete with closedAt | No | Completed with proof. |
cancelled with closedAt | No | Cancelled by user or explicit replacement flow. |
closedAt prevents terminal blocked goals from resurrecting as open goals.
Adapters
Copilot CLI
The CLI adapter uses:skills/goal/SKILL.mdextension.mjshooks/goal-context.sh~/.copilot/settings.json
goal_system_* tools and owns in-session drift counters. The shell hook restores goal context, writes compact snapshots, blocks agentStop, and keeps subagents outside goal ownership.
MCP stdio
The MCP adapter uses:adapters/mcp/server.mjs~/.copilot/mcp-config.json- the shared
GoalStore
goal_system_status, goal_system_open, goal_system_checkpoint, goal_system_finish, goal_system_update, goal_system_close, goal_system_block, and goal_system_cancel over stdio. The tools use the same persisted state and completion validation as the SDK tools and goalctl.
VS Code Copilot Chat
The VS Code Chat adapter uses:adapters/vscode-chat/agents/goal-system.agent.mdadapters/vscode-chat/hooks/goal-system.jsonadapters/vscode-chat/hook-runner.mjsbin/goalctl.mjs
sessionId and cwd into the session context. Direct tools, MCP tools, and goalctl require those values as a cooperative isolation convention so multiple sessions in one workspace stay out of each other’s way during normal use; see Isolation model for why this is not a security boundary. Persisted tool history drives drift enforcement across hook invocations.
UserPromptSubmit gives the hook-backed adapters an activation bridge: /goal creates a persisted draft goal immediately, and VS Code Chat can also hydrate one unambiguous same-directory goal into the current session on an explicit continue prompt.
Drift recovery
Adapters track non-goal tool calls while a goal is open.| Count since checkpoint | Behavior |
|---|---|
| 0-2 | No warning. |
| 3-4 | Prompt-level warning. |
| 5+ | Critical recovery reminder. The tool call is allowed by default so the session can inspect, repair, call goal_system_checkpoint, or keep a manual checkpoint if goal tools are unavailable. |
goal_system_status, goal_system_open, goal_system_checkpoint, and goal_system_finish do not count toward drift. Compatibility goal_system_update, goal_system_close, goal_system_block, and goal_system_cancel also do not count. The local goalctl command can update the same persisted state when direct tools are unavailable.
Hard pre-tool denial is opt-in through GOAL_SYSTEM_HARD_DRIFT_BLOCK=1 and should be used only in hosts where goal_system_checkpoint is known to be available in the same session. Default installs favor recoverability over permission deadlock.
Stop hooks use the same continuation contract in CLI and VS Code Chat: if the current main session still has an open goal, the hook blocks stop and tells the model to reload status, continue the next concrete remaining item, checkpoint persisted state, and finish only with evidence. Alternate stop payloads such as finishReason, completionReason, and terminationReason are treated as stop attempts.
Completion gate
goal_system_finish refuses completion unless the goal contains:
- objective
- doneSoFar
- validationProof
- verificationResults
- inspectionEvidence or inspection tool history
- requirementCoverage for every explicit requirement
- completionAudit
- empty remaining
- empty blockers
- no unresolved discovered issues, except those covered by specific evidence-backed issue resolutions
- action or verification evidence beyond claims
Subagent boundary
Subagents are useful for bounded inspection or test runs, but they do not own the goal. The system protects this in two places:- CLI
subagentStarthook injects a boundary message without full goal state. - VS Code
SubagentStarthook injects the same boundary message without full goal state. - SDK goal tools return failure when invocation metadata looks like a subagent.
- Direct goal tools, MCP tools, and
goalctlrequire the main sessionsessionIdandcwdfrom hook context.