Context
Filed from PR #1482 (fix for #1473 recommendation 1, the running-retro compute-don't-assert prompt
rule). Both claude[bot]'s PR review and Codex's automated review independently flagged the same
consistency gap: the detached observer's headless analysis prompt (observer.py's
_analysis_prompt) instructs the analyzer to derive tool-call sequencing/batching/delegation claims
by "grouping tool-use events by API message id" — but summarize_record() (the function that
produces the distilled observations the headless run actually receives) never preserves a
message-id field. It extracts only type, timestamp, tools, a truncated text snippet, and
stop_reason.
PR #1482 closed the immediate correctness gap defensively: the prompt now states explicitly that an
absent message-id field makes the claim uncomputable, so the analyzer drops it rather than asserting
it from impression (the compute-don't-assert rule's fallback already covered this). That is safe,
but it means the headless observer path can never actually compute a sequencing/batching/delegation
finding — it can only ever drop that finding class. The in-session checkpoint path (which reads the
full transcript directly) is unaffected.
Codex's suggested remediation is a genuine capability restoration, not just a prompt clarification:
preserve enough structure in the distilled observations (either the real API message id, or an
explicit per-assistant-turn grouping boundary) so the headless path can compute these findings
instead of always dropping them. That is a change to summarize_record()'s output schema and the
observations' on-disk shape — a larger, separately-scoped piece of work, not a text-only fix, so it
was deferred out of #1482 rather than expanding that PR's scope.
Addendum (from claude[bot]'s review of PR #1482 commit 8f29a28): the same gap extends to the
dependency check PR #1482 later added (a computed sequencing fact isn't proof of a missed batching
opportunity when the calls are dependent). That check tells the headless agent to inspect the calls'
tool inputs and result content — but summarize_record() strips both: it preserves only tool
names (out["tools"] = [c.get("name") ...]) and result counts (out["tool_results"] = results),
never the actual input arguments or result values. So the dependency check is equally uncomputable
on the headless path today — same interim fallback applies (drop the Efficiency finding rather than
infer dependency from tool names alone), same underlying schema gap this item tracks.
Proposed work
- Add a stable grouping key to each distilled observation
summarize_record() emits — either the
transcript record's own message id (if present in the raw JSONL) or an explicit per-assistant-turn
boundary marker the headless prompt can group on.
- Preserve enough of each tool call's input arguments and result content (or a bounded/truncated
summary of them) for the headless path to check for a data/control/resource/side-effect dependency
between calls, not just their names and result counts.
- Update
observer.py's _analysis_prompt to reference the new fields instead of (or in addition
to) "API message id" / "tool inputs/results," and drop the corresponding "may not carry..." caveats
once those fields are reliably present.
- Verify token-cost impact is acceptable —
summarize_record()'s whole design goal is a token-cheap
distillation; any added field should stay minimal (an integer/short id, a truncated argument/result
summary, not full message objects).
Acceptance criteria
References
Metadata
| Field |
Value |
| Category |
session-flow / running-retro |
| Area |
session-flow |
| Ecosystem |
python |
This was generated by AI during work-loop execution.
Context
Filed from PR #1482 (fix for #1473 recommendation 1, the running-retro compute-don't-assert prompt
rule). Both
claude[bot]'s PR review and Codex's automated review independently flagged the sameconsistency gap: the detached observer's headless analysis prompt (
observer.py's_analysis_prompt) instructs the analyzer to derive tool-call sequencing/batching/delegation claimsby "grouping tool-use events by API message id" — but
summarize_record()(the function thatproduces the distilled observations the headless run actually receives) never preserves a
message-id field. It extracts only
type,timestamp,tools, a truncated text snippet, andstop_reason.PR #1482 closed the immediate correctness gap defensively: the prompt now states explicitly that an
absent message-id field makes the claim uncomputable, so the analyzer drops it rather than asserting
it from impression (the compute-don't-assert rule's fallback already covered this). That is safe,
but it means the headless observer path can never actually compute a sequencing/batching/delegation
finding — it can only ever drop that finding class. The in-session checkpoint path (which reads the
full transcript directly) is unaffected.
Codex's suggested remediation is a genuine capability restoration, not just a prompt clarification:
preserve enough structure in the distilled observations (either the real API message id, or an
explicit per-assistant-turn grouping boundary) so the headless path can compute these findings
instead of always dropping them. That is a change to
summarize_record()'s output schema and theobservations' on-disk shape — a larger, separately-scoped piece of work, not a text-only fix, so it
was deferred out of #1482 rather than expanding that PR's scope.
Addendum (from
claude[bot]'s review of PR #1482 commit8f29a28): the same gap extends to thedependency check PR #1482 later added (a computed sequencing fact isn't proof of a missed batching
opportunity when the calls are dependent). That check tells the headless agent to inspect the calls'
tool inputs and result content — but
summarize_record()strips both: it preserves only toolnames (
out["tools"] = [c.get("name") ...]) and result counts (out["tool_results"] = results),never the actual input arguments or result values. So the dependency check is equally uncomputable
on the headless path today — same interim fallback applies (drop the Efficiency finding rather than
infer dependency from tool names alone), same underlying schema gap this item tracks.
Proposed work
summarize_record()emits — either thetranscript record's own message id (if present in the raw JSONL) or an explicit per-assistant-turn
boundary marker the headless prompt can group on.
summary of them) for the headless path to check for a data/control/resource/side-effect dependency
between calls, not just their names and result counts.
observer.py's_analysis_promptto reference the new fields instead of (or in additionto) "API message id" / "tool inputs/results," and drop the corresponding "may not carry..." caveats
once those fields are reliably present.
summarize_record()'s whole design goal is a token-cheapdistillation; any added field should stay minimal (an integer/short id, a truncated argument/result
summary, not full message objects).
Acceptance criteria
summarize_record()'s output carries a grouping key sufficient to reconstruct tool-callbatching/sequencing without the raw transcript.
summarize_record()'s output carries enough of each tool call's inputs/results (or a boundedsummary) to support a dependency check between calls without the raw transcript.
observer.pycan compute a sequencing/batching/delegationfinding, and a data/control/resource/side-effect dependency check backing an Efficiency
finding, from the distilled observations alone — verified by a test that builds observations
from real transcript-shaped records and checks both round-trip correctly.
References
message-id gap and the tool-inputs/results gap)
plugins/session-flow/skills/running-retro/scripts/observer.py:summarize_record()(~line 51),_analysis_prompt()(~line 621)Metadata
This was generated by AI during work-loop execution.