docs(voyage): S16 — stale counts/strings truth-pass + 4 doc-consistency pins
Truth-pass over the README/CLAUDE/contract docs the S14 audit flagged as
stale (findings #2/#6/#7/#8/#9). Re-grepped every count against the actual
files first — line numbers in the audit are S14 snapshots that had already
rotted. docs + tests only; lib/ runtime and all behaviour untouched.
Corrections:
- README architecture block: 23 → 24 agents; 5 → 7 hooks (add the two it
omitted: post-compact-flush, otel-export); dropped the rotting test-count
("109" — already wrong twice, 109→683→686) for "comprehensive node:test
suite" so the number can never drift again (operator choice).
- brief-reviewer five → six dimensions (+ memory alignment); plan-critic
9 → 10 dimensions (README ×2 + CLAUDE.md), matching the agent's 10 numbered
dims and the v5.5 flagship 6th brief dimension.
- phantom "v5.4" contract-freeze references → v5.5.0 (CLAUDE.md + HANDOVER-
CONTRACTS ×3). v5.2–5.4 never shipped; the formalization landed with 2.2 in
v5.5.0 (CHANGELOG:9), so line 46 got a prose tweak (one release both
established 2.1 and evolved it to 2.2). CHANGELOG history left intact — it
correctly explains the phantom.
- trekplan Phase-8 inline-sealing rationale "Opus 4.7" → 4.8.
- bonus (operator-approved): end-session helper name trekplan-end-session →
trekendsession (the actual command).
New doc-consistency pins (TDD red→green): README agent-count and hook-count
(file counts), plan-critic dim-count (computed from ### N. headers),
brief-reviewer dim-count (cross-file, excludes agent-list lines that
co-mention plan-critic's count). Updated the existing pin that guarded the
phantom "v5.4 froze 2.1" string to the corrected wording + a !/v5.4/ guard.
Version-string pins (Opus 4.8 / v5.5.0) deliberately omitted as S19 prose-bloat.
Tests 686 → 690 (688 pass / 2 skip / 0 fail). claude plugin validate passes
(1 accepted CLAUDE.md-at-root warning).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
This commit is contained in:
parent
7a5749ddcc
commit
6ba58fda9b
5 changed files with 78 additions and 15 deletions
|
|
@ -6,7 +6,7 @@ Voyage — a contract-driven Claude Code pipeline: brief, research, plan, execut
|
||||||
|
|
||||||
> **v3.0.0 — architect step extracted from this plugin.** The plan command still auto-discovers `architecture/overview.md` if present, so any compatible producer (architect plugin no longer publicly distributed; the architecture/overview.md slot remains available for any compatible producer) plugs into the same slot. See [CHANGELOG.md](CHANGELOG.md) for migration history.
|
> **v3.0.0 — architect step extracted from this plugin.** The plan command still auto-discovers `architecture/overview.md` if present, so any compatible producer (architect plugin no longer publicly distributed; the architecture/overview.md slot remains available for any compatible producer) plugs into the same slot. See [CHANGELOG.md](CHANGELOG.md) for migration history.
|
||||||
|
|
||||||
> **Trinity context (2026-05-13, informational).** Voyage is Tier 1 (per-task) of a three-tier architecture in active design under the author's private marketplace: Tier 2 `app-creator` (per-app — "what does the app need, what's the next brief?") produces briefs Voyage consumes; Tier 3 `app-factory` (per-portfolio — "which app needs me now?") aggregates state across multiple app-creator instances. Both are pre-implementation and will ship to Forgejo when ready. **Asymmetry is a hard invariant:** Voyage stays unaware of Tier 2/3. Handover 1 (brief format) is the only integration point — any compatible producer can feed Voyage, app-creator is not privileged. Brief-schema changes are therefore breaking changes for downstream consumers, formalized as a public contract in v5.4 — see `docs/HANDOVER-CONTRACTS.md` §Handover 1 (PUBLIC CONTRACT).
|
> **Trinity context (2026-05-13, informational).** Voyage is Tier 1 (per-task) of a three-tier architecture in active design under the author's private marketplace: Tier 2 `app-creator` (per-app — "what does the app need, what's the next brief?") produces briefs Voyage consumes; Tier 3 `app-factory` (per-portfolio — "which app needs me now?") aggregates state across multiple app-creator instances. Both are pre-implementation and will ship to Forgejo when ready. **Asymmetry is a hard invariant:** Voyage stays unaware of Tier 2/3. Handover 1 (brief format) is the only integration point — any compatible producer can feed Voyage, app-creator is not privileged. Brief-schema changes are therefore breaking changes for downstream consumers, formalized as a public contract in v5.5.0 — see `docs/HANDOVER-CONTRACTS.md` §Handover 1 (PUBLIC CONTRACT).
|
||||||
|
|
||||||
> **Cross-cutting invariant: brief framing must match operator intent (2026-05-15).** Etablert etter residiv. Briefen er pipelinens source of truth; operatørens intent lever i hodet + i memory-filer (`feedback_*`, `project_*`); pipelinen tvinger ikke alignment. Høyere reasoning-kraft polerer feil premiss istedenfor å utfordre det. **Tre lag av forsvar (input-siden), implementert i S6 som `brief_version 2.2`-gate (v5.5), alle BLOCKER ved brudd for briefer som deklarerer ≥ 2.2:** (1) eksplisitt `framing: preserve|refine|replace|new-direction` i brief-frontmatter, `AskUserQuestion`-validert i `/trekbrief` Phase 2.5 før brief-prosa skrives (ikke-skippbar, også i `--quick`); enum-feil → `BRIEF_INVALID_FRAMING` (alle versjoner), fravær ved ≥ 2.2 → `BRIEF_MISSING_FRAMING`; (2) memory-alignment som dimensjon 6 i `brief-reviewer` — sammenlikner brief-prosa + `framing` mot relevante memory-filer, rapporterer kun eksplisitte motsigelser (degraderer til score 5 N/A uten memory-kontekst), wired til Phase 4e-gate (`memory_alignment.score ≥ 4`); (3) obligatorisk `## TL;DR`-seksjon (≤ 5 linjer, soft-cap → `BRIEF_TLDR_TOO_LONG`) øverst i `brief.md`. Eksisterende 2.0/2.1-briefer forblir gyldige (forward+backward-compat, speiler `phase_signals ≥ 2.1`-presedensen); `trekreview`-briefer er unntatt. **Schema-akse bumpet (2.1→2.2); plugin-versjon-badge + CHANGELOG bumpes ved den koordinerte releasen (S10).** Kontrakt-evolusjon dokumentert i `docs/HANDOVER-CONTRACTS.md` §Handover 1. Den gamle manuelle stopgap-sjekken er dermed retired for ≥ 2.2-briefer.
|
> **Cross-cutting invariant: brief framing must match operator intent (2026-05-15).** Etablert etter residiv. Briefen er pipelinens source of truth; operatørens intent lever i hodet + i memory-filer (`feedback_*`, `project_*`); pipelinen tvinger ikke alignment. Høyere reasoning-kraft polerer feil premiss istedenfor å utfordre det. **Tre lag av forsvar (input-siden), implementert i S6 som `brief_version 2.2`-gate (v5.5), alle BLOCKER ved brudd for briefer som deklarerer ≥ 2.2:** (1) eksplisitt `framing: preserve|refine|replace|new-direction` i brief-frontmatter, `AskUserQuestion`-validert i `/trekbrief` Phase 2.5 før brief-prosa skrives (ikke-skippbar, også i `--quick`); enum-feil → `BRIEF_INVALID_FRAMING` (alle versjoner), fravær ved ≥ 2.2 → `BRIEF_MISSING_FRAMING`; (2) memory-alignment som dimensjon 6 i `brief-reviewer` — sammenlikner brief-prosa + `framing` mot relevante memory-filer, rapporterer kun eksplisitte motsigelser (degraderer til score 5 N/A uten memory-kontekst), wired til Phase 4e-gate (`memory_alignment.score ≥ 4`); (3) obligatorisk `## TL;DR`-seksjon (≤ 5 linjer, soft-cap → `BRIEF_TLDR_TOO_LONG`) øverst i `brief.md`. Eksisterende 2.0/2.1-briefer forblir gyldige (forward+backward-compat, speiler `phase_signals ≥ 2.1`-presedensen); `trekreview`-briefer er unntatt. **Schema-akse bumpet (2.1→2.2); plugin-versjon-badge + CHANGELOG bumpes ved den koordinerte releasen (S10).** Kontrakt-evolusjon dokumentert i `docs/HANDOVER-CONTRACTS.md` §Handover 1. Den gamle manuelle stopgap-sjekken er dermed retired for ≥ 2.2-briefer.
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ Full flag reference for each command (modes, `--gates`, `--profile`, breaking ch
|
||||||
| brief-conformance-reviewer | opus | Brief conformance review (SC + Non-Goal traceability) |
|
| brief-conformance-reviewer | opus | Brief conformance review (SC + Non-Goal traceability) |
|
||||||
| code-correctness-reviewer | opus | Code correctness review (7 dimensions) |
|
| code-correctness-reviewer | opus | Code correctness review (7 dimensions) |
|
||||||
| review-coordinator | opus | Judge Agent — dedup + reasonableness filter + verdict |
|
| review-coordinator | opus | Judge Agent — dedup + reasonableness filter + verdict |
|
||||||
| plan-critic | opus | Adversarial plan review (9 dimensions) |
|
| plan-critic | opus | Adversarial plan review (10 dimensions) |
|
||||||
| scope-guardian | opus | Scope alignment (creep + gaps) |
|
| scope-guardian | opus | Scope alignment (creep + gaps) |
|
||||||
| session-decomposer | opus | Splits plans into headless sessions with dependency graph |
|
| session-decomposer | opus | Splits plans into headless sessions with dependency graph |
|
||||||
| synthesis-agent | opus | Distills Phase-5/7 exploration outputs into a findings digest (NW3 PoC — **dormant**, not wired; delegating Phase 7 alone yields Δ main-context ≈ 0, see `docs/T1-synthesis-poc-results.md`) |
|
| synthesis-agent | opus | Distills Phase-5/7 exploration outputs into a findings digest (NW3 PoC — **dormant**, not wired; delegating Phase 7 alone yields Δ main-context ≈ 0, see `docs/T1-synthesis-poc-results.md`) |
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -155,7 +155,7 @@ Concrete capabilities, observable in the code — not aspirations.
|
||||||
|
|
||||||
Interactive requirements-gathering command. Runs a **dynamic, quality-gated interview** and produces a **task brief** with an explicit research plan. Optionally orchestrates the rest of the pipeline.
|
Interactive requirements-gathering command. Runs a **dynamic, quality-gated interview** and produces a **task brief** with an explicit research plan. Optionally orchestrates the rest of the pipeline.
|
||||||
|
|
||||||
A section-driven interview loop fills required brief sections (Intent / Goal / Success Criteria / Research Plan) until each shows initial signal, then `brief-reviewer` scores the draft on five dimensions (completeness, consistency, testability, scope clarity, research-plan validity) and gates publication. Max 3 review iterations; force-stop yields a `brief_quality: partial` brief with the failing dimensions documented.
|
A section-driven interview loop fills required brief sections (Intent / Goal / Success Criteria / Research Plan) until each shows initial signal, then `brief-reviewer` scores the draft on six dimensions (completeness, consistency, testability, scope clarity, research-plan validity, memory alignment) and gates publication. Max 3 review iterations; force-stop yields a `brief_quality: partial` brief with the failing dimensions documented.
|
||||||
|
|
||||||
Output: `.claude/projects/{YYYY-MM-DD}-{slug}/brief.md`
|
Output: `.claude/projects/{YYYY-MM-DD}-{slug}/brief.md`
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ Research uses up to 5 local agents (architecture-mapper, dependency-tracer, task
|
||||||
|
|
||||||
Produces an implementation plan detailed enough for autonomous execution. **v2.0 breaking change:** requires `--brief` or `--project`. There is no longer an interview inside `/trekplan` — use `/trekbrief` first.
|
Produces an implementation plan detailed enough for autonomous execution. **v2.0 breaking change:** requires `--brief` or `--project`. There is no longer an interview inside `/trekplan` — use `/trekbrief` first.
|
||||||
|
|
||||||
After `brief-reviewer` validates the input brief, 6–8 exploration agents analyze the codebase in parallel and merge findings into a synthesis. Optional research briefs (`--research`, or auto-discovered in `{project_dir}/research/`) enrich the plan; `architecture/overview.md` priors are loaded if an opt-in upstream architect plugin (not bundled) produced one. Opus then writes the plan with per-step YAML manifests, which `plan-critic` (9 dimensions) and `scope-guardian` adversarially review before handoff.
|
After `brief-reviewer` validates the input brief, 6–8 exploration agents analyze the codebase in parallel and merge findings into a synthesis. Optional research briefs (`--research`, or auto-discovered in `{project_dir}/research/`) enrich the plan; `architecture/overview.md` priors are loaded if an opt-in upstream architect plugin (not bundled) produced one. Opus then writes the plan with per-step YAML manifests, which `plan-critic` (10 dimensions) and `scope-guardian` adversarially review before handoff.
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
- With `--project <dir>`: `{dir}/plan.md`
|
- With `--project <dir>`: `{dir}/plan.md`
|
||||||
|
|
@ -263,7 +263,7 @@ Every implementation step includes:
|
||||||
- **Checkpoint:** — git commit after success
|
- **Checkpoint:** — git commit after success
|
||||||
- **Manifest:** — the objective completion predicate (Hard Rule 17)
|
- **Manifest:** — the objective completion predicate (Hard Rule 17)
|
||||||
|
|
||||||
Exploration uses 6–8 agents in parallel (architecture-mapper, dependency-tracer, task-finder, test-strategist, git-historian, risk-assessor, plus convention-scanner on medium+ codebases and research-scout when unfamiliar tech is detected). Adversarial review then runs `brief-reviewer`, `plan-critic` (9 dimensions, no-placeholder enforcement, manifest audit), and `scope-guardian` (creep + gap detection). Per-agent details in [`agents/`](agents/).
|
Exploration uses 6–8 agents in parallel (architecture-mapper, dependency-tracer, task-finder, test-strategist, git-historian, risk-assessor, plus convention-scanner on medium+ codebases and research-scout when unfamiliar tech is detected). Adversarial review then runs `brief-reviewer`, `plan-critic` (10 dimensions, no-placeholder enforcement, manifest audit), and `scope-guardian` (creep + gap detection). Per-agent details in [`agents/`](agents/).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -801,12 +801,12 @@ Top-level layout:
|
||||||
|
|
||||||
```
|
```
|
||||||
trekplan/
|
trekplan/
|
||||||
├── agents/ 23 specialized agents (all `model: opus`-pinned; per-phase model set by `--profile`)
|
├── agents/ 24 specialized agents (all `model: opus`-pinned; per-phase model set by `--profile`)
|
||||||
├── commands/ 6 slash commands (trekbrief, trekresearch, trekplan, trekexecute, trekreview, trekcontinue) + trekplan-end-session helper
|
├── commands/ 6 slash commands (trekbrief, trekresearch, trekplan, trekexecute, trekreview, trekcontinue) + trekendsession helper
|
||||||
├── templates/ Frontmatter templates for brief, research, plan, session, launch
|
├── templates/ Frontmatter templates for brief, research, plan, session, launch
|
||||||
├── hooks/ 5 hooks (pre-bash, pre-write, session-title, post-bash-stats, pre-compact-flush)
|
├── hooks/ 7 hooks (pre-bash, pre-write, session-title, post-bash-stats, pre-compact-flush, post-compact-flush, otel-export)
|
||||||
├── lib/ Zero-dep parsers and validators (CLI shims under lib/validators/)
|
├── lib/ Zero-dep parsers and validators (CLI shims under lib/validators/)
|
||||||
├── tests/ 109 node:test cases — `npm test` is the fork-readiness gate
|
├── tests/ comprehensive node:test suite — `npm test` is the fork-readiness gate
|
||||||
├── docs/ HANDOVER-CONTRACTS.md + architect-bridge-test.md
|
├── docs/ HANDOVER-CONTRACTS.md + architect-bridge-test.md
|
||||||
└── examples/ 01-add-verbose-flag/ — calibrated end-to-end pipeline demo
|
└── examples/ 01-add-verbose-flag/ — calibrated end-to-end pipeline demo
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -571,7 +571,7 @@ Do NOT write this synthesis to disk. It is internal working context only.
|
||||||
## Phase 8 — Deep planning
|
## Phase 8 — Deep planning
|
||||||
|
|
||||||
> **Schema-drift defense (sealed inline so this contract survives even when
|
> **Schema-drift defense (sealed inline so this contract survives even when
|
||||||
> `agents/planning-orchestrator.md` is not implicitly loaded by Opus 4.7).**
|
> `agents/planning-orchestrator.md` is not implicitly loaded by Opus 4.8).**
|
||||||
>
|
>
|
||||||
> The plan you write MUST satisfy these regexes. The executor parses with
|
> The plan you write MUST satisfy these regexes. The executor parses with
|
||||||
> strict regex matching; any deviation breaks parsing and forces a re-plan.
|
> strict regex matching; any deviation breaks parsing and forces a re-plan.
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ Every validator exposes a CLI: `node lib/validators/<name>.mjs --json <path>` re
|
||||||
|
|
||||||
## Handover 1 — `brief.md` → research/ (PUBLIC CONTRACT)
|
## Handover 1 — `brief.md` → research/ (PUBLIC CONTRACT)
|
||||||
|
|
||||||
**Stability tier: PUBLIC CONTRACT.** Handover 1 is the *only* public integration boundary of the pipeline: `brief.md` is what an upstream producer hands to Voyage, and Voyage consumes it without any knowledge of who produced it. This asymmetry is a hard invariant (see `CLAUDE.md` §Trinity context) — the interactive `/trekbrief` interview is just one producer; a `manual` brief, or an external per-app / per-portfolio producer, is equally valid as long as the artifact conforms to the schema below. No producer is privileged. The consequence: changing this schema — renaming or removing a field, narrowing an enum, or promoting an optional field to required — is a **breaking change for every downstream consumer** and MUST follow the [breaking-change protocol](#breaking-change-protocol) (version bump + N-1 compatibility window). Additive *optional* fields are non-breaking by design: the validator tolerates unknown frontmatter keys silently (forward-compat), so a newer brief still validates against an older consumer. The v5.4 contract formalization froze `brief_version` **2.1** as the public-contract baseline; **v5.5 evolves it to `2.2`** under the breaking-change protocol — adding the required `framing` field + `## TL;DR` section gated at ≥ 2.2 (existing 2.0/2.1 briefs stay valid). See the Versioning paragraph below for the 2.2 details.
|
**Stability tier: PUBLIC CONTRACT.** Handover 1 is the *only* public integration boundary of the pipeline: `brief.md` is what an upstream producer hands to Voyage, and Voyage consumes it without any knowledge of who produced it. This asymmetry is a hard invariant (see `CLAUDE.md` §Trinity context) — the interactive `/trekbrief` interview is just one producer; a `manual` brief, or an external per-app / per-portfolio producer, is equally valid as long as the artifact conforms to the schema below. No producer is privileged. The consequence: changing this schema — renaming or removing a field, narrowing an enum, or promoting an optional field to required — is a **breaking change for every downstream consumer** and MUST follow the [breaking-change protocol](#breaking-change-protocol) (version bump + N-1 compatibility window). Additive *optional* fields are non-breaking by design: the validator tolerates unknown frontmatter keys silently (forward-compat), so a newer brief still validates against an older consumer. The v5.5.0 contract formalization established `brief_version` **2.1** as the public-contract baseline and, in the same coordinated release, **evolved it to `2.2`** under the breaking-change protocol — adding the required `framing` field + `## TL;DR` section gated at ≥ 2.2 (existing 2.0/2.1 briefs stay valid). See the Versioning paragraph below for the 2.2 details.
|
||||||
|
|
||||||
**Producer:** `/trekbrief` Phase 4g (after `brief-reviewer` stop-gate passes or iteration cap is hit).
|
**Producer:** `/trekbrief` Phase 4g (after `brief-reviewer` stop-gate passes or iteration cap is hit).
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ Optional but standard sections: `## Non-Goals`, `## Constraints`, `## Preference
|
||||||
|
|
||||||
**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.
|
**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.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. The v5.4 contract formalization **froze `2.1` as the public-contract baseline** (see the PUBLIC CONTRACT callout under the Handover 1 heading): per the S3 effort-axis decision, `phase_signals` stays **optional**, and the v2.1 sequencing gate (`phase_signals` **or** `phase_signals_partial`) is the stability mechanism. Promoting `phase_signals` to required would be a future breaking change under the protocol above — explicitly *not* part of v5.4.
|
**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. The v5.5.0 contract formalization **established `2.1` as the public-contract baseline** (see the PUBLIC CONTRACT callout under the Handover 1 heading): per the S3 effort-axis decision, `phase_signals` stays **optional**, and the v2.1 sequencing gate (`phase_signals` **or** `phase_signals_partial`) is the stability mechanism. Promoting `phase_signals` to required would be a future breaking change under the protocol above — explicitly *not* part of the v5.5.0 formalization.
|
||||||
|
|
||||||
**v5.5 → `2.2` (framing enforcement).** `2.2` adds two **required-at-2.2** elements — the `framing` enum field and the `## TL;DR` body section — gated identically to the v2.1 mechanism: the new requirements fire only on `brief_version ≥ 2.2`, so every existing `2.0` / `2.1` brief still validates (forward- and backward-compatible). Because adding required elements is a breaking change for any producer that declares `2.2`, this is a controlled version bump under the breaking-change protocol: downstream producers (e.g. a Tier-2 per-app producer) that emit `brief_version: "2.2"` MUST also emit `framing` + `## TL;DR`. The framing enum is additionally enforced on *any* version when the field is present (`BRIEF_INVALID_FRAMING`). The three framing-alignment layers (framing field, `brief-reviewer` memory-alignment dimension, obligatory TL;DR) implement the `CLAUDE.md` cross-cutting invariant. Note: the *plugin* version bump + CHANGELOG entry for this schema change land at the coordinated release (matrix §S10), separate from this schema axis.
|
**v5.5 → `2.2` (framing enforcement).** `2.2` adds two **required-at-2.2** elements — the `framing` enum field and the `## TL;DR` body section — gated identically to the v2.1 mechanism: the new requirements fire only on `brief_version ≥ 2.2`, so every existing `2.0` / `2.1` brief still validates (forward- and backward-compatible). Because adding required elements is a breaking change for any producer that declares `2.2`, this is a controlled version bump under the breaking-change protocol: downstream producers (e.g. a Tier-2 per-app producer) that emit `brief_version: "2.2"` MUST also emit `framing` + `## TL;DR`. The framing enum is additionally enforced on *any* version when the field is present (`BRIEF_INVALID_FRAMING`). The three framing-alignment layers (framing field, `brief-reviewer` memory-alignment dimension, obligatory TL;DR) implement the `CLAUDE.md` cross-cutting invariant. Note: the *plugin* version bump + CHANGELOG entry for this schema change land at the coordinated release (matrix §S10), separate from this schema axis.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,18 @@ const ROOT = join(HERE, '..', '..');
|
||||||
|
|
||||||
function read(rel) { return readFileSync(join(ROOT, rel), 'utf-8'); }
|
function read(rel) { return readFileSync(join(ROOT, rel), 'utf-8'); }
|
||||||
function listMd(rel) { return readdirSync(join(ROOT, rel)).filter(f => f.endsWith('.md')); }
|
function listMd(rel) { return readdirSync(join(ROOT, rel)).filter(f => f.endsWith('.md')); }
|
||||||
|
function listMjs(rel) { return readdirSync(join(ROOT, rel)).filter(f => f.endsWith('.mjs')); }
|
||||||
|
|
||||||
|
// Normalize a "<N|word> dimensions" descriptor to an integer (four..ten + digits).
|
||||||
|
const NUMWORDS = { four: 4, five: 5, six: 6, seven: 7, eight: 8, nine: 9, ten: 10 };
|
||||||
|
function dimCounts(text, anchor) {
|
||||||
|
// Every dimension count stated on a line mentioning `anchor`.
|
||||||
|
return text.split('\n')
|
||||||
|
.filter(l => l.includes(anchor) && /dimensions/.test(l))
|
||||||
|
.map(l => l.match(/(\d+|four|five|six|seven|eight|nine|ten)\s+(?:numbered\s+)?dimensions/i))
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(m => /^\d+$/.test(m[1]) ? Number(m[1]) : NUMWORDS[m[1].toLowerCase()]);
|
||||||
|
}
|
||||||
|
|
||||||
test('CLAUDE.md agents table row count == agents/*.md file count', () => {
|
test('CLAUDE.md agents table row count == agents/*.md file count', () => {
|
||||||
const md = read('CLAUDE.md');
|
const md = read('CLAUDE.md');
|
||||||
|
|
@ -35,6 +47,55 @@ test('CLAUDE.md agents table row count == agents/*.md file count', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('README architecture block agent-count == agents/*.md file count', () => {
|
||||||
|
const md = read('README.md');
|
||||||
|
const m = md.match(/agents\/\s+(\d+)\s+specialized agents/);
|
||||||
|
assert.ok(m, 'README architecture block must state "N specialized agents"');
|
||||||
|
assert.equal(
|
||||||
|
Number(m[1]),
|
||||||
|
listMd('agents').length,
|
||||||
|
`Drift: README says ${m[1]} specialized agents vs ${listMd('agents').length} agents/*.md files.`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('README architecture block hook-count == hooks/scripts/*.mjs file count', () => {
|
||||||
|
const md = read('README.md');
|
||||||
|
const m = md.match(/hooks\/\s+(\d+)\s+hooks\s*\(/);
|
||||||
|
assert.ok(m, 'README architecture block must state "N hooks (...)"');
|
||||||
|
assert.equal(
|
||||||
|
Number(m[1]),
|
||||||
|
listMjs('hooks/scripts').length,
|
||||||
|
`Drift: README says ${m[1]} hooks vs ${listMjs('hooks/scripts').length} hooks/scripts/*.mjs files.`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('plan-critic dimension count: README + CLAUDE.md match the agent definition', () => {
|
||||||
|
const dims = (read('agents/plan-critic.md').match(/^### \d+\./gm) || []).length;
|
||||||
|
assert.ok(dims > 0, 'agents/plan-critic.md must define numbered "### N." dimensions');
|
||||||
|
const stated = [...dimCounts(read('README.md'), 'plan-critic'), ...dimCounts(read('CLAUDE.md'), 'plan-critic')];
|
||||||
|
assert.ok(stated.length >= 2, 'README + CLAUDE.md must cite plan-critic (N dimensions)');
|
||||||
|
for (const n of stated) {
|
||||||
|
assert.equal(n, dims, `Drift: a doc cites plan-critic (${n} dimensions) vs ${dims} numbered dims in agents/plan-critic.md.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('brief-reviewer dimension count: README matches the agent definition', () => {
|
||||||
|
const agentDims = dimCounts(read('agents/brief-reviewer.md'), 'dimensions');
|
||||||
|
assert.ok(agentDims.length >= 1, 'agents/brief-reviewer.md must state "N dimensions"');
|
||||||
|
const expected = agentDims[0];
|
||||||
|
// Only the line stating brief-reviewer's OWN count — exclude agent-list lines
|
||||||
|
// that co-mention `plan-critic (N dimensions)` (those carry plan-critic's count).
|
||||||
|
const readmeDims = read('README.md').split('\n')
|
||||||
|
.filter(l => l.includes('brief-reviewer') && /dimensions/.test(l) && !l.includes('plan-critic'))
|
||||||
|
.map(l => l.match(/(\d+|four|five|six|seven|eight|nine|ten)\s+dimensions/i))
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(m => /^\d+$/.test(m[1]) ? Number(m[1]) : NUMWORDS[m[1].toLowerCase()]);
|
||||||
|
assert.ok(readmeDims.length >= 1, 'README must cite brief-reviewer (N dimensions)');
|
||||||
|
for (const n of readmeDims) {
|
||||||
|
assert.equal(n, expected, `Drift: README cites brief-reviewer (${n} dimensions) vs ${expected} in agents/brief-reviewer.md.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('CLAUDE.md commands table mentions every commands/*.md file', () => {
|
test('CLAUDE.md commands table mentions every commands/*.md file', () => {
|
||||||
const md = read('CLAUDE.md');
|
const md = read('CLAUDE.md');
|
||||||
const commandFiles = listMd('commands');
|
const commandFiles = listMd('commands');
|
||||||
|
|
@ -670,12 +731,14 @@ test('v5.5 — brief_version 2.2 is current in the public-contract schema table
|
||||||
'Handover 1 schema table must mark brief_version "2.2" as current');
|
'Handover 1 schema table must mark brief_version "2.2" as current');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('v5.4 — phase_signals stays optional: not promoted to required', () => {
|
test('v5.5 — phase_signals stays optional: not promoted to required', () => {
|
||||||
const t = read('docs/HANDOVER-CONTRACTS.md');
|
const t = read('docs/HANDOVER-CONTRACTS.md');
|
||||||
assert.ok(!t.includes('v5.4 may promote'),
|
assert.ok(!t.includes('v5.4 may promote'),
|
||||||
'the speculative "v5.4 may promote phase_signals to required" line must be resolved (S3 froze the shape)');
|
'the speculative "v5.4 may promote phase_signals to required" line must be resolved (S3 froze the shape)');
|
||||||
assert.ok(t.includes('froze `2.1` as the public-contract baseline'),
|
assert.ok(!/v5\.4/.test(t),
|
||||||
'the Versioning note must record that v5.4 froze 2.1 as the public-contract baseline');
|
'the contract doc must not cite the phantom v5.4 release (v5.2–5.4 never shipped; S16 → v5.5.0)');
|
||||||
|
assert.ok(t.includes('established `2.1` as the public-contract baseline'),
|
||||||
|
'the Versioning note must record the v5.5.0 contract formalization establishing 2.1 as the baseline');
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- v5.5 — framing enforcement: brief_version 2.2 (Handover 1 contract evolution) ---
|
// --- v5.5 — framing enforcement: brief_version 2.2 (Handover 1 contract evolution) ---
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue