Reconciles divergence after parallel-session race: includes both Spor 1 wiring (validators inn i 4 commands + 1 agent) og Spor 2 (HANDOVER-CONTRACTS.md + PreCompact-hook).
Spor 1 wiring (re-applied etter rebase):
- /ultrabrief-local Phase 4g — brief-validator post-write
- /ultraplan-local Phase 1 — brief-validator --soft + research-validator --dir + architecture-discovery
- planning-orchestrator Phase 5.5 — plan-validator --strict erstatter 3 grep -cE-kall
- /ultraexecute-local Phase 2.3 (--validate) — plan-validator + progress-validator
- YAML-parser-utvidelse: list-of-dicts (must_contain), støtter v1.7 template-format
Spor 2 NEW:
- docs/HANDOVER-CONTRACTS.md (~310 linjer) — single source of truth for de 5 pipeline-handover-formatene m/ faste sub-headinger (Producer / Consumer / Path / Frontmatter schema / Body invariants / Validation strategy / Versioning / Failure modes)
- hooks/scripts/pre-compact-flush.mjs (NY) — fikser dokumentert P0 i docs/ultraexecute-v2-observations-from-config-audit-v4.md:
* Fyrer på PreCompact-event (CC v2.1.105+)
* Lokaliserer progress.json under .claude/projects/*/
* Sammenligner stored current_step mot git log {session_start_sha}..HEAD
* Atomisk write (tmp + rename), monoton — current_step kan aldri reduseres
* Aldri blokkerer compaction (exit 0)
- hooks/hooks.json registrerer PreCompact-hooken
Resultat: /ultraexecute-local --resume virker nå etter context compaction selv ved skill-driven execution.
Docs:
- README.md (plugin): "Quality infrastructure", "Handover contracts", "PreCompact resume integrity"
- CLAUDE.md (plugin): peker til HANDOVER-CONTRACTS.md + dokumenterer pre-compact-flush
- README.md (marketplace root): bullet-liste over Spor 2-deliverables (resolved merge-konflikt fra parallell-sesjon)
Tester: 109 grønn (ingen regresjon).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
295 lines
15 KiB
Markdown
295 lines
15 KiB
Markdown
# 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"` |
|
|
|
|
## Breaking-change protocol
|
|
|
|
1. Bump the artifact's version field.
|
|
2. Update the matching validator in `lib/validators/`.
|
|
3. Add a fixture under `tests/fixtures/` covering both old and new shapes.
|
|
4. Document the change in `MIGRATION.md` with at least an N-1 compatibility window in the validator (read both shapes; warn on old, fail only after one minor version of warning).
|
|
5. Bump the plugin version in `package.json` and `.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` |
|
|
|
|
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 message
|
|
- `FM_MISSING` → file has no frontmatter; halt
|
|
- `BRIEF_WRONG_TYPE` → file is not a brief; halt
|
|
- `BRIEF_MISSING_FIELD` → strict halt; soft-mode warning
|
|
- `BRIEF_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):**
|
|
|
|
1. Top-level structure:
|
|
- `## Implementation Plan` heading present
|
|
- One or more `### Step N: <title>` headings, numbered 1..N contiguously
|
|
- `### Step N: ` is the literal canonical form — colon + space
|
|
2. Forbidden narrative-drift heading forms (Opus 4.7 regression guard):
|
|
- `## Fase N` (Norwegian)
|
|
- `### Phase N`
|
|
- `### Stage N`
|
|
- `### Steg N` (Norwegian variant)
|
|
3. 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)
|
|
4. 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:
|
|
1. `/ultraexecute-local --validate` (alongside plan-validator)
|
|
2. `/ultraexecute-local --resume` entry (must pass `checkResumeReadiness`)
|
|
3. `pre-compact-flush` hook (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 halts
|
|
- `PROGRESS_SCHEMA_MISMATCH` → unknown schema version; resume halts
|
|
- `PROGRESS_MISSING_FIELD` → required top-level field absent; resume halts
|
|
- `PROGRESS_STEP_RANGE` → `current_step` outside `[0, total_steps]`; resume halts
|
|
- `PROGRESS_ALREADY_DONE` → `status === completed`; nothing to resume
|
|
- `PROGRESS_STEP_COUNT_MISMATCH` → warning; not a blocker
|
|
|
|
---
|
|
|
|
## 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+) |
|
|
|
|
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.
|