ADR-061 — End-of-session synthesis is agent-orchestrated¶
Status · Accepted Date · 2026-04-29 TA anchor · /components/context, /components/session Related RFC · RFC-014 (closes); ADR-029 (transcripts), ADR-031 (propose-and-confirm)
Context¶
RFC-014 asked: when a Mode A session winds down, who orchestrates the
wrap-up? Two options were considered: an engine-side end_session()
convenience tool that triggers the agent's wrap-up sequence, or a
fully agent-orchestrated pattern where the agent uses existing tools
(propose_*, log_vocabulary_gap, tag, snapshot) without an
engine-side callback.
v0.5.0 ships the agent-orchestrated pattern; the engine has no
end_session tool. The Mode A v1 prompt's "End of session" section
documents the canonical wrap-up sequence; the transcript writer's
close() method is called by the MCP server when the stdio session
ends.
Decision¶
End-of-session is agent-orchestrated. No engine end_session tool.
The canonical wrap-up sequence (per Mode A v1 prompt):
- Suggest 0–2 taste-file additions noticed during the session — patterns
across multiple moves, not single observations. Use
propose_taste_update+confirm_taste_update. - Confirm any vocabulary gaps logged this session via
log_vocabulary_gap(full RFC-013 shape;satisfactionhonest). - Propose 1
notes.mdupdate summarizing the session's decisions viapropose_notes_update+confirm_notes_update. - Confirm pending exports if any (existing
export_finaltool). - Optionally
tagthe session's final snapshot for later reference.
Two propose-and-confirm cycles maximum. The agent doesn't drag out the wrap-up; it orchestrates a small, predictable closing.
The transcript writer's close() is called by the MCP server (or by
test harnesses) when the session terminates — typically on stdio EOF.
The footer line records entry_count and ended_at.
Rationale¶
- Fits the agent-only-writer discipline (ADR-024). The agent
initiates every state change. An engine-side
end_session()would either mutate state on its own (bad) or be a no-op wrapper (then why have it). - Same toolkit as the rest of the session. Wrap-up uses the tools the agent already used during the session — propose, confirm, tag, snapshot, log_gap. Agents don't need a special end-of-session vocabulary; transcripts read uniformly throughout.
- Prompt-driven, not engine-driven. The Mode A prompt's wrap-up section is where the orchestration lives. New patterns (e.g., a cross-session summary tool in Phase 5) get added to the prompt; the engine tool surface stays narrow.
- Test surface is unchanged. No new tool means no new schema, no new error category, no new ADR-057-style contract to lock down.
Alternatives considered¶
end_session(image_id, session_id, summary?)convenience tool. Rejected: would either bundle a chain of confirmations (which violates propose-and-confirm) or be a thin wrapper overtranscript.close()that doesn't earn its place.- Engine-side scheduler that fires "wrap up now" prompts. Rejected: agent agency. The photographer signals the end; the agent responds via the prompt's instructions. No engine timer.
- No documented wrap-up at all — let agents improvise. Rejected: the prompt's "End of session" section earns its place by capping the cycles at two and naming the canonical sequence. Without it, agents drift toward over-orchestration ("would you like me to also re-tag, re-snapshot, summarize again?").
Consequences¶
Positive:
- Session transcripts read uniformly: same tool_call /
tool_result / proposal / confirmation entries throughout, no
special markers for the wrap-up phase.
- The Mode A prompt is the single source of orchestration truth.
Iterations (Mode A v2 in Slice 6) can refine the wrap-up without
changing the engine.
- Implementing wrap-up logic against a different agent (Mode B in
Phase 5) is just a new prompt; no engine work.
Negative:
- Photographers don't get a single tool button to "wrap this up." The
agent has to know to do it. Mitigation: the Mode A v1 prompt's "End
of session" section makes the sequence explicit.
- Cross-session reflection ("across the last 3 sessions you've reached
for X") is harder to add later — it would either be a new tool or
baked into read_context. Slice 6 evidence dictates which.
Implementation notes¶
- No new tool in v0.5.0.
- The Mode A v1 prompt's "End of session" section (lines 121–134 of
src/chemigram/mcp/prompts/mode_a/system_v1.j2) documents the canonical sequence. v2 in Slice 6 will refine based on real-session evidence. chemigram.core.session.SessionTranscript.close()writes the footer- closes the file handle. Idempotent.
- Test evidence:
tests/integration/mcp/test_full_session_with_context.pyexercises propose_taste + confirm_taste + log_gap + propose_notes + confirm_notes + transcript.close() in the wrap-up sequence; the transcript footer + entry counts are asserted.