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:
parent
2849157ba2
commit
2df0cbb372
8 changed files with 160 additions and 6 deletions
|
|
@ -53,6 +53,8 @@ Full flag reference for each command (modes, `--gates`, `--profile`, breaking ch
|
|||
| contrarian-researcher | opus | Counter-evidence, overlooked alternatives |
|
||||
| gemini-bridge | opus | Gemini Deep Research second opinion (conditional) |
|
||||
|
||||
> **Inventory (S33 reconcile).** 24 agent files = **21 spawnable** (one of which, `synthesis-agent`, ships **dormant** — Δ≈0, wired to nothing, see `docs/T1-synthesis-poc-results.md`) **+ 3 orchestrator reference docs** (`planning-/research-/review-orchestrator` document the inline `/trek*` workflow; they are reference documentation, not spawnable capabilities). All 24 stay `model: opus` (operator pin `40d8742`); the glue/mechanical/retrieval/dormant roles (V09 gemini-bridge, V16 session-decomposer, V11 retrieval agents, V08 researchers, V35 synthesis) were reconsidered for a sonnet downgrade and **kept opus** — document-only, no frontmatter change (the decision record lives in `docs/voyage-vs-cc-balance-analysis.md` §10).
|
||||
|
||||
> **Model & effort (CC 2.1.154+).** `opus` resolves to **Opus 4.8** (default reasoning effort `high`); `sonnet` to Sonnet 4.6. Select agents carry native `effort:` — retrieval agents (`task-finder`, `git-historian`, `dependency-tracer`, `architecture-mapper`) at `medium`, adversarial-reasoning agents (`plan-critic`, `risk-assessor`, `contrarian-researcher`, `review-coordinator`) at `high`. This native per-spawn **reasoning** effort is a different axis from brief `phase_signals.effort` (orchestration shape — which agents/passes run). See `docs/profiles.md` §Model & effort axes.
|
||||
|
||||
## Reference docs (read on demand)
|
||||
|
|
|
|||
|
|
@ -802,6 +802,7 @@ Top-level layout:
|
|||
```
|
||||
trekplan/
|
||||
├── agents/ 24 specialized agents (all `model: opus`-pinned; per-phase model set by `--profile`)
|
||||
│ └ 21 spawnable (1 dormant: synthesis-agent, Δ≈0) + 3 orchestrator reference docs (not spawned)
|
||||
├── commands/ 6 slash commands (trekbrief, trekresearch, trekplan, trekexecute, trekreview, trekcontinue) + trekendsession helper
|
||||
├── templates/ Frontmatter templates for brief, research, plan, session, launch
|
||||
├── hooks/ 7 hooks (pre-bash, pre-write, session-title, post-bash-stats, pre-compact-flush, post-compact-flush, otel-export)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
name: planning-orchestrator
|
||||
description: |
|
||||
Inline reference (v2.4.0) — documents the planning workflow that
|
||||
/trekplan executes in main context. Historically not spawned as a
|
||||
sub-agent: before Claude Code 2.1.172 the harness did not expose the
|
||||
/trekplan executes in main context.
|
||||
This file is a reference document, not a spawnable capability.
|
||||
Historically not spawned as a sub-agent: before Claude Code 2.1.172
|
||||
the harness did not expose the
|
||||
Agent tool to sub-agents, so a background orchestrator could not spawn
|
||||
the exploration swarm (architecture-mapper, task-finder, plan-critic,
|
||||
etc.). As of CC 2.1.172 sub-agents can spawn sub-agents (up to 5 levels
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
name: research-orchestrator
|
||||
description: |
|
||||
Inline reference (v2.4.0) — documents the research workflow that
|
||||
/trekresearch executes in main context. Historically not spawned as
|
||||
a sub-agent: before Claude Code 2.1.172 the harness did not expose the
|
||||
/trekresearch executes in main context.
|
||||
This file is a reference document, not a spawnable capability.
|
||||
Historically not spawned as a sub-agent: before Claude Code 2.1.172
|
||||
the harness did not expose the
|
||||
Agent tool to sub-agents, so a background orchestrator could not spawn
|
||||
the research swarm. As of CC 2.1.172 sub-agents can spawn sub-agents
|
||||
(up to 5 levels deep), so a delegated-orchestration redesign is under
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
name: review-orchestrator
|
||||
description: |
|
||||
Inline reference (v3.2.0) — documents the review workflow that
|
||||
/trekreview executes in main context. Historically not spawned
|
||||
as a sub-agent: before Claude Code 2.1.172 the harness did not expose
|
||||
/trekreview executes in main context.
|
||||
This file is a reference document, not a spawnable capability.
|
||||
Historically not spawned as a sub-agent: before Claude Code 2.1.172
|
||||
the harness did not expose
|
||||
the Agent tool to sub-agents, so a background orchestrator could not
|
||||
spawn the reviewer swarm (brief-conformance-reviewer,
|
||||
code-correctness-reviewer, review-coordinator). As of CC 2.1.172
|
||||
|
|
|
|||
|
|
@ -114,6 +114,40 @@ The exporter is hardened against three CWE classes:
|
|||
| `prom/node-exporter` | `1.10.2` | textfile collector path normalization |
|
||||
| `grafana/grafana` | `11.4.0` | datasource provisioning hardening |
|
||||
|
||||
## Why direct export rather than a native collector
|
||||
|
||||
A balance review (`docs/voyage-vs-cc-balance-analysis.md` §4, V32) asked
|
||||
whether the custom exporters should be dropped in favour of pointing the
|
||||
standard `OTEL_*` environment variables at a co-located OTLP collector,
|
||||
letting that collector own egress and field selection. The operator
|
||||
decision (D2, 2026-06-20) is to **keep direct export**. The rationale is
|
||||
the security boundary, not a preference for re-hosting a collector:
|
||||
|
||||
- **The three guards run in-process, before any byte leaves Voyage.**
|
||||
`path-validator.mjs` (CWE-22), `endpoint-validator.mjs` (CWE-918 / SSRF),
|
||||
and `field-allowlist.mjs` (CWE-212) are applied inside `otel-export.mjs`
|
||||
and covered by `tests/hooks/otel-export-validators.test.mjs`. The
|
||||
records carry operator-private data (paths, prompts, brief content);
|
||||
the allowlist drops everything not explicitly named before export.
|
||||
- **A native-collector design moves that boundary out of audited code.**
|
||||
Handing raw JSONL to a sidecar collector means either re-expressing the
|
||||
field allowlist in collector config (a second source of truth that can
|
||||
drift) or shipping un-allowlisted private fields and trusting the
|
||||
collector's egress rules. The S21 SSRF hardening — 169.254.169.254
|
||||
permanently blocked, loopback/RFC1918 gated behind
|
||||
`VOYAGE_OTEL_ALLOW_PRIVATE` — is a property of `endpoint-validator.mjs`
|
||||
and would have to be re-created in collector configuration to be
|
||||
preserved.
|
||||
- **The collector path is still available, by design.** Operators who
|
||||
want collector semantics (retry, persistence, relabelling) use
|
||||
`textfile` mode and scrape `voyage.prom` with node-exporter / vector /
|
||||
otel-collector. Direct export is the minimal default, not a rejection
|
||||
of collectors — it keeps the data-sanitization boundary in Voyage's
|
||||
own validated code for the common case.
|
||||
|
||||
This is a deliberate direct-export-over-collector choice; the custom
|
||||
exporters and their guards are kept, not pruned.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Stop-hook is normal-exit only.** If Claude Code crashes or is killed
|
||||
|
|
|
|||
|
|
@ -194,3 +194,34 @@ All items are **operator-gated** (scope-guard: analysis only). Tagged **[non-bre
|
|||
## 9. Meta-note (dogfooding the Workflow tool)
|
||||
|
||||
Running this analysis *on* the CC Workflow tool produced first-hand evidence for the audit's own V27/V29 rows: the `pipeline()` shape gave clean coverage (35/35, no row dropped), schema-forced output eliminated JSON-parse fragility (the CC-27 F2 win, reconfirmed), and the conditional adversarial stage worked. Costs also reconfirmed: ~2.97M subagent tokens / 96 agents / 4.6 min for a 35×3 fan-out — Workflow is a good substrate for *bounded, verifiable, parallel* fan-out, exactly the ~20% core CC-27 identified, and a poor fit for the ~80% judgment-heavy glue (scout + synthesis, which ran inline in main context here). This is dogfooding of the **Workflow tool**, not of Voyage's pipeline (the latter was declined by the operator).
|
||||
|
||||
---
|
||||
|
||||
## 10. Decision record — resolved forks (operator, 2026-06-20)
|
||||
|
||||
§4/§6 deferred three forks to the operator. All three resolved to the
|
||||
**conservative** option (preserve the capability / the security code / the
|
||||
deliberate pin). The model and observability work therefore collapses from
|
||||
code-deletion to a documentation record; the only real code changes in the
|
||||
backlog are V15 (export trim, S31) and V30 (economy calibration, S34). These
|
||||
decisions are now the implemented baseline of the multi-session backlog plan
|
||||
(`docs/balance-backlog-plan.md`), shipped across S31–S34.
|
||||
|
||||
| Fork | Item | Resolution | What changed |
|
||||
|------|------|------------|--------------|
|
||||
| **D1** | V09 `gemini-bridge` | **Keep it as an agent.** | No removal / inline-rewiring. The capability count stays 24; its model is governed by D3 (stays opus). |
|
||||
| **D2** | V32 observability export | **Keep `lib/exporters/*` + `otel-export.mjs`; document the direct-export rationale.** | Doc-only — no deletion of S21 security code. The rationale (preserve the in-process path / SSRF / field-allowlist guards rather than re-host a collector) is recorded in `docs/observability.md` §"Why direct export rather than a native collector". |
|
||||
| **D3** | 24-agent `model: opus` pin (`40d8742`) | **Keep the pin firm.** | Document-only. opus on V09 (glue — does not reason), V35 (dormant `synthesis-agent`), V11 (retrieval agents, already `effort:medium`), V16 (`session-decomposer`, mechanical parsing), and V08 (researchers) was reconsidered for a sonnet downgrade and **kept opus**. **No agent frontmatter is changed** — `tests/lib/agent-frontmatter.test.mjs` remains the structural source-of-truth and is untouched. |
|
||||
|
||||
**Inventory framing (V35 doc half, reconciled).** The "24 agents" headline is
|
||||
**21 spawnable + 3 orchestrator reference docs**. The three orchestrators
|
||||
(`planning-/research-/review-orchestrator`) document the inline `/trek*`
|
||||
workflow and are not spawned as sub-agents. Of the 21 spawnable, one —
|
||||
`synthesis-agent` — ships **dormant** (Δ≈0, wired to nothing;
|
||||
`docs/T1-synthesis-poc-results.md`). Reconciled across `README.md`,
|
||||
`CLAUDE.md`, and this doc; pinned in `tests/lib/doc-consistency.test.mjs` (S33).
|
||||
|
||||
**Still explicitly NOT done** (out of scope, per the plan): no `gemini-bridge`
|
||||
removal (D1), no exporter deletion (D2), no model downgrade (D3), no Handover-1
|
||||
change, no Workflow substrate swap (CC-27 declined), no delegated-orchestration
|
||||
redesign (CC-26 lean-NO), no reopening of the premium-default / framing-gate pins.
|
||||
|
|
|
|||
|
|
@ -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 D1–D3 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 D1–D3 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)');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue