Skip to content

feat: implement the 0.6.0 Human Review Loop (#17)#23

Merged
OriNachum merged 9 commits into
mainfrom
feat/human-review-loop
May 23, 2026
Merged

feat: implement the 0.6.0 Human Review Loop (#17)#23
OriNachum merged 9 commits into
mainfrom
feat/human-review-loop

Conversation

@OriNachum

Copy link
Copy Markdown
Contributor

What

Implements the 0.6.0 Human Review Loop (#17) — the full 7-task plan exported
by /spec-to-plan in #22. Makes the user-only confirmation step ergonomic at
scale while preserving the anti-fabrication guarantee. No LLM in the CLI; no
external services.

The loop

devague review                 # list every proposed item with ids (un-gated, no mutation)
devague review --json          # structured
devague confirm c2 h1 h3       # confirm many ids in one transactional call
devague reject c4 c5           # reject many ids in one call
devague confirm --from-review .devague/reviews/<slug>.md   # apply an edited review file
devague question ""           # record / --list / --resolve pending decisions

review writes a non-authoritative artifact to .devague/reviews/<slug>.md;
each item carries an editable pending marker (change to confirm/reject,
then confirm --from-review). pending lines are never auto-confirmed; batches
are transactional (one bad id ⇒ nothing changes). devague manages .gitignore
so .devague/reviews/ and .devague/questions/ stay uncommitted working state.

Tasks (each its own commit)

Task Commit Covers
t1 multi-id transactional confirm/reject feat(t1) c14, h3
t2 devague review (un-gated, no mutation) feat(t2) c12, h1
t3 non-authoritative artifact + .gitignore feat(t3) c13, h2
t4 confirm --from-review round-trip feat(t4) c21, h13
t5 devague question working state feat(t5) c15, h4
t6 cross-cutting invariant suite test(t6) c16,h5,c6,h11,c5,h10,c3,h8
t7 docs + version 0.6.0 + integration feat(t7) c1,h6,c2,h7,c4,h9,c7,h12

All 26 plan coverage targets implemented and tested.

Verification

  • 210 tests pass (was 181 on main); coverage 96.97% (gate 95%).
  • flake8 / black / isort / markdownlint all clean.
  • The no-auto-confirm invariant, review-before-convergence, multi-id transactional
    confirm/reject, and the --from-review round-trip each have explicit tests; an
    integration test asserts 0.6.0 introduced no new claim/condition states.
  • End-to-end smoke (review → edit markers → --from-review) verified outside the
    test harness.

Bumps 0.5.1 → 0.6.0. Closes #17 (and the directions of #11, #14).

  • devague (Claude)

OriNachum and others added 7 commits May 23, 2026 18:26
confirm/reject now accept one or more ids (nargs+). The batch is
transactional: all ids are validated first, and if any is unknown the batch
aborts with a hint and resolves nothing — no half-applied state (decision c17).
Covers plan targets c14, h3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#17)

New 'devague review' (+ --json) surfaces every proposed claim and proposed
honesty condition with ids. It is NOT gated on convergence and never mutates
state — a read-only view of what awaits a user decision. Adds a review-md
renderer with an explicit non-authoritative banner. Covers plan targets c12, h1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…re (#17)

devague review now writes .devague/reviews/<slug>.md (banner-marked, distinct
from docs/specs/) and ensures .devague/reviews/ is git-ignored so it stays
uncommitted working state by default (decision c19). --no-write prints only.
Adds store.ensure_ignored / review_path / write_review. Covers plan targets
c13, h2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#17)

The review artifact is now round-trippable: each proposed item is emitted with
an editable 'pending' marker. Change it to 'confirm'/'reject' and run
'devague confirm --from-review <file>' to apply exactly those decisions via the
transactional path. 'pending' lines are never auto-confirmed; conflicting,
empty, missing-file, and ids+file inputs all error cleanly. Covers c21, h13.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…<slug>.md (#17)

New 'devague question' records/lists/resolves pending user decisions in durable,
uncommitted working state under .devague/questions/<slug>.md (CLI-owns the
round-trippable format, decision c20; git-ignored, decision c19). --resolve only
records a human's decision; applying it into the frame stays an explicit move.
Covers plan targets c15, h4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the verification suite proving the spec's honesty conditions: no review-flow
command auto-confirms (c16/h5); one review + one batched confirm/reject resolves a
chosen set on a non-converged frame (c3/h8); an llm proposal stays proposed until
an explicit user confirm (c5/h10). Coverage 96.97%. Covers c16,h5,c6,h11,c5,h10,c3,h8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ecks (#17)

README documents the review loop (review, multi-id + --from-review confirm/reject,
question) and which .devague/ files are committed vs uncommitted working state.
CLAUDE.md status + flat-moves and the /think SKILL.md move table updated for the
new verbs (doc-test alignment). Integration tests assert the announcement is
literally true at scale and that 0.6.0 introduced no new claim/condition states
(c1,h6,c2,h7,c4,h9,c7,h12). Bump 0.5.1 -> 0.6.0; CHANGELOG updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Implement 0.6.0 Human Review Loop with multi-id transactional confirm/reject and review artifacts

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Implements the 0.6.0 Human Review Loop with devague review command
  - Lists every proposed (unconfirmed) claim and honesty condition with ids
  - Un-gated by convergence; never mutates state; writes non-authoritative artifact to
  .devague/reviews/.md
• Extends confirm/reject to accept multiple ids in one transactional call
  - Validates all ids first; if any unknown, batch aborts with no half-applied state
  - Adds confirm --from-review  to apply edited review artifacts
• Introduces devague question command for recording/listing/resolving pending user decisions
  - Durable working state in .devague/questions/.md
  - Uncommitted by default; devague manages .gitignore
• Adds review artifact parser and renderer with round-trippable pending/confirm/reject markers
Diagram
flowchart LR
  A["User proposes items<br/>via LLM"] -->|"devague review"| B["Review artifact<br/>.devague/reviews/"]
  B -->|"Edit pending→confirm/reject"| C["Apply decisions<br/>confirm --from-review"]
  C -->|"Transactional batch"| D["Frame state<br/>confirmed/rejected"]
  E["Pending decisions"] -->|"devague question"| F[".devague/questions/<br/>working state"]
  F -->|"--resolve"| G["Record decision"]

Loading

File Changes

1. devague/cli/__init__.py ✨ Enhancement +4/-0

Register new review and question commands

devague/cli/init.py


2. devague/cli/_commands/confirm.py ✨ Enhancement +68/-10

Multi-id transactional confirm with --from-review support

devague/cli/_commands/confirm.py


3. devague/cli/_commands/reject.py ✨ Enhancement +4/-8

Refactor reject to support multiple ids

devague/cli/_commands/reject.py


View more (17)
4. devague/cli/_commands/review.py ✨ Enhancement +63/-0

New review command listing proposed items

devague/cli/_commands/review.py


5. devague/cli/_commands/question.py ✨ Enhancement +73/-0

New question command for pending decisions

devague/cli/_commands/question.py


6. devague/questions_io.py ✨ Enhancement +60/-0

Parse and render pending decisions markdown

devague/questions_io.py


7. devague/render/review_md.py ✨ Enhancement +76/-0

Renderer and parser for review artifacts

devague/render/review_md.py


8. devague/render/__init__.py ✨ Enhancement +2/-0

Register review-md renderer format

devague/render/init.py


9. devague/store.py ✨ Enhancement +52/-0

Add review/question paths and .gitignore management

devague/store.py


10. tests/test_cli_moves.py 🧪 Tests +32/-0

Add tests for multi-id and transactional confirm/reject

tests/test_cli_moves.py


11. tests/test_cli_review.py 🧪 Tests +153/-0

Comprehensive tests for review command and --from-review

tests/test_cli_review.py


12. tests/test_cli_question.py 🧪 Tests +57/-0

Tests for question command record/list/resolve

tests/test_cli_question.py


13. tests/test_review_loop_integration.py 🧪 Tests +66/-0

Integration tests for 0.6.0 review loop at scale

tests/test_review_loop_integration.py


14. tests/test_review_loop_invariants.py 🧪 Tests +72/-0

Cross-cutting invariant tests for review flow

tests/test_review_loop_invariants.py


15. tests/test_render.py 🧪 Tests +20/-0

Add tests for review-md renderer

tests/test_render.py


16. .claude/skills/think/SKILL.md 📝 Documentation +3/-1

Update move documentation for new commands

.claude/skills/think/SKILL.md


17. CHANGELOG.md 📝 Documentation +15/-0

Document 0.6.0 Human Review Loop features

CHANGELOG.md


18. CLAUDE.md 📝 Documentation +12/-2

Update status with Human Review Loop landing

CLAUDE.md


19. README.md 📝 Documentation +44/-0

Add Human Review Loop section with examples

README.md


20. pyproject.toml ⚙️ Configuration changes +1/-1

Bump version to 0.6.0

pyproject.toml


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 23, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Question decision parse corrupts ✓ Resolved 🐞 Bug ≡ Correctness
Description
In questions_io.parse(), resolved lines are split on the *first* occurrence of the decided
delimiter (" — decided: "), so a question whose text contains that substring will be truncated and
its decision field mis-parsed on the next parse/render cycle. This silently corrupts durable
.devague/questions/<slug>.md working state when users record/resolve such questions.
Code

devague/questions_io.py[R33-36]

Evidence
render() appends the decided delimiter at the end of resolved lines, but parse() splits on the
first occurrence of that delimiter, so delimiter text inside the question body will be
misinterpreted as the separator and corrupt the stored fields.

devague/questions_io.py[25-39]
devague/questions_io.py[47-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`questions_io.parse()` currently splits resolved question lines using `rest.split(_DECIDED, 1)`, which splits on the *first* occurrence of the delimiter. If the question text itself contains the delimiter substring (`" — decided: "`), the parser will incorrectly split inside the text rather than at the appended decision tail, corrupting the persisted question text/decision.

### Issue Context
`questions_io.render()` appends the decided tail at the end of the line when `decision` is present, so parsing should split from the rightmost occurrence (or otherwise explicitly parse a trailing `decided:` suffix).

### Fix Focus Areas
- devague/questions_io.py[25-39]
- devague/questions_io.py[47-60]

### Suggested fix
- Replace `rest.split(_DECIDED, 1)` with `rest.rsplit(_DECIDED, 1)` (or use `rpartition`) so only the final appended delimiter is treated as the separator.
- Add a regression test where the original question text includes the delimiter substring (e.g., `"Should we use — decided: as syntax?"`) and verify round-trip parse/render preserves text + decision.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread devague/questions_io.py
…ex (ReDoS hotspot)

PR #23 SonarCloud gate (ERROR):
- python:S3516 (BLOCKER): cmd_question always returned 0 via three identical
  returns. Split into _resolve_question/_add_question/_list_questions with one
  return point.
- DoS hotspot: the questions _LINE regex had a \s+...\s+...\.* shape flagged
  for polynomial backtracking. Replaced variable whitespace with single literal
  spaces (rest is stripped in parse) so the pattern is linear. Behaviour and
  round-trip are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… corruption)

questions_io.parse() split resolved lines on the FIRST ' — decided: ', so a
question whose text contains that delimiter was truncated on the next
parse/render cycle, silently corrupting .devague/questions/<slug>.md. render()
appends the decided tail last, so split from the right (rsplit). Adds a
regression test with the delimiter inside the question text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@OriNachum
OriNachum merged commit 753fe24 into main May 23, 2026
8 checks passed
@OriNachum
OriNachum deleted the feat/human-review-loop branch May 23, 2026 15:56
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.

0.6.0: Human Review Loop for proposed claims, honesty conditions, and pending decisions

1 participant