prove-cli 0.3.0__tar.gz → 0.4.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 (79) hide show
  1. prove_cli-0.4.1/.claude/skills/challenge/SKILL.md +290 -0
  2. prove_cli-0.4.1/.claude/skills/deviate/SKILL.md +176 -0
  3. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/recall/scripts/recall.sh +35 -13
  4. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/remember/scripts/remember.sh +39 -13
  5. prove_cli-0.4.1/.claude/skills/scope/SKILL.md +167 -0
  6. prove_cli-0.4.1/.claude/skills/summarize-delivery/SKILL.md +440 -0
  7. {prove_cli-0.3.0 → prove_cli-0.4.1}/CHANGELOG.md +51 -0
  8. prove_cli-0.4.1/CLAUDE.md +208 -0
  9. {prove_cli-0.3.0 → prove_cli-0.4.1}/PKG-INFO +7 -4
  10. {prove_cli-0.3.0 → prove_cli-0.4.1}/README.md +6 -3
  11. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/explain/catalog.py +5 -0
  12. {prove_cli-0.3.0 → prove_cli-0.4.1}/pyproject.toml +1 -1
  13. {prove_cli-0.3.0 → prove_cli-0.4.1}/uv.lock +1 -1
  14. prove_cli-0.3.0/CLAUDE.md +0 -28
  15. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/agent-config/SKILL.md +0 -0
  16. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
  17. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/agent-config/scripts/show.sh +0 -0
  18. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/ask-colleague/SKILL.md +0 -0
  19. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  20. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  21. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  22. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  23. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  24. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  25. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/cicd/SKILL.md +0 -0
  26. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  27. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  28. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  29. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  30. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  31. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/communicate/SKILL.md +0 -0
  32. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  33. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  34. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  35. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  36. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
  37. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  38. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  39. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  40. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
  41. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
  42. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/recall/SKILL.md +0 -0
  43. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/remember/SKILL.md +0 -0
  44. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/run-tests/SKILL.md +0 -0
  45. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  46. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/sonarclaude/SKILL.md +0 -0
  47. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  48. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  49. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  50. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/think/SKILL.md +0 -0
  51. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/think/scripts/think.sh +0 -0
  52. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/version-bump/SKILL.md +0 -0
  53. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills/version-bump/scripts/bump.py +0 -0
  54. {prove_cli-0.3.0 → prove_cli-0.4.1}/.claude/skills.local.yaml.example +0 -0
  55. {prove_cli-0.3.0 → prove_cli-0.4.1}/.flake8 +0 -0
  56. {prove_cli-0.3.0 → prove_cli-0.4.1}/.github/workflows/publish.yml +0 -0
  57. {prove_cli-0.3.0 → prove_cli-0.4.1}/.github/workflows/tests.yml +0 -0
  58. {prove_cli-0.3.0 → prove_cli-0.4.1}/.gitignore +0 -0
  59. {prove_cli-0.3.0 → prove_cli-0.4.1}/.markdownlint-cli2.yaml +0 -0
  60. {prove_cli-0.3.0 → prove_cli-0.4.1}/LICENSE +0 -0
  61. {prove_cli-0.3.0 → prove_cli-0.4.1}/culture.yaml +0 -0
  62. {prove_cli-0.3.0 → prove_cli-0.4.1}/docs/skill-sources.md +0 -0
  63. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/__init__.py +0 -0
  64. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/__main__.py +0 -0
  65. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/__init__.py +0 -0
  66. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_commands/__init__.py +0 -0
  67. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_commands/cli.py +0 -0
  68. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_commands/doctor.py +0 -0
  69. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_commands/explain.py +0 -0
  70. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_commands/learn.py +0 -0
  71. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_commands/overview.py +0 -0
  72. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_commands/whoami.py +0 -0
  73. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_errors.py +0 -0
  74. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/cli/_output.py +0 -0
  75. {prove_cli-0.3.0 → prove_cli-0.4.1}/prove/explain/__init__.py +0 -0
  76. {prove_cli-0.3.0 → prove_cli-0.4.1}/sonar-project.properties +0 -0
  77. {prove_cli-0.3.0 → prove_cli-0.4.1}/tests/__init__.py +0 -0
  78. {prove_cli-0.3.0 → prove_cli-0.4.1}/tests/test_cli.py +0 -0
  79. {prove_cli-0.3.0 → prove_cli-0.4.1}/tests/test_cli_introspection.py +0 -0
