Skip to content

feat(roadmap): a parser failure gets one smaller-edit message #3571

Closed as not planned

Description

@dan-petty

Problem or Motivation

Three stops (#2554, #2579 and #3318) had class parser (XML syntax error ... unexpected EOF) and cost 1.77M input tokens. The upstream report ollama/ollama PR 18571 says the failure is deterministic for one conversation. _turn sends the same conversation again with no new text (CONST_ROADMAP_IMPLEMENT_MODEL_RESENDS is 1), so the resend cannot help. Split from #3053.

Proposed Solution & Architectural Design

  • On class parser, the resend carries one message that asks for a smaller edit.
  • A second parser failure stops the step with class parser. The work of the group stays as wip: commits.
  • Acceptance: a stub model that fails twice with a parser error stops the step after one smaller-edit message; the message is in the second request.

Target Subsystem

src/devops_cli/roadmap/implement_step.py (_run_agent, _resend_after).

Existing Tool Considered

No open tool covers a smaller-edit message for a parser error of a provider. The project builds it: about 30000 tokens to build, 2000 each quarter to keep, 6000 to fix one miss, about 2 hours.

Source: #3053

Proposed design

Problem statement

A parser error of a provider is deterministic for one conversation. The step resends a failed request with one message that asks for a smaller edit. A second parser failure must stop the step with class parser and keep the wip: commits. The tree at this commit still moves a parser failure to the next tier, so the step can continue instead of stopping.

Acceptance criteria

Requirement: Parser resend asks for a smaller edit

On a parser failure of a model call, the step SHALL send the failed request again with one message that asks for a smaller edit.

Scenario: The second request holds the smaller-edit message

  • WHEN the stub model fails the first request with a parser error and answers the second request
  • THEN the second request ends with the implement_parser_smaller_edit text, and the step made two requests

Requirement: A second parser failure stops the step

When the request that carries the smaller-edit message fails with a parser error, the step SHALL stop with class parser and move no tier.

Scenario: Two parser failures stop the step on the first tier

  • WHEN the stub model fails both requests with a parser error and the ladder names two tiers
  • THEN the outcome is stopped with failure_class parser, the step made two requests, and tiers names only the first tier

Requirement: A parser stop keeps the wip commits

A step that stops with class parser SHALL keep the wip: commits of its group and discard only the work after the last one.

Scenario: The checkout is at the last wip commit

  • WHEN the agent ticked a task whose verification passed and the stub model then failed with a parser error twice
  • THEN the checkout is at the last wip: commit, and the stop reports the discarded diff

Requirement: Other in-stream failures keep the plain resend

A failure that is not of class parser SHALL send the request again with the same messages and no new text.

Scenario: An in-stream 400 that is not a parser error adds no message

  • WHEN the stub model fails the first request with an in-stream 400 that the classifier does not read as parser
  • THEN the second request holds the same messages as the first, and no smaller-edit text

Existing Tool Considered

No alternatives check has run. No open, maintained tool or library covers a smaller-edit message for a provider parser error. The project builds it: about 30,000 tokens to build, 2,000 each quarter to keep, 6,000 to fix one miss, about 2 hours. The tree already holds the message and the resend, so the remaining change is small.

Key questions

  • Which failure class gets the smaller-edit message?: Only class parser. _run_turn sets the class with _run_class, which calls classify_failure on the reason. The parser rule comes before the provider rule, because an Ollama that cannot parse a tool call answers with HTTP 400. _run_agent reads the class in run.failure_class. [sources: src/devops_cli/roadmap/implement_step.py, src/devops_cli/roadmap/failure.py]
  • How does the smaller-edit message reach the second request?: _ask_smaller_edit appends one UserPromptPart with MESSAGES.roadmap.implement_parser_smaller_edit to the last ModelRequest of the resend conversation. _run_agent then calls _run_turn with None, so the second request ends with that text. (best practice) [sources: src/devops_cli/roadmap/implement_step.py]
    • Chosen: Append a UserPromptPart in _ask_smaller_edit. It adds one part to the request that _resend_after returns, and _run_turn already resumes a conversation.
    • Rejected: Pass the text as the message of _run_turn. It starts a new user turn, not a resend of the failed request.
    • Rejected: Append a whole ModelRequest to the conversation. It builds a message by hand and puts the text outside the failed request.
  • Where does the text of the smaller-edit message live?: In MESSAGES.roadmap.implement_parser_smaller_edit, beside implement_repair_turn. It asks for one file or one small part of a file in each tool call, and it is bounded. (best practice) [sources: src/devops_cli/lang/en/messages.py, src/devops_cli/roadmap/implement_step.py]
    • Chosen: Add implement_parser_smaller_edit to MESSAGES.roadmap. The step keeps its texts in MESSAGES.roadmap, and the text says only what a parser failure needs.
    • Rejected: Reuse implement_repair_turn. It names a failed check and the paths of a repair turn, and a parser failure has neither.
  • How many sends does one parser failure get, and do they count in the limits?: One. CONST_ROADMAP_IMPLEMENT_MODEL_RESENDS stays 1. _resend_after adds the failed request and the replies before it to the usage of the conversation, so the request limit counts every send. [sources: src/devops_cli/config/constants.py, src/devops_cli/roadmap/implement_step.py]
  • Does a parser failure try the next tier of the ladder?: No. A second parser failure must stop the step with class parser and must not run the next tier. _moves_tier must return false for a parser failure, even when provider_error is set, because the parser failure is deterministic for one conversation and another tier would get the same conversation. (best practice) [sources: src/devops_cli/roadmap/failure.py, src/devops_cli/roadmap/implement_step.py, openspec/changes/3060-a-model-ladder-for-retries-with/design.md]
  • What delivers this item, and what happens when that pull request merges?: The item delivers with its own pull request. The pull request changes _moves_tier so a parser failure moves no tier, and adds the regression tests. The tree at this commit already holds _ask_smaller_edit and implement_parser_smaller_edit. When the pull request merges, it closes feat(roadmap): a parser failure gets one smaller-edit message #3571, and no separate close is needed. (best practice) [sources: src/devops_cli/roadmap/implement_step.py, src/devops_cli/lang/en/messages.py]

Fit

Fits in one pull request.

Suspected block

None.

Dependencies

None.

Activity

  1. added
    type/featureNew user-facing functionality or capability addition
    scope/roadmapThe roadmap jobs: intake, refine, stage, implement and the Service that runs them
    status/newStatus New: the item is not ready yet
    effort/lowEffort Low: little work
    on Nov 5, 2026
  2. dan-petty commented on Nov 5, 2026

    @dan-petty
    OwnerAuthor

    Intake placed this item to the backlog: intake puts every new item in the backlog. A person plans the milestones.

    • Type: type/feature. The feat prefix of the title gives the type.
    • Scope: scope/roadmap. The (roadmap) scope of the title gives the scope.
    • Priority: P2-Medium. It reduces token waste and improves step reliability but does not block a release.
    • Value: Medium. It saves tokens and makes the implement step recover from one parser miss.
    • Effort: Low. The body states about 2 hours of work in one file with two functions.
    • Related: fix(roadmap): an implement step stops when Ollama cannot parse a qwen3-coder tool call, and the stop hides the cause #2922, which asks for a different change. Both handle parser failures in the implement step, but #2922 asks to show the cause of the stop while the candidate asks to change the resend to a smaller-edit message.
  3. added
    priority/p2-mediumStandard priority feature work and architectural improvements
    on Nov 5, 2026
  4. dan-petty commented on Nov 6, 2026

    @dan-petty
    OwnerAuthor

    The work of this issue is built on the branch of #3053 (change 3053-parser-smaller-edit): the resend after a parser failure carries one message that asks for a smaller edit, and the second parser failure of a step stops it with class parser. The pull request closes #3053, not this issue. Close this issue as delivered when that pull request merges.

  5. devops-cli-service commented on Nov 6, 2026

    @devops-cli-service
    Contributor

    Refine cannot set this item to Ready at 29309f0 until a person answers the questions below. Reply in a comment or edit the body, and the next refine round looks at the item again.

    • What delivers this item, and what happens when that pull request merges? (a person decides)
  6. added
    status/readyStatus Ready: problem, acceptance criteria and an answer to each key question
    and removed
    status/newStatus New: the item is not ready yet
    on Nov 6, 2026
  7. devops-cli-service commented on Nov 6, 2026

    @devops-cli-service
    Contributor

    Status set to Ready at 8714474 (see proposed design in issue body).

  8. dan-petty commented on Nov 6, 2026

    @dan-petty
    OwnerAuthor

    Checked on origin/main c714551. The item cannot be built as written, and most of it exists.

    Delivered already (PR 3737, change 3053-parser-smaller-edit): _run_agent and _ask_smaller_edit send the request again with MESSAGES.roadmap.implement_parser_smaller_edit on class parser. A second parser failure on a tier ends the turn with class parser. The wip: commits stay. Tests: tests/roadmap/test_roadmap_implement_step_parser.py. Three of the four requirements in the refined design hold now.

    The one open part is "_moves_tier must return false for parser". Its reason is false. The refined design says a tier move "continues the step with the same conversation, which fails the same way". The code does not do that. run_step builds a new agent for each tier with build_agent(tier_model, ...) and starts it with _turn(agent, tier_model, log, inputs.message), with no conversation. A new tier is a new conversation on another model. The upstream report (ollama PR 18571) says the failure is deterministic for one conversation, not for every model.

    The change would also undo a settled decision. ACTION_BY_CLASS maps parser to CHANGE_TIER, and the design of change 3060 says why: a model family with a tool-call parser bug repeats the same error, so a tier move fixes a model defect and spends no repair round. Change 3053 kept this on purpose ("the ladder decides after that"), and docs/roadmap/implement.md states it. The default ladder holds one tier, so the default step already stops with class parser after the one smaller-edit message.

    To follow the issue, the code must make parser stop on every ladder. That removes the tier move that #3060 added for the failure it was made for, and it makes ACTION_BY_CLASS and the stop-comment lines (failure_lines) disagree with the step. I wrote no code. If the owner wants a stop on parser for every ladder, the work is to change the action of parser in ACTION_BY_CLASS and to decide what the job does after the stop. That is a decision for a new issue.

  9. added
    status/newStatus New: the item is not ready yet
    and removed
    status/readyStatus Ready: problem, acceptance criteria and an answer to each key question
    on Nov 6, 2026
  10. dan-petty commented on Nov 6, 2026

    @dan-petty
    OwnerAuthor

    Closed as not planned: #3053 (PR #3737) already delivers it, and the one open part would undo a decision. Checked on origin/main f1130d3 on 2026-11-06.

    • Three of the four requirements exist in src/devops_cli/roadmap/implement_step.py: _ask_smaller_edit adds MESSAGES.roadmap.implement_parser_smaller_edit for the class parser, and a second parser failure on a tier ends the turn with that class.
    • The open part says _moves_tier must return false for parser. In run_step each tier builds a new agent and starts a new conversation, and it resets log.parser_failures. The upstream report says the failure is deterministic for one conversation, not for every model. failure.py maps PARSER to CHANGE_TIER on purpose (change 3060). Following the issue would make ACTION_BY_CLASS disagree with the step.
    • The default ladder has one tier, so the step already stops after one smaller-edit message.

    If a person wants a parser failure to stop the step on every ladder, file a new issue for that decision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/lowEffort Low: little workpriority/p2-mediumStandard priority feature work and architectural improvementsscope/roadmapThe roadmap jobs: intake, refine, stage, implement and the Service that runs themsource/agentFiled by an agenttype/featureNew user-facing functionality or capability additionvalue/mediumValue Medium

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions