Skip to content

feat(phase-32-wave-2): adapter-side consolidation — MCP helpers + FileJsonStore + shared specialist prompt#41

Open
fall-development-rob wants to merge 1 commit into
mainfrom
phase-32-wave-2-impl
Open

feat(phase-32-wave-2): adapter-side consolidation — MCP helpers + FileJsonStore + shared specialist prompt#41
fall-development-rob wants to merge 1 commit into
mainfrom
phase-32-wave-2-impl

Conversation

@fall-development-rob

Copy link
Copy Markdown
Owner

Summary

Phase 32 Wave 2 of the architecture refactor planned in docs/plans/phase-32-architecture.md. Three independent consolidations shipped via parallel ruflo swarm; all touch packages/harness/ only.

Job Result
A. MCP transport helpers toCanonicalTool + unwrapMcpContent central in name-resolver.ts; 3 transports consume. 3 → 1 declarations each, closes the [0] vs .find() divergence.
B. FileJsonStore<T> primitive New src/persistence/. audit/chain.ts 77 → 27 LOC. memory/session.ts 90 → 38 LOC. Two structurally identical stores → one typed primitive.
C. Shared specialist prompt 4 governance-prose constants in shared-prompt.ts; 8 specialists refactored to interpolate. Single source of truth for the 4 cross-cutting rules (operating mode / tool envelope / no-LLM-arithmetic / traceability footer).

Tests: 159 passed (110 → 159, +49). Typecheck clean. Lockfile guard passes.

Why

  • MCP helpers: previously, wireToolToCanonical (stdio) / sdkToolToCanonical (sse) / sdkToolToCanonical (http) were three near-identical 10-line copies. The content-unwrap helpers had a real bug surface — stdio and sse used result.content[0] while http used .find(). When a server returns a non-text leading part (resource ref), stdio/sse would crash. Standardized on .find().
  • FileJsonStore: audit and session stores were the same 80-LOC pattern (atomic writes, mkdir-on-init, list/filter/sort) parametrized by record type and filter shape. Now both are ~30-LOC closures over a typed primitive.
  • Shared prompt: 4 governance rules ("LLM-generated arithmetic prohibited", { "input": { ... } } envelope, traceability table mandate, delegated-mode preamble) were repeated across 8 specialist files. A future strengthening edit would have required 8 file changes; now it's 1. A test asserts every specialist still mentions the canonical phrases — silent drift becomes a CI failure.

Architecture invariants closed

Invariant Pre Post
toCanonicalTool / unwrapMcpContent declarations 3 each 1 each
Audit + Session file-store implementations 2 (duplicated) 1 (FileJsonStore)
.find() vs [0] content-unwrap divergence open closed (.find())
Specialist governance-prose duplication 8 × ~25 LOC 1 module + 8 imports

Files

New

  • packages/harness/src/persistence/{file-json-store,index}.ts — typed file-backed store
  • packages/harness/src/agents/specialists/shared-prompt.ts — 4 governance constants
  • packages/harness/tests/file-json-store.test.ts — 10 tests
  • packages/harness/tests/shared-prompt.test.ts — 29 tests (8 specialists × 3 governance asserts + 5 constant-level)

Modified (consumed shared helpers / refactored)

  • mcp-client/{stdio,sse,http,name-resolver}.ts — 3 transports consume central helpers
  • audit/chain.ts (77 → 27 LOC), memory/session.ts (90 → 38 LOC) — thin wrappers over FileJsonStore
  • 8 specialist files — interpolate ${SPECIALIST_OPERATING_MODE} etc.
  • index.ts — public exports for createFileJsonStore
  • tests/mcp-client.test.ts — +10 tests for new helpers

Deleted: nothing — all consolidations are non-breaking.

Test plan

  • Lockfile guard passes (bash scripts/check-no-workspace-lockfiles.sh)
  • npm run typecheck clean across harness
  • npm test in harness: 159 passed | 8 skipped
  • All existing tests (audit-chain, session-memory, mcp-client-sse, mcp-client-http, providers-*, agents, core) still pass — public APIs preserved
  • CI green on this PR