@@ -0,0 +1,290 @@
1
+ ---
2
+ name: challenge
3
+ description: >
4
+ Run a risk-scaled blind-spot discovery pass over a converged, exported
5
+ frame BETWEEN /think and /spec-to-plan (the seventh origin skill, third
6
+ leg in flow order): pressure-test the spec through structured lenses,
7
+ route every finding back through the existing deterministic moves as
8
+ proposed-only content the human adjudicates, and on a clean pass record
9
+ the examined lenses/surfaces and residual uncertainty — never a claim
10
+ that there are no unknown unknowns. Use when the user says "challenge
11
+ this spec", "blind-spot pass", "pressure-test the frame", "what are we
12
+ missing", "unknown unknowns", or after /think exports and before
13
+ `devague plan new`. Authored and maintained in agentculture/devague
14
+ (origin = devague); guildmaster pulls this skill from here and broadcasts
15
+ it to the AgentCulture mesh — it is NOT vendored from guildmaster like
16
+ the inbound skills here.
17
+ type: command
18
+ ---
19
+
20
+ # challenge — hunt the blind spots before the plan inherits them
21
+
22
+ The skill is named **`challenge`**; it is the **blind-spot discovery leg** of
23
+ the devague method — the *seventh* origin skill, sitting *third* in flow
24
+ order, between the spec leg and the plan leg:
25
+
26
+ ```text
27
+ scope -> think -> challenge -> spec-to-plan -> assign-to-workforce -> deviate -> summarize-delivery
28
+ ```
29
+
30
+ Before this leg existed, a frame could converge on precisely stated claims
31
+ while the original framing was still incomplete: open questions only captured
32
+ uncertainty someone had already noticed — **nothing actively hunted omitted
33
+ dimensions, hidden dependencies, or assumptions shared by everyone in the
34
+ frame, and no record existed of which surfaces were ever examined**
35
+ (issue 73's problem statement). Strictly speaking, an unknown unknown cannot
36
+ be listed directly — once articulated, it becomes a known unknown. The useful
37
+ capability is therefore to **raise the odds of discovering blind spots and
38
+ lower the cost of the surprises that remain**, not to promise their
39
+ elimination.
40
+
41
+ That is the surprise-cost rationale: an articulated blind spot becomes a
42
+ known unknown the method can manage; an unexamined one surfaces later as a
43
+ mid-run `/deviate` or a production surprise. Discovery *before* planning is
44
+ cheaper than either — a proposed claim the human rejects costs minutes; the
45
+ same gap found mid-fan-out stops a wave, and found in production it costs
46
+ whatever the blast radius costs.
47
+
48
+ This doc is written for two readers. The **operator** — the main agent — runs
49
+ the pass: sweeps the lenses, drives the deterministic CLI move by move, and
50
+ proposes findings. The **gate-owning human** adjudicates: every finding lands
51
+ `proposed`, and confirming, rejecting, or resolving it is the human exercising
52
+ the existing **spec gate** (gate 1) — challenge adds no fourth gate, mirroring
53
+ how `/deviate` amends gate 2 rather than adding one.
54
+
55
+ ## When it runs
56
+
57
+ The timing is a recorded decision — quote it, don't re-derive it:
58
+
59
+ > the challenge pass runs after /think exports: challenge the converged,
60
+ > exported frame before `devague plan new`; findings reopen the frame,
61
+ > reconverge, and re-export the same dated spec file — /think stays
62
+ > self-contained (resolves q1)
63
+
64
+ — decision c17 in `docs/specs/2026-07-15-challenge-skill.md`.
65
+
66
+ Concretely: `/think` finishes its own arc (converge, export) untouched. Then
67
+ `/challenge` pressure-tests the exported spec **before** `devague plan new`
68
+ seeds a plan from it. Findings land as proposed claims, honesty conditions,
69
+ questions, or parks — which reopens the frame — the human adjudicates, the
70
+ frame **reconverges**, and `devague export` **re-exports the same dated
71
+ file** (`docs/specs/<created-date>-<slug>.md`; exports are prefixed with the
72
+ frame's creation date, so a re-export overwrites in place rather than
73
+ spawning a duplicate). That reconverge-and-re-export loop repeats until the
74
+ pass's findings are all adjudicated and the spec artifact carries them.
75
+
76
+ ## Proportionality — scale the pass to the risk
77
+
78
+ The pass is **mandatory but proportional**: lightweight for ordinary work,
79
+ rigorous for high-risk work (integration option 1 from issue 73, per the
80
+ issue author — a distinct operator skill, no new CLI engine until the
81
+ workflow proves it needs one). Which work is high-risk is likewise a recorded
82
+ decision:
83
+
84
+ > the named escalation signals that deepen the pass from lightweight to
85
+ > rigorous: migrations, security-sensitive work, distributed state, hardware,
86
+ > destructive operations, other hard-to-reverse changes, concurrency hazards,
87
+ > and any surface that can lose user data (resolves q3)
88
+
89
+ — decision c19 in `docs/specs/2026-07-15-challenge-skill.md`.
90
+
91
+ If **any** escalation signal applies, run the rigorous form: every lens,
92
+ deliberate counter-evidence hunting, and cheap probes where they would settle
93
+ a real question. If none applies, a lightweight sweep — one pass over the
94
+ lenses against the exported spec, minutes not hours — satisfies the method.
95
+ Lightweight never means skipped: even the lightest pass leaves durable
96
+ records (see the hard rules).
97
+
98
+ ## The lenses
99
+
100
+ Sweep the exported spec, the live frame, and the surfaces the idea touches
101
+ through these structured lenses (from issue 73):
102
+
103
+ - **adjacent systems and hidden dependencies** — what else reads, writes, or
104
+ assumes the thing being changed;
105
+ - **unstated assumptions and missing counter-evidence** — what everyone in
106
+ the frame believes without a claim saying so, and what was never checked
107
+ because nobody argued the other side;
108
+ - **overlooked actors, lifecycle stages, data flows, and failure modes** —
109
+ who and what the frame forgot: other users, upgrade/downgrade paths,
110
+ half-completed operations;
111
+ - **security, migration, concurrency, operations, and reversibility** — the
112
+ classic hard surfaces, each also an escalation signal when present;
113
+ - **missing observability, containment, rollback, and recovery paths** —
114
+ when the surprise happens anyway, how it is seen, bounded, and undone;
115
+ - **cheap probes or experiments** — small, scratch-space checks that could
116
+ expose a surprise now instead of mid-run (probes never mutate the repo or
117
+ `.devague/` state).
118
+
119
+ ## The method
120
+
121
+ 1. **Confirm the entry condition.** A converged frame that `/think` has
122
+ already exported, and no plan seeded from it yet (`devague status` shows
123
+ where the frame stands; the exported spec-md is the artifact under
124
+ challenge).
125
+ 2. **Set the depth.** Check the idea against the c19 escalation signals
126
+ above. Any hit → rigorous; none → lightweight. Say which you chose and
127
+ why — the depth decision is part of the pass's record.
128
+ 3. **Sweep the lenses read-only.** Read the exported spec claim by claim,
129
+ the frame's parked vagueness and resolved questions, and the actual
130
+ surfaces the idea touches. Nothing mutates during the sweep; the only
131
+ mutations are the deterministic moves that record findings.
132
+ 4. **Route every finding through an existing move.** Use the routing table
133
+ below. Everything the agent proposes carries `--origin llm` and lands
134
+ `proposed` — the pass cannot silently convert speculation into confirmed
135
+ requirements.
136
+ 5. **Let the human adjudicate.** `devague review` lists every proposal with
137
+ ids; `devague confirm` / `devague reject` / `devague question --resolve`
138
+ are user-only decisions. This is the existing spec gate doing its job.
139
+ 6. **Reconverge and re-export.** `devague converge`, then `devague export` —
140
+ the same dated spec file now carries the adjudicated findings and the
141
+ pass's provenance (the exported spec renders scope entries in its
142
+ Scope exploration section).
143
+ 7. **On a clean pass, record the pass itself.** A sweep that finds nothing
144
+ still records which lenses and surfaces were examined (`devague scope`
145
+ entries, one per lens/surface, e.g. `challenge pass / concurrency lens:
146
+ devague/store.py`) and what residual uncertainty remains (`park`). A bare
147
+ "no issues found" is not an outcome this skill produces.
148
+
149
+ ## Where findings land
150
+
151
+ Challenge keeps **no parallel prose-only artifact** — the frame is the
152
+ record, and every output category from issue 73 has an existing deterministic
153
+ move to land in:
154
+
155
+ | Output category (issue 73) | What it is | Landing move |
156
+ |----------------------------|------------|--------------|
157
+ | known facts | something the pass established, with provenance | `capture --kind requirement` / `--kind decision` / `--kind boundary` (`--origin llm` → lands `proposed`) |
158
+ | assumptions | beliefs the frame leaned on unstated | `capture --kind assumption --origin llm`, then pressure-test with `interrogate --honesty` / `--hard-question` / `--contradicts` |
159
+ | known unknowns / open questions | articulated uncertainty | `question "<text>"` when it needs a user decision; `park --kind unknown_nonblocking\|unknown_blocking` when not decidable now |
160
+ | unexamined surfaces | what this pass did not (or could not) look at | `devague scope "<surface>" --finding "<what was and wasn't examined, and why>"` |
161
+ | residual surprise risk | uncertainty that survives the pass | `park` on the frame while speccing; `devague plan risk --kind <kind>` once the plan exists |
162
+ | resilience measures | containment, rollback, recovery the surprise cost demands | spec-side `capture --kind requirement` / `--kind boundary`; plan-side `devague plan risk` (see below) |
163
+
164
+ Every finding names the **lens and surface** it came from (the
165
+ `challenge pass / <lens>: <surface>` convention in scope entries; provenance
166
+ citations inside claim text). That provenance bar is how the method hunts
167
+ blind spots **without encouraging speculative issue generation** — a finding
168
+ you cannot trace to something you actually read is speculation, not a
169
+ finding.
170
+
171
+ ## Resilience placement — spec-side or plan-side
172
+
173
+ Where a resilience measure lands is a recorded decision:
174
+
175
+ > resilience measures land in both spec and plan by nature: spec-side as
176
+ > requirement/boundary claims when they change what to build, plan-side as
177
+ > plan risks or tasks when they change how to build it — the skill coaches
178
+ > which is which (resolves q2)
179
+
180
+ — decision c18 in `docs/specs/2026-07-15-challenge-skill.md`.
181
+
182
+ The coaching: ask *"does this change **what** ships, or **how** it gets
183
+ built?"* A rollback path the user needs, a fail-closed version check, a
184
+ containment boundary — those change the product: `capture` them spec-side as
185
+ `requirement` / `boundary` claims so the re-exported spec carries them.
186
+ A staging sequence, a merge-order constraint, an uncertainty the workforce
187
+ must build around — those change the build: land them plan-side via
188
+ `devague plan risk --kind <kind>` (blocking or nonblocking, honestly chosen)
189
+ once `/spec-to-plan` seeds the plan, where the plan's convergence gate keeps
190
+ blocking risks visible until resolved.
191
+
192
+ ## Hard rules (do not violate)
193
+
194
+ - **Never conclude there are no unknown unknowns.** Not after a rigorous
195
+ pass, not after a clean one. The only honest clean-pass output is a record
196
+ of which lenses and surfaces were examined — `devague scope` entries — plus
197
+ the residual uncertainty that remains — `park` — so the pass leaves durable
198
+ provenance instead of a comforting absolute (issue 73 success criteria;
199
+ the anti-fabrication contract in `docs/llm-guidance.md`).
200
+ - **LLM-origin findings stay proposed until the user confirms.** Every
201
+ finding the agent proposes carries `--origin llm` and lands `proposed`;
202
+ only the user's `confirm` makes it real. The pass must not be able to
203
+ silently convert speculation into confirmed requirements.
204
+ - **Findings route through existing deterministic moves only.** `capture`,
205
+ `interrogate`, `question`, `park`, `devague scope`, `devague plan risk` —
206
+ nothing else. No parallel prose artifact, no new CLI verb, engine, or
207
+ state model (issue 20; issue 73's stated preference). If it didn't land in
208
+ a move, it didn't land.
209
+ - **Provenance on every finding.** Name the lens and the surface it came
210
+ from. If you didn't read it, don't claim it — same bar as `/scope`.
211
+ - **Proportional, never skipped.** Lightweight is the floor, not an
212
+ exemption; any c19 escalation signal makes the rigorous form mandatory.
213
+ Skipping the pass entirely is not a depth setting.
214
+ - **Not a fourth standing gate.** The three human gates stay: exported spec,
215
+ implementation split plan, final PR. Challenge output is adjudicated
216
+ inside the existing spec gate — mirroring how `/deviate` amends gate 2
217
+ rather than adding one.
218
+ - **The sweep is read-only until it routes.** Reading spec, frame, and
219
+ surfaces never edits files or state; probes run in scratch space. The only
220
+ mutations are the recording moves themselves.
221
+
222
+ ## Worked example
223
+
224
+ Challenging the exported spec for a store-schema migration (illustrative
225
+ slug `store-schema-v3`) — "migrations" and "any surface that can lose user
226
+ data" are both c19 escalation signals, so the pass runs rigorous:
227
+
228
+ ```bash
229
+ # Entry condition: /think exported docs/specs/2026-07-15-store-schema-v3.md
230
+ # and no plan exists yet. Depth: rigorous (migration + data-loss signals).
231
+
232
+ # adjacent-systems lens: an older installed devague reads the same store
233
+ devague capture --origin llm --kind assumption "older installed devague binaries refuse a v3 store via the fail-closed schema_version check in devague/store.py"
234
+ devague interrogate c9 --origin llm --honesty "a v2-reading binary pointed at a v3 store exits with the version hint, not a traceback"
235
+ devague scope "challenge pass / adjacent-systems lens: devague/store.py schema_version gate" --finding "older binaries fail closed on v3; seeded the compat assumption" --seeds c9
236
+
237
+ # failure-mode lens: the migration can die halfway
238
+ devague capture --origin llm --kind requirement "migration writes to a temp file and renames — a killed run never leaves a half-written store"
239
+
240
+ # overlooked-actors lens: needs a user decision, not a guess
241
+ devague question "do mesh agents share one store, or does each checkout own its own?"
242
+
243
+ # reversibility lens: genuinely unknown, not decidable now
244
+ devague park "whether a v3->v2 downgrade path is ever needed" --kind unknown_nonblocking
245
+
246
+ # concurrency lens found nothing — record the clean pass, not a conclusion
247
+ devague scope "challenge pass / concurrency lens: devague/store.py + delivery_store.py" --finding "single-writer CLI, no locking today; clean pass — residual risk only if two agents ever share a checkout"
248
+
249
+ # --- HUMAN adjudicates: the existing spec gate at work ---
250
+ devague review
251
+ devague confirm c9 h4 c10
252
+ devague question --resolve q1 --decision "each checkout owns its own store"
253
+
254
+ # Reconverge and re-export — the SAME dated file, now carrying the pass
255
+ devague converge
256
+ devague export
257
+
258
+ # Residual risk that changes HOW to build lands plan-side once
259
+ # /spec-to-plan seeds the plan:
260
+ devague plan new --frame store-schema-v3
261
+ devague plan risk "two agents sharing a checkout could interleave store writes mid-migration" --kind unknown_nonblocking
262
+ ```
263
+
264
+ Every finding above is traceable to a lens and a surface; the clean lens is
265
+ recorded as examined rather than silently dropped; and nothing the agent
266
+ proposed became confirmed without the human's `confirm`.
267
+
268
+ ## After the pass — hand off to /spec-to-plan
269
+
270
+ Once the frame reconverges and the same dated spec file is re-exported, the
271
+ pass is done — the examined surfaces, residual uncertainty, and adjudicated
272
+ findings all live in frame state and render into the spec artifact. Continue
273
+ with `/spec-to-plan` as usual: the plan seeds from the challenged frame, and
274
+ any residual surprise risk you routed plan-side lands via
275
+ `devague plan risk` as first-class plan state. If a surprise still gets
276
+ through mid-fan-out, that is `/deviate`'s job — and every approved `dN`
277
+ deviation record is evidence for what the *next* challenge pass's lenses
278
+ should look harder at.
279
+
280
+ ## Provenance
281
+
282
+ This is a **first-party** skill — its origin is `agentculture/devague`, the
283
+ *seventh* in the outbound family after `/scope`, `/think`, `/spec-to-plan`,
284
+ `/assign-to-workforce`, `/deviate`, and `/summarize-delivery`, sitting third
285
+ in flow order as the blind-spot discovery leg between `/think` and
286
+ `/spec-to-plan`. guildmaster pulls it from here and broadcasts it to the
287
+ AgentCulture mesh; because devague is upstream, it is **never re-vendored
288
+ back** from guildmaster's re-broadcast copy. The `cite, don't import` policy
289
+ still holds: downstream repos copy it, they don't symlink or depend on it.
290
+ See `docs/skill-sources.md`.
@@ -0,0 +1,176 @@
1
+ ---
2
+ name: deviate
3
+ description: >
4
+ Stop an in-flight assign-to-workforce run the moment execution must diverge
5
+ from the confirmed plan, get explicit human approval for the divergence, and
6
+ record it as a first-class, append-only deviation record via `devague
7
+ deviate` before resuming — never fold a deviation silently into drift after
8
+ the fact. Use when the user says "deviate from the plan", "we need to change
9
+ the plan mid-run", "record a deviation", "this isn't matching the plan
10
+ anymore", or when a task agent discovers the confirmed plan no longer
11
+ matches reality partway through a workforce run. Authored and maintained in
12
+ agentculture/devague (origin = devague); guildmaster pulls this skill from
13
+ here and broadcasts it to the AgentCulture mesh — it is NOT vendored from
14
+ guildmaster like the inbound skills here.
15
+ type: command
16
+ ---
17
+
18
+ # deviate — record an approved mid-run departure from the confirmed plan
19
+
20
+ The skill is named **`deviate`**; it is the **execution-time leg** of the
21
+ devague method — the *sixth* leg, sitting between the two execution skills:
22
+
23
+ ```text
24
+ scope -> think -> spec-to-plan -> assign-to-workforce -> deviate -> summarize-delivery
25
+ ```
26
+
27
+ Where `/assign-to-workforce` fans out a converged plan's waves and
28
+ `/summarize-delivery` closes the loop afterward, `/deviate` runs **during**
29
+ the fan-out, at the exact moment reality stops matching the plan the human
30
+ approved at gate 2 (the implementation split plan). It is not a new standing
31
+ gate — it is the human owner of gate 2 **amending** the approved split
32
+ mid-flight, scoped to the one deviation in front of them.
33
+
34
+ The plan the user confirmed is a contract. Task agents hit constraints,
35
+ discover a dependency was wrong, or find the acceptance criteria no longer
36
+ make sense once code is in front of them. Until this skill existed, that
37
+ reality either silently reshaped what got built (undocumented drift) or the
38
+ run stalled with no recorded path forward. `/deviate` is that path: the
39
+ departure is named, approved, and recorded **before** anyone keeps building
40
+ against it.
41
+
42
+ ## The method
43
+
44
+ 1. **STOP the run.** The moment a task agent (or the main agent) discovers
45
+ the confirmed plan no longer matches what needs to happen, halt — do not
46
+ keep implementing against the stale contract and do not let the fan-out
47
+ continue past this task.
48
+ 2. **Present what, why, and what it affects.** Lay out, for the human:
49
+ - **what** is diverging — the specific change from the confirmed task(s);
50
+ - **why** — the constraint or discovery that forced it;
51
+ - **what it affects** — the plan item ref(s) involved (`--task`), any
52
+ other task ids or coverage targets it touches (`--affects`), and which
53
+ acceptance criteria are no longer accurate.
54
+ 3. **Get explicit human approval.** This is the one non-negotiable step. A
55
+ deviation is not real until a human says yes to *this specific* departure
56
+ — not a standing blanket permission, not an inference from silence.
57
+ 4. **Record it via `devague deviate`.** Once approved, record the deviation
58
+ as a first-class ledger entry (never edit the plan itself — the plan
59
+ stays the untouched historical contract; see `devague/delivery.py`). An
60
+ `--origin llm` record lands `proposed` and still needs the user's
61
+ `--confirm` — recording is not the same as approval landing as final.
62
+ 5. **Adjust the affected task briefs.** Update the working instructions the
63
+ task agent(s) are building against so they reflect the approved
64
+ departure, not the stale plan text.
65
+ 6. **Resume.** Only after the record exists (and, for an `llm`-origin record,
66
+ only after it is confirmed) does the fan-out continue.
67
+
68
+ Deviations are never silently folded into drift after the fact — by the time
69
+ `/summarize-delivery` runs, every departure from the plan already has a
70
+ `dN` record with a reason, an approval, and an optional classification. The
71
+ delivery summary quotes these records; it does not reconstruct drift from
72
+ memory.
73
+
74
+ ## The shipped CLI surface
75
+
76
+ This skill invokes the CLI directly and stays self-contained (if `devague`
77
+ isn't on your PATH: `uv tool install devague`). Deviation state persists
78
+ under `.devague/deliveries/<plan-slug>.json` — a peer of the plan store, keyed
79
+ by the plan slug, and never touches the plan JSON itself.
80
+
81
+ | Move | What it does |
82
+ |------|---------------|
83
+ | `devague deviate "<what>" --task <tN> --reason "<text>"` | Record a deviation against plan item `<tN>`. User-origin (the default) auto-approves. |
84
+ | `devague deviate "<what>" --task <tN> --reason "<text>" --affects <ref> [<ref> ...]` | Same, also naming every other plan item ref or coverage target the deviation touches (repeatable). |
85
+ | `devague deviate "<what>" --task <tN> --reason "<text>" --classification acceptable\|risky\|needs-follow-up` | Same, tagging the deviation with the classification the drift-entry contract consumes downstream. |
86
+ | `devague deviate "<what>" --task <tN> --reason "<text>" --origin llm` | An LLM-proposed record; lands `proposed`, not `approved`. |
87
+ | `devague deviate --confirm <dN>` | User-only: approve a `proposed` deviation. |
88
+ | `devague deviate --reject <dN>` | User-only: reject a `proposed` (or any) deviation. |
89
+ | `devague deviate --list [--json]` | Read every recorded deviation back (also the default action with no positional/flag). |
90
+ | `devague deviate ... --plan <slug>` | Target a plan other than the current one. |
91
+
92
+ `--reason` is required on every record — omitting it is refused with a hint.
93
+ `--task` naming the plan item ref is likewise required.
94
+
95
+ ## Hard rules (do not violate)
96
+
97
+ - **Never record a deviation the human did not approve.** `devague deviate`
98
+ without `--origin llm` auto-approves the instant it is run — so a
99
+ user-origin record IS the approval. Only run it after the human has said
100
+ yes to this specific departure, never preemptively "to be safe."
101
+ - **Never continue past a refused approval.** If the human does not approve,
102
+ the run stays stopped on that task. Do not record the deviation anyway, do
103
+ not quietly implement the diverging approach, and do not advance the wave.
104
+ - **LLM-origin records stay proposed until the user confirms.** `--origin
105
+ llm` lands `proposed`; only `devague deviate --confirm <dN>` (user-only)
106
+ makes it `approved`. Same anti-fabrication contract as every other origin
107
+ in the method — an agent's own proposal never self-confirms.
108
+ - **Deviations are never silently folded into drift after the fact.** Every
109
+ departure from the confirmed plan gets a `dN` record at the moment it
110
+ happens — not reconstructed from memory when `/summarize-delivery` runs
111
+ later.
112
+ - **The CLI stays non-orchestrating (issue #20).** `devague deviate` records
113
+ a decision the human already made; it does not spawn agents, gate merges,
114
+ mark tasks done, or make the approval decision itself. Recording is
115
+ deterministic — no LLM calls inside the CLI.
116
+ - **This is not a fourth standing gate.** `/assign-to-workforce`'s three
117
+ human gates are the exported spec, the implementation split plan, and the
118
+ final PR. `/deviate` does not add a fourth — it is the human owner of gate
119
+ 2 amending the approved split for one scoped, in-flight decision.
120
+
121
+ ## Worked example
122
+
123
+ Mid-fan-out on task `t4`, the task agent discovers the acceptance criteria
124
+ assumed a helper that task `t2` never actually shipped:
125
+
126
+ ```bash
127
+ # 1. STOP — the main agent halts t4's worktree before more code lands
128
+ # against a criterion that can't be met as written.
129
+
130
+ # 2. Present what/why/what-it-affects to the human:
131
+ # what: t4's acceptance criterion 2 assumes a `--json` flag on a
132
+ # helper that t2 shipped without one
133
+ # why: t2's scope was cut to land its wave on time
134
+ # affects: t4 (this task), and coverage target c9 (the criterion in
135
+ # question)
136
+
137
+ # --- HUMAN: approves dropping the --json assumption from t4's criterion ---
138
+
139
+ # 3. Record the approved deviation
140
+ devague deviate "drop the --json assumption from t4's acceptance criterion" \
141
+ --task t4 --reason "t2 shipped its helper without --json to land its wave on time" \
142
+ --affects t2 --affects c9 --classification acceptable
143
+
144
+ # 4. Adjust t4's working brief to match what was approved, then resume the
145
+ # task agent against the corrected instruction.
146
+
147
+ # Read the ledger back at any point:
148
+ devague deviate --list
149
+ devague deviate --list --json
150
+ ```
151
+
152
+ If the agent had proposed the record itself (`--origin llm`), the same
153
+ record would land `proposed` and need an explicit
154
+ `devague deviate --confirm d1` from the user before `/summarize-delivery`
155
+ could cite it as approved.
156
+
157
+ ## After recording — resume, then hand off to /summarize-delivery
158
+
159
+ Once the affected task briefs are adjusted and the fan-out resumes, nothing
160
+ further is needed from this skill — the record already lives in the delivery
161
+ store. When the run reaches `/summarize-delivery`, that skill's Drift From
162
+ Plan and Mid-work Decisions sections quote these records by their `dN` id
163
+ instead of reconstructing drift from memory, so the connective tissue between
164
+ the confirmed plan and the delivery summary is the ledger this skill wrote,
165
+ not anyone's recollection of the run.
166
+
167
+ ## Provenance
168
+
169
+ This is a **first-party** skill — its origin is `agentculture/devague`, the
170
+ *sixth* in the outbound family after `/scope`, `/think`, `/spec-to-plan`,
171
+ `/assign-to-workforce`, and `/summarize-delivery`, covering the execution-time
172
+ leg that runs inside an `/assign-to-workforce` fan-out. guildmaster pulls it
173
+ from here and broadcasts it to the AgentCulture mesh; because devague is
174
+ upstream, it is **never re-vendored back** from guildmaster's re-broadcast
175
+ copy. The `cite, don't import` policy still holds: downstream repos copy it,
176
+ they don't symlink or depend on it. See `docs/skill-sources.md`.
@@ -6,10 +6,11 @@
6
6
  # forwards every flag verbatim — so `recall.sh "<query>" --mode hybrid --json`
7
7
  # is exactly `eidetic recall "<query>" --mode hybrid --json`.
8
8
  #
9
- # The store is the files backend at ~/.eidetic/memory by default — a home-dir
10
- # path OUTSIDE any git worktree, so Claude and the colleague backend (which runs
11
- # in throwaway worktrees) read the SAME memories. Set EIDETIC_DATA_DIR to opt out
12
- # of sharing; set EIDETIC_MONGO_URI / NEO4J_URI + --backend for a server store.
9
+ # The store is the files backend. Default location resolves per-operation:
10
+ # PUBLIC records inside a git repo <repo-root>/.eidetic/memory (committed,
11
+ # team-shared); PRIVATE records, or any record outside a git repo
12
+ # $HOME/.eidetic/memory (never committed). Recall reads both stores and merges.
13
+ # An explicit EIDETIC_DATA_DIR wins and short-circuits to that single dir.
13
14
 
14
15
  set -euo pipefail
15
16
 
@@ -34,12 +35,10 @@ resolve_eidetic() {
34
35
  fi
35
36
  dir=$(dirname "$dir")
36
37
  done
37
- cat >&2 <<'EOF'
38
- error: eidetic CLI not found.
39
- hint: install it with `uv tool install eidetic-cli` (or `pipx install eidetic-cli`),
40
- or run from inside the eidetic-cli checkout with `uv` available.
41
- The console script is `eidetic` (dist name: eidetic-cli).
42
- EOF
38
+ # In a vendored copy there is no eidetic-cli checkout to fall back to, so the
39
+ # only honest remedy is to install the CLI. One `error:` + one `hint:` line.
40
+ printf 'error: eidetic CLI not found.\n' >&2
41
+ printf 'hint: install it with: uv tool install eidetic-cli (or pipx install eidetic-cli); the console script is eidetic.\n' >&2
43
42
  return 1
44
43
  }
45
44
 
@@ -65,10 +64,17 @@ EOF
65
64
  }
