Skip to content

The fifteen-issue backlog sweep (0.21.0) - #96

Merged
OriNachum merged 44 commits into
mainfrom
issue-backlog-sweep
Jul 28, 2026
Merged

The fifteen-issue backlog sweep (0.21.0)#96
OriNachum merged 44 commits into
mainfrom
issue-backlog-sweep

Conversation

@OriNachum

Copy link
Copy Markdown
Contributor

Closes the entire open issue backlog — fifteen issues — in one release, plus one opened and fixed during the run.

Closes #48, #49, #52, #79, #82, #83, #84, #85, #86, #87, #88, #90, #91, #92, #93, #94.

Executed as a single /assign-to-workforce fan-out of the issue-backlog-sweep plan: 19 tasks, 6 dependency waves, one agent per task per wave in an isolated git worktree, every merge TDD-gated (suite green before and after). No merge was reverted.

Why one release

Three of the fifteen were being worked around by hand downstream, and the workarounds were getting expensive:

All three are verified deletable — see the closure map.

What ships

New moves

Move Closes
devague interrogate <cN> --resolve <qN> [--decision] #48, #52
devague amend <cN> [--text] [--kind] [--reason] #84
devague scope --amend <sN> --finding #84
devague plan defer <target> --reason / --undo #85
devague plan risk --amend <rN> --text #84
assign-to-workforce split-plan --write #82

