Skip to content

[bug] plan: prompt omits TaskGraph schema (tier values), malformed graph is not retried or preserved #45

Closed

Description

@smota

Summary

On 0.3.0, a live meshloop plan dispatched the planner, and the worker wrote a plan whose node used "tier": "analysis". Meshloop rejected the whole graph:

meshloop:plan probing 2 harness(es) in parallel (timeout 30s)
Decomposition failed: Plan(Malformed("unknown variant `analysis`, expected one of `Tier1`, `Tier2`, `Tier3` at line 9 column 24"))

There was no retry, and the plan wasn't kept for inspection. The worker had done the decomposition; it was lost because of one enum value the prompt never defined.

Meshloop version

meshloop 0.3.0 (source origin/main @ 58c01dd), Windows 11 native. Claude Code 2.1.292 planner through a stdin shim (see #27).

Preliminary analysis

1. The planning prompt doesn't define the schema it validates against.
build_planning_spec (crates/meshloop-engine/src/agent.rs:193) asks for "optional tier" with no allowed values. Tier (crates/meshloop-domain/src/task_graph.rs:11) accepts only Tier1 | Tier2 | Tier3. An LLM planner will naturally fill a field called "tier" with role or phase names (analysis, design, review). The other fields are also given only by name: allowed_paths (repo-relative?) and empty_diff_ok (when to use it?).

2. A malformed plan is not retried.
planner.rs says a schema-invalid graph should become "a bounded retry (a new attempt, ADR 0005)", but plan stopped after the first malformed result, although max_retries = 2 was configured.

3. The invalid output is discarded.
parse_graph reads meshloop-plan.json from the planning worktree, but the error doesn't say where that file is, and nothing is saved in the store. The user can't see or repair the near-miss plan.

4. Strict enum parsing for an optional, advisory field.
tier is optional, and assign_tiers fills missing values with DefaultTierAssigner. An unknown value could become None plus a warning, so the heuristic or reviewer decides, instead of failing the whole graph. This is a design choice. Strict rejection is reasonable if 1–3 are fixed.

Suggested fixes

  1. Put the exact JSON schema in the planning output contract: the allowed tier values (or "omit it"), path semantics for allowed_paths, when to set empty_diff_ok, and a one-node example. Better still, generate it from the domain types, so the prompt can't drift from TaskGraph.
  2. On Malformed/Invalid, retry within max_retries, adding the parse error to the retry prompt ("your previous graph failed: …").
  3. Keep the rejected file, or record it in the store, and print its path in the error so plan failures can be inspected.
  4. Optionally, accept an unknown tier as None with a warning, since tiers are assigned later anyway.

Acceptance criteria

  • The planning prompt names every TaskNode field with its allowed values. A test checks that it lists every Tier variant.
  • Fixture test: the first planner response has an invalid tier and the second is valid → plan succeeds on attempt 2. The retry prompt contains the parse error.
  • Fixture test: every attempt is malformed → the error output includes the saved path of the last rejected plan, and that file exists.
  • Existing planner tests pass.

Workaround

Put the exact schema in --intent-file, which is passed into the prompt as the situation.


🤖 Generated with Claude Code

Activity

  1. added
    bugSomething isn't working
    drafted-by:claudeIssue, plan, or spec drafted by Claude in the current session
    on Oct 7, 2026
  2. smota commented on Oct 7, 2026

    @smota
    OwnerAuthor

    Workflow status — claimed by Claude Code (Opus 5.5 orchestrator).

  3. smota commented on Oct 7, 2026

    @smota
    OwnerAuthor

    Workflow status — implementation done; PR #49 (branch work/store-ignore-plan-retry @ dbfe0b1). Local clippy/test/xtask check pass; CI pending. Next owner: independent reviewer.

  4. added and removed on Oct 7, 2026
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

    bugSomething isn't workingdrafted-by:claudeIssue, plan, or spec drafted by Claude in the current sessionimplemented-by:claudeImplementation completed by Claude Code

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions