{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://taskfleet.dev/schemas/plan.v3.schema.json",
"title": "taskfleet plan.json (v3)",
"description": "The interface contract the spec-node writes and the supervisor + orchestrator read. Immutable per revision, versioned, provenance-bearing. See issues/code-pipeline/plan-schema.md and design.md §4/§7/§13. v3 makes the baseline provenance fields (commit_oid, toolchain, enumerated_targets_hash) structurally REQUIRED — a plan missing them is rejected, not silently defaulted. This schema is the checked-in machine-readable artifact; the operative source of truth for readers/writers is the taskfleet_core::plan Rust validator, and a drift-guard test keeps the two in agreement.",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"plan_rev",
"intent_rev",
"feature",
"baseline",
"acceptance",
"chunks"
],
"properties": {
"schema_version": {
"description": "Major schema version. Readers reject unsupported majors (do NOT silently ignore).",
"type": "integer",
"const": 3
},
"plan_rev": {
"description": "Immutable revision of this plan. A fix or re-spec writes plan.v(N+1).json; each chunk attempt records the plan_rev it consumed.",
"type": "integer",
"minimum": 0
},
"intent_rev": {
"description": "The orchestrator-owned intent.md revision this plan targets. Intent is referenced, not embedded.",
"type": "integer",
"minimum": 0
},
"feature": {
"type": "object",
"additionalProperties": false,
"required": ["slug", "source_branch", "integration_branch"],
"properties": {
"slug": { "type": "string", "minLength": 1 },
"source_branch": { "type": "string", "minLength": 1 },
"integration_branch": { "type": "string", "minLength": 1 }
}
},
"baseline": {
"description": "Snapshot at feat/<slug> fork; verify + the deterministic floor diff against it. Supervisor-owned.",
"type": "object",
"additionalProperties": false,
"required": ["ref", "commit_oid", "toolchain", "test_passlist_hash", "clippy_warnings_hash", "enumerated_targets_hash"],
"properties": {
"ref": { "type": "string", "minLength": 1 },
"commit_oid": { "type": "string", "minLength": 1, "description": "Ref resolved to an immutable commit OID at capture time (floor provenance; required as of v3)." },
"toolchain": { "type": "string", "minLength": 1, "description": "rustc -V fingerprint the snapshot was captured with (floor provenance; required as of v3)." },
"test_passlist_hash": { "type": "string", "minLength": 1 },
"clippy_warnings_hash": { "type": "string", "minLength": 1 },
"enumerated_targets_hash": { "type": "string", "minLength": 1, "description": "Hash of the enumerated (package, target_kind, target) test-target set (floor F7 superset check; required as of v3)." }
}
},
"acceptance": {
"description": "Whole-feature intent gate. Each item is an executable check or an LLM-judged assertion; at least one must be a check.",
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/acceptance_item" }
},
"chunks": {
"description": "The DAG of implementation chunks. deps form an acyclic dependency graph.",
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/chunk" }
}
},
"$defs": {
"acceptance_item": {
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": ["kind", "desc", "run"],
"properties": {
"kind": { "const": "check" },
"desc": { "type": "string", "minLength": 1 },
"run": { "type": "string", "minLength": 1 },
"cwd": {
"description": "Optional working directory to run `run` in; absent = worktree root. Must be a safe repo-relative path (no absolute paths, `~`, `..`, backslash, `:`, control chars, or a bare `.`) — enforced by the taskfleet_core::plan validator, as for files_touched.",
"type": "string",
"minLength": 1
},
"expect_exit": {
"description": "Optional expected exit code; the check passes iff the command exits with it. Absent = 0. A shell exit status is 0..=255.",
"type": "integer",
"minimum": 0,
"maximum": 255
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["kind", "desc"],
"properties": {
"kind": { "const": "assertion" },
"desc": { "type": "string", "minLength": 1 }
}
}
]
},
"check": {
"description": "An executable check: the general goal (desc) plus a flexible shell command (run), with optional cwd/expect_exit precision. Precision is available, not forced.",
"type": "object",
"additionalProperties": false,
"required": ["desc", "run"],
"properties": {
"desc": {
"description": "The general goal of the check — what it verifies. Always present.",
"type": "string",
"minLength": 1
},
"run": {
"description": "A flexible shell command executed via `sh -c`.",
"type": "string",
"minLength": 1
},
"cwd": {
"description": "Optional working directory to run `run` in; absent = worktree root. Must be a safe repo-relative path (no absolute paths, `~`, `..`, backslash, `:`, control chars, or a bare `.`) — enforced by the taskfleet_core::plan validator, as for files_touched.",
"type": "string",
"minLength": 1
},
"expect_exit": {
"description": "Optional expected exit code; the check passes iff the command exits with it. Absent = 0. A shell exit status is 0..=255.",
"type": "integer",
"minimum": 0,
"maximum": 255
}
}
},
"chunk": {
"type": "object",
"additionalProperties": false,
"required": ["id", "title", "tier", "brief", "files_touched", "checks"],
"properties": {
"id": {
"description": "Unique within the plan; referenced by other chunks' deps.",
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$"
},
"title": { "type": "string", "minLength": 1 },
"deps": {
"description": "Ids of chunks this one depends on. Every entry must reference an existing chunk; entries must be unique; the graph must be acyclic (acyclicity is enforced by the Rust validator, not expressible in JSON Schema).",
"type": "array",
"items": { "type": "string" },
"uniqueItems": true,
"default": []
},
"tier": {
"description": "Starting model tier hint; the orchestrator owns promotion.",
"type": "string",
"enum": ["code", "mid", "high"]
},
"brief": {
"description": "Turnkey, self-contained implementation brief.",
"type": "string",
"minLength": 1
},
"files_touched": {
"description": "Merge-time constraint (not just a hint): repo-relative paths, no traversal.",
"type": "array",
"minItems": 1,
"items": { "type": "string", "minLength": 1 }
},
"checks": {
"description": "Executable per-chunk checks; at least one required.",
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/check" }
},
"assertions": {
"description": "LLM-judged criteria, additive above the deterministic floor. Each must be a non-empty string (the Rust validator additionally rejects whitespace-only strings).",
"type": "array",
"items": { "type": "string", "minLength": 1 },
"default": []
},
"requires_tests": {
"description": "If true, the supervisor blocks a merge that added/modified no tests.",
"type": "boolean",
"default": false
}
}
}
}
}