fix(map-plan): add normalize_blueprint repair pass for decomposer drift (#168)#172
Merged
Merged
Conversation
…ft (#168) task-decomposer routinely emits blueprints that fail the framework's own validate_blueprint_contract (Step 5.6), forcing manual JSON surgery between decompose and validate. Two mechanical self-consistency drifts recur: 1. Forward-dependency ordering — a subtask declared before the dependency it declares. The runtime walker consumes subtasks in declaration order, so the validator rejects this (deadlock guard); array position is the only problem. 2. coverage_map <-> bracket-tag mismatch — a coverage_map[req]=owner whose owner subtask's validation_criteria does not cite [req]. Add a deterministic normalize_blueprint repair pass (runner function + CLI + /map-plan Step 5.55) that: - stably topologically sorts subtasks[] so deps precede dependents (independent subtasks keep their relative order; a true cycle is left untouched for the validator to reject); - injects a [req]-tagged validation criterion for every coverage_map entry whose owner lacks the tag. It never invents coverage_map ownership or rewrites dependency edges, so genuine semantic gaps still fail Step 5.6. Idempotent (changed=false / writes nothing on already-normalized input). Wired into /map-plan as Step 5.55 (before 5.6). Regression tests reproduce the exact #168 scenario (both drifts at once) and prove validate_blueprint_contract flips false->true after normalize, plus idempotency, stable-order preservation, cycle pass-through, and CLI --check/in-place paths. Edited the .jinja single source and re-rendered (make render-templates); all generated trees + the live .map/scripts copy stay byte-identical (check-render). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #168.
task-decomposer(Step 5) routinely emits ablueprint.jsonthat fails the framework's ownvalidate_blueprint_contract(Step 5.6), forcing manual JSON surgery before planning can proceed. Two mechanical self-consistency drifts recur:subtasks[]in declaration order (the validator's deadlock guard), so this is rejected even though the dependency DAG is fine; only the array position is wrong.coverage_map↔ bracket-tag mismatch — acoverage_map[req] = ownerwhose owner subtask'svalidation_criteriadoes not cite[req].What this does
Adds a deterministic
normalize_blueprintrepair pass (runner function + CLI +/map-planStep 5.55, before validation) so the flow is self-serve —decompose → normalize → validate → proceed— with no hand-editing:subtasks[]so every dependency is declared before its dependents. Independent subtasks keep their relative order (minimal diff); a true cycle is left untouched sovalidate_blueprint_contractstill reports it.coverage_map[req] = ownerwhose owner lacks[req], appends a[req]-taggedvalidation_criteriaentry.Why a deterministic repair pass rather than loosening the validator or re-prompting the agent: the runtime walker relies on declaration order (loosening would risk a dependent running before its dependency), and a prompt fix is unreliable — the issue notes it fails "routinely" despite the decomposer being prompted with the contract. A topo-sort is safe under either scheduling model.
Conservative by design: never invents
coverage_mapownership, never rewrites dependency edges, never touches a soft constraint that relies ontradeoff_rationale. Genuine semantic gaps (a hard constraint missing fromcoverage_map, an unknown/cyclic dependency) still fail Step 5.6. Idempotent —changed: falseand writes nothing on already-normalized input.Tests
New regression tests in
tests/test_map_step_runner.py:test_normalize_blueprint_fixes_issue_168_drift— reproduces the exact task-decomposer emits blueprints that fail validate_blueprint_contract (forward-dep array order + missing coverage bracket-tags) #168 scenario (both drifts at once) and assertsvalidate_blueprint_contractflipsfalse → trueafter normalize.--check/in-place paths.Invariants honored
.jinjasingle source and re-rendered (make render-templates); all generated trees and the live.map/scripts/copy stay byte-identical (make check-render).make checkgreen: ruff + mypy + pyright clean, 2291 passed, 3 skipped.🤖 Generated with Claude Code