66
65
 
67
66
  case "${1:-}" in
68
- -h | --help | help | "")
67
+ -h | --help)
69
68
  usage
70
69
  exit 0
71
70
  ;;
71
+ "")
72
+ # A missing query is a usage error, not success. The bareword `help` is
73
+ # a legitimate search term, so it is intentionally NOT a usage alias.
74
+ printf 'error: no query given.\n' >&2
75
+ printf 'hint: recall.sh "<query>" [--mode ...] [--json]; run recall.sh --help for usage.\n' >&2
76
+ exit 1
77
+ ;;
72
78
  esac
73
79
 
74
80
  resolve_eidetic || exit 2
@@ -100,9 +106,12 @@ resolve_scope() {
100
106
  # inline `# comment` or trailing space can't bleed into the scope),
101
107
  # then strip surrounding quotes only — matching the canonical parser
102
108
  # in .claude/skills/cicd/scripts/_resolve-nick.sh.
109
+ # `|| true`: under `set -o pipefail`, `head -n1` closing the pipe
110
+ # early can SIGPIPE `sed`, making the substitution non-zero and
111
+ # aborting the script. An empty parse must yield "" here, not exit.
103
112
  suffix=$(sed -n \
104
113
  's/^[[:space:]]*-\{0,1\}[[:space:]]*suffix:[[:space:]]*\([^[:space:]]*\).*/\1/p' \
105
- "$dir/culture.yaml" | head -n1 | tr -d "\"'")
114
+ "$dir/culture.yaml" | head -n1 | tr -d "\"'" || true)
106
115
  break
