Closes the iteration loop: review.md → plan via source_findings audit trail. Adds versioning row, validator-map entry, full Handover 6 section, and stability summary row mirroring the shape of Handovers 1-5. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
20 KiB
Handover Contracts (ultra-suite local pipeline)
This document is the single source of truth for the file formats that pass between the four commands of the ultraplan-local pipeline. When you fork the plugin or extend a stage, the contracts below tell you what every producer must write and what every consumer is allowed to assume.
For each handover, the same headings appear in the same order: Producer, Consumer, Path conventions, Frontmatter schema, Body invariants, Validation strategy, Versioning, Failure modes.
Versioning policy
Each artifact carries an explicit version field. Schema bumps are coordinated:
| Artifact | Field | Current |
|---|---|---|
brief.md |
brief_version (frontmatter) |
2.0 |
research/*.md |
(implicit; tracked via type: ultraresearch-brief) |
unversioned |
plan.md |
plan_version (frontmatter) |
1.7 |
progress.json |
schema_version (top-level) |
"1" |
review.md |
review_version (frontmatter) |
1.0 |
Breaking-change protocol
- Bump the artifact's version field.
- Update the matching validator in
lib/validators/. - Add a fixture under
tests/fixtures/covering both old and new shapes. - Document the change in
MIGRATION.mdwith at least an N-1 compatibility window in the validator (read both shapes; warn on old, fail only after one minor version of warning). - Bump the plugin version in
package.jsonand.claude-plugin/plugin.json.
Validator → handover map
| Handover | Validator |
|---|---|
| 1. brief → research | lib/validators/brief-validator.mjs |
| 2. research → plan | lib/validators/research-validator.mjs |
| 3. architecture → plan | lib/validators/architecture-discovery.mjs |
| 4. plan → execute | lib/validators/plan-validator.mjs |
| 5. progress.json (resume) | lib/validators/progress-validator.mjs |
| 6. review → plan | lib/validators/review-validator.mjs |
Every validator exposes a CLI: node lib/validators/<name>.mjs --json <path> returns {valid, errors[], warnings[], parsed}. Errors and warnings have stable code fields for downstream tooling.
Handover 1 — brief.md → research/
Producer: /ultrabrief-local Phase 4g (after brief-reviewer stop-gate passes or iteration cap is hit).
Consumer: /ultraresearch-local Phase 1 (mode parse + brief validation).
Path conventions:
- Project-dir mode (recommended):
.claude/projects/{YYYY-MM-DD}-{slug}/brief.md. - Legacy / loose mode: any path passed via
--brief <file>.
Frontmatter schema:
| Field | Type | Required | Allowed values | Notes |
|---|---|---|---|---|
type |
string | yes | ultrabrief |
Hard-coded discriminator |
brief_version |
string | yes | "2.0" (current) |
Bump on schema change |
created |
date | yes | YYYY-MM-DD | |
task |
string | yes | one-line description | |
slug |
string | yes | URL-safe slug | Used in project_dir |
project_dir |
string | yes | .claude/projects/{date}-{slug}/ |
|
research_topics |
number | yes | ≥ 0 | |
research_status |
string | yes | pending | in_progress | complete | skipped |
State machine — see below |
auto_research |
bool | optional | true | false |
|
interview_turns |
number | optional | ≥ 0 | |
source |
string | optional | interview | manual |
|
brief_quality |
string | optional | complete | partial |
Set when iteration cap is hit |
Body invariants: required sections (validator runs in strict mode at write-time, soft mode at read-time):
## Intent## Goal## Success Criteria
Optional but standard sections: ## Non-Goals, ## Constraints, ## Preferences, ## Non-Functional Requirements, ## Research Plan.
Validation strategy:
| Layer | When | What |
|---|---|---|
| Frontmatter parse | every read | YAML subset; reject nested dicts |
| Required fields | every read | All BRIEF_REQUIRED_FRONTMATTER present |
| Type discriminator | every read | type === "ultrabrief" |
| Status enum | every read | research_status ∈ allowed values |
| State machine | every read | research_topics > 0 && research_status === "skipped" requires brief_quality === "partial" |
| Body sections | strict only | All BRIEF_BODY_SECTIONS present |
State machine detail: a brief that says it has research topics but skipped them must explicitly admit it (via brief_quality: partial). This is the most common failure mode the validator catches.
Versioning: current is 2.0. There are no live 1.x briefs; remove legacy paths in next major.
Failure modes:
BRIEF_NOT_FOUND→ consumer halts with a usage messageFM_MISSING→ file has no frontmatter; haltBRIEF_WRONG_TYPE→ file is not a brief; haltBRIEF_MISSING_FIELD→ strict halt; soft-mode warningBRIEF_STATE_INCOHERENT→ strict halt; soft-mode warning (incoherence will haunt downstream agents)BRIEF_MISSING_SECTION→ strict halt; soft-mode warning
Handover 2 — research/*.md → plan
Producer: /ultraresearch-local Phase 7 (synthesis + brief writer).
Consumer: /ultraplan-local Phase 1 (project-dir auto-discovery) + planning-orchestrator (consumes findings as context).
Path conventions:
- Project-dir mode:
.claude/projects/{YYYY-MM-DD}-{slug}/research/{NN}-{topic-slug}.md(sorted by filename). - Legacy:
.claude/research/ultraresearch-{date}-{slug}.md.
Frontmatter schema:
| Field | Type | Required | Allowed values |
|---|---|---|---|
type |
string | yes | ultraresearch-brief |
created |
date | yes | YYYY-MM-DD |
question |
string | yes | the research question |
confidence |
number | optional | [0.0, 1.0] — strongly recommended |
dimensions |
number | optional | ≥ 1 |
mcp_servers_used |
list | optional | server names |
local_agents_used |
list | optional | agent names |
external_agents_used |
list | optional | agent names |
Missing confidence is a warning, not an error — but downstream planning has no signal to weight findings.
Body invariants: required sections (strict mode):
## Executive Summary## Dimensions
Optional: ## Local Context, ## External Knowledge, ## Triangulation, ## Sources, ## Recommendations.
Validation strategy: schema parse + body-section check. Per-file by validateResearch; whole-directory by validateResearchDir. Anchoring back to brief topics is currently best-effort, not enforced (planned for a future minor).
Versioning: unversioned — research briefs are write-once read-once; no migration concern. If schema changes, change type discriminator or add research_brief_version.
Failure modes: all same shape as brief (RESEARCH_* codes). Default soft mode in plan Phase 1 — research drift does not block planning, but warnings surface in the user-visible summary.
Handover 3 — architecture/ → plan (EXTERNAL CONTRACT)
This is the only handover where the producer is in a different plugin. The architecture/overview.md (and optional gaps.md) are produced by /ultra-cc-architect-local from the separate ultra-cc-architect plugin (v0.1.0+). When that plugin is not installed, this handover is absent — and that is fine.
Producer: /ultra-cc-architect-local (external plugin).
Consumer: /ultraplan-local Phase 1 (architecture-discovery) + planning-orchestrator Phase 7 (cross-reference architecture-note as priors during synthesis).
Path conventions:
- Canonical:
{project_dir}/architecture/overview.md - Optional:
{project_dir}/architecture/gaps.md - Tolerated alternatives (with warning):
architecture-overview.md,overview.markdown,README.md
Frontmatter schema: unenforced. This is the external contract — ultraplan-local does not validate the format. We sniff only the first H1 heading.
Body invariants: unenforced. We never read body content beyond the first heading.
Validation strategy: drift-WARN, never drift-FAIL.
| Detection | Result |
|---|---|
| File at canonical path | found: true, no warnings |
| File at known alternative path | found: true, warning ARCH_NON_CANONICAL_OVERVIEW |
Loose *.md files in architecture/ not in known set |
warning ARCH_LOOSE_FILES |
No architecture/ dir |
found: false, no warnings |
The validator (lib/validators/architecture-discovery.mjs) is intentionally minimal. It is unit-tested to assert it does NOT read body content beyond the first heading — guarding against scope creep into the sister plugin's territory.
Versioning: the producer (ultra-cc-architect) owns its schema. We do not version this handover from our side.
Failure modes: none. Discovery always succeeds (returns found: false if absent). The handover is additive.
Handover 4 — plan.md → execute
Producer: planning-orchestrator Phase 5 (plan synthesis) + Phase 5.5 (schema self-check via plan-validator --strict).
Consumer: /ultraexecute-local Phase 2 (plan parsing) + --validate mode.
Path conventions:
- Project-dir:
{project_dir}/plan.md - Legacy:
.claude/plans/ultraplan-{date}-{slug}.md
Frontmatter schema:
| Field | Type | Required | Allowed |
|---|---|---|---|
plan_version |
string | yes | "1.7" (current) |
Body invariants (strict, v1.7):
- Top-level structure:
## Implementation Planheading present- One or more
### Step N: <title>headings, numbered 1..N contiguously ### Step N:is the literal canonical form — colon + space
- Forbidden narrative-drift heading forms (Opus 4.7 regression guard):
## Fase N(Norwegian)### Phase N### Stage N### Steg N(Norwegian variant)
- Per-step Manifest block — required for every step:
- Indented fenced YAML:
```yaml\n manifest:\n ...\n ``` - Required keys:
expected_paths(list),min_file_count(number),commit_message_pattern(string compilable to RegExp),bash_syntax_check(list),forbidden_paths(list),must_contain(list of{path, pattern}dicts or empty list)
- Indented fenced YAML:
- Step count == manifest count
Validation strategy:
The strongest validation in the entire pipeline. Phase 5.5 (planning-orchestrator) must run plan-validator --strict before handing the plan to plan-critic. --validate mode of /ultraexecute-local runs the same check + progress-validator.
| Code | Meaning | Recovery |
|---|---|---|
PLAN_FORBIDDEN_HEADING |
Narrative drift detected | Rewrite using literal Phase 5 template |
PLAN_NO_STEPS |
No ### Step N: headings |
Plan is empty; restart |
PLAN_STEP_NUMBERING |
Steps skip a number | Renumber sequentially |
PLAN_MANIFEST_COUNT_MISMATCH |
Some step lost its manifest | Add missing manifest |
MANIFEST_MISSING |
Specific step has no manifest YAML | Add Manifest block |
MANIFEST_MISSING_KEY |
Manifest is missing a required key | Add the key |
MANIFEST_PATTERN_INVALID |
commit_message_pattern does not compile |
Check escaping (\\( not \( in YAML double-quoted strings) |
PLAN_VERSION_MISMATCH |
Older plan_version |
Warning only; planner should bump |
Versioning: v1.7 has been stable since v1.8.0 of the plugin (when literal-template + Phase 5.5 self-check were added to fix Opus 4.7 schema drift). v1.6 → v1.7 added the Manifest block (mandatory). Before bumping to v1.8, write the new validator branch + fixtures first.
Failure modes: strict mode is the default for both producer and consumer. There is no soft mode here — a malformed plan is a hard failure for execute.
Handover 5 — progress.json (resume contract)
Producer: /ultraexecute-local per-step (after Verify + Manifest audit + Checkpoint).
Consumer: /ultraexecute-local --resume (re-entry) + pre-compact-flush hook (drift detection before context compaction).
Path conventions:
- Project-dir:
{project_dir}/progress.json - Legacy:
{plan-dir}/.ultraexecute-progress-{slug}.json
Schema (top-level):
| Field | Type | Required | Notes |
|---|---|---|---|
schema_version |
string | yes | "1" (current) |
plan |
string | yes | Path to the plan being executed |
plan_type |
string | optional | plan | session-spec |
plan_version |
string | yes | Mirrors plan's frontmatter |
started_at |
ISO string | yes | |
updated_at |
ISO string | yes | Bumped on every write |
completed_at |
ISO string | optional | Set when status flips to completed |
mode |
string | yes | execute | dry-run | validate |
total_steps |
number | yes | |
current_step |
number | yes | 0..total_steps |
status |
string | yes | pending | in_progress | completed | failed | partial |
session_start_sha |
string | optional | git sha at execute start |
session_end_sha |
string | optional | git sha at execute end |
steps |
object | yes | Map of step number → step record |
Per-step record:
| Field | Type | Notes |
|---|---|---|
status |
completed | in_progress | failed | pending | deferred | skipped |
|
attempts |
number | 1..N |
error |
string | null | |
completed_at |
ISO string | null | |
commit |
string | null | git sha after Checkpoint |
manifest_audit |
string | pass | fail | pass-with-note | n/a |
note |
string | optional human-readable annotation |
Validation strategy: progress-validator.mjs runs at:
/ultraexecute-local --validate(alongside plan-validator)/ultraexecute-local --resumeentry (must passcheckResumeReadiness)pre-compact-flushhook (drift check before compaction; never blocks)
Drift detection: the pre-compact-flush hook compares progress.steps[N].commit against git log --oneline {session_start_sha}..HEAD. If git reality has progressed past the recorded current_step, the hook updates progress.json atomically (tmp + rename, monotonic only) before allowing compaction. This guards against the documented P0 drift in docs/ultraexecute-v2-observations-from-config-audit-v4.md.
Versioning: schema_version: "1" is current. Future bump (e.g. "2") should add a backward-compat read path that downgrades unknown fields to warnings.
Failure modes:
PROGRESS_PARSE_ERROR→ JSON corruption; resume haltsPROGRESS_SCHEMA_MISMATCH→ unknown schema version; resume haltsPROGRESS_MISSING_FIELD→ required top-level field absent; resume haltsPROGRESS_STEP_RANGE→current_stepoutside[0, total_steps]; resume haltsPROGRESS_ALREADY_DONE→status === completed; nothing to resumePROGRESS_STEP_COUNT_MISMATCH→ warning; not a blocker
Handover 6 — review.md → plan
Handover 6 closes the iteration loop. Where Handovers 1–4 flow forward (brief → research → plan → execute) and Handover 5 makes execute resumable, Handover 6 routes review findings back into planning so a remediation plan can be produced with full traceability via source_findings.
Producer: /ultrareview-local Phase 7 (write review.md after coordinator dedup + verdict).
Consumer: /ultraplan-local Phase 1 when --brief review.md is supplied and the consumer detects type: ultrareview in frontmatter. The plan command branches into a remediation-plan path: BLOCKER + MAJOR findings become plan goals, the produced plan.md carries a source_findings: [<id>, ...] frontmatter list as the audit trail back to the consumed findings. MINOR + SUGGESTION are skipped for v1.0 plan-input.
Path conventions:
- Project-dir mode (recommended):
{project_dir}/review.md(one per review iteration; subsequent runs overwrite atomically). - Multiple review iterations are allowed in the same project; each overwrites the canonical path. Audit trail lives in git history.
Frontmatter schema:
| Field | Type | Required | Allowed values | Notes |
|---|---|---|---|---|
type |
string | yes | ultrareview |
Hard-coded discriminator |
review_version |
string | yes | "1.0" (current) |
Bump on schema change |
task |
string | yes | one-line description | Mirrors brief task |
slug |
string | yes | URL-safe slug | Used in project_dir |
project_dir |
string | yes | .claude/projects/{date}-{slug}/ |
|
brief_path |
string | yes | path to consumed brief.md |
Audit trail back to brief |
scope_sha_end |
string | yes | git sha of HEAD at review time | Defines "after" boundary |
reviewed_files_count |
number | yes | ≥ 0 | From triage gate Coverage |
findings |
list | yes | block-style YAML list of 40-char hex IDs | Flat array; full objects in body |
created |
date | optional | YYYY-MM-DD | |
scope_sha_start |
string | optional | git sha at review start | null if mtime fallback used |
verdict |
string | optional | BLOCK | WARN | ALLOW |
Coordinator output |
findings: is a flat array of finding-IDs (40-char hex from lib/parsers/finding-id.mjs). The full finding objects (severity, location, message, evidence, fix) live in the body as ### <id> subsections under per-severity ## Findings (...) headings — same pattern as brief-reviewer to avoid frontmatter-parser fragility on lists of dicts.
Body invariants: required sections (validator runs in strict mode at write-time, soft mode at read-time):
## Executive Summary## Coverage## Remediation Summary
Optional but standard sections: ## Findings (BLOCKER), ## Findings (MAJOR), ## Findings (MINOR), ## Findings (SUGGESTION). The ## Coverage section enumerates which files were deep-reviewed, summary-only, or skipped (with reason) — this is how the triage gate stays honest and avoids Copilot-style silent skips.
Validation strategy:
| Layer | When | What |
|---|---|---|
| Frontmatter parse | every read | YAML subset; reject nested dicts |
| Required fields | every read | All REVIEW_REQUIRED_FRONTMATTER present |
| Type discriminator | every read | type === "ultrareview" |
| Findings shape | every read | Array of strings, each matching ^[0-9a-f]{40}$ |
| Body sections | strict only | Executive Summary, Coverage, Remediation Summary |
| Version format | every read | review_version matches N.M; warning otherwise |
The validator (lib/validators/review-validator.mjs) exposes the same CLI as the others: node lib/validators/review-validator.mjs --json <review.md>. Strict mode is the default; --soft downgrades section-missing errors to warnings. /ultrareview-local Phase 8 runs --strict. /ultraplan-local Phase 1 (when consuming --brief review.md) runs --soft so a partially-valid review can still seed a plan.
Versioning: current is 1.0. There are no live 0.x reviews. Future schema changes follow the breaking-change protocol above.
Failure modes:
REVIEW_NOT_FOUND→ consumer halts with usage messageREVIEW_READ_ERROR→ I/O failure; haltFM_MISSING→ file has no frontmatter; haltREVIEW_WRONG_TYPE→type !== "ultrareview"; haltREVIEW_MISSING_FIELD→ strict halt; soft-mode warningREVIEW_BAD_FINDINGS_TYPE→findingsis not an array; halt (covers the YAML flow-style trap)REVIEW_BAD_FINDING_ID→ an ID is not 40-char hex; haltREVIEW_MISSING_SECTION→ strict halt; soft-mode warningREVIEW_VERSION_FORMAT→ warning only; review_version not inN.Mform
Stability summary
| Handover | Validation strength | Owner | Risk |
|---|---|---|---|
| 1. brief → research | strict at write, soft at read | this plugin | low |
| 2. research → plan | soft, drift-warn | this plugin | low |
| 3. architecture → plan | discovery-only, drift-WARN | external (ultra-cc-architect) | low — by design we tolerate drift |
| 4. plan → execute | strict, both ends | this plugin | medium — Opus 4.7 narrative drift requires constant vigilance |
| 5. progress.json | shape + resume readiness | this plugin | medium — drift during compaction handled by pre-compact-flush hook (CC v2.1.105+) |
| 6. review → plan | strict at write, soft at read | this plugin | low — additive feedback loop; consumer falls back gracefully when source_findings is absent |
When extending the plugin or adding a new pipeline stage, follow the same pattern: produce an artifact with a versioned frontmatter (or schema_version for JSON), write a validator under lib/validators/, add fixtures under tests/fixtures/, and add an entry to this document.