@runecraft/grimoire 1.0.0

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -0
  3. package/catalog.json +9 -0
  4. package/dist/grimoire.js +1758 -0
  5. package/package.json +54 -0
  6. package/references/definition-of-done.md +67 -0
  7. package/references/testing-patterns.md +260 -0
  8. package/skills/code-review-and-quality/README.md +13 -0
  9. package/skills/code-review-and-quality/SKILL.md +389 -0
  10. package/skills/code-simplification/README.md +13 -0
  11. package/skills/code-simplification/SKILL.md +338 -0
  12. package/skills/debugging-and-error-recovery/README.md +13 -0
  13. package/skills/debugging-and-error-recovery/SKILL.md +343 -0
  14. package/skills/debugging-and-error-recovery/scripts/__pycache__/triage_state.cpython-314.pyc +0 -0
  15. package/skills/debugging-and-error-recovery/scripts/triage_state.py +206 -0
  16. package/skills/deprecation-and-migration/README.md +13 -0
  17. package/skills/deprecation-and-migration/SKILL.md +248 -0
  18. package/skills/deprecation-and-migration/scripts/__pycache__/migration_tracker.cpython-314.pyc +0 -0
  19. package/skills/deprecation-and-migration/scripts/migration_tracker.py +237 -0
  20. package/skills/doubt-driven-development/README.md +13 -0
  21. package/skills/doubt-driven-development/SKILL.md +251 -0
  22. package/skills/git-commit-learning/.skill-meta.json +14 -0
  23. package/skills/git-commit-learning/README.md +205 -0
  24. package/skills/git-commit-learning/SKILL.md +435 -0
  25. package/skills/git-commit-learning/references/commit-patterns.md +595 -0
  26. package/skills/git-worktree/README.md +13 -0
  27. package/skills/git-worktree/SKILL.md +220 -0
  28. package/skills/idea-refine/README.md +13 -0
  29. package/skills/idea-refine/SKILL.md +186 -0
  30. package/skills/interview-me/README.md +13 -0
  31. package/skills/interview-me/SKILL.md +233 -0
  32. package/skills/linkedin-audit/SKILL.md +98 -0
  33. package/skills/linkedin-audit/references/dashboard-spec.md +43 -0
  34. package/skills/memory-management/README.md +13 -0
  35. package/skills/memory-management/SKILL.md +198 -0
  36. package/skills/security-and-hardening/README.md +13 -0
  37. package/skills/security-and-hardening/SKILL.md +472 -0
  38. package/skills/shipping-and-launch/README.md +13 -0
  39. package/skills/shipping-and-launch/SKILL.md +317 -0
  40. package/skills/skill-forge/README.md +153 -0
  41. package/skills/skill-forge/SKILL.md +291 -0
  42. package/skills/skill-forge/assets/SKILL.template.md +73 -0
  43. package/skills/skill-forge/references/authoring-patterns.md +249 -0
  44. package/skills/skill-forge/references/description-optimization.md +171 -0
  45. package/skills/skill-forge/references/output-evaluation.md +276 -0
  46. package/skills/skill-forge/references/scripts-guide.md +232 -0
  47. package/skills/skill-forge/references/spec.md +175 -0
  48. package/skills/skill-forge/scripts/validate.py +536 -0
  49. package/skills/spec-driven/.skill-meta.json +14 -0
  50. package/skills/spec-driven/README.md +335 -0
  51. package/skills/spec-driven/SKILL.md +174 -0
  52. package/skills/spec-driven/references/code-analysis.md +98 -0
  53. package/skills/spec-driven/references/coding-principles.md +56 -0
  54. package/skills/spec-driven/references/context-limits.md +31 -0
  55. package/skills/spec-driven/references/design.md +199 -0
  56. package/skills/spec-driven/references/discuss.md +136 -0
  57. package/skills/spec-driven/references/implement.md +425 -0
  58. package/skills/spec-driven/references/lessons.md +113 -0
  59. package/skills/spec-driven/references/memory.md +126 -0
  60. package/skills/spec-driven/references/specify.md +210 -0
  61. package/skills/spec-driven/references/sub-agents.md +96 -0
  62. package/skills/spec-driven/references/tasks.md +484 -0
  63. package/skills/spec-driven/references/validate.md +350 -0
  64. package/skills/spec-driven/scripts/__pycache__/lessons.cpython-314.pyc +0 -0
  65. package/skills/spec-driven/scripts/lessons.py +370 -0
  66. package/skills/spec-loop/README.md +36 -0
  67. package/skills/spec-loop/SKILL.md +61 -0
  68. package/skills/test-driven-development/README.md +13 -0
  69. package/skills/test-driven-development/SKILL.md +388 -0
  70. package/skills/typescript-patterns/README.md +13 -0
  71. package/skills/typescript-patterns/SKILL.md +346 -0
  72. package/skills/using-agent-skills/README.md +13 -0
  73. package/skills/using-agent-skills/SKILL.md +187 -0
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ lessons.py — deterministic bookkeeping for the spec-driven lessons layer.
4
+
5
+ The LLM supplies judgment (which failure happened, how to phrase the lesson, what
6
+ signal grounds it). This script owns everything mechanical: IDs, distinct-feature
7
+ recurrence counting, candidate->confirmed promotion, pruning, demotion, and
8
+ rendering the human/agent-readable playbook. Bookkeeping by hand is exactly what
9
+ rots a lessons file, so it lives here, not in a prompt.
10
+
11
+ Canonical state: .specs/lessons.json (machine-owned — do NOT hand-edit)
12
+ Rendered view: .specs/LESSONS.md (regenerated on every write)
13
+
14
+ Pure standard library. No dependencies. Run from the project root (the dir that
15
+ contains .specs), or pass --root.
16
+
17
+ Commands:
18
+ add Record a grounded lesson from a verification signal.
19
+ list Print lessons (default: confirmed) for loading at Specify/Design.
20
+ penalize Mark a confirmed lesson as having failed when applied (-> quarantine).
21
+ prune Drop stale uncorroborated candidates (also runs automatically on add/list).
22
+ status Print counts (used by the self-check in validate.md).
23
+ init Create empty store + rendered file.
24
+
25
+ Exit codes: 0 ok, 2 usage/validation error (e.g. missing grounding).
26
+ """
27
+
28
+ import argparse
29
+ import datetime as _dt
30
+ import json
31
+ import os
32
+ import re
33
+ import sys
34
+
35
+ STORE_REL = os.path.join(".specs", "lessons.json")
36
+ RENDER_REL = os.path.join(".specs", "LESSONS.md")
37
+
38
+ SIGNALS = {
39
+ "ac_gap": "Acceptance criterion not covered / failed",
40
+ "surviving_mutant": "Discrimination sensor mutant survived (weak test)",
41
+ "spec_precision_gap": "Spec did not define a precise outcome",
42
+ "spec_deviation": "Implementation diverged from spec/design (SPEC_DEVIATION)",
43
+ "gate_fail": "Build-level gate check failed",
44
+ }
45
+
46
+ DEFAULTS = {"promote_threshold": 2, "window_days": 45, "quarantine_threshold": 2}
47
+
48
+
49
+ def _now():
50
+ return _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
51
+
52
+
53
+ def _parse_date(s):
54
+ try:
55
+ return _dt.datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=_dt.timezone.utc)
56
+ except Exception:
57
+ return _dt.datetime.now(_dt.timezone.utc)
58
+
59
+
60
+ def _store_path(root):
61
+ return os.path.join(root, STORE_REL)
62
+
63
+
64
+ def _render_path(root):
65
+ return os.path.join(root, RENDER_REL)
66
+
67
+
68
+ def _load(root):
69
+ path = _store_path(root)
70
+ if not os.path.exists(path):
71
+ return {
72
+ "schema": 1,
73
+ "promote_threshold": DEFAULTS["promote_threshold"],
74
+ "window_days": DEFAULTS["window_days"],
75
+ "quarantine_threshold": DEFAULTS["quarantine_threshold"],
76
+ "next_id": 1,
77
+ "lessons": [],
78
+ }
79
+ with open(path, "r", encoding="utf-8") as f:
80
+ data = json.load(f)
81
+ for k, v in DEFAULTS.items():
82
+ data.setdefault(k, v)
83
+ data.setdefault("schema", 1)
84
+ data.setdefault("next_id", 1)
85
+ data.setdefault("lessons", [])
86
+ return data
87
+
88
+
89
+ def _save(root, data):
90
+ os.makedirs(os.path.join(root, ".specs"), exist_ok=True)
91
+ with open(_store_path(root), "w", encoding="utf-8") as f:
92
+ json.dump(data, f, indent=2, ensure_ascii=False)
93
+ f.write("\n")
94
+ _render(root, data)
95
+
96
+
97
+ def _norm(text):
98
+ """Normalized dedup key: lowercase, strip punctuation, collapse whitespace.
99
+ Exact-after-normalization only — no semantic matching (stdlib-only limitation).
100
+ Phrase lessons tersely and canonically so recurrences actually merge."""
101
+ t = text.lower().strip()
102
+ t = re.sub(r"[^a-z0-9\s]", " ", t)
103
+ t = re.sub(r"\s+", " ", t).strip()
104
+ return t
105
+
106
+
107
+ def _key(signal, text):
108
+ return signal + "::" + _norm(text)
109
+
110
+
111
+ def _auto_prune(data):
112
+ """Drop candidates that never recurred within the window. Mutates data."""
113
+ threshold = data["promote_threshold"]
114
+ window = data["window_days"]
115
+ now = _dt.datetime.now(_dt.timezone.utc)
116
+ kept = []
117
+ dropped = []
118
+ for l in data["lessons"]:
119
+ if l["status"] == "candidate" and l["recurrence"] < threshold:
120
+ age_days = (now - _parse_date(l.get("last_seen", l.get("created", _now())))).days
121
+ if age_days > window:
122
+ dropped.append(l["id"])
123
+ continue
124
+ kept.append(l)
125
+ data["lessons"] = kept
126
+ return dropped
127
+
128
+
129
+ def _find(data, signal, text):
130
+ k = _key(signal, text)
131
+ for l in data["lessons"]:
132
+ if l.get("key") == k:
133
+ return l
134
+ return None
135
+
136
+
137
+ def _render(root, data):
138
+ lines = []
139
+ lines.append("# LESSONS — auto-maintained by scripts/lessons.py")
140
+ lines.append("")
141
+ lines.append("> Machine-owned. Do NOT hand-edit. Changes are overwritten on the next `lessons.py` write.")
142
+ lines.append("> Canonical state lives in `.specs/lessons.json`. Edit lessons only via the script.")
143
+ lines.append(f"> promote_threshold={data['promote_threshold']} distinct features · window_days={data['window_days']} · quarantine_threshold={data['quarantine_threshold']}")
144
+ lines.append("")
145
+
146
+ by_status = {"confirmed": [], "candidate": [], "quarantined": []}
147
+ for l in data["lessons"]:
148
+ by_status.get(l["status"], by_status["candidate"]).append(l)
149
+
150
+ def block(title, items, note):
151
+ out = [f"## {title}", ""]
152
+ if note:
153
+ out.append(note)
154
+ out.append("")
155
+ if not items:
156
+ out.append("_none_")
157
+ out.append("")
158
+ return out
159
+ for l in sorted(items, key=lambda x: x["id"]):
160
+ scope = f" · scope: `{l['scope']}`" if l.get("scope") else ""
161
+ out.append(f"### {l['id']} — {l['text']}")
162
+ out.append(
163
+ f"- signal: `{l['signal']}` · recurrence: {l['recurrence']} feature(s){scope} · harmful: {l.get('harmful', 0)}"
164
+ )
165
+ feats = ", ".join(l.get("features", [])) or "—"
166
+ out.append(f"- features: {feats}")
167
+ ev = l.get("evidence", [])
168
+ if ev:
169
+ out.append(f"- evidence: {ev[0]}" + (f" (+{len(ev) - 1} more)" if len(ev) > 1 else ""))
170
+ out.append(f"- last seen: {l.get('last_seen', '—')}")
171
+ out.append("")
172
+ return out
173
+
174
+ lines += block(
175
+ "Confirmed (load these at Specify/Design)",
176
+ by_status["confirmed"],
177
+ "Corroborated across multiple features. Safe to apply as guidance.",
178
+ )
179
+ lines += block(
180
+ "Candidates (under observation — do NOT load as guidance yet)",
181
+ by_status["candidate"],
182
+ "Seen once or not yet corroborated. Tracked, not trusted.",
183
+ )
184
+ lines += block(
185
+ "Quarantined (failed when applied — ignore)",
186
+ by_status["quarantined"],
187
+ "A confirmed lesson that recurred alongside failure. Kept for the maintainer to review.",
188
+ )
189
+
190
+ with open(_render_path(root), "w", encoding="utf-8") as f:
191
+ f.write("\n".join(lines).rstrip() + "\n")
192
+
193
+
194
+ # ----------------------------- commands -----------------------------
195
+
196
+ def cmd_init(root, args):
197
+ data = _load(root)
198
+ _save(root, data)
199
+ print(f"Initialized lessons store at {_store_path(root)} and {_render_path(root)}")
200
+ return 0
201
+
202
+
203
+ def cmd_add(root, args):
204
+ signal = args.signal
205
+ source = (args.source or "").strip()
206
+ text = (args.text or "").strip()
207
+ feature = (args.feature or "").strip()
208
+
209
+ # Grounding is enforced here, deterministically — not left to the prompt.
210
+ if signal not in SIGNALS:
211
+ print(f"ERROR: --signal must be one of {sorted(SIGNALS)}", file=sys.stderr)
212
+ return 2
213
+ if not feature:
214
+ print("ERROR: --feature is required (the feature the signal came from).", file=sys.stderr)
215
+ return 2
216
+ if not source:
217
+ print("ERROR: --source is required (file:line / AC id / mutant id / SPEC_DEVIATION ref).", file=sys.stderr)
218
+ print(" A lesson with no grounding in validation.md is an opinion, not a lesson. Refused.", file=sys.stderr)
219
+ return 2
220
+ if len(text) < 12:
221
+ print("ERROR: --text too short. State the actionable lesson in one terse sentence.", file=sys.stderr)
222
+ return 2
223
+
224
+ data = _load(root)
225
+ _auto_prune(data)
226
+ existing = _find(data, signal, text)
227
+ now = _now()
228
+
229
+ if existing:
230
+ if feature not in existing["features"]:
231
+ existing["features"].append(feature)
232
+ existing["recurrence"] = len(existing["features"])
233
+ existing["last_seen"] = now
234
+ ev = source if not args.scope else f"{source} ({args.scope})"
235
+ if ev not in existing["evidence"]:
236
+ existing["evidence"].append(ev)
237
+ promoted = False
238
+ if existing["status"] == "candidate" and existing["recurrence"] >= data["promote_threshold"]:
239
+ existing["status"] = "confirmed"
240
+ promoted = True
241
+ _save(root, data)
242
+ msg = f"UPDATED {existing['id']} (recurrence={existing['recurrence']}, status={existing['status']})"
243
+ if promoted:
244
+ msg += " — PROMOTED to confirmed"
245
+ print(msg)
246
+ else:
247
+ lid = f"L-{data['next_id']:03d}"
248
+ data["next_id"] += 1
249
+ data["lessons"].append(
250
+ {
251
+ "id": lid,
252
+ "key": _key(signal, text),
253
+ "text": text,
254
+ "signal": signal,
255
+ "scope": (args.scope or "").strip(),
256
+ "status": "candidate",
257
+ "features": [feature],
258
+ "recurrence": 1,
259
+ "harmful": 0,
260
+ "evidence": [source if not args.scope else f"{source} ({args.scope})"],
261
+ "created": now,
262
+ "last_seen": now,
263
+ }
264
+ )
265
+ _save(root, data)
266
+ print(f"ADDED {lid} (status=candidate, recurrence=1)")
267
+ return 0
268
+
269
+
270
+ def cmd_penalize(root, args):
271
+ data = _load(root)
272
+ target = None
273
+ for l in data["lessons"]:
274
+ if l["id"].lower() == args.id.lower():
275
+ target = l
276
+ break
277
+ if not target:
278
+ print(f"ERROR: no lesson with id {args.id}", file=sys.stderr)
279
+ return 2
280
+ target["harmful"] = target.get("harmful", 0) + 1
281
+ target["last_seen"] = _now()
282
+ if target["harmful"] >= data["quarantine_threshold"]:
283
+ target["status"] = "quarantined"
284
+ _save(root, data)
285
+ print(f"PENALIZED {target['id']} (harmful={target['harmful']}, status={target['status']})")
286
+ return 0
287
+
288
+
289
+ def cmd_list(root, args):
290
+ data = _load(root)
291
+ if _auto_prune(data):
292
+ _save(root, data)
293
+ want = args.status
294
+ q = (args.query or "").lower().strip()
295
+ scope = (args.scope or "").lower().strip()
296
+ rows = []
297
+ for l in data["lessons"]:
298
+ if want != "all" and l["status"] != want:
299
+ continue
300
+ if q and q not in l["text"].lower():
301
+ continue
302
+ if scope and scope not in (l.get("scope", "").lower()):
303
+ continue
304
+ rows.append(l)
305
+ if not rows:
306
+ print(f"(no {want} lessons" + (f" matching '{q or scope}'" if (q or scope) else "") + ")")
307
+ return 0
308
+ for l in sorted(rows, key=lambda x: x["id"]):
309
+ sc = f" [scope:{l['scope']}]" if l.get("scope") else ""
310
+ print(f"{l['id']} ({l['status']}, x{l['recurrence']}){sc}: {l['text']}")
311
+ return 0
312
+
313
+
314
+ def cmd_prune(root, args):
315
+ data = _load(root)
316
+ dropped = _auto_prune(data)
317
+ _save(root, data)
318
+ print(f"Pruned {len(dropped)} stale candidate(s): {', '.join(dropped) if dropped else '—'}")
319
+ return 0
320
+
321
+
322
+ def cmd_status(root, args):
323
+ data = _load(root)
324
+ counts = {"confirmed": 0, "candidate": 0, "quarantined": 0}
325
+ for l in data["lessons"]:
326
+ counts[l["status"]] = counts.get(l["status"], 0) + 1
327
+ total = len(data["lessons"])
328
+ print(f"lessons: {total} total | confirmed={counts['confirmed']} candidate={counts['candidate']} quarantined={counts['quarantined']}")
329
+ return 0
330
+
331
+
332
+ def main(argv=None):
333
+ p = argparse.ArgumentParser(prog="lessons.py", description="Deterministic lessons bookkeeping for spec-driven.")
334
+ p.add_argument("--root", default=".", help="Project root containing .specs/ (default: current dir)")
335
+ sub = p.add_subparsers(dest="cmd", required=True)
336
+
337
+ sp = sub.add_parser("init", help="Create empty store + rendered file")
338
+ sp.set_defaults(fn=cmd_init)
339
+
340
+ sp = sub.add_parser("add", help="Record a grounded lesson")
341
+ sp.add_argument("--feature", required=True)
342
+ sp.add_argument("--signal", required=True, choices=sorted(SIGNALS))
343
+ sp.add_argument("--source", required=True, help="file:line / AC id / mutant id / SPEC_DEVIATION ref")
344
+ sp.add_argument("--text", required=True, help="One terse, actionable sentence")
345
+ sp.add_argument("--scope", default="", help="Optional: path/layer/tag for retrieval filtering")
346
+ sp.set_defaults(fn=cmd_add)
347
+
348
+ sp = sub.add_parser("penalize", help="Mark a confirmed lesson as failed-when-applied")
349
+ sp.add_argument("--id", required=True)
350
+ sp.set_defaults(fn=cmd_penalize)
351
+
352
+ sp = sub.add_parser("list", help="Print lessons for loading")
353
+ sp.add_argument("--status", default="confirmed", choices=["confirmed", "candidate", "quarantined", "all"])
354
+ sp.add_argument("--query", default="", help="Substring filter on lesson text")
355
+ sp.add_argument("--scope", default="", help="Substring filter on scope")
356
+ sp.set_defaults(fn=cmd_list)
357
+
358
+ sp = sub.add_parser("prune", help="Drop stale uncorroborated candidates")
359
+ sp.set_defaults(fn=cmd_prune)
360
+
361
+ sp = sub.add_parser("status", help="Print counts")
362
+ sp.set_defaults(fn=cmd_status)
363
+
364
+ args = p.parse_args(argv)
365
+ root = os.path.abspath(args.root)
366
+ return args.fn(root, args)
367
+
368
+
369
+ if __name__ == "__main__":
370
+ raise SystemExit(main())
@@ -0,0 +1,36 @@
1
+ # spec-loop
2
+
3
+ Milestone-loop runner: drives every `.specs/` artifact to completion — ROADMAP → milestones → tasks → verification gates → atomic commits → STATE.md.
4
+
5
+ | Field | Value |
6
+ |-------|-------|
7
+ | Version | 1.0.0 |
8
+ | Trigger | "execute the specs", "run the plan", "loop the milestones" |
9
+ | PT trigger | "executar as specs", "rodar o plano", "começar a executar", "siga o roadmap" |
10
+
11
+ **Use when** a tlc-spec-driven project has pending `tasks.md` items and you want autonomous execution, milestone by milestone, with verification gates and atomic commits.
12
+
13
+ **Do not use for** creating the plan itself — run [`spec-driven`](../spec-driven/README.md) first to produce the `.specs/` artifacts (ROADMAP.md, tasks.md, design.md, context.md), then hand execution to this skill.
14
+
15
+ ## The loop
16
+
17
+ ```
18
+ ROADMAP.md → pending milestones (M0..Mn)
19
+ → feature tasks.md → atomic task
20
+ → execute → verify ("Verificar:" criteria) → atomic commit → STATE.md
21
+ → milestone gate (exit criteria) → next milestone
22
+ → final spec.md acceptance (Success Criteria) → project done
23
+ ```
24
+
25
+ One task at a time, in tasks.md order — never skip a "Depends on" edge. Verification runs for real: red means fix or stop, never advance. STATE.md is updated after every task.
26
+
27
+ ## States
28
+
29
+ - `⬜ planned` → `▶️ in progress` → `✅ done` | `🛑 blocked` (reason + evidence)
30
+ - Milestone done only with green exit criteria; project done only with the spec.md final acceptance.
31
+
32
+ ## Resume
33
+
34
+ Interrupted? Read STATE.md + recent commits → continue from the first non-done task. Never re-run tasks already done.
35
+
36
+ See [SKILL.md](SKILL.md) for the full process (rules, gates, escalation, delegation).
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: spec-loop
3
+ description: |
4
+ Drive the execution of every .specs/ artifact to completion — milestone by
5
+ milestone, task by task, with verification gates, atomic commits and STATE.md
6
+ progress tracking. Use when user says "execute the specs", "run the plan",
7
+ "loop the milestones", "executar as specs", "rodar o plano", "começar a
8
+ executar", "siga o roadmap", or when a tlc-spec-driven project has pending
9
+ tasks.md items and the user wants autonomous execution.
10
+ license: CC-BY-4.0
11
+ metadata:
12
+ author: runecraft
13
+ version: 1.0.0
14
+ ---
15
+
16
+ # Spec Loop
17
+
18
+ Executa todos os artefatos `.specs/` até a conclusão: ROADMAP → milestones → tasks → verificação → gates → commits atômicos → STATE.md.
19
+
20
+ ## Loop principal
21
+
22
+ ```
23
+ ROADMAP.md → milestones pendentes (M0..Mn)
24
+ → tasks.md da feature → tarefa atômica
25
+ → executar → verificar (critério "Verificar:") → commit atômico → STATE.md
26
+ → gate do milestone (exit criteria) → próximo milestone
27
+ → acceptance final do spec.md (Success Criteria) → projeto done
28
+ ```
29
+
30
+ ## Regras (ordem de prioridade)
31
+
32
+ 1. **Ler antes de tocar**: ROADMAP.md (milestones), tasks.md (tarefas e dependências), design.md (tabelas de mapeamento são fonte da verdade), context.md (decisões AD-* são fechadas).
33
+ 2. **Uma tarefa por vez**, na ordem do tasks.md; nunca pule uma dependência (campo "Depends on").
34
+ 3. **Verificar antes de avançar**: toda tarefa tem critérios `**Verificar:**` — rode-os de fato; verificação vermelha = corrigir ou parar, nunca avançar.
35
+ 4. **Commits atômicos** por tarefa concluída; mensagem com o REQ-ID/ID da tarefa quando existir.
36
+ 5. **STATE.md atualizado após cada tarefa** (done/blocked + riscos novos observados).
37
+ 6. **Safety valve**: se uma tarefa revelar >5 passos inesperados ou novas dependências → PARE e estenda tasks.md antes de continuar.
38
+ 7. **Escalação**: blocker real → pare e reporte ao usuário com evidência; nenhuma decisão AD-* muda silenciosamente.
39
+ 8. **Gates de milestone**: exit criteria do ROADMAP + grep guards (design.md §8) verdes antes de marcar o milestone como done.
40
+ 9. **Escopo cirúrgico**: só arquivos da tarefa; nunca edite clones de referência, repos externos ou histórico git.
41
+ 10. **Delegação opcional**: fatias mecânicas extensas → fighter/ranger; review de milestones → cleric. O loop permanece no agente principal.
42
+
43
+ ## Estados
44
+
45
+ - `⬜ planned` → `▶️ in progress` → `✅ done` | `🛑 blocked` (motivo + evidência)
46
+ - Milestone done somente com exit criteria verdes.
47
+ - Projeto done somente com a acceptance final do spec.md.
48
+
49
+ ## Retomada
50
+
51
+ Se interrompido: leia STATE.md + últimos commits → continue da primeira tarefa não-done. Nunca re-execute tarefas já done.
52
+
53
+ ## Exemplo (Squad)
54
+
55
+ ```
56
+ ROADMAP: M0 → M1 → {M2, M3} → M4 · M5 ∥ M4
57
+ 1. M0: T-M0-01 (repo init) → verificar git status → commit → STATE.md
58
+ 2. M0: T-M0-02..05 → gate M0 (exit criteria) → M0 ✅
59
+ 3. M1: T-M1-01..12 (sweep) → full suite + grep guards → M1 ✅
60
+ ...até a acceptance final de spec.md
61
+ ```
@@ -0,0 +1,13 @@
1
+ # test-driven-development
2
+
3
+ Drive development with tests. Fail first, make them pass, then refactor.
4
+
5
+ | Field | Value |
6
+ |-------|-------|
7
+ | Version | 1.0.0 |
8
+ | Trigger | `/test`, "TDD", "prove it", "80/15/5 pyramid", "Beyonce Rule" |
9
+ | PT trigger | `/teste`, "testes primeiro", "provar com teste" |
10
+
11
+ **Do not use for** production incident triage (use `/debug`) or cosmetic-only changes with no behavioral surface.
12
+
13
+ See [SKILL.md](SKILL.md) for the full process, and [references/testing-patterns.md](../../references/testing-patterns.md) for common testing patterns and the 80/15/5 pyramid.