107
116
  fi
108
117
  dir=$(dirname "$dir")
@@ -127,7 +136,20 @@ if ! has_flag --scope "$@"; then
127
136
  EIDETIC_SCOPE=$(resolve_scope)
128
137
  if [ -n "$EIDETIC_SCOPE" ]; then
129
138
  SCOPE_ARGS+=(--scope "$EIDETIC_SCOPE")
130
- has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility private)
139
+ # rollout-cli eidetic-memory recipe POLICY OVERRIDE (not eidetic's
140
+ # upstream private default): default to PUBLIC, so a plain recall queries
141
+ # the in-repo public pool (<repo>/.eidetic/memory) this repo writes to.
142
+ # Pass --visibility private to also surface this agent's private ($HOME)
143
+ # notes. The two-store read model reads both dirs regardless.
144
+ has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility public)
145
+ elif ! has_flag --visibility "$@"; then
146
+ # No suffix AND no explicit --visibility: the query runs against
147
+ # eidetic's own default (scope=default, visibility=public), not this
148
+ # agent's private personal scope — so an empty result isn't silently
149
+ # misread. Warn on stderr (stdout stays clean for --json). Warn ONLY
150
+ # here: an explicit --scope (outer guard) or --visibility (this guard) is
151
+ # a deliberate choice, honored verbatim, so either flag silences this.
152
+ printf 'warning: no culture.yaml suffix resolved; querying the public default scope rather than a private personal scope. Pass --scope or --visibility to target deliberately.\n' >&2
131
153
  fi
