wiki-vault/Sources/Dev/synthesis-layer.md
2026-05-30 01:17:45 +00:00

71 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
created: '2026-05-30'
path: Sources/Dev
project: synthesis-layer
tags:
- mcp
- dispatch
- lovebug
- claude-code
- automation
type: project-plan
---
# Synthesis Layer
Short-term memory for agent orchestration. The execution journal is the durable long-term record (immutable, complete, append-only). The synthesis layer is the small, bounded, fast working view that the harness actually reads when deciding the next move. Workers write journal entries; the synthesis layer is how those entries become consumable context for Lovebug without bloating Lovebug's own window.
This plan is **not fully baked** — captured here so the design discussion doesn't get lost. Iteration expected. Companion to `execution-journal`.
## Goal
Give the harness (Lovebug) a bounded, current, queryable view of project state that:
- Stays small regardless of how long a project runs — the harness's per-cycle context cost is roughly constant whether step 3 or step 50.
- Carries forward the things that matter (active constraints, rule-outs, current state) so a new actor doesn't re-propose what's already been tried and rejected.
- Is recoverable from the journal — the synthesis layer can be lossy *safely* because the journal is the system of record.
- Doesn't require Lovebug to read raw worker output. Ever. Lovebug consumes conclusions; the synthesis layer produces them.
## Locked Decisions
- **Long-term / short-term split.** Journal = long-term (durable, complete, immutable). Synthesis layer = short-term (small, current, volatile). The harness reads short-term; nobody holds long-term in context. The journal is queryable on disk but not normally read in the hot path.
- **Per-step summaries, not cumulative.** Each summary describes its own step and is immutable once written. No fold-forward, no rewriting, no carry-forward discipline. The "history" is the ordered list itself. Trades read-time cost (read several summaries) for write-time safety (no drift surface inside any summary).
- **Why not cumulative.** A cumulative rolling summary introduces per-step drift at the synthesis pass and the recovery story is circular — "regenerate from the journal" is itself the hard synthesis problem we don't fully trust. Per-step summaries sidestep this: no artifact gets rewritten, so no artifact can drift.
- **Synthesis is deferred until the list is long enough to need compression.** At current scope (small projects, ~1030 steps), reading the full list of per-step summaries is fine — token budget is not the constraint. A compression layer is added *later*, when project length forces it, with real data about what's worth keeping. Premature compression is a synthesis-prompt-design problem we don't have yet.
- **Synthesizer (when it eventually exists) is a stateless function over durable state.** Same pattern as every other layer in the stack. Spawn → read inputs → emit output → terminate. Never accumulates context across invocations.
- **Drift is bounded, not eliminated.** 100% accurate history is not the goal; it's never been a thing. Good checks and balances anchored to the journal (immutable reference) are. Bounded sawtooth via mechanical comparison against ground truth, not unbounded ramp.
## Open Items
- **What exactly is a per-step summary?** Probably very close to the worker's own structured `handoff` field from the journal — goal, current state, next step, ruled-out + why. The synthesis layer may collapse to "lightly normalize the worker's handoff and append to a list" for v1. If so, no separate synthesizer agent is needed yet — the worker's handoff *is* the summary.
- **When does a real synthesizer get built?** Trigger: the per-step list grows past the point where reading it all on every harness cycle is comfortable. At that point, add a compression pass that produces a bounded rolling brief from the list. Defer until it's a real problem with real numbers.
- **Read shape for the harness.** `get_project_summaries(slug)` returning the ordered list, newest-first, with optional limit. Probably the same MCP surface as the journal — synthesis layer may just be a view over journal handoff fields rather than a separate table. To be decided.
- **Drift detection for v2 (cumulative).** If/when compression is added, drift checks are mechanical and anchor to the journal: structural invariants (active rule-outs must persist unless explicitly reversed), periodic reconciliation (rebuild from journal, diff against rolling brief), harness sanity checks (claimed task done → testable plan condition holds). None of these are "another agent's second opinion" — they're measurements against immutable references. Not building this for v1; documenting it so we don't have to re-derive when we do.
- **Speed/volume asymmetry.** The reason this *feels* harder than human institutional drift is correction-loop tempo, not novelty. Agents can compound 50 generations of lossy retelling in an afternoon while the correction loop (Travis reading on Tuesday) is slow. Implication: checks need to run automatically and frequently *if* we ever go cumulative. Another reason per-step summaries are the right v1.
## Phases
### Phase 1 — Trivial synthesis (v1)
- [ ] Confirm that the worker's structured `handoff` from the journal can serve directly as the per-step summary. If yes, no new layer is built — the synthesis layer is a *read pattern* over the journal, not a separate write path.
- [ ] Define the MCP read: `get_project_summaries(slug, limit?, order=desc)` — ordered list of handoffs across the project. Newest-first by default.
- [ ] Harness reads the summary list + immutable plan to decide the next step. No synthesizer agent in this phase.
### Phase 2 — Compression (only when triggered)
- [ ] Trigger condition: summary list is long enough that reading it all on every harness cycle is uncomfortable. Define "uncomfortable" with real numbers when we get there, not now.
- [ ] Design the synthesizer agent: stateless, spawns per harness cycle (or per N worker completions), reads recent summaries, emits a bounded rolling brief, terminates.
- [ ] Open question deferred to this phase: cumulative rolling brief vs. windowed (last-N summaries compressed). Cumulative is smaller-steady-state but reintroduces drift; windowed is simpler but doesn't bound size as tightly. Decide with data.
### Phase 3 — Drift detection (only if Phase 2 is cumulative)
- [ ] Structural invariant check: active rule-outs from prior brief persist in current brief unless a journal entry explicitly reverses them. Mechanical, deterministic.
- [ ] Periodic reconciliation: regenerate brief from journal, diff against rolling brief, flag divergence.
- [ ] Anchor every check to the journal (immutable) or the plan (immutable). Never anchor a drift check to another synthesis pass — that's the regress.
## Notes
**Why this is a separate plan from `execution-journal`.** The journal is the ground-truth store. The synthesis layer is the consumption pattern on top. Journal is build-now; synthesis layer is build-as-needed. Keeping them separate prevents the journal scope from creeping into "and also a synthesizer" when the journal stands on its own.
**On the "history" framing.** The instinct that motivated this whole thread — "doomed to repeat history if you don't know it" — resolves cleanly under per-step summaries: history *is* the list, no information is dropped, the harness can read as far back as it needs. Under cumulative summaries the same instinct produces a real problem (silently dropped rule-outs cause re-proposal) which is why cumulative needs the carry-forward discipline + drift checks. Per-step is the simpler answer that doesn't trade away the safety property.
**On scope.** This whole discussion was over-scoped relative to current project size. At ~1030 steps per project, the simple read-the-whole-list approach is correct and finishes the problem. The deeper analysis (cumulative summaries, drift detection, synthesizer regress) is captured here so when scale forces a revisit, the reasoning isn't redone from scratch — and so the simple v1 is "simple because we reasoned to it," not "simple because we didn't look."
**Companion plan.** See `execution-journal` for the durable-store side of this architecture. The two were discussed together and split intentionally — journal is concrete and near-term; synthesis is a pattern that may or may not need its own agent depending on how the journal-handoff field shapes up.