8.3 KiB
| created | path | project | tags | type | |||||
|---|---|---|---|---|---|---|---|---|---|
| 2026-05-30 | Sources/Dev | synthesis-layer |
|
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, ~10–30 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
handofffield 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
handofffrom 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 ~10–30 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.