132
154
  fi
133
155
 
@@ -12,10 +12,11 @@
12
12
  # Upsert is idempotent by id (and dedups by content hash): re-remembering the
13
13
  # same record updates it in place, never duplicates.
14
14
  #
15
- # The store is the files backend at ~/.eidetic/memory by default — a home-dir
16
- # path OUTSIDE any git worktree, so a record Claude remembers is recallable by
17
- # the colleague backend (which runs in throwaway worktrees), and vice versa.
18
- # Set EIDETIC_DATA_DIR to opt out of sharing; use --backend mongo|neo4j (with
15
+ # The store is the files backend. Default location resolves per-operation:
16
+ # PUBLIC records inside a git repo <repo-root>/.eidetic/memory (committed,
17
+ # team-shared); PRIVATE records, or any record outside a git repo
18
+ # $HOME/.eidetic/memory (never committed). An explicit EIDETIC_DATA_DIR still
19
+ # wins and short-circuits to that single dir. Use --backend mongo|neo4j (with
19
20
  # EIDETIC_MONGO_URI / NEO4J_URI) for a server-backed shared store.
20
21
 
21
22
  set -euo pipefail
@@ -40,12 +41,10 @@ resolve_eidetic() {
40
41
  fi
41
42
  dir=$(dirname "$dir")
42
43
  done
43
- cat >&2 <<'EOF'
44
- error: eidetic CLI not found.
45
- hint: install it with `uv tool install eidetic-cli` (or `pipx install eidetic-cli`),
46
- or run from inside the eidetic-cli checkout with `uv` available.
47
- The console script is `eidetic` (dist name: eidetic-cli).
48
- EOF
44
+ # In a vendored copy there is no eidetic-cli checkout to fall back to, so the
45
+ # only honest remedy is to install the CLI. One `error:` + one `hint:` line.
46
+ printf 'error: eidetic CLI not found.\n' >&2
47
+ printf 'hint: install it with: uv tool install eidetic-cli (or pipx install eidetic-cli); the console script is eidetic.\n' >&2
49
48
  return 1
50
49
  }
