voyage/tests/fixtures/synthesis/exploration/dependency-tracer.md
Kjell Tore Guttormsen 6b30483304 feat(voyage): S12 — NW3 synthesis-agent built + measured → declined per measurement [skip-docs]
NW3 (CC-26 §6 PoC): delegate trekplan Phase 7 synthesis to a synthesis-agent,
adopt only if Δ main-context ≥30% with no quality loss. Operator chose the
deterministic-proof path (live ≥3-run bake-off was env-blocked: no API key;
installed plugin is a cache copy so a new agent is invisible to `claude -p`).

Decisive structural finding: trekplan Phase 5 runs the swarm FOREGROUND, so its
outputs are already resident in main before Phase 7. Delegating only Phase 7
evicts nothing → Δ_faithful = 0% (BASE-independent). The ≥30% saving needs an
out-of-scope Phase-5 redesign (swarm-writes-to-disk / nested orchestrator).
VERDICT: DECLINED per measurement.

- agents/synthesis-agent.md — dormant, schema-conformant deliverable (NOT wired)
- lib/plan/synthesis-digest-schema.mjs — digest output contract (+ tests)
- scripts/synthesis-measure.mjs — deterministic Δ-accounting core (+ tests)
- tests/fixtures/synthesis/ — 7 exploration outputs + representative digest
- docs/T1-synthesis-poc-results.md — measurement + verdict (reproducible)
- CLAUDE.md — agent table row (doc-consistency: 24 agents)

Tests 670 → 695 (693 pass / 2 skip / 0 fail). `claude plugin validate` clean
(only the pre-existing root-CLAUDE.md warning). commands/trekplan.md untouched.

[skip-docs] rationale: no user-facing feature ships (NW3 declined; agent dormant
and unwired). The substantive doc is docs/T1-synthesis-poc-results.md; the
README/CHANGELOG roll-up for NW1–NW3 is the S13 coordinated release per
docs/W1-narrow-wins-plan.md §S13.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB
2026-06-18 17:58:39 +02:00

3.4 KiB
Raw Blame History

Dependency & Data-Flow Report — output of dependency-tracer

Task: Add a per-wave concurrency cap to trekexecute headless launches.

Import / call chain relevant to the task

commands/trekexecute.md  (Phase 2.6 prose)
  └─ generates → templates/headless-launch-template.md  (Bash here-doc)
        ├─ reads  SHARED_CONTEXT_FILE  (append-system-prompt-file)
        ├─ spawns claude -p  ×N  (one per wave member, backgrounded with &)
        └─ waits  via `wait` on collected PIDs
  └─ consumes → .session-state.local.json  (Handover 7; session graph)

Data flow

  1. session-decomposer emits a plan with wave groupings + a dependency graph.
  2. trekexecute Phase 2.6 turns each independent wave into a launch batch.
  3. The template loops over batch members and backgrounds each claude -p, collecting PIDs into a Bash array, then waits for the whole batch.
  4. There is no slot-limiting between "background member" and "wait" — the degree of parallelism equals the batch size.

Side effects

  • Each subprocess does git work under GIT_OPTIONAL_LOCKS=0; high concurrency raises the chance of index-lock contention (mitigated, not eliminated).
  • --max-budget-usd is per-subprocess; total spend scales with batch size, so a concurrency cap also indirectly bounds burst spend.

What a cap touches

  • Pure-addable: a maxParallel resolver in lib/ (reads plan/profile/flag, returns an integer ≥ 1). No existing module imports would change.
  • Wire-in: the template's loop must consume slots (e.g. a counting semaphore in Bash, or xargs -P <n>). This is the only behavioral edit.

No hidden dependents

Grepped for other call sites of the launch template — only trekexecute Phase 2.6 and the headless-launch-template test reference it. A cap is local in blast radius.

Resolver input provenance (what the cap reads)

The resolved integer must be derived from, in lookup order:

  1. CLI flag --max-parallel <n> — parsed by lib/parsers/arg-parser.mjs; highest precedence (operator override).
  2. Brief signalphase_signals already carries per-phase orchestration shape; a max_parallel hint here is honoured if no flag.
  3. Profilelib/profiles/ resolves --profile economy|balanced|premium; each profile can carry a max_parallel default. This is the same lookup order phase_models uses, so the resolver should reuse profile-resolver, not re-implement precedence.
  4. Hard defaultbatchSize (i.e. no cap / current behavior), so the change is a strict no-op until someone opts in.

Downstream of the cap

  • Budget: total burst spend = min(cap, batchSize) × per-member --max-budget-usd. A cap therefore tightens the worst-case spend envelope — worth noting in the plan's risk/observability section.
  • Git contention: fewer concurrent git-touching subprocesses → fewer index.lock races. GIT_OPTIONAL_LOCKS=0 reduces lock acquisition but does not serialize ref updates; the cap is the structural mitigation.
  • Classifier exposure: a smaller concurrent fan-out under auto/bypass lowers the surface the proliferation classifier (S7 F4) scrutinises.

Data-flow invariant to preserve

SHARED_CONTEXT_FILE is built once per wave and read by every member; the cap must not cause it to be rebuilt per-slot (would defeat the cache prefix). Slot limiting happens at spawn time only; the context file is wave-scoped, not slot-scoped.