Copilot Goal System is a local state machine with host-specific adapters. Shared core:
  • lib/goal-core.mjs stores goals, validates completion, formats summaries, redacts sensitive text, and tracks persisted tool history.
Adapters:
  • Copilot CLI stable adapter: skill, Copilot SDK tools, and CLI hooks.
  • MCP stdio adapter: local goalSystem server configured in Copilot CLI mcp-config.json.
  • VS Code Chat preview adapter: custom agent, direct VS Code goal tools, goalctl fallback, and VS Code agent hooks.

Data flow

State locations

State is written to three places:
PathPurpose
~/.copilot/session-state/goal-system/by-session/<session>.jsonSame-session lookup.
~/.copilot/session-state/<session>/goal-state.jsonSession-local workspace state.
~/.copilot/session-state/goal-system/by-cwd-session/<cwd-hash>--<session>.jsonSame-directory continuation and ambiguity detection.
~/.copilot/session-state/goal-system/compact/<session>.txtCompact prompt snapshot written before compaction.
~/.copilot/session-state/goal-system/compact/<session>.txt.jsonMachine-readable compact snapshot metadata.
The duplicated writes are intentional. They let the system survive session resume, compaction, and same-directory continuation while refusing ambiguous multiple-goal states.

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
Subagents do not get goal ownership. Lifecycle hooks give them a boundary message, SDK goal tools reject subagent-looking invocations, VS Code Chat hooks do not expose goal state to subagents, and post-tool history ignores subagent tool use. A main session may record subagent output only after checking the real evidence.

Goal record

Durable evidence fields append by default. 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

StatusOpen?Meaning
draftYesCreated from an activation prompt, still needs real inspection.
activeYesNormal execution state.
blocked without closedAtYesTemporarily blocked, but still resumable.
blocked with closedAtNoTerminal blocker recorded.
complete with closedAtNoCompleted with proof.
cancelled with closedAtNoCancelled 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.md
  • extension.mjs
  • hooks/goal-context.sh
  • ~/.copilot/settings.json
The SDK extension exposes 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
It exposes 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.md
  • adapters/vscode-chat/hooks/goal-system.json
  • adapters/vscode-chat/hook-runner.mjs
  • bin/goalctl.mjs
CLI and VS Code hooks inject 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 checkpointBehavior
0-2No warning.
3-4Prompt-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
Blocked and cancelled goals can close without completion proof, but the state should record the exact blocker or cancellation reason.

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 subagentStart hook injects a boundary message without full goal state.
  • VS Code SubagentStart hook 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 goalctl require the main session sessionId and cwd from hook context.
The main session must verify subagent output before recording it as goal evidence.

Privacy

Prompt source is stored as a hash plus redacted preview. Tool history is redacted and truncated. Goal state should contain evidence summaries, not raw secrets, private documents, or full prompt payloads.