Sharpenedreject cascades onto honesty conditions and unresolved hard questions and converge stops warning about rejected assumptions (#83); plan confirm/plan reject are multi-id transactional and plan-group errors point at plan explain (#86); --dep/depend --on refuse self-deps and unknown ids at creation (#86); cover/--covers validate against live-frame targets (#90); summary scopes to confirmed tasks (#88); scope --seeds accepts q* ids (#84).

Export fidelity — all four park kinds render under ## Open parks, resolved hard questions carry (resolved: <decision>), hard questions on rejected claims are excluded, and a scope seed citing a rejected claim renders (rejected) (#93, #49). New md_safe_text() escapes every verbatim render site — presentational only, stored JSON untouched (#87). New read-only devague/contested.py marks a confirmed claim contested by an approved deviation across the plan-slug boundary; the spec is not rewritten, it points forward to the ledger (#92).

Both schema versions are now 4, and both stores check the declared version against the raw dict before parsing — previously a newer-schema file died with a bare TypeError from HardQuestion(**q) instead of the fail-closed error.

Evidence

  • 967 tests pass, coverage 98.37% (gate 95)
  • markdownlint-cli2 "**/*.md" — 39 files, 0 errors; .claude/skills/** force-linted — 19 files, 0 errors
  • Every issue verified against its own stated acceptance criteria, and where the issue carried repro steps, by running the reporter's sequence against the built CLI in a scratch dir
  • Closure map: docs/deliveries/2026-07-28-issue-backlog-sweep-closure-map.md
  • Delivery summary: docs/deliveries/2026-07-28-issue-backlog-sweep.md

What the verification pass caught

The final task was adversarial cross-task verification, and it earned its place — it found three defects the per-task agents missed:

  1. A regression this release introduced. spec_md.py composed autolink_urls(md_safe_text(t)) while plan_md.py/summary_md.py composed the opposite order, each documenting its own as correct. Both corrupted an underscore-bearing URL — the spec order truncated it at the first underscore, silently pointing a committed artifact's link at the wrong address. This was filed speculatively as autolink_urls and md_safe_text compose without knowing about each other — a URL containing an underscore can be corrupted #94; it was real. Fixed by carving URLs out of md_safe_text the way code spans already were.
  2. export is lossy: shows resolved hard questions as open, drops unknown_nonblocking parks #49 was only half closed — a resolved hard question rendered (resolved) but dropped the decision text, while the issue asked for a pointer to what answered it.
  3. spec-to-plan/SKILL.md was never swept — it still taught plan reject as single-id with a shell loop, the exact workaround plan papercuts: self-dependency accepted at task creation; plan reject is single-id while frame reject is multi-id #86 removes, in a skill guildmaster re-broadcasts to the mesh.

Deviations

Two approved mid-run deviations, both execution-order only, both recorded via devague deviate before resuming:

  • d1 — t17 (learn/explain recipes) moved from wave 3 to after wave 5: three of the four surfaces its acceptance criterion names don't exist until waves 4–5, so documenting them in wave 3 meant writing recipes for unshipped verbs.
  • d2 — t18 (docs sweep + version bump) moved to last, same root cause.

No plan state was mutated — deviate is the marking of the change.

Follow-ups

Review map

The delivery summary is the audit trail: every plan task accounted for, both deviations quoted, every delivery claim carrying a resolvable evidence pointer or an explicit unverified marker.

  • devague (Claude)

OriNachum and others added 30 commits July 28, 2026 10:02
…ague /scope + /think + /challenge)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
…evague /spec-to-plan)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
…79, #91, t16)

Steps 1-2 of the /scope method now instruct subagent fan-out for a broad
survey (5+ candidate surfaces), one read-only exploration subagent per
surface or tight cluster, defaulting to a smaller tier (sonnet). 4 or fewer
surfaces still explore inline, and the no-wizard escape for small ideas is
unchanged. The load-bearing rule survives explicitly in both step 2/4 and
the Hard rules section: subagents explore and report only, never running a
devague move — the main agent alone runs capture/scope/question/park so
provenance and the anti-fabrication contract stay in one place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
`_require_target` only checked the plan's stored coverage-target snapshot,
frozen at `plan new`/last `plan converge` time. But `plan status`/`converge`
re-derive targets from the LIVE source frame, so a frame that legitimately
grows a confirmed claim mid-run left `status` recommending a cover that
`cover`/`task --covers` immediately refused as "unknown coverage target" --
the exact #90 repro.

`_require_target` now falls back to the live frame when a target is absent
from the stored snapshot, and persists the refreshed snapshot on success so
the stored copy catches up without a separate `plan converge`. Decision
(park v4 / plan risk r2): when the source frame has itself regressed below
its own convergence gate, `_live`'s regression error is let through as-is
rather than reworded into "unknown coverage target" -- an unverifiable
target should say why, not blame the wrong thing. A target already known to
the stored snapshot never touches the live frame at all, so it keeps working
through a frame regression exactly as before.
… (t2)

store.load and plan_store.load parsed via from_dict before checking
schema_version, so a genuinely newer-schema file crashed with a raw
TypeError (HardQuestion(**q) / Vagueness(**v) reject unexpected kwargs)
instead of the intended fail-closed IncompatibleSchemaError. Both loaders
now read the raw JSON, check schema_version first, and only then build the
domain object. HardQuestion/Vagueness construction in frame.from_dict is
now tolerant of unknown keys the same way Claim already is, so a future
field lands safely under a version bump instead of crashing same-version
loads that happen to carry it.

Bumps frame.SCHEMA_VERSION 3->4 (reserved for t4's HardQuestion resolution
field) and plan.PLAN_SCHEMA_VERSION 3->4 (reserved for t9's per-target
deferral state) - this task only hardens load order/tolerance, it does not
add either field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
…, t1)

Add md_safe_text() to render/_md_safety.py: wraps underscore/dunder
identifiers (_read_file, __init__.py) in code spans per the #87 issue
comment's preferred fix (fixes MD037 and MD050 in one move, reads better
than backslash-escaping), and backslash-escapes the remaining control
characters (*, [, ], a stray backtick, a leading #) as the fallback.
Text already inside a code span is left byte-for-byte untouched and
double application is a no-op (c32/h25). No renderer call sites yet —
those land in t3/t9/t13.
Add `split-plan --write`, producing docs/plans/<created-date>-<slug>-split.md
alongside the exported plan-md: real per-task summaries, instructions, and
acceptance criteria from `devague plan waves --json`; a Task assignments
(Owner/Model) table the script reads back on re-run so a human's edited
assignment survives regeneration; and the End state section. Re-running
overwrites the same dated path in place. Artifact-only per decision c25 —
no plan schema change, no new devague CLI verb; devague plan waves/show/
deliverables stay read-only. SKILL.md documents the flow.
…48, #52, t4)

interrogate <cN> --resolve <qN> [--decision "<text>"] adds Frame.resolve_hard_question
and a resolve mode on the interrogate CLI parser (mirrors park --resolve), fixing the
permanent convergence deadlock where nothing ever set HardQuestion.resolved. The
blocking-question gate also skips rejected claims, and suggest_move now names the
shipped move instead of the old dead-end "capture/confirm the resulting claim" hint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
…ks (#88, t13)

devague summary rendered every task on the plan -- including rejected ones --
into both Planned Work and the Actual Delivery table, so a plan carrying 19
confirmed and 68 rejected tasks (rebuilt twice after scope changes) emitted 87
undifferentiated rows. A rejected task paired with `<fill: status>` invites
recording a planning decision as a delivery failure, which is a different fact.

Planned Work and Actual Delivery (and the --json summary_data equivalent) now
iterate confirmed tasks only, with a single line preserving the rejected count
without padding either list ("N tasks were rejected during planning -- see
`devague plan show`"). A proposed task -- neither the confirmed contract nor
an explicit rejection -- is excluded from both lists and from the rejected
count, so an open decision is never folded into a closed one. The --pr wave
map is unchanged (dependency_waves already excludes rejected tasks), pinned
here with a dedicated regression test. Also applies the #87 md_safe_text
escaper at every verbatim interpolation site in this renderer (task
summaries, deviation what/reason, announcement/after-state text).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
…, t9)

Add `devague plan defer <target-id> --reason "<text>"` (+ `--undo` to
reverse it) so a plan intentionally scoped to early milestones is not
forced to fake coverage of a later-milestone target just to satisfy the
gate — the exact perverse incentive #85 reported (a reviewer catching a
task that "claims coverage ... but only installs and reports the
counter").

- plan.py: CoverageTarget gains deferred/deferred_reason (schema v4,
  reserved by t2); Plan.defer_target/undefer_target mirror
  resolve_risk's fail-closed contract (unknown id / already-deferred /
  not-deferred all raise).
- plan_convergence.py: _missing_coverage excludes deferred targets from
  blockers; _parked_items now also lists each deferred target labeled
  distinctly ("deferred: coverage target cN ... — <reason>") so `plan
  status`/`converge --json` can tell "deliberately deferred" apart from
  "not yet covered".
- cli/_commands/plan.py: new `defer` subcommand + PLAN_MOVES entry;
  _live/_live_frame_and_targets now carry persisted deferral state
  across every live-frame re-derive (targets_from_frame builds fresh
  CoverageTarget instances with no memory of prior deferrals — without
  this merge, converge/export/status would silently drop a recorded
  deferral on the next call).
- cli/_status.py: emit_status now prints parked_items in text mode too
  (a pre-existing gap — JSON already carried them) so `plan status`
  visibly shows deferred targets, not just JSON.
- render/plan_md.py: new "Deferred targets" section naming each with
  its reason; applies the t1 escaper (md_safe_text) at every verbatim
  site (task heading/instruction/acceptance, risk text, announcement,
  title) — closes the MD050 regression from the #87 comment for
  underscore-bearing task text.

Verified against real markdownlint-cli2 (0 errors) and an end-to-end
`devague` CLI smoke run in a scratch dir (defer -> converge -> export ->
status -> undo), plus the full pytest suite (760 passed) and
black/isort/flake8.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
… verbatim text (#93, #49, #83, #87, t3)

spec_md.py's park-kind filter only ever surfaced follow_up/out_of_scope,
silently dropping every open unknown_nonblocking/unknown_blocking park —
exactly the residual-risk kind that legitimately coexists with a converged
frame (#93, #49). It now renders every open park kind, grouped and labeled.

Hard questions previously carried no resolved marker and rendered
regardless of parent-claim status, so a resolved question read as an open
blocker and a rejected claim's question still leaked into the artifact
(#49, #83). Both are fixed: resolved questions get a "(resolved)" marker,
and any claim.status == "rejected" drops its hard questions entirely.

A scope entry whose seeds cite a claim that was later rejected now renders
a visible "(rejected)" marker instead of a bare dead id (#84's fourth
acceptance criterion, c33/h26).

Every verbatim interpolation site now composes the t1 escaper
(md_safe_text) with the existing autolink_urls/heading_safe passes via new
_safe()/_safe_heading() helpers, fixing MD037/MD050 on underscore/dunder
identifiers throughout the exported spec (#87).

Flips the pinned test_render.py:301-306 (nonblocking parks now render) and
adds regression coverage for all four acceptance criteria, including a
real markdownlint-cli2 double-export byte-stability check. Re-exporting
docs/specs/2026-07-28-issue-backlog-sweep.md (a live corpus mixing
backticked and bare identifiers) produces the expected one-time
presentational diff — committed alongside, per park v5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
… escaper (#87)

Proves the t1/t9 escaping fix end-to-end: three pre-existing MD037 errors in a
committed artifact from PR #81 clear on re-export, with no source text change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
… t5)

Frame.reject cascades a claim's rejection onto its still-live honesty
conditions (status -> rejected) and reports both cascaded honesty and
hard-question ids so the CLI can echo what it took with it
(`c21 -> rejected (also rejected: h3, q1)`). The cascade fires only on
the transition into rejected, so re-rejecting an already-rejected claim
reports nothing (idempotent, no double-reporting).

_assumption_warnings now skips rejected assumptions (only a still-proposed
one is actionable "unconfirmed"), and devague review naturally stops
listing an honesty condition once its parent claim's rejection cascades
over it -- no renderer changes needed, since proposed_honesty() already
filters on status == "proposed".
`plan task --dep` and `depend <tN> --on <tM>` now refuse a self-cycle
(the about-to-be-assigned/naming-itself task id) and an unknown task id
at authoring time, with an actionable hint, instead of silently
recording a broken edge that only surfaces much later as a bare
`dependency cycle: tN -> tN` at `plan converge`/`plan waves`.

`depend --remove` is untouched by the new checks (it must still repair
a dangling dep predating this fix), and the plan_convergence gate for
multi-task cycles is left alone — this is creation-time feedback, not
a replacement for it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
#92, t14)

Adds a read-only derivation (devague/contested.py) joining a frame's confirmed
claims to any approved `devague deviate` record naming them in --affects,
since the deviation ledger knew a claim had been contradicted by execution
but nothing rendered that back-reference (the spec, `show`, and `status` all
stayed silent). Per the #92 maintainer ruling the spec itself is never
rewritten -- this only ever derives a pointer at render time: `export` gets a
rich per-claim marker (announcement blockquote, Requirements, and every
generic claim section), `show`/`status` gain a summary "contested: ..." line
in both text and --json, and the plan engine's own status is untouched (no
"contested" key at all).

The join crosses the frame/plan boundary the instruction called out: frames
carry no reverse pointer to the plans seeded from them, so every plan slug is
enumerated and filtered on frame_slug before its delivery ledger is read.
Fails open throughout -- a plan or delivery file that is missing, truncated,
or declares a newer schema degrades to "no markers from that source" plus a
stderr diagnostic, never a crash or a refused export; zero frame/plan/
delivery mutation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
plan confirm/reject now accept N ids (nargs="+") and apply them
transactionally — every id is validated against the plan first, and if
any is unknown nothing is changed, matching the frame-side contract
(confirm.py's _run). Argument errors raised inside a devague plan
<move> parser now hint at `devague plan explain <move>` instead of
the generic `<prog> --help`, scoped so top-level and other flat-verb
errors keep their existing hint unchanged.
New `devague amend <cN> --text/--kind [--reason]` and `devague scope
--amend <sN> --finding` edit a claim or scope entry in place — same id,
same honesty conditions/hard questions/instruction, same inbound
scope-entry seeds. The only prior route (reject + recapture) churned the
id and left scope-entry seeds dangling. Amending a confirmed claim flips
it back to proposed and echoes the transition, mirroring the
`interrogate --instruction` precedent; origin is never touched (no flag
reaches it). A lightweight `Claim.revisions` trail (text/kind/reason)
records what an amend superseded, added without a schema_version bump
since `from_dict` loads it tolerantly.
OriNachum and others added 13 commits July 28, 2026 11:30
Frame.add_scope_entry validates a seed against find_claim first and the
new find_hard_question second, so a claim-attached hard question id
(q*) records alongside claim ids (c*) -- unknown ids of either kind
still refuse with the existing "unknown seed claim id" error and
"run 'devague show'" hint. render/spec_md._seed_label renders a
question seed as `(question)`, or `(question, resolved)` once
answered, distinct from the existing claim/rejected-claim markers.

This closes the "smaller, related gap" in issue #84: the /scope
skill's own routing table sends a "genuinely unknown, needs a user
decision" finding to the `question` move rather than `capture`, so a
scope entry recording that finding previously had no way to link its
provenance.
Add `Plan.amend_risk` and `devague plan risk --amend RID --text "<corrected>"`
so a risk whose prose names a task id that rotated (rejected + recreated
during a scope change) can be corrected in place instead of resolving it
just to record a duplicate. Preserves id, kind, task link, and resolution
state verbatim -- a resolved risk stays resolved after its text is fixed.
interrogate --resolve, amend, scope --amend (+ --seeds accepting q*
hard-question ids), plan defer, plan risk --amend, transactional multi-id
plan confirm/reject, live plan cover/--covers validation, the flat reject
cascade, and the scope subagent fan-out threshold were all shipped ahead of
learn/explain catching up — devague explain amend even 404'd with "unknown
move: amend" before this. Also fixes plan.py's cmd_plan_learn, which still
named "six operator skills" and omitted challenge from the list.

Closes the #52 acceptance criterion: learn/explain document the resolve
path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
…ue sweep (t18)

Bump 0.20.1 -> 0.21.0 (minor: feature release) and describe the shipped
surface across every teaching doc.

- CHANGELOG.md: one 0.21.0 entry mapping each of the fifteen issues to what
  changed (#48 #49 #52 #79 #82 #83 #84 #85 #86 #87 #88 #90 #91 #92 #93).
- CLAUDE.md: new Status entry; working-backwards and spec->plan method lists
  gain amend / interrogate --resolve / scope --seeds q* / plan defer /
  risk --amend / multi-id plan confirm / live-frame cover; gate 2 now names
  its durable split artifact; module inventory picks up amend.py,
  contested.py, _md_safety.py and plan defer.
- README.md: verb lists, the "nothing is deleted to make a gate go green"
  close-out rule, and the reject cascade.
- docs/skills.md: scope subagent fan-out and the split-plan --write artifact;
  fixes the pre-existing drift that said briefs quote plan show --json /
  the plan-md (SKILL.md is right: plan waves --json is the single source).
- docs/skill-sources.md: version stamps for scope and assign-to-workforce.
- docs/spec-contract.md: schema_version 4 on both stores (check-before-parse),
  ClaimRevision, HardQuestion.resolution, CoverageTarget.deferred, the new
  moves and their validation errors, plus render-time contracts (md-safety,
  contested-by-deviation).
- docs/llm-guidance.md + think/scope SKILL.md: the stale --seeds claim-ids-only
  wording, and the three new close-out/correct-in-place rules.

Docs and version only — no behavior change.
Adversarial verification of all fifteen issues in the sweep, each checked
against its own stated acceptance criteria and — where the issue carried
repro steps — by running the reporter's sequence against the built CLI in a
scratch dir rather than by reading the tests.

Closure map: docs/deliveries/2026-07-28-issue-backlog-sweep-closure-map.md
(967 tests green, 98.37% coverage, 0 markdownlint errors including the
force-linted .claude/skills/** files).

Three defects the per-task agents did not catch, fixed here with tests:

- #94 (regression this release introduced): md_safe_text and autolink_urls
  were composed in OPPOSITE orders by spec_md vs plan_md/summary_md, and
  both corrupted a URL containing an underscore — the plan order backticked
  inside the link, the spec order truncated it at the first underscore,
  silently pointing a committed artifact's link at the wrong address.
  md_safe_text now carves out URLs exactly as it already carved out code
  spans, so both orders produce identical intact links.
- #49 was only half closed: a resolved hard question rendered "(resolved)"
  but dropped the recorded decision text, while the parks renderer already
  rendered its own. The issue asked for "a pointer to the claim/decision
  that answered them", so the export now renders
  "(resolved: <decision>)" when interrogate --resolve --decision recorded one.
- spec-to-plan/SKILL.md was never swept and still taught `plan reject` as
  single-id with a shell loop — the exact workaround #86 removed — in the
  skill guildmaster re-broadcasts to the mesh. Moves table corrected and
  pinned against `devague plan --help` by a new test.

The markdownlint integration test now exports the repo's own real
issue-backlog-sweep frame and plan as lint corpus, per the task instruction.

Two CHANGELOG factual errors corrected: "Three of those" listed only two
workarounds (#87's lint ignores were missing), and the swept-surfaces list
omitted spec-to-plan.
…resh clone

The contested-marker derivation (#92) reads .devague/deliveries/<plan-slug>.json;
without it committed, a fresh clone cannot reproduce the markers. Flagged by t19.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
Eight-section accountability artifact built from the devague summary skeleton:
19/19 tasks delivered, 2 approved deviations (d1/d2, execution-order only),
3 defects found and fixed by t19's cross-task verification, and the honest
remaining-work list including the two follow-up issues opened during the run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
@OriNachum

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

Copy link
Copy Markdown

Qodo is busy working

Check back in a few minutes. Qodo's code review agents are on it.

Grey Divider

- contested.py: extract _delivery_for_frame and _markers_from_delivery so
  find_contested_markers drops from cognitive complexity 24 to under the 15
  allowed (python:S3776).
- _md_safety.py: name the file-extension alternation instead of relying on
  implicit string concatenation in the regex (python:S5799).

Behavior unchanged; 967 tests still pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtUixLUjgEt51PXS9TPVt3
@sonarqubecloud

Copy link
Copy Markdown

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Issue backlog sweep (0.21.0): 15 issues — new moves, cascades, and export fidelity fixes

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Closes all fifteen open backlog issues (#48, #49, #52, #79, #82–#94) in one coordinated release.
• Adds new moves: interrogate --resolve, amend, plan defer, and split-plan --write.
• Tightens correctness: reject cascades, transactional batch operations, and creation-time
 validation.
• Improves exports: markdownlint-safe rendering and contested-claim backreferences.
Diagram

graph TD
  CLI["CLI commands"] --> Frame["Frame engine"] --> Store[("Frame/Plan JSON store")]
  CLI --> Plan["Plan engine"] --> Store
  CLI --> Contested["contested.py join"]
  Contested --> Store
  Contested --> Delivery[("Delivery ledger")]
  Frame --> Render["Renderers spec/plan/summary_md"]
  Plan --> Render
  Render --> MdSafety["md_safe_text escaping"]
  Render --> Export["Exported docs/specs, docs/plans"]

  subgraph Legend
    direction LR
    _svc([Service/Module]) ~~~ _db[(Data store)] ~~~ _file[Exported file]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Split into per-wave PRs
  • ➕ Smaller, independently reviewable diffs
  • ➕ Easier to bisect regressions to one feature area
  • ➕ Schema bump could land first as a focused PR
  • ➖ More coordination/merge overhead
  • ➖ Cross-cutting renderer changes still touch many files
  • ➖ Delays shipping urgent workaround-removal fixes
2. Ship only the three costly workaround fixes first
  • ➕ Immediate removal of downstream hand-workarounds
  • ➕ Significantly smaller initial review scope
  • ➖ Leaves the remaining backlog open longer
  • ➖ Some changes were designed to land together (shared schema bump and render contracts)

Recommendation: Given three issues were driving expensive downstream workarounds, the single coordinated release is defensible, especially with TDD-gated merges. The main tradeoff is reviewability/risk isolation: this PR changes schema, convergence gates, and all renderers at once. If repeating this pattern, prefer splitting along the plan’s dependency waves so reviewers can validate one contract change at a time.

Files changed (72) +9580 / -417

Enhancement (14) +1511 / -78
assign-to-workforce.shImplement 'assign-to-workforce split-plan --write' +304/-4

Implement 'assign-to-workforce split-plan --write'

• Adds substantial new logic to split a plan into smaller written artifacts for fan-out execution, plus supporting CLI plumbing and behaviors.

.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh

amend.pyAdd 'devague amend' +88/-0

Add 'devague amend'

• Introduces 'amend' to correct a claim’s text/kind in place (preserving id and attachments) and flips confirmed claims back to proposed when edited.

devague/cli/_commands/amend.py

confirm.pyReject cascade echo + JSON details +18/-4

Reject cascade echo + JSON details

• Updates confirm/reject output to report cascaded ids from claim rejection (honesty conditions + unresolved hard questions).

devague/cli/_commands/confirm.py

export.pyAdd contested-by-deviation derivation to spec export +14/-2

Add contested-by-deviation derivation to spec export

• Derives contested markers via 'find_contested_markers', emits diagnostics on join failures, and passes markers into spec rendering.

devague/cli/_commands/export.py

interrogate.pyAdd 'interrogate --resolve' for hard questions +69/-2

Add 'interrogate --resolve' for hard questions

• Adds a resolution mode to close claim-attached blocking hard questions as an explicit user decision; enforces mutual exclusion with add-flag modes.

devague/cli/_commands/interrogate.py

plan.pyPlan deferral, target validation, dep validation, multi-id confirm/reject, risk amend +298/-28

Plan deferral, target validation, dep validation, multi-id confirm/reject, risk amend

• Adds 'plan defer/--undo', validates cover targets against the live frame, persists deferrals across target re-derives, rejects self/unknown deps at creation, makes confirm/reject transactional for multiple ids, and adds 'plan risk --amend'.

devague/cli/_commands/plan.py

scope.pyScope entry amendment and q* seeds +48/-6

Scope entry amendment and q* seeds

• Supports 'scope --amend <sN> --finding' to replace a finding in place and accepts 'q*' hard-question ids as valid seeds alongside 'c*'.

devague/cli/_commands/scope.py

show.pyExpose contested markers in show output +28/-4

Expose contested markers in show output

• Derives contested-by-deviation markers and includes them in JSON and appended text output, emitting diagnostics on failures.

devague/cli/_commands/show.py

status.pyExpose contested markers in status output +15/-1

Expose contested markers in status output

• Derives contested-by-deviation markers and passes them into the shared status renderer.

devague/cli/_commands/status.py

_status.pyStatus renderer supports contested markers and parked items lines +44/-13

Status renderer supports contested markers and parked items lines

• Adds optional contested list support for JSON/text output and renders parked items lines in both pass/fail states.

devague/cli/_status.py

contested.pyAdd fail-open contested-by-deviation join module +206/-0

Add fail-open contested-by-deviation join module

• Introduces a new pure derivation module that enumerates plans/deliveries to mark frame claims contested by approved deviations, designed to fail open with diagnostics.

devague/contested.py

frame.pySchema v4 + amend + hard-question resolution + reject cascade +270/-4

Schema v4 + amend + hard-question resolution + reject cascade

• Bumps schema to v4; adds HardQuestion.resolution, claim revision tracking, claim/scope-entry amend APIs, hard-question resolution, q* seed validation, and a rejecting claim cascade onto attached honesty conditions and unresolved questions.

devague/frame.py

plan.pySchema v4 + target deferral + risk amendment +78/-1

Schema v4 + target deferral + risk amendment

• Bumps plan schema to v4; adds per-target deferral state (with reason), defer/undefer APIs, and risk text amendment while preserving id/kind/resolution state.

devague/plan.py

plan_convergence.pyExclude deferred targets from plan convergence blockers +31/-9

Exclude deferred targets from plan convergence blockers

• Treats deferred coverage targets as non-blocking while still listing them as parked/tracked items.

devague/plan_convergence.py

Bug fix (8) +542 / -115
__init__.pyImprove plan parser error remediation and register 'amend' +31/-1

Improve plan parser error remediation and register 'amend'

• Routes argparse errors in nested 'devague plan <move>' parsers to 'devague plan explain <move>' and registers the new 'amend' command.

devague/cli/init.py

convergence.pyTreat rejected claims and resolved hard questions correctly in convergence +22/-4

Treat rejected claims and resolved hard questions correctly in convergence

• Excludes rejected claims’ blocking hard questions from blockers, tightens assumption warnings to proposed-only, and updates suggested next move to 'interrogate --resolve'.

devague/convergence.py

plan_store.pyFail closed on newer plan schemas before parsing +14/-6

Fail closed on newer plan schemas before parsing

• Checks schema_version on the raw dict before dataclass construction to surface clear IncompatiblePlanSchemaError for newer files.

devague/plan_store.py

_md_safety.pyAdd md_safe_text() for markdownlint-safe verbatim rendering +122/-8

Add md_safe_text() for markdownlint-safe verbatim rendering

• Introduces 'md_safe_text' to wrap underscore/dunder identifiers in code spans and escape stray markdown control characters while protecting URLs and existing code spans.

devague/render/_md_safety.py

plan_md.pyRender deferred targets + apply md_safe_text +48/-7

Render deferred targets + apply md_safe_text

• Adds a Deferred targets section to plan exports and applies md_safe_text/URL autolinking to headings and body fields for markdownlint safety.

devague/render/plan_md.py

spec_md.pySpec export fidelity: contested markers, parks, hard-question rendering, md safety +200/-64

Spec export fidelity: contested markers, parks, hard-question rendering, md safety

• Adds contested marker rendering, unifies open parks across kinds, marks resolved hard questions with decision text, omits rejected-claim questions, and applies md_safe_text consistently.

devague/render/spec_md.py

summary_md.pySummary scoped to confirmed tasks; md safety +91/-18

Summary scoped to confirmed tasks; md safety

• Restricts planned/actual delivery sections to confirmed tasks, preserves rejected tasks as a count line/JSON list, and applies md_safe_text to verbatim fields.

devague/render/summary_md.py

store.pyFail closed on newer frame schemas before parsing +14/-7

Fail closed on newer frame schemas before parsing

• Checks schema_version on the raw dict before constructing Frame objects to avoid opaque TypeErrors on newer schemas.

devague/store.py

Tests (25) +4229 / -35
resolved_vagueness_spec.mdUpdate golden for resolved vagueness rendering +4/-0

Update golden for resolved vagueness rendering

• Adjusts expected markdown output to match updated spec rendering rules.

tests/goldens/resolved_vagueness_spec.md

sharper_spec.mdUpdate golden for sharpened spec rendering +1/-1

Update golden for sharpened spec rendering

• Updates golden fixture for sharpened rendering/convergence behavior.

tests/goldens/sharper_spec.md

test_assign_to_workforce_script.pyTests for split-plan workflow +141/-1

Tests for split-plan workflow

• Adds tests validating the assign-to-workforce script’s new plan splitting functionality.

tests/test_assign_to_workforce_script.py

test_cli_converge_export.pyTests for contested marker export behavior +70/-0

Tests for contested marker export behavior

• Adds coverage for contested marker derivation and fail-open diagnostics in export paths.

tests/test_cli_converge_export.py

test_cli_moves.pyTests for reject cascade + interrogate --resolve +288/-0

Tests for reject cascade + interrogate --resolve

• Adds extensive tests covering cascaded reject echoes/JSON/idempotence and hard-question resolution via 'interrogate --resolve'.

tests/test_cli_moves.py

test_cli_plan.pyTests for plan defer/dep validation/transactional batch ops +579/-3

Tests for plan defer/dep validation/transactional batch ops

• Adds tests for 'plan defer', creation-time dep validation ('--dep'/'--on'), cover target validation, and multi-id transactional confirm/reject behaviors.

tests/test_cli_plan.py

test_cli_review.pyUpdate review tests for new reject semantics +25/-0

Update review tests for new reject semantics

• Updates tests to ensure review behavior aligns with cascaded reject changes and related state transitions.

tests/test_cli_review.py

test_cli_scope.pyTests for scope amend and q* seeds +130/-0

Tests for scope amend and q* seeds

• Adds tests validating 'scope --amend' and accepting q* ids in '--seeds'.

tests/test_cli_scope.py

test_contested.pyNew test suite for contested.py +626/-0

New test suite for contested.py

• Adds comprehensive tests for contested marker derivation, deterministic ordering, and fail-open behavior on unreadable stores.

tests/test_contested.py

test_convergence.pyTests for convergence changes around rejection/resolution +60/-0

Tests for convergence changes around rejection/resolution

• Validates convergence behavior for resolved hard questions and rejected-claim filtering.

tests/test_convergence.py

test_e2e_resolve.pyE2E tests for new resolve flows +188/-3

E2E tests for new resolve flows

• Updates end-to-end tests to include hard-question resolution and related workflow paths.

tests/test_e2e_resolve.py

test_export_markdownlint_integration.pyMarkdownlint integration coverage for escaped exports +344/-0

Markdownlint integration coverage for escaped exports

• Adds large integration coverage ensuring generated markdown passes markdownlint after md_safe_text changes.

tests/test_export_markdownlint_integration.py

test_frame.pyTests for Frame amend/reject cascade/resolve_hard_question +378/-2

Tests for Frame amend/reject cascade/resolve_hard_question

• Adds extensive unit coverage for new Frame APIs and schema v4 behaviors.

tests/test_frame.py

test_frame_schema_v2.pyAdjust schema tests for v4 bump +6/-4

Adjust schema tests for v4 bump

• Updates schema tests to account for the frame schema version bump.

tests/test_frame_schema_v2.py

test_md_safety.pyNew tests for md_safe_text +217/-2

New tests for md_safe_text

• Adds unit tests verifying escaping is correct, URL-safe, and idempotent.

tests/test_md_safety.py

test_plan.pyTests for plan deferral and risk amendment +119/-4

Tests for plan deferral and risk amendment

• Adds unit tests for CoverageTarget deferral and Plan.amend_risk behavior.

tests/test_plan.py

test_plan_convergence.pyTests for deferred targets in convergence +93/-0

Tests for deferred targets in convergence

• Ensures deferred targets don’t block plan convergence and remain visible as tracked items.

tests/test_plan_convergence.py

test_plan_escape_hatches.pyUpdate escape hatch tests +10/-1

Update escape hatch tests

• Minor adjustments to existing tests impacted by the new plan behaviors.

tests/test_plan_escape_hatches.py

test_plan_store.pyTests for schema-version hardening in plan_store +65/-0

Tests for schema-version hardening in plan_store

• Adds tests that newer schema plan files fail with a clear error before parsing.

tests/test_plan_store.py

test_render.pyTests for spec renderer fidelity changes +181/-10

Tests for spec renderer fidelity changes

• Adds coverage for contested marker rendering, hard-question markers, and unified open parks rendering.

tests/test_render.py

test_render_plan.pyTests for plan renderer deferred targets + md safety +102/-1

Tests for plan renderer deferred targets + md safety

• Adds tests for the Deferred targets section and md_safe_text behavior in plan markdown exports.

tests/test_render_plan.py

test_spec_to_plan_skill.pyUpdate tests for spec-to-plan skill text +105/-0

Update tests for spec-to-plan skill text

• Adjusts tests to match updated skill documentation and teaching surfaces.

tests/test_spec_to_plan_skill.py

test_store.pyTests for schema-version hardening in frame store +126/-1

Tests for schema-version hardening in frame store

• Adds tests that newer schema frame files fail with a clear error before parsing.

tests/test_store.py

test_summary.pyTests for summary confirmed-task scoping +124/-2

Tests for summary confirmed-task scoping

• Adds tests that delivery summaries only list confirmed tasks and report rejected tasks separately.

tests/test_summary.py

test_teaching_surface_sweep.pyTeaching surface tests for updated moves/skills +247/-0

Teaching surface tests for updated moves/skills

• Validates updated learn/skills teaching content reflects newly added moves and behaviors.

tests/test_teaching_surface_sweep.py

Documentation (18) +1680 / -155
SKILL.mdDocument split-plan capability for workforce runs +54/-4

Document split-plan capability for workforce runs

• Updates the assign-to-workforce skill documentation to include 'split-plan --write' behavior (issue #82).

.claude/skills/assign-to-workforce/SKILL.md

SKILL.mdAdd scope exploration fan-out guidance +62/-10

Add scope exploration fan-out guidance

• Updates scope skill steps to recommend fanning out read-only exploration to smaller-tier subagents for 5+ surfaces, keeping moves centralized (#79/#91).

.claude/skills/scope/SKILL.md

SKILL.mdUpdate spec-to-plan skill for new plan controls +18/-5

Update spec-to-plan skill for new plan controls

• Extends skill documentation to reflect updated plan authoring controls (e.g., deferral) and related workflow tweaks.

.claude/skills/spec-to-plan/SKILL.md

SKILL.mdRefresh think skill text +13/-7

Refresh think skill text

• Minor edits to the think skill guidance to align with the updated operator workflow.

.claude/skills/think/SKILL.md

CHANGELOG.mdChangelog for 0.21.0 backlog sweep +158/-0

Changelog for 0.21.0 backlog sweep

• Documents the new moves and the set of issues closed in 0.21.0.

CHANGELOG.md

CLAUDE.mdUpdate contributor/operator guidance for new moves +123/-29

Update contributor/operator guidance for new moves

• Updates internal guidance to reflect new moves, tightened contracts, and schema v4 behavior.

CLAUDE.md

README.mdDocument new CLI moves +14/-2

Document new CLI moves

• Updates README to mention the newly added CLI functionality introduced in the sweep.

README.md

learn.pyTeach new moves and sharpened behaviors +44/-8

Teach new moves and sharpened behaviors

• Updates 'devague learn' to describe amend, hard-question resolution, cascaded reject semantics, and scope fan-out guidance.

devague/cli/_commands/learn.py

2026-07-28-issue-backlog-sweep-closure-map.mdAdd closure map for the sweep +122/-0

Add closure map for the sweep

• Adds a document mapping closed issues to the specific changes/tasks that resolved them.

docs/deliveries/2026-07-28-issue-backlog-sweep-closure-map.md

2026-07-28-issue-backlog-sweep.mdAdd sweep delivery write-up +175/-0

Add sweep delivery write-up

• Adds the human-readable delivery summary for the issue backlog sweep.

docs/deliveries/2026-07-28-issue-backlog-sweep.md

llm-guidance.mdUpdate LLM guidance +40/-12

Update LLM guidance

• Updates operator guidance to reflect new workflow and skills introduced/updated by the sweep.

docs/llm-guidance.md

2026-07-17-resolve-parked-vagueness.mdRe-render plan doc with new renderer behavior +28/-28

Re-render plan doc with new renderer behavior

• Updates an existing exported plan markdown to match the updated rendering rules.

docs/plans/2026-07-17-resolve-parked-vagueness.md

2026-07-28-issue-backlog-sweep-split.mdAdd split-plan output for the sweep +217/-0

Add split-plan output for the sweep

• Adds generated output from splitting the sweep plan into smaller pieces.

docs/plans/2026-07-28-issue-backlog-sweep-split.md

2026-07-28-issue-backlog-sweep.mdAdd exported plan for the sweep +164/-0

Add exported plan for the sweep

• Adds the exported plan markdown for the backlog sweep plan.

docs/plans/2026-07-28-issue-backlog-sweep.md

skill-sources.mdUpdate skill sources list +2/-2

Update skill sources list

• Minor updates to skill source references.

docs/skill-sources.md

skills.mdUpdate skills index +85/-16

Update skills index

• Updates skill documentation to reflect new/changed skills and behaviors.

docs/skills.md

spec-contract.mdUpdate spec contract documentation +216/-32

Update spec contract documentation

• Updates the spec contract to reflect new moves, tighter behavior, and export fidelity rules.

docs/spec-contract.md

2026-07-28-issue-backlog-sweep.mdAdd exported spec for the sweep +145/-0

Add exported spec for the sweep

• Adds the exported spec markdown for the backlog sweep frame.

docs/specs/2026-07-28-issue-backlog-sweep.md

Other (7) +1618 / -34
current_planPoint current plan to issue-backlog-sweep +1/-1

Point current plan to issue-backlog-sweep

• Updates the pointer file so the sweep plan is the active plan.

.devague/current_plan

issue-backlog-sweep.jsonAdd sweep delivery ledger +38/-0

Add sweep delivery ledger

• Adds a delivery ledger recording deviations/outcomes for the issue backlog sweep plan.

.devague/deliveries/issue-backlog-sweep.json

issue-backlog-sweep.jsonAdd sweep frame JSON +840/-0

Add sweep frame JSON

• Adds the frame state (claims, parks, questions, etc.) for the backlog sweep release.

.devague/frames/issue-backlog-sweep.json

issue-backlog-sweep.jsonAdd sweep plan JSON +648/-0

Add sweep plan JSON

• Adds the plan state for the sweep (19 tasks, 6 dependency waves, 52 targets).

.devague/plans/issue-backlog-sweep.json

resolve-parked-vagueness.jsonUpdate existing plan JSON for schema/behavior alignment +89/-31

Update existing plan JSON for schema/behavior alignment

• Adjusts an existing plan state file, consistent with the schema bump and renderer/convergence changes.

.devague/plans/resolve-parked-vagueness.json

pyproject.tomlBump version to 0.21.0 +1/-1

Bump version to 0.21.0

• Updates the project version for the 0.21.0 release.

pyproject.toml

uv.lockLockfile update +1/-1

Lockfile update

• Updates uv lockfile consistent with the version bump/release state.

uv.lock

@OriNachum
OriNachum merged commit e5047a4 into main Jul 28, 2026
8 checks passed
@OriNachum
OriNachum deleted the issue-backlog-sweep branch July 28, 2026 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a CLI verb to resolve blocking hard questions (converge is permanently blocked without one)

1 participant