51
50
 
@@ -60,7 +59,9 @@ Usage:
60
59
 
61
60
  A record needs `id`, `text`, and `type`; `hash` and `metadata` are recommended
62
61
  (hash is derived from text when omitted). Upsert is idempotent by id.
63
- Public data only. Every flag is forwarded verbatim to `eidetic remember`.
62
+ Records default to this agent's PRIVATE personal scope (--scope from the
63
+ culture.yaml suffix); pass --visibility public to contribute to the shared
64
+ public pool. Every flag is forwarded verbatim to `eidetic remember`.
64
65
  See `eidetic explain remember`.
65
66
  EOF
66
67
  }
@@ -72,6 +73,16 @@ case "${1:-}" in
72
73
  ;;
73
74
  esac
74
75
 
76
+ # No record argument AND stdin is an interactive terminal → `eidetic remember`
77
+ # would block forever waiting for NDJSON. Show usage instead of hanging. A piped
78
+ # or redirected stdin (`cat records.ndjson | remember.sh`) is not a TTY and
79
+ # proceeds to the batch path normally.
80
+ if [ "$#" -eq 0 ] && [ -t 0 ]; then
81
+ usage >&2
82
+ printf 'hint: pass a JSON record as an argument, or pipe NDJSON on stdin.\n' >&2
83
+ exit 1
84
+ fi
85
+
75
86
  resolve_eidetic || exit 2
