devague 0.7.0__tar.gz → 0.8.0__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 (122) hide show
  1. {devague-0.7.0 → devague-0.8.0}/CHANGELOG.md +7 -0
  2. {devague-0.7.0 → devague-0.8.0}/CLAUDE.md +3 -1
  3. {devague-0.7.0 → devague-0.8.0}/PKG-INFO +1 -1
  4. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/learn.py +47 -0
  5. devague-0.8.0/docs/llm-guidance.md +130 -0
  6. {devague-0.7.0 → devague-0.8.0}/docs/spec-contract.md +4 -0
  7. {devague-0.7.0 → devague-0.8.0}/pyproject.toml +1 -1
  8. devague-0.8.0/tests/test_cli_affordances.py +111 -0
  9. {devague-0.7.0 → devague-0.8.0}/uv.lock +1 -1
  10. devague-0.7.0/tests/test_cli_affordances.py +0 -64
  11. {devague-0.7.0 → devague-0.8.0}/.claude/skills/cicd/SKILL.md +0 -0
  12. {devague-0.7.0 → devague-0.8.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  13. {devague-0.7.0 → devague-0.8.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  14. {devague-0.7.0 → devague-0.8.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  15. {devague-0.7.0 → devague-0.8.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  16. {devague-0.7.0 → devague-0.8.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  17. {devague-0.7.0 → devague-0.8.0}/.claude/skills/communicate/SKILL.md +0 -0
  18. {devague-0.7.0 → devague-0.8.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  19. {devague-0.7.0 → devague-0.8.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  20. {devague-0.7.0 → devague-0.8.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  21. {devague-0.7.0 → devague-0.8.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  22. {devague-0.7.0 → devague-0.8.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  23. {devague-0.7.0 → devague-0.8.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  24. {devague-0.7.0 → devague-0.8.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  25. {devague-0.7.0 → devague-0.8.0}/.claude/skills/run-tests/SKILL.md +0 -0
  26. {devague-0.7.0 → devague-0.8.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  27. {devague-0.7.0 → devague-0.8.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
  28. {devague-0.7.0 → devague-0.8.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  29. {devague-0.7.0 → devague-0.8.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  30. {devague-0.7.0 → devague-0.8.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  31. {devague-0.7.0 → devague-0.8.0}/.claude/skills/think/SKILL.md +0 -0
  32. {devague-0.7.0 → devague-0.8.0}/.claude/skills/think/scripts/think.sh +0 -0
  33. {devague-0.7.0 → devague-0.8.0}/.claude/skills/version-bump/SKILL.md +0 -0
  34. {devague-0.7.0 → devague-0.8.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
  35. {devague-0.7.0 → devague-0.8.0}/.claude/skills.local.yaml.example +0 -0
  36. {devague-0.7.0 → devague-0.8.0}/.devague/current_plan +0 -0
  37. {devague-0.7.0 → devague-0.8.0}/.devague/frames/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
  38. {devague-0.7.0 → devague-0.8.0}/.devague/frames/devague-now-ships-a-documented-spec-contract-every.json +0 -0
  39. {devague-0.7.0 → devague-0.8.0}/.devague/plans/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
  40. {devague-0.7.0 → devague-0.8.0}/.devague/plans/devague-now-ships-a-documented-spec-contract-every.json +0 -0
  41. {devague-0.7.0 → devague-0.8.0}/.flake8 +0 -0
  42. {devague-0.7.0 → devague-0.8.0}/.github/workflows/publish.yml +0 -0
  43. {devague-0.7.0 → devague-0.8.0}/.github/workflows/security-checks.yml +0 -0
  44. {devague-0.7.0 → devague-0.8.0}/.github/workflows/tests.yml +0 -0
  45. {devague-0.7.0 → devague-0.8.0}/.gitignore +0 -0
  46. {devague-0.7.0 → devague-0.8.0}/.markdownlint-cli2.yaml +0 -0
  47. {devague-0.7.0 → devague-0.8.0}/.pre-commit-config.yaml +0 -0
  48. {devague-0.7.0 → devague-0.8.0}/LICENSE +0 -0
  49. {devague-0.7.0 → devague-0.8.0}/README.md +0 -0
  50. {devague-0.7.0 → devague-0.8.0}/culture.yaml +0 -0
  51. {devague-0.7.0 → devague-0.8.0}/devague/__init__.py +0 -0
  52. {devague-0.7.0 → devague-0.8.0}/devague/__main__.py +0 -0
  53. {devague-0.7.0 → devague-0.8.0}/devague/cli/__init__.py +0 -0
  54. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/__init__.py +0 -0
  55. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/capture.py +0 -0
  56. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/confirm.py +0 -0
  57. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/converge.py +0 -0
  58. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/explain.py +0 -0
  59. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/export.py +0 -0
  60. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/interrogate.py +0 -0
  61. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/list_frames.py +0 -0
  62. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/new.py +0 -0
  63. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/park.py +0 -0
  64. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/plan.py +0 -0
  65. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/question.py +0 -0
  66. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/reject.py +0 -0
  67. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/review.py +0 -0
  68. {devague-0.7.0 → devague-0.8.0}/devague/cli/_commands/show.py +0 -0
  69. {devague-0.7.0 → devague-0.8.0}/devague/cli/_errors.py +0 -0
  70. {devague-0.7.0 → devague-0.8.0}/devague/cli/_frames.py +0 -0
  71. {devague-0.7.0 → devague-0.8.0}/devague/cli/_output.py +0 -0
  72. {devague-0.7.0 → devague-0.8.0}/devague/cli/_plans.py +0 -0
  73. {devague-0.7.0 → devague-0.8.0}/devague/convergence.py +0 -0
  74. {devague-0.7.0 → devague-0.8.0}/devague/frame.py +0 -0
  75. {devague-0.7.0 → devague-0.8.0}/devague/plan.py +0 -0
  76. {devague-0.7.0 → devague-0.8.0}/devague/plan_convergence.py +0 -0
  77. {devague-0.7.0 → devague-0.8.0}/devague/plan_store.py +0 -0
  78. {devague-0.7.0 → devague-0.8.0}/devague/questions_io.py +0 -0
  79. {devague-0.7.0 → devague-0.8.0}/devague/render/__init__.py +0 -0
  80. {devague-0.7.0 → devague-0.8.0}/devague/render/frame_md.py +0 -0
  81. {devague-0.7.0 → devague-0.8.0}/devague/render/plan_md.py +0 -0
  82. {devague-0.7.0 → devague-0.8.0}/devague/render/review_md.py +0 -0
  83. {devague-0.7.0 → devague-0.8.0}/devague/render/spec_md.py +0 -0
  84. {devague-0.7.0 → devague-0.8.0}/devague/store.py +0 -0
  85. {devague-0.7.0 → devague-0.8.0}/docs/examples/contract-example.json +0 -0
  86. {devague-0.7.0 → devague-0.8.0}/docs/plans/devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
  87. {devague-0.7.0 → devague-0.8.0}/docs/plans/devague-now-ships-a-documented-spec-contract-every.md +0 -0
  88. {devague-0.7.0 → devague-0.8.0}/docs/reviews/spec-contract-frame-review.md +0 -0
  89. {devague-0.7.0 → devague-0.8.0}/docs/skill-sources.md +0 -0
  90. {devague-0.7.0 → devague-0.8.0}/docs/specs/devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
  91. {devague-0.7.0 → devague-0.8.0}/docs/specs/devague-now-ships-a-documented-spec-contract-every.md +0 -0
  92. {devague-0.7.0 → devague-0.8.0}/docs/superpowers/plans/2026-05-22-specifix-onboarding.md +0 -0
  93. {devague-0.7.0 → devague-0.8.0}/docs/superpowers/plans/2026-05-23-devague-rename.md +0 -0
  94. {devague-0.7.0 → devague-0.8.0}/docs/superpowers/plans/2026-05-23-devague-working-backwards-engine.md +0 -0
  95. {devague-0.7.0 → devague-0.8.0}/docs/superpowers/specs/2026-05-22-specifix-onboarding-design.md +0 -0
  96. {devague-0.7.0 → devague-0.8.0}/docs/superpowers/specs/2026-05-23-devague-spec-to-plan-design.md +0 -0
  97. {devague-0.7.0 → devague-0.8.0}/docs/superpowers/specs/2026-05-23-devague-working-backwards-design.md +0 -0
  98. {devague-0.7.0 → devague-0.8.0}/sonar-project.properties +0 -0
  99. {devague-0.7.0 → devague-0.8.0}/tests/__init__.py +0 -0
  100. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_chassis.py +0 -0
  101. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_converge_export.py +0 -0
  102. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_errors.py +0 -0
  103. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_moves.py +0 -0
  104. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_output.py +0 -0
  105. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_plan.py +0 -0
  106. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_question.py +0 -0
  107. {devague-0.7.0 → devague-0.8.0}/tests/test_cli_review.py +0 -0
  108. {devague-0.7.0 → devague-0.8.0}/tests/test_contract.py +0 -0
  109. {devague-0.7.0 → devague-0.8.0}/tests/test_convergence.py +0 -0
  110. {devague-0.7.0 → devague-0.8.0}/tests/test_frame.py +0 -0
  111. {devague-0.7.0 → devague-0.8.0}/tests/test_offline.py +0 -0
  112. {devague-0.7.0 → devague-0.8.0}/tests/test_package.py +0 -0
  113. {devague-0.7.0 → devague-0.8.0}/tests/test_plan.py +0 -0
  114. {devague-0.7.0 → devague-0.8.0}/tests/test_plan_convergence.py +0 -0
  115. {devague-0.7.0 → devague-0.8.0}/tests/test_plan_store.py +0 -0
  116. {devague-0.7.0 → devague-0.8.0}/tests/test_render.py +0 -0
  117. {devague-0.7.0 → devague-0.8.0}/tests/test_render_plan.py +0 -0
  118. {devague-0.7.0 → devague-0.8.0}/tests/test_review_loop_integration.py +0 -0
  119. {devague-0.7.0 → devague-0.8.0}/tests/test_review_loop_invariants.py +0 -0
  120. {devague-0.7.0 → devague-0.8.0}/tests/test_spec_to_plan_skill.py +0 -0
  121. {devague-0.7.0 → devague-0.8.0}/tests/test_store.py +0 -0
  122. {devague-0.7.0 → devague-0.8.0}/tests/test_think_skill.py +0 -0
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/). This project
6
6
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.8.0] - 2026-05-23
9
+
10
+ ### Added
11
+
12
+ - **Portable LLM guidance contract (#19).** New `docs/llm-guidance.md` — a runtime-agnostic operating contract for any assisting model driving Devague (not just Claude Code): the move-driven mental model, the (state × origin) vocabulary, the anti-fabrication hard rules, adaptive-not-scripted ordering, good/bad operator examples, and the forward (plan) leg. Distilled from the `/think` and `/spec-to-plan` skill contracts; it complements, and does not replace, an agent runtime's own main instruction file (`AGENTS.md`, `CLAUDE.md`, a system prompt).
13
+ - `devague learn` (text and `--json`) now always surfaces the operating rules: a `devague is NOT` framing (not a wizard / questionnaire / PRD generator), the anti-fabrication rules, and a pointer to `docs/llm-guidance.md`. JSON gains `not_a`, `operating_rules`, `guidance_doc` (a portable canonical URL — `docs/` is not shipped in the wheel), and `guidance_doc_repo_path` keys.
14
+
8
15
  ## [0.7.0] - 2026-05-23
9
16
 
10
17
  ### Added
@@ -50,7 +50,9 @@ itself. The workflow:
50
50
  shipped successfully — what would you announce to users, teammates, or
51
51
  yourself?"). Creates a Frame seeded with the announcement claim
52
52
  (auto-confirmed, since it comes from the user). `devague learn` documents the
53
- full ten-stage guided sequence.
53
+ full ten-stage guided sequence plus the always-on **operating rules** (the
54
+ anti-fabrication contract); the portable, agent-agnostic version of that
55
+ contract lives in `docs/llm-guidance.md` (#19).
54
56
  2. `devague capture --kind <kind> "<text>"` — add claims; LLM-proposed ones
55
57
  (`--origin llm`) land as `proposed` and require explicit user `confirm`.
56
58
  3. `devague interrogate <claim-id>` — attach honesty conditions and hard
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devague
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: devague — turns a vague feature idea into a buildable spec, then a buildable plan.
5
5
  Project-URL: Homepage, https://github.com/agentculture/devague
6
6
  Project-URL: Issues, https://github.com/agentculture/devague/issues
@@ -26,6 +26,40 @@ SUPPORTING_PROMPT = (
26
26
  "teammates, or yourself?"
27
27
  )
28
28
 
29
+ # The portable, runtime-agnostic operating contract for any assisting model
30
+ # (devague#19). The full version lives in the guidance doc; this is the core
31
+ # surfaced in every `learn`. These rules are what make convergence mean something.
32
+ #
33
+ # `docs/` is not shipped in the wheel (only the `devague` package is), and an
34
+ # installed devague is operated from an arbitrary repo — so a bare relative path
35
+ # wouldn't resolve for most consumers. The portable, always-resolvable reference
36
+ # is the canonical URL; the in-repo path is kept for contributors.
37
+ GUIDANCE_DOC_URL = "https://github.com/agentculture/devague/blob/main/docs/llm-guidance.md"
38
+ GUIDANCE_DOC_REPO_PATH = "docs/llm-guidance.md"
39
+
40
+ # What devague is NOT — the framing that keeps it from degrading into a form.
41
+ NOT_A = (
42
+ "a wizard (no fixed prompt sequence)",
43
+ "a scripted questionnaire (you don't read questions off a form)",
44
+ "a PRD generator (it never invents content to fill a template)",
45
+ )
46
+
47
+ # The anti-fabrication rules. Agent-agnostic: repo-specific agreements live in
48
+ # your agent's main instruction file (AGENTS.md, CLAUDE.md, a system prompt, …),
49
+ # not here.
50
+ OPERATING_RULES = (
51
+ "LLM proposals stay proposed — capture your own ideas with --origin llm; "
52
+ "never confirm your own proposal. Confirmation is a user-only decision.",
53
+ "Honesty conditions route through the user — propose freely with "
54
+ "'interrogate --honesty'; the user owns whether each one holds.",
55
+ "Park real unknowns instead of papering over them — 'park' a genuine "
56
+ "unknown rather than writing confident prose that hides the gap.",
57
+ "Converge, don't vibe — 'export' is gated on 'converge'; resolve every "
58
+ "listed gap instead of declaring readiness on a hunch.",
59
+ "Order is adaptive — the ten stages are an artifact shape, not a mandatory "
60
+ "conversation order; capture what the user gives you and circle back.",
61
+ )
62
+
29
63
  # The canonical guided sequence (devague#4). The engine is move-driven, not a
30
64
  # rigid wizard — this is the recommended arc, with the move that advances each.
31
65
  STAGES = [
@@ -57,6 +91,15 @@ _TEXT = (
57
91
  )
58
92
  + "\n\nMoves:\n"
59
93
  + "\n".join(f" {name:<11} {desc}" for name, desc in MOVES.items())
94
+ + "\n\ndevague is NOT:\n"
95
+ + "\n".join(f" - {n}" for n in NOT_A)
96
+ + "\n\nOperating rules (the anti-fabrication contract — do not violate):\n"
97
+ + "\n".join(f" - {r}" for r in OPERATING_RULES)
98
+ + "\n\nFull portable guidance for any assisting model:\n"
99
+ f" {GUIDANCE_DOC_URL}\n"
100
+ f" (in the devague repo: {GUIDANCE_DOC_REPO_PATH})\n"
101
+ "Agent-agnostic; your repo-specific agreements live in your agent's main\n"
102
+ "instruction file — AGENTS.md, CLAUDE.md, a system prompt — not there."
60
103
  )
61
104
 
62
105
 
@@ -73,6 +116,10 @@ def cmd_learn(args: argparse.Namespace) -> int:
73
116
  for i, (name, prompt, move) in enumerate(STAGES, 1)
74
117
  ],
75
118
  "moves": list(MOVES),
119
+ "not_a": list(NOT_A),
120
+ "operating_rules": list(OPERATING_RULES),
121
+ "guidance_doc": GUIDANCE_DOC_URL,
122
+ "guidance_doc_repo_path": GUIDANCE_DOC_REPO_PATH,
76
123
  "summary": _TEXT,
77
124
  },
78
125
  json_mode=True,
@@ -0,0 +1,130 @@
1
+ # Operating Devague — portable guidance for assisting models
2
+
3
+ This is the **portable, runtime-agnostic contract** for any LLM or agent that
4
+ operates Devague. It does not assume a particular agent runtime (Claude Code,
5
+ a Codex/`AGENTS.md` agent, Copilot, an ACP host, a bare system prompt, …). It
6
+ complements — it does **not** replace — your agent's own main instruction file
7
+ (`AGENTS.md`, `CLAUDE.md`, a system prompt, or equivalent), which carries the
8
+ repo-specific working agreements. Where the two overlap, this document is the
9
+ authority on *how Devague itself must be driven*.
10
+
11
+ The authoritative entity model and the per-move input/output/transition
12
+ contract live in [`spec-contract.md`](spec-contract.md). For the live shape of
13
+ any move, run it with `--json`, or run `devague learn` / `devague explain
14
+ <move>`.
15
+
16
+ ## 1. What Devague is — and is not
17
+
18
+ Devague is a **deterministic, move-driven state machine** over claims, honesty
19
+ conditions, open vagueness, and a convergence gate. There are **no LLM calls
20
+ inside the CLI** — it only records moves and reports what is still missing. The
21
+ intelligence is *you*, the operating model.
22
+
23
+ It is **not**:
24
+
25
+ - **not a wizard** — there is no fixed sequence of prompts to march through;
26
+ - **not a scripted questionnaire** — you do not read questions off a form;
27
+ - **not a PRD generator** — it will not invent content to fill a template.
28
+
29
+ You choose each move from the live state; the CLI tracks state and tells you
30
+ what remains before a spec (or plan) can be exported.
31
+
32
+ ## 2. The state you operate on
33
+
34
+ Two legs share one chassis:
35
+
36
+ - **Frame (idea → spec).** Claims (each with a *kind* — `announcement`,
37
+ `audience`, `after_state`, `before_state`, `why_it_matters`, `boundary`,
38
+ `success_signal`, `open_question`, `non_goal`, `requirement`, `assumption`,
39
+ `decision`); honesty conditions and hard questions attached to claims; and
40
+ **open vagueness** (parked unknowns, kinds `unknown_nonblocking` /
41
+ `unknown_blocking` / `out_of_scope` / `follow_up`).
42
+ - **Plan (spec → plan).** Coverage targets (derived from a converged frame),
43
+ tasks (with acceptance criteria, dependencies, and the targets they cover),
44
+ and first-class plan risks.
45
+
46
+ Every element carries two orthogonal axes:
47
+
48
+ - **origin** — `user` or `llm` (who proposed it);
49
+ - **status** — `proposed`, `confirmed`, or `rejected`.
50
+
51
+ `origin` and `status` are independent. An `llm`-proposed claim is *proposed*
52
+ until a human acts on it; a `user`-provided claim is *confirmed* on arrival.
53
+ Keeping these distinct is the whole point of the tool — see §4.
54
+
55
+ ## 3. You choose the move; order is adaptive
56
+
57
+ The moves are `new`, `capture`, `interrogate`, `confirm`, `reject`, `review`,
58
+ `question`, `park`, `converge`, `export`, `show`, `list` (plus the `plan …`
59
+ moves for the forward leg). Pick the move that fits the live state — not a
60
+ predetermined script.
61
+
62
+ When unsure what to do next, ask the gate, don't guess: run `converge --json`
63
+ (it returns `{ready_for_spec, blockers, warnings, parked_items,
64
+ required_next_moves}`; plans return `ready_for_plan`) and act on the first
65
+ blocker.
66
+
67
+ The canonical **ten-stage arc** (announcement → audience → after → matter →
68
+ before → honest → FAQ → boundaries → success → spec) that `devague learn`
69
+ prints is an **artifact shape and a recommended arc — not a mandatory
70
+ conversation order**. If the user hands you the audience and the success signal
71
+ before the announcement is crisp, capture those now and circle back. Drive
72
+ toward the shape; do not impose a sequence on the user.
73
+
74
+ ## 4. Hard rules — the anti-fabrication contract
75
+
76
+ These are not style preferences. Convergence is only meaningful if these hold.
77
+
78
+ - **LLM proposals stay proposed.** Capture your own ideas freely with `--origin
79
+ llm` (claims) or by attaching honesty conditions; they land as `proposed`.
80
+ **Never `confirm` your own proposal.** Confirmation is a **user-only**
81
+ decision. Surface the proposal and let the user confirm or reject it — proposed
82
+ content must never silently become an authoritative requirement.
83
+ - **Honesty conditions route through the user.** Propose them generously with
84
+ `interrogate --honesty`; the user owns whether each one actually holds.
85
+ - **Park real unknowns; do not paper over them.** If something is genuinely
86
+ unknown, `park` it (blocking or non-blocking) instead of writing confident
87
+ prose that hides the gap. Blocking vagueness holds back convergence by design.
88
+ - **Converge, don't vibe.** `export` is gated on `converge` passing. Never
89
+ declare a frame or plan "ready" on a hunch — run `converge` and resolve every
90
+ listed gap first.
91
+
92
+ ## 5. Good vs. bad operator behavior
93
+
94
+ | Situation | ❌ Bad (fabricating) | ✅ Good (honest) |
95
+ |-----------|---------------------|------------------|
96
+ | You have a strong guess at the audience | `capture --kind audience … --origin user` (passing your guess off as the user's) | `capture --kind audience … --origin llm`, then ask the user to `confirm` |
97
+ | You proposed an honesty condition | `confirm h3` yourself so the gate passes | leave `h3` proposed; surface it for the user to confirm |
98
+ | A key detail is genuinely unknown | invent a plausible answer to keep momentum | `park "<the unknown>" --kind unknown_blocking` |
99
+ | User asks "is this ready?" | "Yes, looks solid." | run `converge`; report the actual blockers/warnings |
100
+ | The user skipped a stage | march through the stages in order anyway | capture what they gave you; let the arc fill in adaptively |
101
+ | Plan: a task has no clear acceptance test | mark it confirmed and move on | leave it without criteria (the gate blocks it) or `park` the risk |
102
+
103
+ ## 6. The forward leg (spec → plan), in brief
104
+
105
+ The plan engine is the structural peer of the frame engine and obeys the same
106
+ spirit:
107
+
108
+ - **Seed from a converged spec only** — `plan new` refuses an unconverged frame.
109
+ - **LLM-proposed tasks stay proposed**; the user confirms them.
110
+ - **Cover every target, criteria on every task** — the gate requires it.
111
+ - **Keep the dependency graph honest** — real task ids, acyclic.
112
+ - **Park genuine unknowns as risks** (`unknown_blocking` holds convergence back).
113
+ - **Converge against the live frame** — `converge`/`export` re-load the source
114
+ frame; if it regressed below convergence, re-converge the spec first.
115
+
116
+ ## 7. Output contract
117
+
118
+ Results go to **stdout**; diagnostics and errors go to **stderr** — a strict
119
+ split you can parse. Pass `--json` to any move for a structured payload on the
120
+ same stream. Exit code is `0` on success, non-zero on user error (with a
121
+ `hint:` line and no Python traceback). Frames and plans persist under
122
+ `.devague/` in the current directory.
123
+
124
+ ## 8. Where authority lives
125
+
126
+ - **Entity model + per-move contract:** [`spec-contract.md`](spec-contract.md).
127
+ - **Live shape of any move:** run it with `--json`, or `devague learn` /
128
+ `devague explain <move>`.
129
+ - **Repo-specific working agreements:** your agent's main instruction file
130
+ (`AGENTS.md`, `CLAUDE.md`, system prompt, …) — not this document.
@@ -11,6 +11,10 @@ CLI tracks state. Every move accepts and emits JSON (`--json`), with a strict
11
11
  without guessing internal state. All operations run fully offline against local
12
12
  `.devague/` state.
13
13
 
14
+ For the portable, runtime-agnostic contract on *how* an assisting model should
15
+ operate Devague — the move-driven mental model and the anti-fabrication rules —
16
+ see [`llm-guidance.md`](llm-guidance.md) (also surfaced in `devague learn`).
17
+
14
18
  ## Versioning
15
19
 
16
20
  Every frame carries an integer `schema_version` (currently `1`). It is written
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devague"
3
- version = "0.7.0"
3
+ version = "0.8.0"
4
4
  description = "devague — turns a vague feature idea into a buildable spec, then a buildable plan."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1,111 @@
1
+ """Tests for the agent-affordance verbs: learn / explain."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+ from devague.cli import main
11
+
12
+ _GUIDANCE_DOC = Path(__file__).resolve().parents[1] / "docs" / "llm-guidance.md"
13
+
14
+
15
+ def test_learn_describes_moves(capsys: pytest.CaptureFixture[str]) -> None:
16
+ rc = main(["learn"])
17
+ assert rc == 0
18
+ out = capsys.readouterr().out.lower()
19
+ assert "working backwards" in out
20
+ assert "capture" in out and "converge" in out
21
+
22
+
23
+ def test_learn_teaches_first_question_and_guided_stages(
24
+ capsys: pytest.CaptureFixture[str],
25
+ ) -> None:
26
+ rc = main(["learn"])
27
+ assert rc == 0
28
+ out = capsys.readouterr().out.lower()
29
+ # Issue #4's mandated entry point and supporting framing.
30
+ assert "what's the announcement?" in out
31
+ assert "users, teammates, or yourself" in out
32
+ # The canonical 10-step guided sequence is documented.
33
+ for stage in (
34
+ "announcement",
35
+ "audience",
36
+ "after",
37
+ "matter",
38
+ "before",
39
+ "honest",
40
+ "faq",
41
+ "boundaries",
42
+ "success",
43
+ "spec",
44
+ ):
45
+ assert stage in out
46
+
47
+
48
+ def test_learn_json_lists_moves_and_stages(capsys: pytest.CaptureFixture[str]) -> None:
49
+ rc = main(["learn", "--json"])
50
+ assert rc == 0
51
+ payload = json.loads(capsys.readouterr().out)
52
+ assert payload["tool"] == "devague"
53
+ assert "capture" in payload["moves"]
54
+ assert len(payload["stages"]) == 10
55
+ assert payload["first_question"] == "What's the announcement?"
56
+
57
+
58
+ def test_learn_surfaces_operating_rules(capsys: pytest.CaptureFixture[str]) -> None:
59
+ # devague#19: the anti-fabrication contract is always-on in `learn` output.
60
+ rc = main(["learn"])
61
+ assert rc == 0
62
+ out = capsys.readouterr().out.lower()
63
+ assert "operating rules" in out
64
+ assert "anti-fabrication" in out
65
+ # The core rules and the not-a framing.
66
+ assert "stay proposed" in out and "user-only" in out
67
+ assert "not a mandatory conversation order" in out # order is adaptive
68
+ assert "questionnaire" in out and "prd generator" in out
69
+ # Agent-agnostic pointer — not hardcoded to one runtime.
70
+ assert "agents.md" in out and "claude.md" in out
71
+ # A portable, always-resolvable URL (the wheel doesn't ship docs/) plus the
72
+ # in-repo path for contributors.
73
+ assert "https://github.com/agentculture/devague" in out
74
+ assert "docs/llm-guidance.md" in out
75
+
76
+
77
+ def test_learn_json_exposes_operating_contract(capsys: pytest.CaptureFixture[str]) -> None:
78
+ rc = main(["learn", "--json"])
79
+ assert rc == 0
80
+ payload = json.loads(capsys.readouterr().out)
81
+ # guidance_doc is a portable, always-resolvable URL (docs/ isn't shipped in
82
+ # the wheel); the in-repo source path is exposed separately for contributors.
83
+ assert payload["guidance_doc"].startswith("https://")
84
+ assert payload["guidance_doc"].endswith("docs/llm-guidance.md")
85
+ assert payload["guidance_doc_repo_path"] == "docs/llm-guidance.md"
86
+ assert len(payload["operating_rules"]) >= 4
87
+ assert len(payload["not_a"]) == 3
88
+ assert any("proposed" in r.lower() for r in payload["operating_rules"])
89
+
90
+
91
+ def test_guidance_doc_exists_and_documents_the_contract() -> None:
92
+ # The CLI points agents at this doc, so it must exist and carry the contract.
93
+ assert _GUIDANCE_DOC.is_file()
94
+ text = _GUIDANCE_DOC.read_text(encoding="utf-8").lower()
95
+ assert "anti-fabrication" in text
96
+ assert "never `confirm` your own proposal" in text or "never confirm your own proposal" in text
97
+ assert "not a mandatory" in text # adaptive order
98
+ # Agent-agnostic, not Claude-specific.
99
+ assert "agents.md" in text
100
+
101
+
102
+ def test_explain_a_move(capsys: pytest.CaptureFixture[str]) -> None:
103
+ rc = main(["explain", "converge"])
104
+ assert rc == 0
105
+ assert "converge" in capsys.readouterr().out.lower()
106
+
107
+
108
+ def test_explain_unknown_move_errors(capsys: pytest.CaptureFixture[str]) -> None:
109
+ rc = main(["explain", "nope"])
110
+ assert rc == 1
111
+ assert "unknown" in capsys.readouterr().err.lower()
@@ -183,7 +183,7 @@ wheels = [
183
183
 
184
184
  [[package]]
185
185
  name = "devague"
186
- version = "0.7.0"
186
+ version = "0.8.0"
187
187
  source = { editable = "." }
188
188
 
189
189
  [package.dev-dependencies]
@@ -1,64 +0,0 @@
1
- """Tests for the agent-affordance verbs: learn / explain."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
-
7
- import pytest
8
-
9
- from devague.cli import main
10
-
11
-
12
- def test_learn_describes_moves(capsys: pytest.CaptureFixture[str]) -> None:
13
- rc = main(["learn"])
14
- assert rc == 0
15
- out = capsys.readouterr().out.lower()
16
- assert "working backwards" in out
17
- assert "capture" in out and "converge" in out
18
-
19
-
20
- def test_learn_teaches_first_question_and_guided_stages(
21
- capsys: pytest.CaptureFixture[str],
22
- ) -> None:
23
- rc = main(["learn"])
24
- assert rc == 0
25
- out = capsys.readouterr().out.lower()
26
- # Issue #4's mandated entry point and supporting framing.
27
- assert "what's the announcement?" in out
28
- assert "users, teammates, or yourself" in out
29
- # The canonical 10-step guided sequence is documented.
30
- for stage in (
31
- "announcement",
32
- "audience",
33
- "after",
34
- "matter",
35
- "before",
36
- "honest",
37
- "faq",
38
- "boundaries",
39
- "success",
40
- "spec",
41
- ):
42
- assert stage in out
43
-
44
-
45
- def test_learn_json_lists_moves_and_stages(capsys: pytest.CaptureFixture[str]) -> None:
46
- rc = main(["learn", "--json"])
47
- assert rc == 0
48
- payload = json.loads(capsys.readouterr().out)
49
- assert payload["tool"] == "devague"
50
- assert "capture" in payload["moves"]
51
- assert len(payload["stages"]) == 10
52
- assert payload["first_question"] == "What's the announcement?"
53
-
54
-
55
- def test_explain_a_move(capsys: pytest.CaptureFixture[str]) -> None:
56
- rc = main(["explain", "converge"])
57
- assert rc == 0
58
- assert "converge" in capsys.readouterr().out.lower()
59
-
60
-
61
- def test_explain_unknown_move_errors(capsys: pytest.CaptureFixture[str]) -> None:
62
- rc = main(["explain", "nope"])
63
- assert rc == 1
64
- assert "unknown" in capsys.readouterr().err.lower()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes