handoff-spec.json Schema
Ref: SC-HANDOFF-SESSIONCTX-01 (#3748) — ADR-0131 §4.5
Used by provision_handoff_full.py (provisioning) and
build_handoff_session_context.py (session_context injection).
Required fields
| Field | Type | Description |
|---|---|---|
task_slug |
string | Canonical task identifier. Format: <short-desc>-<YYYYMMDD>. Used in vault path, PAT name, and audit trail. Must match the GitHub issue slug. |
surfaces |
string[] | Credential surfaces to provision. Valid values: infisical, github, stripe, cloudflare, aws, heroku, apple_iap. |
operator_approval |
string | Free-text approval record. Convention: "Kristerpher YYYY-MM-DD". Required by ADR-0131 §6.2. |
linked_card |
integer | GitHub issue number this handoff is performing work for. |
Optional fields
| Field | Type | Default | Description |
|---|---|---|---|
expected_runtime |
string (ISO 8601 duration) | "PT1H" |
Expected task duration. Tasks exceeding 1h are enrolled in Velvet auto-revoke (SC-HANDOFF-VELVET-01). |
ttl_expires_at |
string (ISO 8601 UTC) | 24h from provisioning | Hard TTL. The gate blocks dispatch after this timestamp. Format: "2026-06-22T04:00:00Z". |
repo |
string | "TradeMasterAPI" |
GitHub repository name (no owner prefix). |
owner |
string | "raxx-app" |
GitHub repository owner. |
ttl_hours |
integer | 24 |
PAT TTL in hours. Maximum 24 (ADR-0131 H3). |
mcp_tools |
string[] | baseline set | Explicit MCP tool allowlist for the agent session. See tool catalogue below. |
aws_inline_policy |
object | none | IAM inline policy JSON for STS AssumeRole narrowing. |
MCP tool allowlist
The mcp_tools field lists the EXACT set of MCP tools the agent may call.
Tools NOT on the list are stripped from the session and recorded in
mcp_tools_excluded for audit clarity.
build_handoff_session_context.py enforces:
-
BASELINE_ALLOWED_TOOLS (always included, cannot be removed by spec): -
github_pr_create,github_pr_comment,github_pr_update,github_issue_comment-infisical_get_secret-git_read,git_status,git_diff,git_log -
ALWAYS_EXCLUDED_TOOLS (hard never-allow, stripped even if in spec): -
stripe_charge,stripe_refund,stripe_customer_create-apple_iap_purchase,apple_iap_refund-heroku_scale_dynos-filesystem_write_outside_worktree-infisical_set_secret,infisical_delete_secret-email_send-aws_s3_write,aws_invoke_lambda-cf_dns_update -
Spec-addable tools (must be listed in
mcp_toolsto be available): -git_commit,git_push,git_fetch— source control write ops -filesystem_read,filesystem_write— in-worktree filesystem -bash_exec_in_worktree— bounded shell access -github_pr_merge,github_issue_create,github_issue_close-heroku_config_get— read-only Heroku config -aws_s3_read,email_read
Example: PR-writing agent (issues #3629, #3206)
{
"task_slug": "iap-notif-handler-20260621",
"surfaces": ["infisical", "github"],
"expected_runtime": "PT4H",
"ttl_expires_at": "2026-06-22T04:00:00Z",
"operator_approval": "Kristerpher 2026-06-21",
"linked_card": 3629,
"repo": "TradeMasterAPI",
"owner": "raxx-app",
"ttl_hours": 4,
"mcp_tools": [
"github_pr_create",
"github_pr_comment",
"infisical_get_secret",
"git_commit",
"git_push",
"filesystem_read",
"filesystem_write",
"bash_exec_in_worktree"
]
}
The resulting session_context env will contain:
- INFISICAL_CLIENT_ID — per-task machine identity (not operator ID)
- INFISICAL_CLIENT_SECRET — per-task machine secret
- INFISICAL_SECRET_PATH — /MooseQuest/handoffs/iap-notif-handler-20260621/
- GH_TOKEN — scoped fine-grained PAT (contents:write, pull_requests:write, issues:write only)
- HANDOFF_TASK_SLUG — iap-notif-handler-20260621
- HANDOFF_EXPIRES_AT — 2026-06-22T04:00:00Z
Gate + injection flow
operator runs:
python3 scripts/agents/provision_handoff_full.py --spec handoff-spec.json
orchestrator runs (Step 0 of every dispatch):
python3 scripts/agents/build_handoff_session_context.py \
--task-slug <slug> \
--spec handoff-spec.json
# Internally calls check_handoff_gate.run_gate() — blocks if gate fails.
# Reads scoped creds from vault.
# Emits session_context JSON to stdout.
# Orchestrator injects into CCR job_config.ccr.session_context.
Revocation
python3 scripts/agents/provision_handoff_full.py --task-slug <slug> --revoke
Removes the vault path and revokes the GitHub PAT. Stripe / Cloudflare / AWS
tokens also need per-surface external revocation (see runbook:
docs/ops/runbooks/handoff-provisioning.md).