stacklok / stacklok/mecatl

steer: drained mid-run steer is recorded with no model-visible framing, so the model abandons the in-flight task

Open
#1,590 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Go
Stars
152
Forks
16
Avg merge
14h 48m
Merged PRs (30d)
536

Description

Bug description

Typing a follow-up while a run is streaming (mecatui: enter mid-run on a
steer-capable server) makes the model abandon the in-flight task and start
the new one
. From the operator's seat it reads as "adding a queued request
cancels the previous one and starts processing the new one."

Verified against the code: no cancellation exists anywhere on this path.

  • Client: cmd/mecatui/ui/update.go (enqueuePromptsendSteer) sends a
    steer frame into the live Converse stream; it never cancels the run.
  • Engine: engine/agent/steer.go parks the steer in the single-slot
    steerInbox; the loop drains it at the next turn boundary — the
    in-flight provider turn always completes; the run is never interrupted.
  • Commit: engine/agent/steer.go (commitSteer) records the drained text via
    session.Session.RecordUserPromptWithParts(content.text, …) — a bare,
    unframed user message
    . There is no harness note, no prefix, and no
    prompt-layer contract anywhere (grep for steer framing in engine/agent /
    internal/app comes back empty) telling the model what a mid-run steer IS.

So the "termination" is the model's doing, structurally invited: mid-task it
receives a fresh user message carrying only the new instruction, and an LLM
treats the newest user instruction as superseding. It silently drops the
in-flight task and pivots. The harness's own docs promise the opposite
semantic — docs/tui.md keys table: "enter (while a run streams) → steer
the current run
when supported (applies at the next turn boundary)" —
steering means guiding the current work, not replacing it.

This is the ADR 0070 bug class, verbatim from AGENTS.md: "A model-facing
gate/affordance that depends on the model's behavior is incomplete without a
model-visible prompt instruction + a test proving that instruction lands in
the system prompt." The steer affordance depends entirely on the model
treating the injected message as mid-run guidance — and nothing tells it so
(the same failure shape as issue #206's plan-approval gate).

Steps to reproduce

  1. mecatui against a steer-capable server; start a long multi-turn task
    ("implement X across these files").
  2. While it streams, type a second, unrelated instruction ("also file an
    issue about Y") and press enter → status shows "steering…".
  3. At the next turn boundary the steer text is recorded as a plain user turn.
  4. Observe the model abandon task X (often without acknowledging it) and work
    exclusively on Y. Task X's remaining steps are silently lost.

Expected behavior

The documented steer semantic: the current task continues; the steer is
incorporated as guidance/additional work. At minimum the model should
explicitly acknowledge the pivot-vs-continue decision rather than silently
dropping the in-flight task.

Actual behavior

The model treats the steer as a replacement instruction; the previous task is
abandoned mid-flight. Indistinguishable, from the operator's seat, from the
harness cancelling the run.

Environment

  • mecatl @ ebb14c78, mecatui embedded engine (steer-capable), observed
    repeatedly during a long implementation session on macOS 26.5.1.

Additional context

Candidate fix-shape, following the repo's own idiom:

  1. Frame the drained steer at commitcommitSteer wraps the recorded
    continuation with a short harness note (the trusted-principal analogue of
    the *PostureNote constants), e.g. a prefix marking it as a MID-RUN
    STEERING NOTE: incorporate into the current task; do not abandon in-flight
    work unless it explicitly says to stop. The steer text itself is the
    principal's own input (trusted, WithTeamGoal-style provenance), so this
    is framing, not fencing.
  2. Or a prompt-layer contract — a steer posture note in the system prompt
    (the applyXPosture idiom) telling the model steered continuations are
    additive guidance; per ADR 0070 this needs the paired test asserting the
    instruction lands in the BUILT engine's prompt via the real factory path.
  3. mecatui's keys-table wording and the enqueuePrompt doc comment describe
    queue-mode as "follow-up when the turn ends" and steer-mode as "steer the
    current run" — whichever fix lands should make the runtime behavior match
    that documented split, and the local-queue drain (popAndSubmit, which
    merges ALL staged lines into ONE prompt at run end) may deserve the same
    framing treatment for its multi-task case.

Not a duplicate of the steer plumbing tests (steer_test.go etc.) — those pin
the wire/inbox mechanics (accepted/appended/retracted/too_late), which all
work as designed. The gap is exclusively the missing model-visible contract on
the recorded continuation.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in engine/agent/steer.go at commitSteer and trace the existing steer tests in engine/agent/steer_test.go; compare the recorded continuation with the documented behavior in docs/tui.md and the ADR 0070 guidance in AGENTS.md. Check the real engine factory and prompt path described by the issue. Done means a mid-run steer is model-visible as guidance, in-flight work is not silently abandoned, and a regression test proves the contract.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
ai, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.