76
87
 
77
88
  # ── default to this agent's PERSONAL, PRIVATE scope (culture.yaml `suffix`) ──
@@ -100,9 +111,12 @@ resolve_scope() {
100
111
  # inline `# comment` or trailing space can't bleed into the scope),
101
112
  # then strip surrounding quotes only — matching the canonical parser
102
113
  # in .claude/skills/cicd/scripts/_resolve-nick.sh.
114
+ # `|| true`: under `set -o pipefail`, `head -n1` closing the pipe
115
+ # early can SIGPIPE `sed`, making the substitution non-zero and
116
+ # aborting the script. An empty parse must yield "" here, not exit.
103
117
  suffix=$(sed -n \
104
118
  's/^[[:space:]]*-\{0,1\}[[:space:]]*suffix:[[:space:]]*\([^[:space:]]*\).*/\1/p' \
105
- "$dir/culture.yaml" | head -n1 | tr -d "\"'")
119
+ "$dir/culture.yaml" | head -n1 | tr -d "\"'" || true)
106
120
  break
107
121
  fi
108
122
  dir=$(dirname "$dir")
@@ -127,7 +141,19 @@ if ! has_flag --scope "$@"; then
127
141
  EIDETIC_SCOPE=$(resolve_scope)
128
142
  if [ -n "$EIDETIC_SCOPE" ]; then
129
143
  SCOPE_ARGS+=(--scope "$EIDETIC_SCOPE")
130
- has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility private)
144
+ # rollout-cli eidetic-memory recipe POLICY OVERRIDE (not eidetic's
145
+ # upstream private default): default to PUBLIC so a plain remember lands
146
+ # in <repo>/.eidetic/memory — committed, team- and mesh-shared. Pass
147
+ # --visibility private to keep a record in $HOME (uncommitted).
148
+ has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility public)
149
+ elif ! has_flag --visibility "$@"; then
150
+ # No suffix AND no explicit --visibility: the record falls back to
151
+ # eidetic's own default (scope=default, visibility=public). Don't let an
152
+ # expected-private record go public silently — warn on stderr (stdout
153
+ # stays clean for --json). Warn ONLY here: an explicit --scope (outer
154
+ # guard) or --visibility (this guard) means the caller chose deliberately
155
+ # and is honored verbatim, so either flag silences this.
156
+ printf 'warning: no culture.yaml suffix resolved; this record falls back to the public default scope. Pass --scope or --visibility to place it deliberately.\n' >&2
131
157
  fi
132
158
  fi
133
159