devague 0.4.1__tar.gz → 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. {devague-0.4.1 → devague-0.5.1}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +13 -41
  2. {devague-0.4.1 → devague-0.5.1}/.claude/skills/think/SKILL.md +16 -12
  3. {devague-0.4.1 → devague-0.5.1}/.claude/skills/think/scripts/think.sh +13 -47
  4. devague-0.5.1/.devague/current_plan +1 -0
  5. devague-0.5.1/.devague/frames/devague-0-6-0-ships-the-human-review-loop-devague.json +299 -0
  6. devague-0.5.1/.devague/plans/devague-0-6-0-ships-the-human-review-loop-devague.json +288 -0
  7. {devague-0.4.1 → devague-0.5.1}/CHANGELOG.md +25 -0
  8. {devague-0.4.1 → devague-0.5.1}/CLAUDE.md +9 -0
  9. {devague-0.4.1 → devague-0.5.1}/PKG-INFO +1 -1
  10. {devague-0.4.1 → devague-0.5.1}/culture.yaml +1 -1
  11. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/capture.py +9 -1
  12. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/converge.py +18 -7
  13. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/export.py +2 -2
  14. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/plan.py +20 -12
  15. {devague-0.4.1 → devague-0.5.1}/devague/cli/_frames.py +16 -1
  16. devague-0.5.1/devague/convergence.py +149 -0
  17. {devague-0.4.1 → devague-0.5.1}/devague/frame.py +45 -1
  18. {devague-0.4.1 → devague-0.5.1}/devague/plan_convergence.py +47 -3
  19. {devague-0.4.1 → devague-0.5.1}/devague/render/frame_md.py +5 -1
  20. devague-0.5.1/devague/render/spec_md.py +61 -0
  21. {devague-0.4.1 → devague-0.5.1}/devague/store.py +11 -1
  22. devague-0.5.1/docs/examples/contract-example.json +160 -0
  23. devague-0.5.1/docs/plans/devague-0-6-0-ships-the-human-review-loop-devague.md +71 -0
  24. devague-0.5.1/docs/spec-contract.md +180 -0
  25. devague-0.5.1/docs/specs/devague-0-6-0-ships-the-human-review-loop-devague.md +54 -0
  26. {devague-0.4.1 → devague-0.5.1}/pyproject.toml +1 -1
  27. {devague-0.4.1 → devague-0.5.1}/tests/test_cli_converge_export.py +7 -7
  28. {devague-0.4.1 → devague-0.5.1}/tests/test_cli_moves.py +27 -0
  29. {devague-0.4.1 → devague-0.5.1}/tests/test_cli_plan.py +2 -2
  30. devague-0.5.1/tests/test_contract.py +129 -0
  31. devague-0.5.1/tests/test_convergence.py +97 -0
  32. devague-0.5.1/tests/test_frame.py +111 -0
  33. devague-0.5.1/tests/test_offline.py +58 -0
  34. {devague-0.4.1 → devague-0.5.1}/tests/test_plan_convergence.py +14 -14
  35. {devague-0.4.1 → devague-0.5.1}/tests/test_render.py +44 -0
  36. {devague-0.4.1 → devague-0.5.1}/tests/test_store.py +43 -1
  37. {devague-0.4.1 → devague-0.5.1}/uv.lock +1 -1
  38. devague-0.4.1/.devague/current_plan +0 -1
  39. devague-0.4.1/devague/convergence.py +0 -72
  40. devague-0.4.1/devague/render/spec_md.py +0 -50
  41. devague-0.4.1/tests/test_convergence.py +0 -62
  42. devague-0.4.1/tests/test_frame.py +0 -42
  43. {devague-0.4.1 → devague-0.5.1}/.claude/skills/cicd/SKILL.md +0 -0
  44. {devague-0.4.1 → devague-0.5.1}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  45. {devague-0.4.1 → devague-0.5.1}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  46. {devague-0.4.1 → devague-0.5.1}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  47. {devague-0.4.1 → devague-0.5.1}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  48. {devague-0.4.1 → devague-0.5.1}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  49. {devague-0.4.1 → devague-0.5.1}/.claude/skills/communicate/SKILL.md +0 -0
  50. {devague-0.4.1 → devague-0.5.1}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  51. {devague-0.4.1 → devague-0.5.1}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  52. {devague-0.4.1 → devague-0.5.1}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  53. {devague-0.4.1 → devague-0.5.1}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  54. {devague-0.4.1 → devague-0.5.1}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  55. {devague-0.4.1 → devague-0.5.1}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  56. {devague-0.4.1 → devague-0.5.1}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  57. {devague-0.4.1 → devague-0.5.1}/.claude/skills/run-tests/SKILL.md +0 -0
  58. {devague-0.4.1 → devague-0.5.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  59. {devague-0.4.1 → devague-0.5.1}/.claude/skills/sonarclaude/SKILL.md +0 -0
  60. {devague-0.4.1 → devague-0.5.1}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  61. {devague-0.4.1 → devague-0.5.1}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  62. {devague-0.4.1 → devague-0.5.1}/.claude/skills/version-bump/SKILL.md +0 -0
  63. {devague-0.4.1 → devague-0.5.1}/.claude/skills/version-bump/scripts/bump.py +0 -0
  64. {devague-0.4.1 → devague-0.5.1}/.claude/skills.local.yaml.example +0 -0
  65. {devague-0.4.1 → devague-0.5.1}/.devague/frames/devague-now-ships-a-documented-spec-contract-every.json +0 -0
  66. {devague-0.4.1 → devague-0.5.1}/.devague/plans/devague-now-ships-a-documented-spec-contract-every.json +0 -0
  67. {devague-0.4.1 → devague-0.5.1}/.flake8 +0 -0
  68. {devague-0.4.1 → devague-0.5.1}/.github/workflows/publish.yml +0 -0
  69. {devague-0.4.1 → devague-0.5.1}/.github/workflows/security-checks.yml +0 -0
  70. {devague-0.4.1 → devague-0.5.1}/.github/workflows/tests.yml +0 -0
  71. {devague-0.4.1 → devague-0.5.1}/.gitignore +0 -0
  72. {devague-0.4.1 → devague-0.5.1}/.markdownlint-cli2.yaml +0 -0
  73. {devague-0.4.1 → devague-0.5.1}/.pre-commit-config.yaml +0 -0
  74. {devague-0.4.1 → devague-0.5.1}/LICENSE +0 -0
  75. {devague-0.4.1 → devague-0.5.1}/README.md +0 -0
  76. {devague-0.4.1 → devague-0.5.1}/devague/__init__.py +0 -0
  77. {devague-0.4.1 → devague-0.5.1}/devague/__main__.py +0 -0
  78. {devague-0.4.1 → devague-0.5.1}/devague/cli/__init__.py +0 -0
  79. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/__init__.py +0 -0
  80. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/confirm.py +0 -0
  81. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/explain.py +0 -0
  82. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/interrogate.py +0 -0
  83. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/learn.py +0 -0
  84. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/list_frames.py +0 -0
  85. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/new.py +0 -0
  86. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/park.py +0 -0
  87. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/reject.py +0 -0
  88. {devague-0.4.1 → devague-0.5.1}/devague/cli/_commands/show.py +0 -0
  89. {devague-0.4.1 → devague-0.5.1}/devague/cli/_errors.py +0 -0
  90. {devague-0.4.1 → devague-0.5.1}/devague/cli/_output.py +0 -0
  91. {devague-0.4.1 → devague-0.5.1}/devague/cli/_plans.py +0 -0
  92. {devague-0.4.1 → devague-0.5.1}/devague/plan.py +0 -0
  93. {devague-0.4.1 → devague-0.5.1}/devague/plan_store.py +0 -0
  94. {devague-0.4.1 → devague-0.5.1}/devague/render/__init__.py +0 -0
  95. {devague-0.4.1 → devague-0.5.1}/devague/render/plan_md.py +0 -0
  96. {devague-0.4.1 → devague-0.5.1}/docs/plans/devague-now-ships-a-documented-spec-contract-every.md +0 -0
  97. {devague-0.4.1 → devague-0.5.1}/docs/reviews/spec-contract-frame-review.md +0 -0
  98. {devague-0.4.1 → devague-0.5.1}/docs/skill-sources.md +0 -0
  99. {devague-0.4.1 → devague-0.5.1}/docs/specs/devague-now-ships-a-documented-spec-contract-every.md +0 -0
  100. {devague-0.4.1 → devague-0.5.1}/docs/superpowers/plans/2026-05-22-specifix-onboarding.md +0 -0
  101. {devague-0.4.1 → devague-0.5.1}/docs/superpowers/plans/2026-05-23-devague-rename.md +0 -0
  102. {devague-0.4.1 → devague-0.5.1}/docs/superpowers/plans/2026-05-23-devague-working-backwards-engine.md +0 -0
  103. {devague-0.4.1 → devague-0.5.1}/docs/superpowers/specs/2026-05-22-specifix-onboarding-design.md +0 -0
  104. {devague-0.4.1 → devague-0.5.1}/docs/superpowers/specs/2026-05-23-devague-spec-to-plan-design.md +0 -0
  105. {devague-0.4.1 → devague-0.5.1}/docs/superpowers/specs/2026-05-23-devague-working-backwards-design.md +0 -0
  106. {devague-0.4.1 → devague-0.5.1}/sonar-project.properties +0 -0
  107. {devague-0.4.1 → devague-0.5.1}/tests/__init__.py +0 -0
  108. {devague-0.4.1 → devague-0.5.1}/tests/test_cli_affordances.py +0 -0
  109. {devague-0.4.1 → devague-0.5.1}/tests/test_cli_chassis.py +0 -0
  110. {devague-0.4.1 → devague-0.5.1}/tests/test_cli_errors.py +0 -0
  111. {devague-0.4.1 → devague-0.5.1}/tests/test_cli_output.py +0 -0
  112. {devague-0.4.1 → devague-0.5.1}/tests/test_package.py +0 -0
  113. {devague-0.4.1 → devague-0.5.1}/tests/test_plan.py +0 -0
  114. {devague-0.4.1 → devague-0.5.1}/tests/test_plan_store.py +0 -0
  115. {devague-0.4.1 → devague-0.5.1}/tests/test_render_plan.py +0 -0
  116. {devague-0.4.1 → devague-0.5.1}/tests/test_spec_to_plan_skill.py +0 -0
  117. {devague-0.4.1 → devague-0.5.1}/tests/test_think_skill.py +0 -0
@@ -108,7 +108,6 @@ cmd_status() {
108
108
  python3 - <<'PY'
109
109
  import json
110
110
  import os
111
- import re
112
111
  import sys
113
112
 
114
113
 
@@ -154,52 +153,25 @@ if conv is None:
154
153
  print("next move: devague plan show # inspect the plan")
155
154
  sys.exit(0)
156
155
 
157
- if conv.get("passed"):
156
+ if conv.get("ready_for_plan"):
158
157
  print("convergence: PASSED ✓")
158
+ for w in conv.get("warnings") or []:
159
+ print(f" ⚠ {w}")
159
160
  print("next move: devague plan export # write the buildable plan")
160
161
  sys.exit(0)
161
162
 
162
- missing = conv.get("missing") or []
163
- print(f"convergence: NOT passed — {len(missing)} gap(s):")
164
- for gap in missing:
165
- print(f" - {gap}")
166
-
167
-
168
- def suggest(gap):
169
- if "no tasks yet" in gap:
170
- return 'devague plan task "<summary>" --covers <c*/h*> --accept "<criterion>"'
171
- m = re.search(r"coverage target (\w+) ", gap)
172
- if m:
173
- tid = m.group(1)
174
- return (
175
- f'cover {tid}: devague plan task "<summary>" --covers {tid} --accept "<...>"'
176
- f" (or: devague plan cover <tN> --target {tid})"
177
- )
178
- m = re.search(r"task (t\d+) has no acceptance", gap)
179
- if m:
180
- return f'devague plan accept {m.group(1)} "<acceptance criterion>"'
181
- m = re.search(r"task (t\d+) still proposed", gap)
182
- if m:
183
- tid = m.group(1)
184
- return (
185
- f"this is an LLM proposal — the USER decides:"
186
- f" devague plan confirm {tid} (or: devague plan reject {tid})"
187
- )
188
- m = re.search(r"task (t\d+) depends on unknown task (t\d+)", gap)
189
- if m:
190
- return f"fix {m.group(1)}'s dependency on missing {m.group(2)} (add it, or drop the dep)"
191
- if "dependency cycle" in gap:
192
- return "break the dependency cycle: re-point one task's --dep so the graph is acyclic"
193
- m = re.search(r"blocking risk (r\d+)", gap)
194
- if m:
195
- return f"resolve {m.group(1)}: cover it with a task, or re-record it as non-blocking"
196
- return "devague plan show # inspect and decide"
197
-
198
-
199
- if missing:
163
+ blockers = conv.get("blockers") or []
164
+ print(f"convergence: NOT passed — {len(blockers)} gap(s):")
165
+ for b in blockers:
166
+ print(f" - {b}")
167
+ for w in conv.get("warnings") or []:
168
+ print(f" ⚠ {w}")
169
+
170
+ moves = conv.get("required_next_moves") or []
171
+ if moves:
200
172
  print()
201
173
  print("recommended next move (first gap):")
202
- print(f" {suggest(missing[0])}")
174
+ print(f" {moves[0]}")
203
175
  PY
204
176
  }
205
177
 
@@ -66,27 +66,31 @@ portable resolution and the `status` helper.
66
66
  | `learn` / `explain <move>` | Teach the method / explain one move. |
67
67
 
68
68
  Claim kinds: `announcement`, `audience`, `after_state`, `before_state`,
69
- `why_it_matters`, `boundary`, `success_signal`, `open_question`. Vagueness kinds:
70
- `unknown_nonblocking`, `unknown_blocking`, `out_of_scope`, `follow_up`.
69
+ `why_it_matters`, `boundary`, `success_signal`, `open_question`, `non_goal`,
70
+ `requirement`, `assumption`, `decision`. Vagueness kinds: `unknown_nonblocking`,
71
+ `unknown_blocking`, `out_of_scope`, `follow_up`.
71
72
 
72
73
  These are exactly the kinds the **shipped CLI enforces** (`CLAIM_KINDS` /
73
74
  `VAGUENESS_KINDS` in `devague/frame.py`) — the skill documents the surface as
74
- built, so every command here passes the CLI's `choices=` validation. A fuller
75
- proposed type/state set, plus the formal per-move input/output/transition
76
- contract, is tracked on the CLI side in
77
- [#5](https://github.com/agentculture/devague/issues/5); for the authoritative
75
+ built, so every command here passes the CLI's `choices=` validation. `requirement`
76
+ is spec-affecting (needs a confirmed honesty condition); `non_goal` / `decision`
77
+ are descriptive; an unconfirmed `assumption` is a convergence *warning*, not a
78
+ blocker. The formal entity model, the `(state × origin)` vocabulary, and the
79
+ per-move input/output/transition/error contract are documented in
80
+ [`docs/spec-contract.md`](../../../docs/spec-contract.md) (issue
81
+ [#5](https://github.com/agentculture/devague/issues/5)); for the authoritative
78
82
  live shape of any move, run it with `--json` (or `devague learn --json` /
79
- `devague explain <move>`). When the CLI's contract grows, re-sync this list.
83
+ `devague explain <move>`).
80
84
 
81
85
  ### `status` — the next-move helper
82
86
 
83
87
  `status` is a wrapper-only verb (the CLI has no `status`). It reads
84
88
  `converge --json` + `list --json` and prints where the current frame stands, the
85
- remaining gaps, and the recommended next move derived from the first gap.
86
- `converge --json` currently emits `{passed, missing}`, which is what the helper
87
- consumes; if [#5](https://github.com/agentculture/devague/issues/5) enriches that
88
- payload (e.g. structured `blockers` / `warnings` / `required_next_moves`),
89
- `status` will surface the richer fields then.
89
+ remaining gaps, and the recommended next move. `converge --json` emits the
90
+ structured result `{ready_for_spec, blockers, warnings, parked_items,
91
+ required_next_moves}` (issue [#5](https://github.com/agentculture/devague/issues/5));
92
+ the helper reads `ready_for_spec`, lists the `blockers` and `warnings`, and shows
93
+ `required_next_moves[0]` as the recommended move — no longer deriving it itself.
90
94
 
91
95
  ```text
92
96
  frame: my-feature (1 frame total)
@@ -114,7 +114,6 @@ cmd_status() {
114
114
  python3 - <<'PY'
115
115
  import json
116
116
  import os
117
- import re
118
117
  import sys
119
118
 
120
119
 
@@ -161,58 +160,25 @@ if conv is None:
161
160
  print("next move: devague show # inspect the frame")
162
161
  sys.exit(0)
163
162
 
164
- if conv.get("passed"):
163
+ if conv.get("ready_for_spec"):
165
164
  print("convergence: PASSED ✓")
165
+ for w in conv.get("warnings") or []:
166
+ print(f" ⚠ {w}")
166
167
  print("next move: devague export # write the buildable spec")
167
168
  sys.exit(0)
168
169
 
169
- missing = conv.get("missing") or []
170
- print(f"convergence: NOT passed — {len(missing)} gap(s):")
171
- for gap in missing:
172
- print(f" - {gap}")
173
-
174
-
175
- def suggest(gap):
176
- # Confirmation is a USER-only transition; a plain (user-origin) capture
177
- # is already confirmed, so never imply the agent should confirm its own
178
- # work. Spell out who confirms wherever a confirm is in play.
179
- m = re.search(r"missing confirmed '([a-z_]+)' claim", gap)
180
- if m:
181
- kind = m.group(1)
182
- return (f'devague capture --kind {kind} "<text>"'
183
- f' (a user capture auto-confirms; an --origin llm capture'
184
- f' then needs the USER to confirm it)')
185
- if "before_state" in gap and "why_it_matters" in gap:
186
- return 'devague capture --kind why_it_matters "<text>"'
187
- if "boundary" in gap:
188
- return 'devague capture --kind boundary "<text>"'
189
- if "success_signal" in gap:
190
- return 'devague capture --kind success_signal "<text>"'
191
- m = re.search(r"claim (c\d+) still proposed", gap)
192
- if m:
193
- cid = m.group(1)
194
- return (f'this is an LLM proposal — the USER decides:'
195
- f' devague confirm {cid} (or: devague reject {cid})')
196
- m = re.search(r"claim (c\d+) has no confirmed honesty condition", gap)
197
- if m:
198
- cid = m.group(1)
199
- return (f'devague interrogate {cid} --honesty "<what must be true>"'
200
- f' then the USER runs: devague confirm <hN>')
201
- m = re.search(r"blocking vagueness (v\d+)", gap)
202
- if m:
203
- return (f"resolve {m.group(1)}: capture+confirm the answer, "
204
- f"or re-park it as non-blocking")
205
- m = re.search(r"blocking hard question (q\d+) on (c\d+)", gap)
206
- if m:
207
- return (f"resolve {m.group(1)} on {m.group(2)}: answer it, then "
208
- f"capture/confirm the resulting claim")
209
- return "devague show # inspect and decide"
210
-
211
-
212
- if missing:
170
+ blockers = conv.get("blockers") or []
171
+ print(f"convergence: NOT passed — {len(blockers)} gap(s):")
172
+ for b in blockers:
173
+ print(f" - {b}")
174
+ for w in conv.get("warnings") or []:
175
+ print(f" ⚠ {w}")
176
+
177
+ moves = conv.get("required_next_moves") or []
178
+ if moves:
213
179
  print()
214
180
  print("recommended next move (first gap):")
215
- print(f" {suggest(missing[0])}")
181
+ print(f" {moves[0]}")
216
182
  PY
217
183
  }
218
184
 
@@ -0,0 +1 @@
1
+ devague-0-6-0-ships-the-human-review-loop-devague
@@ -0,0 +1,299 @@
1
+ {
2
+ "slug": "devague-0-6-0-ships-the-human-review-loop-devague",
3
+ "title": "devague 0.6.0 ships the Human Review Loop: 'devague review' surfaces every unconfirmed LLM proposal in one pass, and confirm/reject now take many ids at once \u2014 so honest human review scales to frames full of proposals without ever auto-confirming anything.",
4
+ "schema_version": 1,
5
+ "status": "exported",
6
+ "created": "2026-05-23T11:07:38Z",
7
+ "updated": "2026-05-23T11:24:24Z",
8
+ "claims": [
9
+ {
10
+ "id": "c1",
11
+ "kind": "announcement",
12
+ "text": "devague 0.6.0 ships the Human Review Loop: 'devague review' surfaces every unconfirmed LLM proposal in one pass, and confirm/reject now take many ids at once \u2014 so honest human review scales to frames full of proposals without ever auto-confirming anything.",
13
+ "origin": "user",
14
+ "status": "confirmed",
15
+ "honesty_conditions": [
16
+ {
17
+ "id": "h6",
18
+ "text": "At 0.6.0 release the announcement is literally true of the shipped CLI: 'devague review' exists, and a frame full of proposed items can be reviewed then bulk confirmed/rejected in one pass with no path that auto-confirms.",
19
+ "status": "confirmed"
20
+ }
21
+ ],
22
+ "hard_questions": [],
23
+ "links": []
24
+ },
25
+ {
26
+ "id": "c2",
27
+ "kind": "audience",
28
+ "text": "The human operator driving devague who must review and confirm/reject LLM-proposed claims and honesty conditions, plus the assisting LLM agent that produces those proposals.",
29
+ "origin": "user",
30
+ "status": "confirmed",
31
+ "honesty_conditions": [
32
+ {
33
+ "id": "h7",
34
+ "text": "Both audiences are served: the operator gets a single review + bulk-decide path, and the LLM agent's proposals stay visibly 'proposed' until the operator explicitly acts.",
35
+ "status": "confirmed"
36
+ }
37
+ ],
38
+ "hard_questions": [],
39
+ "links": []
40
+ },
41
+ {
42
+ "id": "c3",
43
+ "kind": "after_state",
44
+ "text": "In one pass the operator sees every unconfirmed proposal (proposed claims + proposed honesty conditions) without the frame needing to converge, then confirms or rejects many in a single command; pending design questions persist as durable .devague working state to decide later.",
45
+ "origin": "user",
46
+ "status": "confirmed",
47
+ "honesty_conditions": [
48
+ {
49
+ "id": "h8",
50
+ "text": "On a non-converged frame, one 'review' shows every proposed item and one 'confirm'/'reject' call resolves a chosen set \u2014 demonstrable end-to-end in a test.",
51
+ "status": "confirmed"
52
+ }
53
+ ],
54
+ "hard_questions": [],
55
+ "links": []
56
+ },
57
+ {
58
+ "id": "c4",
59
+ "kind": "before_state",
60
+ "text": "Review was ad-hoc (hand-rolled from show / show --json) and confirmation was one id per command, so a frame with ~15 proposed conditions meant 15 sequential commands \u2014 pushing toward rubber-stamping instead of genuine review (surfaced dogfooding /think on #5).",
61
+ "origin": "user",
62
+ "status": "confirmed",
63
+ "honesty_conditions": [
64
+ {
65
+ "id": "h9",
66
+ "text": "The before-state pain is real and removed: the pre-0.6.0 flow needed N commands for N conditions with no review artifact; 0.6.0 replaces that with one review plus one bulk decision.",
67
+ "status": "confirmed"
68
+ }
69
+ ],
70
+ "hard_questions": [],
71
+ "links": []
72
+ },
73
+ {
74
+ "id": "c5",
75
+ "kind": "why_it_matters",
76
+ "text": "The user-only confirmation step is devague's whole anti-fabrication guarantee; it must be ergonomic enough to do honestly at scale and support out-of-band review (read proposals somewhere comfortable like NotebookLM or a shared doc, then apply decisions), or it gets skipped or rubber-stamped.",
77
+ "origin": "user",
78
+ "status": "confirmed",
79
+ "honesty_conditions": [
80
+ {
81
+ "id": "h10",
82
+ "text": "The anti-fabrication guarantee is preserved exactly: ergonomics improve but no proposal becomes authoritative without an explicit user action, asserted by test.",
83
+ "status": "confirmed"
84
+ }
85
+ ],
86
+ "hard_questions": [],
87
+ "links": []
88
+ },
89
+ {
90
+ "id": "c6",
91
+ "kind": "success_signal",
92
+ "text": "A frame with many proposed items is reviewed and resolved in a single 'devague review' plus one batched confirm/reject, and the test suite proves: review export works before convergence, multi-id confirm/reject works, and no review-flow command auto-confirms a proposal.",
93
+ "origin": "user",
94
+ "status": "confirmed",
95
+ "honesty_conditions": [
96
+ {
97
+ "id": "h11",
98
+ "text": "The success signals are verified by the committed test suite, not asserted by hand.",
99
+ "status": "confirmed"
100
+ }
101
+ ],
102
+ "hard_questions": [],
103
+ "links": []
104
+ },
105
+ {
106
+ "id": "c7",
107
+ "kind": "boundary",
108
+ "text": "Scope is the review/confirm UX layered over the existing proposed-vs-confirmed contract (#5/#16); it does not change the state model itself \u2014 proposals still only become authoritative by explicit user action.",
109
+ "origin": "user",
110
+ "status": "confirmed",
111
+ "honesty_conditions": [
112
+ {
113
+ "id": "h12",
114
+ "text": "0.6.0 adds only review/confirm UX; the proposed-vs-confirmed state model and convergence gate from #5/#16 are unchanged \u2014 no new claim or condition states are introduced.",
115
+ "status": "confirmed"
116
+ }
117
+ ],
118
+ "hard_questions": [],
119
+ "links": []
120
+ },
121
+ {
122
+ "id": "c8",
123
+ "kind": "non_goal",
124
+ "text": "Does not generate a polished buildable spec from unconfirmed review output \u2014 review output stays explicitly non-authoritative, distinct from what 'export' produces post-convergence.",
125
+ "origin": "user",
126
+ "status": "confirmed",
127
+ "honesty_conditions": [],
128
+ "hard_questions": [],
129
+ "links": []
130
+ },
131
+ {
132
+ "id": "c9",
133
+ "kind": "non_goal",
134
+ "text": "Does not auto-resolve questions and does not auto-confirm any LLM-proposed content anywhere in the review flow.",
135
+ "origin": "user",
136
+ "status": "confirmed",
137
+ "honesty_conditions": [],
138
+ "hard_questions": [],
139
+ "links": []
140
+ },
141
+ {
142
+ "id": "c10",
143
+ "kind": "non_goal",
144
+ "text": "The CLI does not call an LLM.",
145
+ "origin": "user",
146
+ "status": "confirmed",
147
+ "honesty_conditions": [],
148
+ "hard_questions": [],
149
+ "links": []
150
+ },
151
+ {
152
+ "id": "c11",
153
+ "kind": "non_goal",
154
+ "text": "Does not require GitHub, NotebookLM, or any external service.",
155
+ "origin": "user",
156
+ "status": "confirmed",
157
+ "honesty_conditions": [],
158
+ "hard_questions": [],
159
+ "links": []
160
+ },
161
+ {
162
+ "id": "c12",
163
+ "kind": "requirement",
164
+ "text": "'devague review' (and 'devague review --json') emits all proposed claims and proposed honesty conditions, with their ids, without requiring or triggering convergence.",
165
+ "origin": "user",
166
+ "status": "confirmed",
167
+ "honesty_conditions": [
168
+ {
169
+ "id": "h1",
170
+ "text": "Running 'devague review' on a frame that has NOT converged still exits 0 and lists every proposed claim and proposed honesty condition with ids; it never invokes the convergence gate nor mutates any claim/condition state.",
171
+ "status": "confirmed"
172
+ }
173
+ ],
174
+ "hard_questions": [],
175
+ "links": []
176
+ },
177
+ {
178
+ "id": "c13",
179
+ "kind": "requirement",
180
+ "text": "Review output is clearly labelled unconfirmed and non-authoritative, visually distinct from the buildable spec that 'export' produces only after convergence.",
181
+ "origin": "user",
182
+ "status": "confirmed",
183
+ "honesty_conditions": [
184
+ {
185
+ "id": "h2",
186
+ "text": "The review artifact carries an explicit 'nothing confirmed yet \u2014 non-authoritative' banner and is written to a path under .devague/reviews/<slug>.md, distinct from docs/specs/, so it cannot be mistaken for the buildable spec.",
187
+ "status": "confirmed"
188
+ }
189
+ ],
190
+ "hard_questions": [],
191
+ "links": []
192
+ },
193
+ {
194
+ "id": "c14",
195
+ "kind": "requirement",
196
+ "text": "'devague confirm' accepts multiple claim/honesty ids in one invocation, and 'devague reject' likewise.",
197
+ "origin": "user",
198
+ "status": "confirmed",
199
+ "honesty_conditions": [
200
+ {
201
+ "id": "h3",
202
+ "text": "'devague confirm a b c' resolves every listed id in a single call (and 'reject a b c' likewise), and the handling of a batch containing an invalid/unknown id follows one defined, tested rule.",
203
+ "status": "confirmed"
204
+ }
205
+ ],
206
+ "hard_questions": [],
207
+ "links": []
208
+ },
209
+ {
210
+ "id": "c15",
211
+ "kind": "requirement",
212
+ "text": "Open questions / pending user decisions can be written as durable .devague working state (e.g. .devague/questions/<slug>.md), treated as uncommitted working state by default unless the user intentionally promotes one into docs.",
213
+ "origin": "user",
214
+ "status": "confirmed",
215
+ "honesty_conditions": [
216
+ {
217
+ "id": "h4",
218
+ "text": "A pending question the CLI writes persists across runs under .devague/questions/<slug>.md, is treated as uncommitted working state by default, and a documented path exists to apply a confirmed decision back into the frame.",
219
+ "status": "confirmed"
220
+ }
221
+ ],
222
+ "hard_questions": [],
223
+ "links": []
224
+ },
225
+ {
226
+ "id": "c16",
227
+ "kind": "requirement",
228
+ "text": "No command in the review flow auto-confirms LLM-proposed content; every confirm/reject stays an explicit user action.",
229
+ "origin": "user",
230
+ "status": "confirmed",
231
+ "honesty_conditions": [
232
+ {
233
+ "id": "h5",
234
+ "text": "An automated test asserts that no review-flow command (review, multi-id confirm/reject, any --json path) transitions an llm-origin proposed item to confirmed without an explicit user confirm naming that id.",
235
+ "status": "confirmed"
236
+ }
237
+ ],
238
+ "hard_questions": [],
239
+ "links": []
240
+ },
241
+ {
242
+ "id": "c17",
243
+ "kind": "decision",
244
+ "text": "Batch confirm/reject is TRANSACTIONAL: validate all ids first; if any id is unknown/invalid, resolve none and exit non-zero with a hint \u2014 never a half-applied batch.",
245
+ "origin": "user",
246
+ "status": "confirmed",
247
+ "honesty_conditions": [],
248
+ "hard_questions": [],
249
+ "links": []
250
+ },
251
+ {
252
+ "id": "c18",
253
+ "kind": "decision",
254
+ "text": "'devague confirm --from-review <file>' IS in scope for 0.6.0: it parses a reviewed decision set (confirm/reject per id) from the review artifact and applies it, so the review artifact format must be documented and round-trippable.",
255
+ "origin": "user",
256
+ "status": "confirmed",
257
+ "honesty_conditions": [],
258
+ "hard_questions": [],
259
+ "links": []
260
+ },
261
+ {
262
+ "id": "c19",
263
+ "kind": "decision",
264
+ "text": "devague manages .gitignore: it ensures .devague/reviews/ and .devague/questions/ are git-ignored so review/question state is uncommitted working state by default; the user opts in to promote one into docs.",
265
+ "origin": "user",
266
+ "status": "confirmed",
267
+ "honesty_conditions": [],
268
+ "hard_questions": [],
269
+ "links": []
270
+ },
271
+ {
272
+ "id": "c20",
273
+ "kind": "decision",
274
+ "text": "Pending questions/decisions are produced by a CLI move that writes .devague/questions/<slug>.md and owns the format (first-class + unit-testable), not a hand-written skill artifact.",
275
+ "origin": "user",
276
+ "status": "confirmed",
277
+ "honesty_conditions": [],
278
+ "hard_questions": [],
279
+ "links": []
280
+ },
281
+ {
282
+ "id": "c21",
283
+ "kind": "requirement",
284
+ "text": "'devague confirm --from-review <file>' applies a reviewed decision set parsed from the review artifact; the artifact 'devague review' emits is documented and round-trippable (review -> edit decisions -> apply).",
285
+ "origin": "user",
286
+ "status": "confirmed",
287
+ "honesty_conditions": [
288
+ {
289
+ "id": "h13",
290
+ "text": "A review artifact emitted by 'devague review' can be edited with confirm/reject decisions and fed to 'devague confirm --from-review <file>' to apply exactly those decisions \u2014 proven by a round-trip test \u2014 and applying it still auto-confirms nothing the file did not mark confirmed.",
291
+ "status": "confirmed"
292
+ }
293
+ ],
294
+ "hard_questions": [],
295
+ "links": []
296
+ }
297
+ ],
298
+ "open_vagueness": []
299
+ }