docs(voyage): amend HANDOVER-CONTRACTS + add 5 doc-consistency pins (v5.1)

This commit is contained in:
Kjell Tore Guttormsen 2026-05-13 21:18:42 +02:00
commit 113296d7de
2 changed files with 45 additions and 2 deletions

View file

@ -10,7 +10,7 @@ Each artifact carries an explicit version field. Schema bumps are coordinated:
| Artifact | Field | Current |
|---|---|---|
| `brief.md` | `brief_version` (frontmatter) | `2.0` |
| `brief.md` | `brief_version` (frontmatter) | `2.1` |
| `research/*.md` | (implicit; tracked via `type: trekresearch-brief`) | unversioned |
| `plan.md` | `plan_version` (frontmatter) | `1.7` |
| `progress.json` | `schema_version` (top-level) | `"1"` |
@ -67,6 +67,8 @@ Every validator exposes a CLI: `node lib/validators/<name>.mjs --json <path>` re
| `interview_turns` | number | optional | ≥ 0 | |
| `source` | string | optional | `interview \| manual` | |
| `brief_quality` | string | optional | `complete \| partial` | Set when iteration cap is hit |
| `phase_signals` | list | optional (v5.1+) | list of `{phase, effort?, model?}` entries | Per-phase effort + model commitment from Phase 3.5. Mutually exclusive with `phase_signals_partial`. |
| `phase_signals_partial` | bool | optional (v5.1+) | `true` | Force-stop record from Phase 3.5. Mutually exclusive with `phase_signals`. |
**Body invariants:** required sections (validator runs in strict mode at write-time, soft mode at read-time):
- `## Intent`
@ -84,11 +86,12 @@ Optional but standard sections: `## Non-Goals`, `## Constraints`, `## Preference
| Type discriminator | every read | `type === "trekbrief"` |
| Status enum | every read | `research_status ∈ allowed values` |
| **State machine** | every read | `research_topics > 0 && research_status === "skipped"` requires `brief_quality === "partial"` |
| **v5.1 sequencing gate** | every read | `brief_version ≥ 2.1` requires `phase_signals` (list) OR `phase_signals_partial: true` — error `BRIEF_V51_MISSING_SIGNALS` on miss. Validator-only enforcement; commands surface, don't re-enforce. |
| 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.
**Versioning:** current is `2.1` (v5.1 — adds optional `phase_signals` + `phase_signals_partial`). The forward-compat policy in `brief-validator.mjs` header still applies: unknown frontmatter keys flow through silently, so a `2.1` brief still validates against pre-v5.1 consumers. The version bump exists because v2.1 activates the **version-conditional sequencing gate** (above) — the only check in the validator that triggers on `brief_version` rather than field-presence. There are no live `1.x` briefs; remove legacy paths in next major. v5.4 may promote `phase_signals` from optional to required (breaking change → `3.0`).
**Failure modes:**
- `BRIEF_NOT_FOUND` → consumer halts with a usage message
@ -97,6 +100,12 @@ Optional but standard sections: `## Non-Goals`, `## Constraints`, `## Preference
- `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
- `BRIEF_V51_MISSING_SIGNALS` → strict halt (v5.1+ sequencing gate); soft-mode warning. Commands surface a friendly hint pointing back to `/trekbrief` (Phase 3.5).
- `BRIEF_INVALID_PHASE_SIGNALS` → strict halt; phase_signals must be a list of `{phase, effort?, model?}` entries.
- `BRIEF_INVALID_PHASE_SIGNAL_PHASE` → strict halt; phase ∉ `[research, plan, execute, review]`.
- `BRIEF_INVALID_EFFORT` → strict halt; effort ∉ `[low, standard, high]`.
- `BRIEF_INVALID_MODEL` → strict halt; model ∉ `BASE_ALLOWED_MODELS` (currently `[sonnet, opus]`).
- `BRIEF_SIGNALS_MUTUALLY_EXCLUSIVE` → strict halt; cannot set both `phase_signals` and `phase_signals_partial: true`.
---

View file

@ -551,3 +551,37 @@ test('operational files no longer reference trekrevise (v5.0.0 removal)', () =>
);
}
});
// --- v5.1 — phase_signals + brief_version 2.1 ---
test('v5.1 — templates/trekbrief-template.md declares brief_version: 2.1', () => {
const t = read('templates/trekbrief-template.md');
assert.match(t, /^brief_version: 2\.1$/m,
'trekbrief-template.md must declare brief_version: 2.1 at top of frontmatter');
});
test('v5.1 — templates/trekbrief-template.md contains phase_signals: block', () => {
const t = read('templates/trekbrief-template.md');
assert.match(t, /^phase_signals:$/m,
'trekbrief-template.md must contain a phase_signals: block in frontmatter');
});
test('v5.1 — HANDOVER-CONTRACTS.md schema row includes phase_signals + phase_signals_partial', () => {
const t = read('docs/HANDOVER-CONTRACTS.md');
assert.ok(t.includes('| `phase_signals` |'),
'HANDOVER-CONTRACTS must add a phase_signals row to the Handover 1 schema table');
assert.ok(t.includes('| `phase_signals_partial` |'),
'HANDOVER-CONTRACTS must add a phase_signals_partial row to the Handover 1 schema table');
});
test('v5.1 — voyage CLAUDE.md mentions phase_signals', () => {
const t = read('CLAUDE.md');
assert.ok(t.includes('phase_signals'),
'voyage CLAUDE.md must document phase_signals (v5.1)');
});
test('v5.1 — voyage README.md mentions phase_signals', () => {
const t = read('README.md');
assert.ok(t.includes('phase_signals'),
'voyage README.md must mention phase_signals (v5.1 "What\'s new" bullet)');
});