docs(voyage): S33 — reconcile agent inventory (21 spawnable + 3 reference docs) + record D1–D3 considered-and-kept

Balance-backlog S33 (DOC, non-breaking). The D1–D3 forks resolved conservatively
(balance-backlog-plan.md), collapsing the model + observability work into a
documentation record. Three deliverables, all doc-only, no code/frontmatter change:

- V35 (doc half): the "24 agents" headline is reconciled to its honest split —
  21 spawnable (one dormant: synthesis-agent, Δ≈0) + 3 orchestrator reference
  docs (planning-/research-/review-orchestrator document the inline /trek*
  workflow; not spawned). Each orchestrator header now declares itself a
  "reference document, not a spawnable capability". README + CLAUDE.md state the
  split; counts in the new pins are DERIVED from agents/ so they survive reword.
- D2 (V32 rationale): docs/observability.md gains a "Why direct export rather
  than a native collector" section — direct export keeps the path / SSRF /
  field-allowlist guards in audited in-process code (the S21 hardening) instead
  of re-hosting a collector; textfile mode remains the collector escape hatch.
- D3 (kept-opus): docs/voyage-vs-cc-balance-analysis.md §10 decision record —
  opus on V09 (glue), V35 (dormant), V11 (retrieval), V16 (mechanical), V08
  (researchers) was reconsidered for sonnet and KEPT (pin 40d8742 firm).

No agent frontmatter changed — tests/lib/agent-frontmatter.test.mjs is the
structural model source-of-truth and is untouched (diff is description-only:
model: opus + tools lists unchanged, no Agent tool granted). No Handover-1
change; no exporter/gemini-bridge removal. Non-breaking, no version bump.

tests/lib/doc-consistency.test.mjs: +5 S33 pins (inventory split derived from
agents/; synthesis-agent dormant; orchestrator relabel; observability D2
rationale; analysis-doc D1–D3 record). Tests 739 (737 pass / 2 skip / 0 fail),
bar `node --test`; `claude plugin validate` green (1 accepted 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:
Kjell Tore Guttormsen 2026-06-20 09:49:11 +02:00
commit 2df0cbb372
8 changed files with 160 additions and 6 deletions

View file

@ -1100,3 +1100,83 @@ test('S32: trekbrief Phase 3 delegates Q&A turn-taking to AskUserQuestion (V01
'question to ask (policy); AskUserQuestion does the asking (engine). No hand-rolled menu loop.',
);
});
// ── S33 — documentation consolidation: record the considered-and-kept decisions ──
//
// The D1D3 forks (balance-backlog-plan.md) resolved conservatively, collapsing
// the model + observability work into a documentation record. These pins make
// the three deliverables enforced doc-truth:
// • V35 (doc half) — the "24 agents" framing is reconciled to its honest split
// (21 spawnable + 3 reference orchestrators, of which synthesis-agent is the
// one dormant spawnable). Counts are DERIVED from agents/ so they survive
// reword and track new files.
// • D2 (V32 rationale) — observability.md documents why direct export rather
// than a native collector (preserves the S21 in-process guards).
// • D3 (kept-opus) — the analysis doc carries a decision record citing the pin
// commit; the model frontmatter is NOT changed (agent-frontmatter.test.mjs
// stays the structural source-of-truth and is untouched by S33).
// Fix the SOURCE a pin guards, not the test.
test('S33: README + CLAUDE.md reconcile the agent inventory (spawnable + reference orchestrators = file count)', () => {
const agentFiles = listMd('agents');
const total = agentFiles.length;
const orchestrators = agentFiles.filter((f) => /-orchestrator\.md$/.test(f));
const refCount = orchestrators.length;
const spawnable = total - refCount;
assert.equal(refCount, 3, `expected exactly 3 *-orchestrator reference docs, got ${refCount}`);
for (const [name, body] of [['README.md', read('README.md')], ['CLAUDE.md', read('CLAUDE.md')]]) {
assert.ok(
body.includes(`${spawnable} spawnable`),
`${name} must state "${spawnable} spawnable" agents (derived: ${total} files ${refCount} reference orchestrators)`,
);
assert.ok(
body.includes(`${refCount} orchestrator reference doc`),
`${name} must label the ${refCount} orchestrators as reference docs (not spawnable capabilities)`,
);
}
});
test('S33: the inventory framing names synthesis-agent as the one dormant spawnable', () => {
// 1 of the spawnable agents ships dormant (Δ≈0, wired to nothing). The framing
// must say so, so the headline count is honest about live vs. dormant capability.
for (const [name, body] of [['README.md', read('README.md')], ['CLAUDE.md', read('CLAUDE.md')]]) {
assert.ok(
/synthesis-agent/.test(body) && /dormant/i.test(body),
`${name} inventory framing must name synthesis-agent as the dormant spawnable`,
);
}
});
test('S33: the 3 orchestrator files declare themselves reference docs, not spawnable capabilities (V35 relabel)', () => {
for (const f of listMd('agents').filter((x) => /-orchestrator\.md$/.test(x))) {
assert.ok(
/reference document, not a spawnable capability/i.test(read(`agents/${f}`)),
`agents/${f} must declare "reference document, not a spawnable capability" (V35 doc-half relabel)`,
);
}
});
test('S33: docs/observability.md documents the direct-export-vs-collector rationale (D2)', () => {
const t = read('docs/observability.md');
assert.ok(
/direct[- ]export/i.test(t) && /collector/i.test(t),
'observability.md must document why Voyage exports directly rather than via a native collector (D2)',
);
// The rationale must anchor on the in-process security boundary it preserves
// (the S21 hardening), not read as an unjustified re-host.
assert.ok(
/allowlist/i.test(t) && /(SSRF|CWE-918|CWE-212)/.test(t),
'the direct-export rationale must cite the in-process guards it preserves (field allowlist + SSRF/path)',
);
});
test('S33: the analysis doc records the D1D3 resolved forks (considered-and-kept, pin firm)', () => {
const t = read('docs/voyage-vs-cc-balance-analysis.md');
assert.ok(/Decision record/i.test(t), 'analysis doc must carry a "Decision record" addendum for the resolved forks');
for (const d of ['D1', 'D2', 'D3']) {
assert.ok(new RegExp(`\\b${d}\\b`).test(t), `analysis-doc decision record must name fork ${d}`);
}
// D3 kept the model pin firm — the record must cite the pin commit so a future
// reader sees the downgrade was reconsidered-and-declined, not overlooked.
assert.ok(/40d8742/.test(t), 'decision record must cite the model-pin commit 40d8742 (D3 kept firm, document-only)');
});