Decision records
ADR 0034 — Advisory Optimization Pipeline
- Status: Accepted (2026-07-10) — the one net-new item of the forward-requirements evaluation (
loom/docs/requirements/2026-07-10-execution-kernel-forward-requirements-evaluation.md) (Requirement 8). Implemented:advisoryPipeline/formatAdvisoryin@loom/analytics+loom optimizeCLI. - Date: 2026-07-10
- Builds on:
@loom/analytics(runtimeIntelligence,costAnalytics,latencyBreakdown,decisionRecords,outcomeRecords),@loom/reasoning(rankedRouter), ADR 0018 (OUTCOME_RECORDED), ADR 0030
Context
Loom’s advisory-plane components already span the whole optimization loop — but as independent
lenses a caller wires by hand: runtimeIntelligence(events) (observe + analyze + recommend, one
shot), rankModels/rankFromOutcomes (rankings), recordOutcome → OUTCOME_RECORDED (evaluate),
rankedRouter(rankings) (the choice, logged via ctx.decide). What is missing — Requirement 8 — is
a single composed lifecycle (observe → analyze → recommend → evaluate → learn) and, per the
requirement, a way to evaluate a recommendation against the outcome it later produced.
Decision
Add an optional pure read-model advisoryPipeline(events, opts?) in @loom/analytics that composes
the existing components into one lifecycle. Two non-negotiables carry over: G1 (preserve
P2/P9 — never feed the fold) and G2 (additive/opt-in — uncalled ⇒ byte-identical).
Critical design choice — no new event type. A recommendation is derived; outcomes are
already durable (OUTCOME_RECORDED). A RECOMMENDATION_RECORDED event was considered and
rejected: it would add K5 schema churn and raise a “does a stored recommendation feed the fold?”
hazard for zero benefit. The pipeline is a pure projection over the existing log, which
satisfies all three success criteria (reproducible, never influences replay, disable-able) more
cleanly. No schema change; EVENT_SCHEMA_VERSION unchanged.
The lifecycle (advisoryPipeline(events, opts?): AdvisoryReport):
- observe — telemetry summary from
costAnalytics+latencyBreakdown. - analyze —
runtimeIntelligence(events)(analytics + model/outcome rankings + hot failures + budget). - recommend — a structured
Recommendation[]. Each carries a deterministic content-derivedid(stable across runs for the same content), akind(model-ranking|budget|hot-failure), asubject(the model / step / dimension it concerns), a rationale, and evidence. - evaluate — for a recommendation whose
subjectappears inDECISION_RECORDED+OUTCOME_RECORDED, compute the realized outcome stats (was choosing the recommended model actually higher-success?) as aRecommendationOutcome. This closes “record recommendation outcomes” as a correlation over durable facts, not a new stored fact. - learn — expose refreshed
rankFromOutcomes(events)rankings for the nextrankedRouter; the “loop” is simply the app re-calling the pipeline as new outcomes accrue.
Adoption stays explicit. The pipeline never mutates anything. An app that wants to act on a
recommendation does so through an ordinary ctx.decide (logged, replay-deterministic). There is no
code path by which the pipeline can influence an execution.
DX. loom optimize --db <path> [<executionId>] prints the report (read-only, mirrors
loom profile/loom introspect). formatAdvisory(report) renders it.
Consequences
- The full observe→analyze→recommend→evaluate→learn loop is available as one call, while remaining strictly advisory.
- Determinism untouched (G1): no event type, nothing on the durable path; same events → same report.
- Zero-overhead when uncalled (G2): a pure lens; not invoked ⇒ byte-identical.
- Recommendations are reproducible (content-derived ids) and disposable (derived, not stored).
Verification
advisoryPipelineproduces stable recommendations for a fixture log; the same events yield an identical report (determinism).- A recommendation whose subject was chosen and later had outcomes gets a populated
RecommendationOutcome; one without outcomes is left un-evaluated (not fabricated). loom optimizeprints the report and is a no-op-safe read over the store.
Amendments (from the forward-requirements evaluation)
- Requirement 8’s “record recommendation outcomes” is realized as a read-model correlation over
the already-durable
OUTCOME_RECORDEDfacts — noRECOMMENDATION_RECORDEDevent (rejected to preserve minimal-kernel + avoid K5 churn). - Pipeline is advisory-only; any auto-adoption variant is rejected (P10).
Source: docs/adr/0034-advisory-optimization-pipeline.md in the loom repository.