What's next (Phase 33)

Wave 2C centralized governance prose into TypeScript constants, but the deeper architectural answer is skill-driven specialists: .claude/skills/cfa/* already carry structured frontmatter; the harness should consume them instead of baking prose into agent code. Phase 33 will:

  1. Add a skill loader: parse .claude/skills/<id>/SKILL.md frontmatter at startup
  2. Inject the skill body as system context (or expose it as a skill_invoke virtual tool)
  3. Reduce each specialist AgentDef to ~10 lines ({ id, model, skills: [...], tools: [...] })

The shared-prompt fragments from this PR migrate verbatim into a new corp-finance-analyst-shared skill file. The 8 specialist .ts files shrink by ~95%.

🤖 Generated with Claude Code

…eJsonStore + shared specialist prompt

Phase 32 Wave 2 of the architecture refactor planned in
docs/plans/phase-32-architecture.md. Three independent consolidations
shipped via parallel ruflo swarm; all touch packages/harness/ only.

A. MCP transport helpers centralized
   • New `toCanonicalTool(wireTool, prefix)` and `unwrapMcpContent(result)`
     in packages/harness/src/mcp-client/name-resolver.ts.
   • `stdio.ts`, `sse.ts`, `http.ts` now import the shared helpers.
   • Standardized on `.find()` semantics for content extraction (closes
     the previous `[0]` vs `.find()` divergence — stdio + sse both used
     `[0]`, only http used `.find()`; `.find()` is the more defensive
     choice when servers return non-text leading parts like resource refs).
   • LOC: −75 across 3 transports, +57 in name-resolver = net −18.

B. FileJsonStore<T> primitive extracted
   • New packages/harness/src/persistence/file-json-store.ts (93 LOC) and
     persistence/index.ts (8 LOC) — atomic writes via .tmp + rename,
     mkdir-on-init, list/load/save/delete with optional filter + compare.
   • src/audit/chain.ts: 77 → 27 LOC (createFileAuditSink is now a thin
     wrapper that builds an AuditRecord-typed FileJsonStore + composes
     the {agentId, since} filter).
   • src/memory/session.ts: 90 → 38 LOC (createFileSessionStore similarly
     thin; updates updated_at then delegates to the store; lists with
     {agentId, status} filter).
   • Public surface adds `createFileJsonStore`, `FileJsonStore`,
     `FileJsonStoreOptions` exports for any downstream consumer that
     wants a typed file store of their own.

C. Shared specialist prompt fragments
   • New packages/harness/src/agents/specialists/shared-prompt.ts (78 LOC)
     exports four template constants:
       SPECIALIST_OPERATING_MODE      — delegated-mode preamble
       TOOL_CALLING_CONVENTION        — bare-name + { input: {...} }
       QUALITY_GATE_NO_LLM_ARITHMETIC — every number from a tool call
       TRACEABILITY_TABLE_FOOTER      — | # | Tool | Inputs | Output |
   • All 8 specialist files (equity, credit, fixed-income, derivatives,
     quant-risk, macro, private-markets, esg-regulatory) refactored to
     interpolate the shared constants instead of carrying verbatim or
     paraphrased copies. Domain-specific content (role, tool inventory,
     calling examples) stays per-file.
   • A few minor prose alignments where specialists had paraphrased
     governance prose (credit, quant-risk) — converted to canonical
     wording so a single edit propagates.

Tests:
  • 159 passed | 8 skipped (was 110 at Wave 1 baseline) — net +49 tests:
    file-json-store.test.ts (10), mcp-client.test.ts (+10 for the new
    helpers), shared-prompt.test.ts (29 — `it.each` over 8 specialists ×
    3 governance assertions + 5 constant-level checks).
  • Existing tests (audit-chain, session-memory, mcp-client-sse,
    mcp-client-http, providers-*, agents, core) unchanged and passing —
    public APIs of all stores and transports preserved.

Net repo impact: 16 files modified + 4 new = 20 files, +328 / −369 LOC
inside packages/harness/ (~−40 net). The win is structural rather than
LOC-headline: future governance/transport edits become 1-file changes.

Phase 32 Wave 2 success metrics:
  • toCanonicalTool / unwrapMcpContent declarations: 3 each → 1 each ✓
  • Audit + Session file-store implementations: 2 → 1 (FileJsonStore<T>) ✓
  • .find() vs [0] content-unwrap divergence: closed ✓
  • Specialist governance prose duplication (8 × ~25 LOC): centralized ✓

Future: Phase 33 will replace prompt-fragment interpolation with a true
skill-driven specialist model (parse .claude/skills/*/SKILL.md frontmatter,
inject as system context, reduce specialist AgentDefs to ~10 lines each).
Wave 2C's shared-prompt.ts is the stepping stone — the canonical text
already lives in one place, ready to migrate verbatim into a skill file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fall-development-rob added a commit that referenced this pull request May 10, 2026
Phase 34 closes the Phase 31 BC4 "Learning & Adaptation" bounded context
that was documented but never built. Uses ruvnet/RuVector (Rust core +
NAPI-RS bindings) for HNSW similarity search over past dispatch
trajectories.

