/agent-contract-handoff
Use for the exact input/output schema and validation rules of agent_contract_handoff
Claude Code
$ npx -y skills add metraton/gaia --skill agent-contract-handoff --agent claude-code
Codex
$ npx skills add metraton/gaia --skill agent-contract-handoff -a codex -y
Cursor
$ npx skills add metraton/gaia --skill agent-contract-handoff -a cursor -y
Gemini CLI
$ npx skills add metraton/gaia --skill agent-contract-handoff -a gemini-cli -y
GitHub Copilot
$ npx skills add metraton/gaia --skill agent-contract-handoff -a github-copilot -y
OpenCode
$ npx skills add metraton/gaia --skill agent-contract-handoff -a opencode -y
Windsurf
$ npx skills add metraton/gaia --skill agent-contract-handoff -a windsurf -y
SKILL.md
agent-contract-handoff.SKILL.mdname: agent-contract-handoff
description: Use for the exact input/output schema and validation rules of agent_contract_handoff
Agent Contract Handoff Reference
This skill owns schema, not workflow. Use `agent-protocol` to produce a turn and `agent-response` to consume it. The envelope body is JSON, never YAML.
**Two senses of "contract" -- do not collapse them.** This skill's "contract" is the *handoff* envelope: one row per turn, born at dispatch under the injected `# Your Contract` block, mutated by `gaia contract set/add/fill`, closed by `gaia contract finalize`. A *project context contract* is a different thing -- a slice of project knowledge stored per workspace (`project_context_contracts`, seeded by `seed_contract_permissions.py`). It reaches the turn as the kernel's `# Project context` block: the dispatch project's `can_read` sections (from `agent_contract_permissions`), as far as the budget allows; the rest is pulled on demand (the verb that reaches them, and the same-named sibling that reads a different table entirely, are in `agent-protocol/read-map.md`). Updates to any section are proposed via `update_contracts`. When a message says "contract" without qualifying it, ask which one it means before assuming.
Draft creation is implicit
A turn born at dispatch already has a row in `agent_contract_handoffs` AND a pre-created on-disk draft (`dispatch_binding._precreate_draft` writes `gaia.contract.drafts.initial_envelope` at birth) before it runs anything -- no prior `gaia contract init` call is required. Should the draft file be missing anyway, the first `gaia contract set/add/fill/view --draft-id <draft_id>` against that same, already-born `contract_id` materializes it (`bin/cli/contract.py::_maybe_adopt_draft`). Two conditions gate it, and both must hold: the id's agent-id prefix matches `AGENT_ID_PATTERN_TEXT`, and a row already exists for that exact `contract_id` (`gaia.store.writer.agent_contract_handoff_exists`). Neither condition mints anything -- a well-formed id with no row behind it still fails with the same "No draft found... run init" error as before this existed. `gaia contract init` remains, unchanged, the explicit path for a turn that received no injected identity at all. `gaia contract validate` never triggers this: it is documented as never mutating the draft, so it never materializes one either. See `agent-protocol` for when in the turn's cycle this first write happens.
Input context
The injected input is the dispatch kernel: `# Your Contract` (identity, goal, role/surface, `project` with its declared `workflow` line when the project declares one, `can_read`/`can_write`, and -- on a plan-task-bound turn -- the acceptance gates), `# Scope memory` (an index of the preferences, facts and lessons the dispatch's workspace, group and project hold, each line a title and its `ref=memory:...`; never the user's rows), `# Standing notes` (the project's live anchors, one line each, when it has any), `# Environment` (one line: OS, the gaia CLI path, the tools on PATH including the user's own executables), `# Project context` and `# Your CLI`. `# Project context` carries the dispatch project's `can_read` sections whole when they fit, else one `get-contract` pointer per section, else a single count line; it is absent when the row's project does not resolve, none of its `can_read` sections is stored, or the read fails (`hooks/modules/context/kernel_builder.py::build_project_context_block`). No surface routing arrives. Pull what the block only points at on demand before querying anything wider (`agent-protocol/read-map.md`). `update_contracts` may propose any section: `can_write` does not gate a proposal, the orchestrator decides (`hooks/modules/context/context_writer.py`), save a drift correction whose every guard holds (see the `drift` paragraph below).
Minimal increment
Every draft, including a mid-turn checkpoint, contains:
- `agent_status.agent_state`, `agent_status.agent_id`, `pending_steps`, and a
non-empty `next_action`;
- `evidence_report` with all seven keys: `patterns_checked`, `files_checked`,
`commands_run`, `key_outputs`, `verbatim_outputs`, `cross_layer_impacts`, and `open_gaps` (empty lists are valid). Two lanes rebuild `commands_run` and `files_checked`: every finalize rewrites them from the harness tool record, keeping the agent's own extra entries marked `[agent annotation] ` (`gaia/contract/tool_record.py::stamp_harness_fields`); and on Claude Code SubagentStop fills a list still empty with the turn's Bash commands and Read paths, each prefixed `[recovered from transcript] ` (`hooks/modules/agents/evidence_recovery.py::RECOVERED_MARK`): the bare call, never what it showed;
- `consolidation_report` and `approval_request`, normally `null`.
`agent_id` matches `^a[0-9a-f]{16,}$`. The canonical states are `IN_PROGRESS`, `APPROVAL_REQUEST`, `BLOCKED`, `NEEDS_INPUT`, `NEEDS_VERIFICATION`, and `COMPLETE`. Only `COMPLETE` is terminal.
Which fields are lists, and what a second write does
Eight fields hold the lists you write every turn: the seven `evidence_report` keys above -- `patterns_checked`, `files_checked`, `commands_run`, `key_outputs`, `verbatim_outputs`, `cross_layer_impacts`, `open_gaps` -- plus `agent_status.pending_steps`. They are not the only lists in the envelope: `update_contracts` is list-valued too (`ADVISORY_UNTYPED_FIELDS` in `gaia/contract/validator.py` -- allowlisted, and deliberately left untyped so a malformed one never blocks a close). Nothing about them is unguarded, which is the next thing a reader asks once they know they exist: the overwrite screen is generic, firing on any populated list a patch would displace, and it starts at the envelope root where these three sit. They are seeded absent rather than as `[]`, so a first write to one discards nothing.
The three write verbs differ on every one of those lists, and the difference is not cosmetic -- it decides whether your earlier evidenc
Read more
name: agent-contract-handoff description: Use for the exact input/output schema and validation rules of agent_contract_handoff
Agent Contract Handoff Reference
This skill owns schema, not workflow. Use `agent-protocol` to produce a turn and `agent-response` to consume it. The envelope body is JSON, never YAML.
**Two senses of "contract" -- do not collapse them.** This skill's "contract" is the *handoff* envelope: one row per turn, born at dispatch under the injected `# Your Contract` block, mutated by `gaia contract set/add/fill`, closed by `gaia contract finalize`. A *project context contract* is a different thing -- a slice of project knowledge stored per workspace (`project_context_contracts`, seeded by `seed_contract_permissions.py`). It reaches the turn as the kernel's `# Project context` block: the dispatch project's `can_read` sections (from `agent_contract_permissions`), as far as the budget allows; the rest is pulled on demand (the verb that reaches them, and the same-named sibling that reads a different table entirely, are in `agent-protocol/read-map.md`). Updates to any section are proposed via `update_contracts`. When a message says "contract" without qualifying it, ask which one it means before assuming.
Draft creation is implicit
A turn born at dispatch already has a row in `agent_contract_handoffs` AND a pre-created on-disk draft (`dispatch_binding._precreate_draft` writes `gaia.contract.drafts.initial_envelope` at birth) before it runs anything -- no prior `gaia contract init` call is required. Should the draft file be missing anyway, the first `gaia contract set/add/fill/view --draft-id <draft_id>` against that same, already-born `contract_id` materializes it (`bin/cli/contract.py::_maybe_adopt_draft`). Two conditions gate it, and both must hold: the id's agent-id prefix matches `AGENT_ID_PATTERN_TEXT`, and a row already exists for that exact `contract_id` (`gaia.store.writer.agent_contract_handoff_exists`). Neither condition mints anything -- a well-formed id with no row behind it still fails with the same "No draft found... run init" error as before this existed. `gaia contract init` remains, unchanged, the explicit path for a turn that received no injected identity at all. `gaia contract validate` never triggers this: it is documented as never mutating the draft, so it never materializes one either. See `agent-protocol` for when in the turn's cycle this first write happens.
Input context
The injected input is the dispatch kernel: `# Your Contract` (identity, goal, role/surface, `project` with its declared `workflow` line when the project declares one, `can_read`/`can_write`, and -- on a plan-task-bound turn -- the acceptance gates), `# Scope memory` (an index of the preferences, facts and lessons the dispatch's workspace, group and project hold, each line a title and its `ref=memory:...`; never the user's rows), `# Standing notes` (the project's live anchors, one line each, when it has any), `# Environment` (one line: OS, the gaia CLI path, the tools on PATH including the user's own executables), `# Project context` and `# Your CLI`. `# Project context` carries the dispatch project's `can_read` sections whole when they fit, else one `get-contract` pointer per section, else a single count line; it is absent when the row's project does not resolve, none of its `can_read` sections is stored, or the read fails (`hooks/modules/context/kernel_builder.py::build_project_context_block`). No surface routing arrives. Pull what the block only points at on demand before querying anything wider (`agent-protocol/read-map.md`). `update_contracts` may propose any section: `can_write` does not gate a proposal, the orchestrator decides (`hooks/modules/context/context_writer.py`), save a drift correction whose every guard holds (see the `drift` paragraph below).
Minimal increment
Every draft, including a mid-turn checkpoint, contains:
- `agent_status.agent_state`, `agent_status.agent_id`, `pending_steps`, and a
non-empty `next_action`;
- `evidence_report` with all seven keys: `patterns_checked`, `files_checked`,
`commands_run`, `key_outputs`, `verbatim_outputs`, `cross_layer_impacts`, and `open_gaps` (empty lists are valid). Two lanes rebuild `commands_run` and `files_checked`: every finalize rewrites them from the harness tool record, keeping the agent's own extra entries marked `[agent annotation] ` (`gaia/contract/tool_record.py::stamp_harness_fields`); and on Claude Code SubagentStop fills a list still empty with the turn's Bash commands and Read paths, each prefixed `[recovered from transcript] ` (`hooks/modules/agents/evidence_recovery.py::RECOVERED_MARK`): the bare call, never what it showed;
- `consolidation_report` and `approval_request`, normally `null`.
`agent_id` matches `^a[0-9a-f]{16,}$`. The canonical states are `IN_PROGRESS`, `APPROVAL_REQUEST`, `BLOCKED`, `NEEDS_INPUT`, `NEEDS_VERIFICATION`, and `COMPLETE`. Only `COMPLETE` is terminal.
Which fields are lists, and what a second write does
Eight fields hold the lists you write every turn: the seven `evidence_report` keys above -- `patterns_checked`, `files_checked`, `commands_run`, `key_outputs`, `verbatim_outputs`, `cross_layer_impacts`, `open_gaps` -- plus `agent_status.pending_steps`. They are not the only lists in the envelope: `update_contracts` is list-valued too (`ADVISORY_UNTYPED_FIELDS` in `gaia/contract/validator.py` -- allowlisted, and deliberately left untyped so a malformed one never blocks a close). Nothing about them is unguarded, which is the next thing a reader asks once they know they exist: the overwrite screen is generic, firing on any populated list a patch would displace, and it starts at the envelope root where these three sit. They are seeded absent rather than as `[]`, so a first write to one discards nothing.
The three write verbs differ on every one of those lists, and the difference is not cosmetic -- it decides whether your earlier evidenc
Generative AI Interface for Agents. Specialist agents for Claude Code and OpenCode, with a memory that outlives the session and a consent gate on everything that changes state.
Other skills on gaia.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about agent-contract-handoff.
- Which coding agents can use agent-contract-handoff?
- agent-contract-handoff installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

