The planner's transcript: scope it to the task, attribute who acted, and include orchestrator facts #78

Closed
opened 2026-09-30 12:40:02 +00:00 by dev-agent · 3 comments
Collaborator

Found preparing the second live run.

LLMPlanningAgent builds each planning turn from self._store.read_events() with no lower bound (planner.py), and LLMExecutor scans read_events() in full (executor.py, the loop before its turns). The canonical log is never cleared between tasks, and nothing marks where a task begins. After the first live run the store held 95 events from the slugify task. Starting a new task with use, the planner would get that whole conversation — the old task description, plans, approvals and review comments — as if it were the current one.

Expected: each task's model turns see only that task's events: from its use <project> (or the task-start event) onward. Earlier tasks stay in the log as the record (A2/A10), not as context. Resuming a parked work item (0.2) must still see that item's own history.

Options:

  • record the first event id of the task in _data (task_start_event) and read from there;
  • or tag events with task_id and filter on it, which also fits park-and-switch, where tasks interleave.

Tests: two tasks back to back; the second's planner turn contains none of the first's messages; a restart mid-second-task keeps the bound.

Also check: the context budgeting (context.py) and delegation (A13) readers of the log.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE

Found preparing the second live run. `LLMPlanningAgent` builds each planning turn from `self._store.read_events()` with no lower bound (`planner.py`), and `LLMExecutor` scans `read_events()` in full (`executor.py`, the loop before its turns). The canonical log is never cleared between tasks, and nothing marks where a task begins. After the first live run the store held 95 events from the slugify task. Starting a new task with `use`, the planner would get that whole conversation — the old task description, plans, approvals and review comments — as if it were the current one. **Expected:** each task's model turns see only that task's events: from its `use <project>` (or the task-start event) onward. Earlier tasks stay in the log as the record (A2/A10), not as context. Resuming a parked work item (0.2) must still see that item's own history. **Options:** - record the first event id of the task in `_data` (`task_start_event`) and read from there; - or tag events with `task_id` and filter on it, which also fits park-and-switch, where tasks interleave. **Tests:** two tasks back to back; the second's planner turn contains none of the first's messages; a restart mid-second-task keeps the bound. **Also check:** the context budgeting (`context.py`) and delegation (A13) readers of the log. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Author
Collaborator

Correction: the executor is not affected. Its full read_events() scan (LLMExecutor._scope) only finds the latest PlanSubmitted, and its turns read from that event on, so it already sees just the current milestone. The bug is the planner's alone: LLMPlanningAgent renders every event in the store.

Correction: the executor is **not** affected. Its full `read_events()` scan (`LLMExecutor._scope`) only finds the latest `PlanSubmitted`, and its turns read from that event on, so it already sees just the current milestone. The bug is the planner's alone: `LLMPlanningAgent` renders every event in the store.
dev-agent changed title from The transcript is not scoped to the task: a new task's planner sees the previous task to The planner's transcript: scope it to the task, attribute who acted, and include orchestrator facts 2026-09-30 12:58:06 +00:00
Author
Collaborator

Widened after the second live run. Two more problems come from the same design: the planner renders the shared log raw.

1. It can't tell who acted. The executor's tool calls and messages are in the same log and render as assistant turns. Before milestone 2, the planner (Opus) told the operator: "In the last turn I did milestone 1 myself: I ran commands, wrote files, and made commit cb70e1d… My role is to plan only… that commit was made by me, not by an executor, and nobody else has reviewed it." It repeated this in its next plan. Careful reasoning, but a false premise that our rendering created.

2. It lacks orchestrator facts. It said the commit was "not pushed or merged" and offered to open a PR, while PR #165 was already open. Publishing, PRs, branches and gate outcomes never enter the transcript.

Expected: the planner sees

  • its own conversation for this task: operator messages, its plans, questions and answers (the original scope of this issue);
  • orchestrator-written facts as clearly attributed entries, e.g. "milestone 1 executed by the executor (qwen3.8-27b): commit cb70e1d on branch …; summary: …" and "PR #165 opened";
  • the executor's finish summary as a report from the executor, not its raw turns.

Structural, not advisory: attribution belongs in the rendering (A10), not in a prompt asking the model to infer it. The Anthropic passthrough renderer and the OpenAI-shaped one both need it.

Tests:

  • a planner turn after a milestone contains no executor tool calls, and does contain the milestone fact and the PR;
  • the rendered roles never present executor actions as the planner's own.
**Widened after the second live run.** Two more problems come from the same design: the planner renders the shared log raw. **1. It can't tell who acted.** The executor's tool calls and messages are in the same log and render as *assistant* turns. Before milestone 2, the planner (Opus) told the operator: *"In the last turn I did milestone 1 myself: I ran commands, wrote files, and made commit cb70e1d… My role is to plan only… that commit was made by me, not by an executor, and nobody else has reviewed it."* It repeated this in its next plan. Careful reasoning, but a false premise that our rendering created. **2. It lacks orchestrator facts.** It said the commit was "not pushed or merged" and offered to open a PR, while PR #165 was already open. Publishing, PRs, branches and gate outcomes never enter the transcript. **Expected: the planner sees** - its own conversation for **this** task: operator messages, its plans, questions and answers (the original scope of this issue); - **orchestrator-written facts** as clearly attributed entries, e.g. "milestone 1 executed by the executor (qwen3.8-27b): commit cb70e1d on branch …; summary: …" and "PR #165 opened"; - the executor's `finish` summary as a report *from the executor*, **not** its raw turns. **Structural, not advisory:** attribution belongs in the rendering (A10), not in a prompt asking the model to infer it. The Anthropic passthrough renderer and the OpenAI-shaped one both need it. **Tests:** - a planner turn after a milestone contains no executor tool calls, and does contain the milestone fact and the PR; - the rendered roles never present executor actions as the planner's own.
Author
Collaborator

Confirmed live (bring-up run 3, 2026-10-02, on M1.3's branch). After milestone 1 and next milestone, the planner's milestone-2 turn cited the orchestrator's facts: milestone 1 finished, PR #208, the test count. It treated none of the executor's work as its own turns, and asked the operator what milestone 2 should cover instead of inventing work. M1.1 (work items) and M1.2 (per-role views and facts) together close this.

🤖 Generated with Claude Code

Confirmed live (bring-up run 3, 2026-10-02, on M1.3's branch). After milestone 1 and `next milestone`, the planner's milestone-2 turn cited the orchestrator's facts: milestone 1 finished, PR #208, the test count. It treated none of the executor's work as its own turns, and asked the operator what milestone 2 should cover instead of inventing work. M1.1 (work items) and M1.2 (per-role views and facts) together close this. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.