Architecture:
  • New packages/harness/src/reasoning/ — ReasoningBank port + RuVector
    impl + indexer + embeddings helper
  • agent-loop.ts gets a fire-and-forget reasoning.index() call after
    each successful dispatch (never on the hot path; index failures
    log but don't fail the dispatch)
  • Chief specialist skill (Phase 33 output) gains a virtual
    recall_similar(prompt, k) tool — same dispatch-loop intercept
    pattern as delegate_to_*
  • CLI gains --reasoning-dir <dir> mirroring --audit-dir / --session-dir

Why RuVector specifically:
  • HNSW with GNN learning — routing patterns improve with corpus size
  • Hyperedge knowledge graph — Cypher queries for issuer/jurisdiction/
    instrument-type recall ("every Argentine private placement the
    chief has analyzed")
  • NAPI bindings — drops into the harness without adding a service
  • .rvf cognitive container format — portable, single-file persistence

Why NOT RuVector for audit + session (the user's earlier question):
  • Both stores have pure key-value access patterns, no similarity
    search, no embeddings. FileJsonStore<T> from Phase 32 Wave 2B is
    the correct primitive for those.
  • RuVector is the right tool for similarity recall, NOT for append-only
    state. They're complementary — same split AgentDB uses internally.

4 waves, ~9 days. Wave 1 ships the bank port + RuVector wrapper +
embeddings helper. Wave 2 wires the indexer into agent-loop. Wave 3 adds
recall_similar as a virtual tool the chief can invoke. Wave 4 adds
knowledge-graph (Cypher) queries + ADRs.

Depends on Phase 33 — the chief skill body (in .md form, not TS) is the
clean place to add the recall_similar paragraph. Adding it to a
TypeScript-defined chief specialist would require a code release for
every prose tweak.

Sequence: Phase 32 Wave 2 (PR #41) → merge → Phase 33 Waves 1-4 →
Phase 34 Waves 1-4. Phases 33+34 together net ~-1,500 LOC.

Open questions in the doc: embedding provider choice (OpenAI vs
RuVector's local model vs Voyage), multi-tenant isolation, PII
redaction at index time.

No code changes in this commit — planning doc only. Both Phase 33 and
Phase 34 plans now sit on the phase-33-skill-driven-planning branch
per user direction (one branch + one PR for the planning track).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fall-development-rob added a commit that referenced this pull request May 11, 2026
Phase 33 replaces the 8 code-defined specialist .ts files with skill-driven
specialists, consuming the existing four CFA surfaces in their proper roles:

  Skills          — domain prose + governance fragments (single source of
                    truth, works in Claude Code AND harness)
  MCP plugins     — the 623 callable tools (unchanged)
  Plugin manifests — distribution (unchanged)
  cfa CLI         — manifest loader (harness reuses; doesn't reimplement)
  Harness runtime — dispatch loop (Phase 31, unchanged)

Approach: a small skill loader (~200 LOC) in packages/harness/src/skills/
parses .claude/skills/<id>/SKILL.md, resolves `extends:` references,
assembles the final system prompt. Two strategies behind one interface:
direct fs read (no CLI dependency) and `cfa managed-agent deploy <slug>
--json` (when the CLI is on PATH).

Result:
  • 8 specialist .ts files (~2,300 LOC) → 8 skill files (~50 LOC each)
  • shared-prompt.ts (Wave 2C) migrates verbatim into a shared skill
  • Persona updates no longer require a code release
  • Same skill serves Claude Code interactive use AND harness dispatch
  • cfa CLI becomes a real consumer of itself for the harness path

4 waves, ~7 days total. Wave 1 ships the loader and migrates chief-analyst
as a canary. Waves 2-4 are mechanical: 8-agent specialist migration swarm,
CLI loader integration, cleanup + ADRs.

Phase 33 partly revises Phase 31's ADR-037 (Agent Registry as Code) —
the registry stays code, but agent DEFINITIONS move to skills.

No code changes in this commit — planning doc only. Awaiting Phase 32
Wave 2 (PR #41) merge before kicking off Phase 33 Wave 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fall-development-rob added a commit that referenced this pull request May 11, 2026
Phase 34 closes the Phase 31 BC4 "Learning & Adaptation" bounded context
that was documented but never built. Uses ruvnet/RuVector (Rust core +
NAPI-RS bindings) for HNSW similarity search over past dispatch
trajectories.

Architecture:
  • New packages/harness/src/reasoning/ — ReasoningBank port + RuVector
    impl + indexer + embeddings helper
  • agent-loop.ts gets a fire-and-forget reasoning.index() call after
    each successful dispatch (never on the hot path; index failures
    log but don't fail the dispatch)
  • Chief specialist skill (Phase 33 output) gains a virtual
    recall_similar(prompt, k) tool — same dispatch-loop intercept
    pattern as delegate_to_*
  • CLI gains --reasoning-dir <dir> mirroring --audit-dir / --session-dir

Why RuVector specifically:
  • HNSW with GNN learning — routing patterns improve with corpus size
  • Hyperedge knowledge graph — Cypher queries for issuer/jurisdiction/
    instrument-type recall ("every Argentine private placement the
    chief has analyzed")
  • NAPI bindings — drops into the harness without adding a service
  • .rvf cognitive container format — portable, single-file persistence

Why NOT RuVector for audit + session (the user's earlier question):
  • Both stores have pure key-value access patterns, no similarity
    search, no embeddings. FileJsonStore<T> from Phase 32 Wave 2B is
    the correct primitive for those.
  • RuVector is the right tool for similarity recall, NOT for append-only
    state. They're complementary — same split AgentDB uses internally.

4 waves, ~9 days. Wave 1 ships the bank port + RuVector wrapper +
embeddings helper. Wave 2 wires the indexer into agent-loop. Wave 3 adds
recall_similar as a virtual tool the chief can invoke. Wave 4 adds
knowledge-graph (Cypher) queries + ADRs.

Depends on Phase 33 — the chief skill body (in .md form, not TS) is the
clean place to add the recall_similar paragraph. Adding it to a
TypeScript-defined chief specialist would require a code release for
every prose tweak.

Sequence: Phase 32 Wave 2 (PR #41) → merge → Phase 33 Waves 1-4 →
Phase 34 Waves 1-4. Phases 33+34 together net ~-1,500 LOC.

Open questions in the doc: embedding provider choice (OpenAI vs
RuVector's local model vs Voyage), multi-tenant isolation, PII
redaction at index time.

No code changes in this commit — planning doc only. Both Phase 33 and
Phase 34 plans now sit on the phase-33-skill-driven-planning branch
per user direction (one branch + one PR for the planning track).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant