Repository navigation
[bug] plan: prompt omits TaskGraph schema (tier values), malformed graph is not retried or preserved #45
Description
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
- Put the exact JSON schema in the planning output contract: the allowed
tiervalues (or "omit it"), path semantics forallowed_paths, when to setempty_diff_ok, and a one-node example. Better still, generate it from the domain types, so the prompt can't drift fromTaskGraph. - On
Malformed/Invalid, retry withinmax_retries, adding the parse error to the retry prompt ("your previous graph failed: …"). - Keep the rejected file, or record it in the store, and print its path in the error so
planfailures can be inspected. - Optionally, accept an unknown
tierasNonewith a warning, since tiers are assigned later anyway.
Acceptance criteria
- The planning prompt names every
TaskNodefield with its allowed values. A test checks that it lists everyTiervariant. - Fixture test: the first planner response has an invalid tier and the second is valid →
plansucceeds 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
smota commentedon Oct 7, 2026
Workflow status — claimed by Claude Code (Opus 5.5 orchestrator).
work/store-ignore-plan-retry(baseorigin/main@ 2769f08), shared with [feature] enablement: ensure consuming repos ignore the .meshloop/ store #44/[bug] plan: prompt omits TaskGraph schema (tier values), malformed graph is not retried or preserved #45.smota commentedon Oct 7, 2026
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.