park --resolve / plan risk --resolve: the close-out for parked vagueness (0.20.0, #45 #55 #57 #60)#81
Conversation
Frame + plan state and exported artifacts, produced via scope -> think -> challenge -> spec-to-plan. Supersedes the resolve-parked-vagueness branch's flat-verb spec: q1 decided park --resolve (no new verb), q2 pulled in the plan-side twin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UyFmS7dRyBj87DpgmDCu9u
Add Vagueness.resolved/resolution (default False/""), Frame.resolve_vagueness (fails closed on unknown or already-resolved ids), and bump SCHEMA_VERSION to 3. set_status stays untouched — v-ids stay out of confirm/reject (decision c11). A v2 frame JSON without the new keys loads with defaults via the existing Vagueness(**v) splat in from_dict (dataclass defaults apply), and store.load still fails closed above the current version. Also fixes two pre-existing hardcoded schema_version pins (tests/test_store.py, tests/test_frame_schema_v2.py) that this legitimate bump made false, so the full suite stays green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UyFmS7dRyBj87DpgmDCu9u
Mirror t1's frame-side Vagueness.resolved/resolution exactly on the plan engine: PlanRisk gains resolved: bool = False and resolution: str = "", Plan.resolve_risk(rid, resolution) raises ValueError on an unknown or already-resolved id, and PLAN_SCHEMA_VERSION bumps 2 -> 3. PlanRisk(**r) in from_dict already defaults missing keys via dataclass field defaults, so a v2 plan JSON without the new keys loads unchanged. Covers: c13, h9
…solve _missing_open_uncertainty no longer counts a resolved unknown_blocking vagueness as a blocker, and _parked_items drops any resolved item (not just unknown_blocking) from the tracked-open list — a resolved item is closed, not open. suggest_move's blocking-vagueness branch now emits the executable devague park --resolve VID --decision "<the decision>" move in place of the old dead-end "capture+confirm the answer, or re-park it as non-blocking" hint, which could never actually clear the blocker. Covers c3, h3, c18, h14. Method-only change to devague/convergence.py and its tests; no renderer, CLI, plan_convergence.py, or .devague state touched.
Mirrors t5's park --resolve semantics on the risk subcommand: positional text and --kind become optional at the parser level so --resolve can omit both, the create path refuses a missing --kind (or text) in the handler, and --resolve routes through Plan.resolve_risk with the same fail-closed contract — bare --resolve without --decision persists nothing, unknown and already-resolved ids are refused with a hint, and the transition echoes on stdout with --json parity.
Mirrors t3's frame-side change on the plan engine: `_missing_risks` and `_parked_items` in devague/plan_convergence.py now filter `r.resolved`, so a resolved `unknown_blocking` PlanRisk no longer blocks `devague plan converge` and no longer advertises as parked. The blocking-risk hint in `suggest_move` now names the executable `devague plan risk --resolve RID --decision TEXT` syntax verbatim instead of the old vague "cover it with a task, or re-record it as non-blocking" text. Adds 6 tests to tests/test_plan_convergence.py covering: unresolved risk still blocks (baseline), resolved risk unblocks convergence, resolved risk drops out of required_next_moves, the hint's verbatim executable syntax (direct + end-to-end via evaluate()), and resolved risk exclusion from parked_items. Full suite: 640 passed. black/isort/flake8 clean.
Harmonizes the plan-side hint with t3's frame-side shape — the hint now names the real rN instead of a literal RID placeholder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UyFmS7dRyBj87DpgmDCu9u
Add a user-only resolve surface for parked vagueness that mirrors question --resolve. park's positional text becomes optional (nargs='?'), mirroring question.py; --resolve VID, --decision TEXT, and --claim CN route through Frame.resolve_vagueness. A bare --resolve without --decision, an unknown/already-resolved vagueness id, positional text combined with --resolve, and an unknown --claim id are all refused with a hint and persist nothing (decision c21, issue #57's refuse-not-no-op answer). Grants a minimal frame.py extension: Vagueness gains resolution_claim_id (the deciding claim recorded at resolve time, distinct from claim_id, the owning claim set at park time) and resolve_vagueness gains an optional claim_id parameter, validated in the model the same way add_scope_entry validates seed ids. Dataclass defaults keep v2/early-v3 artifacts loading with no SCHEMA_VERSION bump. Covers c4, h4. Full suite: 651 passed, 98% coverage.
frame_md keeps the flat "Open vagueness" list but appends "— resolved: TEXT" to a resolved item's bullet; spec_md gains a new "Resolved vagueness" subsection rendering resolved items of any kind with their resolution verbatim (previously only follow_up/out_of_scope kinds were shown at all, and never with resolution text) — a resolved follow_up/ out_of_scope item now moves out of "Open / follow-up" into that section instead of being fabricated as still open. deliverables_md's surviving-open-items filter now excludes any resolved frame vagueness or plan risk, not just unknown_blocking-kind items. An empty resolution renders nothing extra, matching the existing never-fabricate convention (_instruction_lines). Covers c6, h6, c18, h14 (#53-esd t7). Full suite: 651 passed (was 634); black/isort/flake8 clean; new golden fixtures pass real markdownlint-cli2.
Drives devague.cli.main() end to end through issue 57's exact repro on both engines: park a blocking unknown, capture the deciding claim, resolve via park --resolve/plan risk --resolve, assert converge blocks before and passes after, and export renders the resolved item verbatim. Includes a markdownlint integration check on the exported spec (skips cleanly without the binary). Full suite: 687 passed, 98.12% coverage. flake8/black/isort/bandit clean.
…aught devague learn's move table + operating rules, docs/llm-guidance.md's park rows, .claude/skills/think/SKILL.md's move table + hard rules, and docs/spec-contract.md (Vagueness/PlanRisk entities, the resolve move rows, schema_version 3 for both engines) now all name `park --resolve VID --decision TEXT` / `plan risk --resolve RID --decision TEXT` — the fix shipped in t5/t6 for issues 45/55/57/60. A decided blocking park no longer reads as a permanent dead end in any teaching surface. devague plan learn already carried the risk --resolve text from t6; new tests in tests/test_cli_learn.py lock both `devague learn` and `devague plan learn` naming the resolve moves.
#60) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UyFmS7dRyBj87DpgmDCu9u
|
/agentic_review |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UyFmS7dRyBj87DpgmDCu9u
PR Summary by QodoAdd resolve close-out for parked vagueness and plan risks (schema v3)
AI Description
Diagram
High-Level Assessment
Files changed (35)
|
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UyFmS7dRyBj87DpgmDCu9u
|
Code Review by Qodo
Context used✅ Tickets:
🎫 Add a move to reclassify/resolve a parked vagueness (unblocks /think convergence after a dependency is downgraded) 🎫 No verb to resolve parked vagueness (or edit task text) — operators hand-edit .devague state JSON 🎫 No CLI verb to resolve parked vagueness (blocking vagueness can only be removed by editing frame state)✅ Compliance rules (platform):
75 rules✅ Skills:
cicd, communicate 1. llm claims marked confirmed
|
|
Triage of the latest Qodo review (1 bug, 1 rule violation) — both rejected as not-a-defect after verification, consistent with this repo's prior review history: 1. "LLM claims marked confirmed" (rule 738290) — rejected. 2. "Committed plan pointer file" ( No code changes made for either finding.
|



What ships
The close-out for parked vagueness, on both engines (0.20.0):
devague park --resolve <vN> --decision "<text>" [--claim <cN>]— a decided blocking park resolves through a CLI move: the item stays on the record with its resolution text (and optionally the deciding claim via the newresolution_claim_id), drops out of the convergence gate andparked_items, and renders under a## Resolved vaguenesssection in exported specs. A bare--resolvewithout--decisionis refused (evidence-bearing close-out); unknown and already-resolved ids are refused fail-closed.devague plan risk --resolve <rN> --decision "<text>"— the plan-side twin for blockingPlanRisks.SCHEMA_VERSIONandPLAN_SCHEMA_VERSIONbump 2 → 3; older binaries fail closed on v3 artifacts with the existing upgrade hint, v2 artifacts load with defaults.devague learn,docs/llm-guidance.md, the/thinkskill,docs/spec-contract.md) teach the resolve close-out wherever park is taught.Closes #45, closes #55, closes #57, closes #60 — four independent reports of the same gap in five weeks, across colleague, lobes-cli, league-of-agents, and devague's own dogfooding. (#60's task-text half was already shipped as
plan amendin 0.18.0/#68; only its vagueness half was live.)How it was built
Full seven-leg dogfood:
/scope→/think→/challenge→/spec-to-plan→/assign-to-workforce(9 tasks fanned out to subagent worktrees across 3 waves, TDD-gated merges) → this PR. Artifacts:docs/specs/2026-07-17-resolve-parked-vagueness.md,docs/plans/2026-07-17-resolve-parked-vagueness.md(frame/plan state committed under.devague/).The
/challengepass (rigorous — schema-migration + data-loss signals) caught the two secondary consumers of open vagueness (parked_itemsin both gates,plan deliverablesopen items) that the initial spec missed; they're covered here (c18).Verification
.devaguehand-edits anywhere.git diff main...HEAD -- devague/grep is empty).Note for the reviewer
The pre-existing
resolve-parked-vaguenessbranch (commit b5c9e7c, an earlier session) specs a different shape — a new flatresolveverb with--kindre-kinding, widened to scope entries. This session's frame decisions (q1: extendpark --resolve, no new verb; q3:--decisionrequired) supersede it; that branch was left untouched and can be deleted if you agree it's superseded.