@deftai/directive-content 0.55.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 (250) hide show
  1. package/.agents/skills/deft/SKILL.md +6 -0
  2. package/.agents/skills/deft-directive-article-review/SKILL.md +11 -0
  3. package/.agents/skills/deft-directive-build/SKILL.md +10 -0
  4. package/.agents/skills/deft-directive-cost/SKILL.md +12 -0
  5. package/.agents/skills/deft-directive-debug/SKILL.md +13 -0
  6. package/.agents/skills/deft-directive-gh-arch/SKILL.md +11 -0
  7. package/.agents/skills/deft-directive-gh-slice/SKILL.md +10 -0
  8. package/.agents/skills/deft-directive-glossary/SKILL.md +10 -0
  9. package/.agents/skills/deft-directive-interview/SKILL.md +9 -0
  10. package/.agents/skills/deft-directive-pre-pr/SKILL.md +9 -0
  11. package/.agents/skills/deft-directive-refinement/SKILL.md +10 -0
  12. package/.agents/skills/deft-directive-release/SKILL.md +12 -0
  13. package/.agents/skills/deft-directive-review-cycle/SKILL.md +10 -0
  14. package/.agents/skills/deft-directive-setup/SKILL.md +10 -0
  15. package/.agents/skills/deft-directive-swarm/SKILL.md +10 -0
  16. package/.agents/skills/deft-directive-sync/SKILL.md +9 -0
  17. package/.agents/skills/deft-directive-triage/SKILL.md +9 -0
  18. package/.agents/skills/deft-directive-write-skill/SKILL.md +9 -0
  19. package/LICENSE.md +22 -0
  20. package/QUICK-START.md +167 -0
  21. package/UPGRADING.md +517 -0
  22. package/coding/build-output.md +28 -0
  23. package/coding/coding.md +235 -0
  24. package/coding/debugging.md +110 -0
  25. package/coding/holzmann.md +96 -0
  26. package/coding/hygiene.md +127 -0
  27. package/coding/security.md +158 -0
  28. package/coding/testing.md +162 -0
  29. package/coding/toolchain.md +44 -0
  30. package/commands.md +300 -0
  31. package/context/context.md +57 -0
  32. package/context/deterministic-split.md +67 -0
  33. package/context/examples.md +26 -0
  34. package/context/fractal-summaries.md +69 -0
  35. package/context/long-horizon.md +46 -0
  36. package/context/spec-deltas.md +177 -0
  37. package/context/tool-design.md +34 -0
  38. package/context/working-memory.md +62 -0
  39. package/contracts/boundary-maps.md +65 -0
  40. package/contracts/deterministic-questions.md +59 -0
  41. package/contracts/hierarchy.md +77 -0
  42. package/conventions/content-manifest.json +399 -0
  43. package/conventions/machine-generated-banner.md +130 -0
  44. package/conventions/references.md +120 -0
  45. package/conventions/rule-ownership.json +382 -0
  46. package/conventions/task-caching.md +43 -0
  47. package/conventions/vbrief-filenames.md +70 -0
  48. package/deployments/README.md +37 -0
  49. package/deployments/agentuity/README.md +138 -0
  50. package/deployments/agentuity/via-cli.md +380 -0
  51. package/deployments/agentuity/via-cloud.md +425 -0
  52. package/deployments/agentuity/via-github-actions.md +664 -0
  53. package/deployments/agentuity/via-gravity-network.md +606 -0
  54. package/deployments/agentuity/via-vpc.md +607 -0
  55. package/deployments/aws/README.md +38 -0
  56. package/deployments/aws/via-app-runner.md +612 -0
  57. package/deployments/aws/via-ecs-fargate.md +561 -0
  58. package/deployments/aws/via-elastic-beanstalk.md +628 -0
  59. package/deployments/aws/via-lambda.md +649 -0
  60. package/deployments/azure/README.md +37 -0
  61. package/deployments/azure/via-aks.md +390 -0
  62. package/deployments/azure/via-app-service.md +564 -0
  63. package/deployments/azure/via-container-apps.md +599 -0
  64. package/deployments/azure/via-functions.md +552 -0
  65. package/deployments/cloud-gov/README.md +63 -0
  66. package/deployments/cloud-gov/agents/compliance-docs.md +154 -0
  67. package/deployments/cloud-gov/agents.md +39 -0
  68. package/deployments/cloud-gov/cicd.md +64 -0
  69. package/deployments/cloud-gov/deployment.md +150 -0
  70. package/deployments/cloud-gov/logging.md +43 -0
  71. package/deployments/cloud-gov/manifest.md +121 -0
  72. package/deployments/cloud-gov/overview.md +58 -0
  73. package/deployments/cloud-gov/security.md +46 -0
  74. package/deployments/cloud-gov/services.md +72 -0
  75. package/deployments/cloud-gov/upstream/README.md +18 -0
  76. package/deployments/cloudflare/README.md +33 -0
  77. package/deployments/cloudflare/via-dashboard.md +83 -0
  78. package/deployments/cloudflare/via-git.md +90 -0
  79. package/deployments/cloudflare/via-github-actions.md +185 -0
  80. package/deployments/cloudflare/via-terraform.md +157 -0
  81. package/deployments/cloudflare/via-wrangler.md +165 -0
  82. package/deployments/fly-io/README.md +37 -0
  83. package/deployments/fly-io/via-dockerfile.md +648 -0
  84. package/deployments/fly-io/via-flyctl.md +653 -0
  85. package/deployments/fly-io/via-github-actions.md +695 -0
  86. package/deployments/fly-io/via-multi-region.md +598 -0
  87. package/deployments/google/README.md +34 -0
  88. package/deployments/google/via-app-engine.md +42 -0
  89. package/deployments/google/via-cloud-functions.md +23 -0
  90. package/deployments/google/via-cloud-run.md +330 -0
  91. package/deployments/google/via-gke.md +23 -0
  92. package/deployments/netlify/README.md +99 -0
  93. package/deployments/netlify/via-cli.md +17 -0
  94. package/deployments/netlify/via-functions.md +19 -0
  95. package/deployments/netlify/via-git.md +25 -0
  96. package/deployments/vercel/README.md +90 -0
  97. package/deployments/vercel/via-api.md +16 -0
  98. package/deployments/vercel/via-cli.md +17 -0
  99. package/deployments/vercel/via-git.md +24 -0
  100. package/docs/BROWNFIELD.md +179 -0
  101. package/docs/getting-started.md +137 -0
  102. package/docs/good-agents-md.md +137 -0
  103. package/events/README.md +89 -0
  104. package/events/event-record.schema.json +26 -0
  105. package/events/registry.json +166 -0
  106. package/events/registry.schema.json +71 -0
  107. package/glossary.md +145 -0
  108. package/incidents/README.md +81 -0
  109. package/incidents/_template.md +38 -0
  110. package/interfaces/cli.md +104 -0
  111. package/interfaces/rest.md +212 -0
  112. package/interfaces/tui.md +242 -0
  113. package/interfaces/web.md +123 -0
  114. package/languages/6502-DASM.md +132 -0
  115. package/languages/c.md +235 -0
  116. package/languages/commands.md +23 -0
  117. package/languages/cpp.md +132 -0
  118. package/languages/csharp.md +259 -0
  119. package/languages/dart.md +183 -0
  120. package/languages/delphi.md +218 -0
  121. package/languages/elixir.md +208 -0
  122. package/languages/go.md +78 -0
  123. package/languages/java.md +278 -0
  124. package/languages/javascript.md +163 -0
  125. package/languages/julia.md +175 -0
  126. package/languages/kotlin.md +193 -0
  127. package/languages/markdown.md +168 -0
  128. package/languages/mermaid.md +146 -0
  129. package/languages/officejs.md +392 -0
  130. package/languages/python.md +209 -0
  131. package/languages/r.md +163 -0
  132. package/languages/rust.md +216 -0
  133. package/languages/sql.md +216 -0
  134. package/languages/swift.md +153 -0
  135. package/languages/typescript.md +132 -0
  136. package/languages/vba.md +279 -0
  137. package/languages/vhdl.md +180 -0
  138. package/languages/visual-basic.md +151 -0
  139. package/languages/zig.md +196 -0
  140. package/meta/SOUL.md +27 -0
  141. package/meta/code-field.md +44 -0
  142. package/meta/morals.md +40 -0
  143. package/meta/philosophy.md +39 -0
  144. package/meta/project.md +49 -0
  145. package/meta/ralph.md +223 -0
  146. package/meta/security.md +80 -0
  147. package/meta/versioning.md +326 -0
  148. package/package.json +22 -0
  149. package/packs/lessons/lessons-pack-0.1.json +553 -0
  150. package/packs/patterns/patterns-pack-0.1.json +57 -0
  151. package/packs/rules/rules-pack-0.1.json +4767 -0
  152. package/packs/skills/skills-pack-0.1.json +262 -0
  153. package/packs/strategies/strategies-pack-0.1.json +167 -0
  154. package/packs/swarm-spec/swarm-spec-pack-0.1.json +17 -0
  155. package/patterns/executor-layer-credentials.md +227 -0
  156. package/patterns/llm-app.md +156 -0
  157. package/patterns/multi-agent.md +278 -0
  158. package/patterns/prompt-assembly-layer-ordering.md +154 -0
  159. package/patterns/role-as-overlay.md +179 -0
  160. package/platforms/2600.md +137 -0
  161. package/platforms/unity.md +329 -0
  162. package/references/composer-skill-porting.md +152 -0
  163. package/references/cost-models.md +163 -0
  164. package/references/ip-risk.md +246 -0
  165. package/references/plain-english-ux.md +275 -0
  166. package/resilience/context-pruning.md +67 -0
  167. package/resilience/continue-here.md +62 -0
  168. package/scm/changelog.md +276 -0
  169. package/scm/git.md +139 -0
  170. package/scm/github.md +265 -0
  171. package/secrets/.gitkeep +0 -0
  172. package/skills/deft-build/SKILL.md +20 -0
  173. package/skills/deft-directive-article-review/SKILL.md +156 -0
  174. package/skills/deft-directive-build/SKILL.md +302 -0
  175. package/skills/deft-directive-cost/SKILL.md +201 -0
  176. package/skills/deft-directive-debug/SKILL.md +140 -0
  177. package/skills/deft-directive-decompose/SKILL.md +96 -0
  178. package/skills/deft-directive-gh-arch/SKILL.md +160 -0
  179. package/skills/deft-directive-gh-slice/SKILL.md +199 -0
  180. package/skills/deft-directive-glossary/SKILL.md +118 -0
  181. package/skills/deft-directive-interview/SKILL.md +528 -0
  182. package/skills/deft-directive-pre-pr/SKILL.md +131 -0
  183. package/skills/deft-directive-probe/SKILL.md +127 -0
  184. package/skills/deft-directive-refinement/SKILL.md +403 -0
  185. package/skills/deft-directive-release/SKILL.md +266 -0
  186. package/skills/deft-directive-review-cycle/SKILL.md +401 -0
  187. package/skills/deft-directive-setup/SKILL.md +717 -0
  188. package/skills/deft-directive-swarm/SKILL.md +989 -0
  189. package/skills/deft-directive-sync/SKILL.md +288 -0
  190. package/skills/deft-directive-triage/SKILL.md +137 -0
  191. package/skills/deft-directive-write-skill/SKILL.md +169 -0
  192. package/skills/deft-interview/SKILL.md +16 -0
  193. package/skills/deft-pre-pr/SKILL.md +16 -0
  194. package/skills/deft-review-cycle/SKILL.md +16 -0
  195. package/skills/deft-roadmap-refresh/SKILL.md +16 -0
  196. package/skills/deft-setup/SKILL.md +20 -0
  197. package/skills/deft-swarm/SKILL.md +16 -0
  198. package/skills/deft-sync/SKILL.md +16 -0
  199. package/strategies/README.md +83 -0
  200. package/strategies/artifact-guards.md +85 -0
  201. package/strategies/bdd.md +115 -0
  202. package/strategies/brownfield.md +7 -0
  203. package/strategies/discuss.md +129 -0
  204. package/strategies/emit-hints.md +69 -0
  205. package/strategies/enterprise.md +193 -0
  206. package/strategies/interview.md +551 -0
  207. package/strategies/map.md +179 -0
  208. package/strategies/probe.md +151 -0
  209. package/strategies/rapid.md +155 -0
  210. package/strategies/research.md +155 -0
  211. package/strategies/roadmap.md +9 -0
  212. package/strategies/speckit.md +437 -0
  213. package/strategies/v0-20-contract.md +134 -0
  214. package/strategies/yolo.md +169 -0
  215. package/swarm/swarm.md +300 -0
  216. package/templates/COST-ESTIMATE.md +114 -0
  217. package/templates/PULL_REQUEST_TEMPLATE.md +35 -0
  218. package/templates/agent-prompt-preamble.md +409 -0
  219. package/templates/agents-entry.md +211 -0
  220. package/templates/agents-entry.placeholders.md +75 -0
  221. package/templates/embed.go +20 -0
  222. package/templates/embed_test.go +36 -0
  223. package/templates/make-spec-example.md +9 -0
  224. package/templates/make-spec.md +246 -0
  225. package/templates/project.md.template +52 -0
  226. package/templates/specification.md +1 -0
  227. package/templates/swarm-greptile-poller-prompt.md +556 -0
  228. package/templates/user.md.template +31 -0
  229. package/tools/RWLDL.md +80 -0
  230. package/tools/greptile.md +141 -0
  231. package/tools/installer.md +23 -0
  232. package/tools/taskfile-migration.md +32 -0
  233. package/tools/taskfile.md +185 -0
  234. package/tools/telemetry.md +285 -0
  235. package/vbrief/schemas/cache-meta.schema.json +137 -0
  236. package/vbrief/schemas/candidates.schema.json +130 -0
  237. package/vbrief/schemas/codebase-map.schema.json +213 -0
  238. package/vbrief/schemas/lessons-pack.schema.json +134 -0
  239. package/vbrief/schemas/patterns-pack.schema.json +84 -0
  240. package/vbrief/schemas/rules-pack.schema.json +105 -0
  241. package/vbrief/schemas/skills-pack.schema.json +94 -0
  242. package/vbrief/schemas/slices.schema.json +87 -0
  243. package/vbrief/schemas/strategies-pack.schema.json +89 -0
  244. package/vbrief/schemas/swarm-spec-pack.schema.json +84 -0
  245. package/vbrief/schemas/vbrief-core.schema.json +1022 -0
  246. package/vbrief/vbrief.md +684 -0
  247. package/verification/integration.md +76 -0
  248. package/verification/plan-checking.md +85 -0
  249. package/verification/uat.md +60 -0
  250. package/verification/verification.md +117 -0
@@ -0,0 +1,553 @@
1
+ {
2
+ "pack": "lessons-pack-0.1",
3
+ "version": "0.1",
4
+ "generated_from": "meta/lessons.md",
5
+ "lessons": [
6
+ {
7
+ "id": "context-engineering-2026-03",
8
+ "title": "Context Engineering (2026-03)",
9
+ "date": "2026-03",
10
+ "issue_refs": [],
11
+ "tags": [
12
+ "context",
13
+ "lifecycle"
14
+ ],
15
+ "source": "Anthropic, \"Effective Context Engineering for AI Agents\"",
16
+ "body": "**Source:** Anthropic, \"Effective Context Engineering for AI Agents\"\n\n**Key insight:** Context rot is real \u2014 more tokens \u2260 better performance. Every low-signal token actively degrades output quality. The goal is the smallest set of high-signal tokens.\n\n**What was added:** `context/` directory with five guides (context.md, working-memory.md, long-horizon.md, tool-design.md, examples.md) covering Write/Select/Compress/Isolate strategies, vBRIEF integration for structured scratchpads and checkpoints, and surgical edits to main.md and REFERENCES.md for integration."
17
+ },
18
+ {
19
+ "id": "pr-review-process-2026-03",
20
+ "title": "PR Review Process (2026-03)",
21
+ "date": "2026-03",
22
+ "issue_refs": [],
23
+ "tags": [
24
+ "release",
25
+ "review-cycle",
26
+ "github"
27
+ ],
28
+ "source": "Bootstrap parity PR (#83) \u2014 13-round Greptile review cycle on `fix/45-bootstrap-parity`",
29
+ "body": "**Source:** Bootstrap parity PR (#83) \u2014 13-round Greptile review cycle on `fix/45-bootstrap-parity`\n\n**1. Review bots post to two channels \u2014 both MUST be checked before declaring clean**\n\nGitHub review bots (e.g. Greptile) post inline diff threads (returned by MCP `get_review_comments`) AND a separate \"Comments Outside Diff\" section in the rolling summary comment (NOT returned by MCP). A PR MUST NOT be declared review-clean until both sources are verified. The outside-diff check MUST use `do_not_summarize_output: true` to prevent summarizers silently dropping that section.\n\n**2. Wide PRs have non-linear review costs**\n\nA PR touching CLI code, TUI code, prose documents, and tests simultaneously creates combinatorial review exposure \u2014 each change can generate parity issues in other areas. A 4-surface PR does not take 4\u00d7 the review effort; in practice it took 13 rounds. SHOULD split changes into focused PRs: code changes separate from prose/instructional document changes.\n\n**3. Instructional documents SHOULD be read as a consumer before opening a PR**\n\nProse files (SKILL.md, strategy files, README sections) have flow correctness that diffs do not capture \u2014 missing bridging instructions, wrong step ordering, and one-question-rule violations are all invisible in a diff view but immediately apparent when read linearly. Before opening a PR that modifies instructional documents, SHOULD read them from start to finish as the agent or user following them would.\n\n**4. Lint fixes MUST NOT weaken test fault detection without a conscious decision**\n\nWhen a linter demands an explicit parameter (e.g. `strict=` on `zip()`), the chosen value has semantic meaning. In a content-validation test, `strict=True` is more defensive \u2014 a malformed row causes an immediate, obvious failure. `strict=False` silently drops mismatched data. MUST evaluate whether a lint fix weakens fault detection; if it does, prefer the more defensive value. Satisfying a linter at the cost of test quality is not a net improvement.\n\n**5. CHANGELOG promotion is a release step, not a PR step \u2014 treat them as distinct**\n\nThe PR checklist correctly guards `[Unreleased]` entries during review. But promoting `[Unreleased]` \u2192 `[X.Y.Z]` (and updating the comparison links) is a **post-merge release step** that happens at tag time, not PR time. These two steps are easy to conflate and the promotion is easy to forget when the tag and push happen in rapid succession. Until `task release` is implemented (tracked in issue #74), the release sequence MUST be: (1) promote CHANGELOG, (2) commit, (3) tag, (4) push tag. MUST NOT tag before the CHANGELOG promotion commit is on the target branch."
30
+ },
31
+ {
32
+ "id": "toolchain-validation-gate-2026-03",
33
+ "title": "Toolchain Validation Gate (2026-03)",
34
+ "date": "2026-03",
35
+ "issue_refs": [],
36
+ "tags": [
37
+ "ci",
38
+ "release"
39
+ ],
40
+ "source": "Issue #106 \u2014 full DEFT workflow completed on iOS/Swift project without Xcode or task installed",
41
+ "body": "**Source:** Issue #106 \u2014 full DEFT workflow completed on iOS/Swift project without Xcode or task installed\n\n**The agent completed interview \u2192 PRD \u2192 SPEC \u2192 implementation without verifying the toolchain**\n\nAn agent ran the full DEFT interview (selecting strict release gate: unit + UI + accessibility + performance tests), scaffolded and implemented an iOS app, ran only `swift test` (4 tests, no coverage), and declared success. Neither `task` nor Xcode were available in the session. The quality gates the user explicitly chose were never enforceable.\n\nExisting directives (`! Run all relevant checks`, `\u2297 Claim checks passed without running them`) did not prevent this \u2014 they govern *execution*, not *prerequisite verification*. There was no rule requiring the agent to verify that the tools needed to enforce quality gates existed before implementation began.\n\n**Before beginning any implementation phase, MUST verify that the complete toolchain required for that phase is installed and functional. If the build or test toolchain is unavailable, stop and report \u2014 do not proceed. Quality gates chosen during the interview are meaningless if the tools to enforce them are absent.**"
42
+ },
43
+ {
44
+ "id": "build-script-output-validation-2026-03",
45
+ "title": "Build Script Output Validation (2026-03)",
46
+ "date": "2026-03",
47
+ "issue_refs": [],
48
+ "tags": [
49
+ "agent-orchestration"
50
+ ],
51
+ "source": "Issue #105 \u2014 silent `dist/` failure in a Chrome extension build",
52
+ "body": "**Source:** Issue #105 \u2014 silent `dist/` failure in a Chrome extension build\n\n**AI edits to build scripts can silently drop asset copy steps \u2014 builds succeed but `dist/` is stale**\n\nAn AI edit to `build.mjs` dropped a `copyFileSync` call for `manifest.json`. The build ran without error, but `dist/manifest.json` was stale \u2014 missing `content_scripts`, `storage`, and `host_permissions`. The extension overlay silently failed with no visible error.\n\nExisting directives (`! Run all relevant checks`, `! Call out risk when touching build systems`) did not prevent this \u2014 they are process rules (run checks) not output validation rules (verify what was produced). A build exiting 0 is not proof that `dist/` is correct.\n\n**When modifying a build script, MUST verify that expected output artifacts exist and are structurally valid after the build runs. Non-compiled assets that bundlers don't track (manifests, configs, extension metadata) are especially at risk of silent omission.**"
53
+ },
54
+ {
55
+ "id": "multi-agent-orchestration-via-oz-cli-2026-04",
56
+ "title": "Multi-Agent Orchestration via Oz CLI (2026-04)",
57
+ "date": "2026-04",
58
+ "issue_refs": [],
59
+ "tags": [
60
+ "agent-orchestration",
61
+ "swarm",
62
+ "review-cycle"
63
+ ],
64
+ "source": "Two parallel local agents on roadmap items \u2014 PR #149 (strategy consolidation) and PR #150 (content fixes)",
65
+ "body": "**Source:** Two parallel local agents on roadmap items \u2014 PR #149 (strategy consolidation) and PR #150 (content fixes)\n\n**1. `oz agent run --mcp` with UUID MUST NOT be used from standalone terminals**\n\nThe `--mcp` flag with a Warp-configured MCP server UUID requires Warp app context (OAuth tokens, session state). Spawning `oz agent run` in a standalone PowerShell window via `Start-Process` fails with \"Failed to start MCP servers\". Agents launched outside Warp MUST use `gh` CLI for GitHub operations instead of MCP.\n\n**2. Agent prompts MUST lead with explicit task directives, not context**\n\nAn agent given a prompt starting with \"You are working in the deft directive repository...\" followed by task instructions treated the entire message as passive context, read the directives, and stopped without doing any work. The same tasks given with a prompt starting \"TASK: You must complete 5 documentation fixes...\" executed correctly. When prompting autonomous agents via `oz agent run --prompt`, the first line MUST be an imperative action statement. Context and constraints SHOULD follow the task.\n\n**3. Agents SHOULD be isolated in separate git worktrees for parallel work**\n\nTwo agents working the same repo on different branches need separate working directories. Git worktrees (`git worktree add`) provide branch isolation without full clones. Each worktree gets its own launch script. MUST ensure no file overlap between agents' assigned tasks to avoid merge conflicts.\n\n**4. Review cycle completion is not guaranteed \u2014 monitor agent MUST be prepared to take over**\n\nAgent 1 created its PR and stopped before running the Greptile review cycle (the prompt's Step 9). Agent 2 ran 4 autonomous review rounds successfully. The difference was prompt structure. A monitoring agent MUST check whether each spawned agent completed the full workflow and be prepared to finish incomplete steps."
66
+ },
67
+ {
68
+ "id": "parallel-agent-swarm-first-full-run-2026-04",
69
+ "title": "Parallel Agent Swarm \u2014 First Full Run (2026-04)",
70
+ "date": "2026-04",
71
+ "issue_refs": [],
72
+ "tags": [
73
+ "swarm",
74
+ "agent-orchestration",
75
+ "review-cycle"
76
+ ],
77
+ "source": "4-agent swarm on Phase 1 roadmap items \u2014 PRs #154, #155, #156, #157 (14 issues closed)",
78
+ "body": "**Source:** 4-agent swarm on Phase 1 roadmap items \u2014 PRs #154, #155, #156, #157 (14 issues closed)\n\n**1. ~~`oz agent run` launches CLOUD agents~~ \u2014 CORRECTION: `oz agent run` is LOCAL; `oz agent run-cloud` is the cloud path**\n\n\u26a0\ufe0f **This lesson was incorrect.** Warp confirmed: `oz agent run` runs agents **locally** on the user's machine (supports `--cwd`, `--profile`, `--mcp`; gets codebase indexing and Warp Drive rules). `oz agent run-cloud` runs agents **remotely** on cloud VMs with no local context.\n\nThe original lesson was written after the 4-agent swarm (PRs #154\u2013#157) where agents appeared to lose MCP and local context. The actual cause was not that `oz agent run` routes to cloud. **Corrected rule:** `oz agent run --cwd <path> --prompt \"...\"` is the PREFERRED automated local launch path. MUST use `oz agent run-cloud` only when cloud execution is explicitly desired. MUST NOT conflate the two commands. (#172)\n\n**2. Warp terminal tabs MUST NOT be assumed openable programmatically**\n\nThere is no API or CLI command to open a new Warp terminal tab from an agent or script. When the user said \"launch\", the monitor agent silently used `Start-Process` to open standalone PowerShell windows instead of asking the user to open Warp tabs manually. The user expected Warp tabs with full context. Agents MUST present the tradeoffs (local vs. cloud vs. standalone) and let the user choose before launching.\n\n**3. Sequential merging of PRs with shared append-only files causes rebase cascades**\n\nCHANGELOG.md and SPECIFICATION.md are \"append-only\" shared files \u2014 each agent adds entries without editing existing content. However, when PRs are merged sequentially, each merge changes the file at the same insertion point, causing merge conflicts for remaining PRs. Merging #154 conflicted #155 and #157; merging #155 conflicted #157 again. Each conflict required rebase \u2192 push \u2192 wait for checks (~3 min). Four PRs required 3 rebase cycles. SHOULD merge all PRs in rapid succession or rebase all remaining PRs before starting merges.\n\n**4. File-overlap audit MUST check transitive file touches, not just primary scope**\n\nThe file-overlap audit assigned `skills/deft-review-cycle/SKILL.md` exclusively to Agent 3. But Agent 2 (enforcement rules, #123) added a `/deft:change` verification step to the same file as part of strengthening the review cycle's Phase 1 audit. This was a transitive touch \u2014 the enforcement task's acceptance criteria required changes to a file in another agent's scope. The overlap audit MUST trace each task's acceptance criteria to specific files, not just the task's primary scope.\n\n**5. SPECIFICATION.md task status MUST be verified before assigning work**\n\nThe original Agent 2 was scoped to #31 and #50 (strategy consolidation). Both had spec tasks (t1.4.1, t1.4.2) marked `[completed]` in SPECIFICATION.md, but the ROADMAP.md still listed them as open. Verifying the spec caught this before agents wasted time reimplementing done work. The select phase MUST cross-reference ROADMAP.md against SPECIFICATION.md status before assigning.\n\n**6. PR numbers don't match agent numbers \u2014 include agent ID in branch/PR naming**\n\nGitHub assigns PR numbers in creation order, which depends on which agent finishes first. Agent 2's PR became #154 while Agent 1's became #156. This caused confusion during monitoring and merging. Branch names SHOULD include the agent number (e.g. `agent1/fix/...`) or PR titles SHOULD include `[Agent N]` for traceability.\n\n**7. ~~Cloud agents~~ Agents stopped after PR creation \u2014 likely a prompt completeness issue, not a cloud limitation**\n\n\u26a0\ufe0f **Context correction:** The agents in this lesson were launched via `oz agent run` which (see corrected Lesson #1) is **local**, not cloud. The two-pass behavior was likely due to incomplete prompt instructions, not an inherent limitation of the execution environment.\n\nThe core lesson remains valid: when agents stop before completing the full workflow (PR + review cycle), the monitor MUST be prepared to complete the remaining steps. Ensure the prompt's STEP 6 (review cycle) instruction is explicit enough to prevent early termination \u2014 regardless of whether agents are local or cloud. (#172)"
79
+ },
80
+ {
81
+ "id": "option-a-oz-agent-run-context-limitations-2026-04",
82
+ "title": "Option A (oz agent run) Context Limitations (2026-04)",
83
+ "date": "2026-04",
84
+ "issue_refs": [],
85
+ "tags": [
86
+ "agent-orchestration",
87
+ "github",
88
+ "swarm"
89
+ ],
90
+ "source": "Issue #179 \u2014 live testing of `oz agent run` during swarm orchestration",
91
+ "body": "**Source:** Issue #179 \u2014 live testing of `oz agent run` during swarm orchestration\n\n**1. `oz agent run` does NOT receive global Warp Drive rules, MCP UUIDs, or auto-injected context**\n\nTesting revealed that `oz agent run` launched from the terminal does not automatically receive global Warp Drive rules (personal rules stored in Warp Drive > Personal > Rules), MCP servers via UUID, or Warp Drive notebooks/workflows. The only context an Option A agent gets is: `AGENTS.md` in the `--cwd` directory, the agent profile specified with `--profile`, and codebase indexing (non-blocking, background). This makes Option A effectively as context-limited as cloud agents (`oz agent run-cloud`) \u2014 the only difference is execution location (local vs remote VM).\n\n**Option B (interactive Warp tab) is the correct choice for full local context** until a future Warp build with experimental orchestration support brings Option A to parity. Option B agents get full MCP, global rules, Warp Drive context, warm codebase indexing, and are interruptible mid-run.\n\n**2. Inline MCP JSON is a partial workaround for Option A but not zero-config**\n\nMCP servers can be passed via inline JSON instead of UUID: `--mcp '{\"github\": {\"url\": \"https://api.githubcopilot.com/mcp/\"}}'`. This works around the UUID proxy issue but requires knowing the MCP endpoint URL and managing auth separately. Not a substitute for Option B's zero-config MCP injection."
92
+ },
93
+ {
94
+ "id": "windows-file-editing-2026-03",
95
+ "title": "Windows File Editing (2026-03)",
96
+ "date": "2026-03",
97
+ "issue_refs": [],
98
+ "tags": [
99
+ "encoding",
100
+ "powershell",
101
+ "windows"
102
+ ],
103
+ "source": "ROADMAP.md edits during feat/agents-md-onboarding-54 \u2014 three sequential failures before clean write",
104
+ "body": "**Source:** ROADMAP.md edits during feat/agents-md-onboarding-54 \u2014 three sequential failures before clean write\n\n**1. CRLF line endings break multi-line edit_files searches \u2014 MUST verify line endings before batch edits**\n\nThe edit_files tool matches search strings against file content byte-for-byte. Files with Windows CRLF (\\r\\n) line endings will silently fail to match search strings that assume LF (\\n) only. On any Windows repo, MUST check line endings first ((Get-Content file -Raw) -match '\\r\\n'). If CRLF is present, fall back to PowerShell Get-Content -Raw / [System.IO.File]::WriteAllText for multi-line edits rather than batching multiple edit_files diffs.\n\n**2. PowerShell 5.1 Set-Content MUST NOT be used on UTF-8 files \u2014 not even with -Encoding UTF8**\n\nGet-Content | ... | Set-Content in PowerShell 5.1 defaults to the system ANSI code page (Windows-1252), silently mangling non-ASCII characters. But using `-Encoding UTF8` is also wrong: PowerShell 5.1's UTF8 encoding writes a BOM (byte-order mark, \\xEF\\xBB\\xBF) at byte 0, corrupting every special character across the entire file when re-read by tools that don't expect a BOM. MUST use `[System.IO.File]::WriteAllText(path, content, (New-Object System.Text.UTF8Encoding $false))` \u2014 the `$false` argument explicitly disables the BOM. Never use Set-Content for UTF-8 files on Windows PowerShell 5.1.\n\n**3. Markdown table rows in files with CRLF endings MUST be inserted via PowerShell, not edit_files**\n\nThe edit_files tool matches byte-for-byte. ROADMAP.md uses CRLF line endings. When inserting new table rows using edit_files, the mismatch between LF in the search/replace strings and CRLF in the file causes row content to be inserted with a doubled leading pipe (`|| #NNN |` instead of `| #NNN |`), shifting all columns right and breaking table alignment. This has surfaced in multiple sessions (PR #130, PR #173). When appending rows to the Open Issues Index or any markdown table in a CRLF file, MUST use PowerShell `[System.IO.File]` methods or a targeted regex replace \u2014 never edit_files for table row insertions. After any table edit, MUST verify row prefixes before committing: `Select-String -Path ROADMAP.md -Pattern '\\|\\| #[0-9]'` should return no matches.\n\n**4. PowerShell 5.1 `Set-Content` corrupts UTF-8 files in TWO ways \u2014 BOM removal alone is not a fix**\n\nWhen PS5.1 `Set-Content` (or `Set-Content -Encoding UTF8`) writes a UTF-8 file, it causes two distinct corruptions: (1) a BOM is prepended at byte 0, and (2) the entire file body is re-encoded from UTF-8 to Windows-1252 (ANSI), converting every multi-byte character to mojibake (for example em-dashes `\u2014` become `\u2014`, arrows `\u2192` become `\u2192`, and other Unicode symbols are mangled similarly). These are independent corruptions \u2014 stripping the BOM does NOT restore the body. A file can have no BOM and still be corrupted throughout.\n\nThe only correct recovery from `Set-Content` corruption is: (1) restore the original file bytes via `git checkout <ref> -- path/to/file` \u2014 MUST NOT use `git show <ref>:path/to/file` piped through PowerShell, as the pipeline silently re-decodes the bytes as Windows-1252 and re-introduces the mojibake; (2) read the restored file with `[System.IO.File]::ReadAllText(path, [System.Text.Encoding]::UTF8)`; (3) apply only the intended edits as string operations; (4) write back with `[System.IO.File]::WriteAllText(path, content, (New-Object System.Text.UTF8Encoding $false))`. MUST NOT attempt to fix `Set-Content` corruption by stripping just the BOM \u2014 the body will still be corrupted throughout."
105
+ },
106
+ {
107
+ "id": "review-cycle-monitoring-2026-04",
108
+ "title": "Review Cycle Monitoring (2026-04)",
109
+ "date": "2026-04",
110
+ "issue_refs": [],
111
+ "tags": [
112
+ "review-cycle",
113
+ "github",
114
+ "swarm"
115
+ ],
116
+ "source": "PR #173 review cycle \u2014 shell polling loop against static SHA failed to detect Greptile completion",
117
+ "body": "**Source:** PR #173 review cycle \u2014 shell polling loop against static SHA failed to detect Greptile completion\n\n**1. Greptile review completion MUST be polled via MCP `get_check_runs` against the PR head, not `gh api` with a static commit SHA**\n\nWhen a new commit is pushed while a polling loop is running, Greptile starts a fresh check run on the new head SHA. A shell `while` loop polling `gh api repos/{owner}/{repo}/commits/{old_sha}/check-runs` will never see completion because the completed run is on a different commit. MUST use MCP `pull_request_read` with `method: get_check_runs` \u2014 this always targets the current PR head regardless of how many commits have been pushed. Compare the `completed_at` field and `conclusion` to confirm the review is current and passed.\n\n**2. MUST NOT push any commit while Greptile review is in progress \u2014 even for unrelated changes**\n\nEvery push re-triggers Greptile on the new head. If additional fixes or improvements are identified while waiting for a review, stage them locally but hold the push until the review of the current head is complete and analyzed. \"Trivial\" or \"safe\" commits are not exceptions \u2014 the rule applies unconditionally. Violating this resets Greptile's clock and can create a loop where the bot never finishes reviewing a stable state. (#175, incident: PR #173)\n\n**3. Poll interval MUST include a genuine delay (\u226560 seconds) between `get_check_runs` calls**\n\nGreptile reviews typically take 3\u20137 minutes. Calling `get_check_runs` in rapid back-to-back succession (seconds apart) adds no information and creates noise in the conversation. MUST use a real sleep between polls \u2014 `Start-Sleep -Seconds 60` (PowerShell) or equivalent. Do NOT report \"polling again\" as if time has passed when it has not. (#175, incident: PR #173 monitoring loop)\n\n**4. After pushing, agent MUST autonomously poll for review updates without stopping to ask the user**\n\nAgents dispatched with a review cycle task (especially cloud/swarm agents) stopped after pushing fix commits and asked the user \"should I continue?\" or \"want me to check the review?\" This breaks the autonomous review/fix loop and requires human intervention for every cycle iteration. The review/fix loop in `skills/deft-review-cycle/SKILL.md` is designed to run to the exit condition (no P0/P1 issues, confidence > 3) without human intervention. After pushing, the agent MUST poll for the Greptile review update, analyze findings, and continue fixing \u2014 treating the entire loop as a single autonomous operation. (#184)"
118
+ },
119
+ {
120
+ "id": "greptile-re-review-on-rebase-force-push-2026-04",
121
+ "title": "Greptile Re-Review on Rebase Force-Push (2026-04)",
122
+ "date": "2026-04",
123
+ "issue_refs": [],
124
+ "tags": [
125
+ "review-cycle",
126
+ "swarm"
127
+ ],
128
+ "source": "Issue #207 \u2014 swarm merge cascade latency during PRs #154\u2013#157",
129
+ "body": "**Source:** Issue #207 \u2014 swarm merge cascade latency during PRs #154\u2013#157\n\n**1. Force-pushing a rebased branch triggers a FULL Greptile re-review, not an incremental diff**\n\nDuring merge cascades, each remaining PR must be rebased onto updated master and force-pushed. Each force-push triggers Greptile to re-review the entire PR from scratch \u2014 not just the rebase diff \u2014 because Greptile treats force-push as a new commit history. Expected latency is ~2-5 minutes per PR. For a cascade of N PRs, this adds (N-1) \u00d7 ~2-5 minutes of Greptile wait time on top of CI. MUST factor Greptile re-review latency into merge cascade planning.\n\n**2. Rebase-only force-pushes MAY be annotated with a PR comment for Greptile context**\n\nWhen a force-push contains no logic changes (pure rebase onto updated master), the monitor MAY post a brief PR comment noting \"rebase-only, no logic changes\" before force-pushing. This gives human reviewers (and potentially Greptile) context that the re-review is structural, not functional. This is advisory, not mandatory \u2014 Greptile will re-review regardless.\n\n**3. Merge cascade time estimate MUST include Greptile re-review latency**\n\nThe original merge cascade lesson (#3 in Parallel Agent Swarm) documented ~3 min CI per rebase cycle. The full cost is ~3 min CI + ~2-5 min Greptile re-review per rebase. For N PRs, plan for (N-1) \u00d7 (~3 min CI + ~2-5 min Greptile) total additional wait time."
130
+ },
131
+ {
132
+ "id": "mid-task-instant-fix-drift-2026-04",
133
+ "title": "Mid-Task Instant-Fix Drift (2026-04)",
134
+ "date": "2026-04",
135
+ "issue_refs": [],
136
+ "tags": [
137
+ "review-cycle",
138
+ "github"
139
+ ],
140
+ "source": "Issues #159, #167, #184 \u2014 agents derailed active tasks to apply instant fixes for discovered issues",
141
+ "body": "**Source:** Issues #159, #167, #184 \u2014 agents derailed active tasks to apply instant fixes for discovered issues\n\n**1. Discovered issues MUST be filed as GitHub issues, not fixed in-place mid-task**\n\nAgents repeatedly interrupted their current task to fix an unrelated issue they discovered along the way (e.g. a typo in another file, a missing test, a stale reference). This caused scope drift, broke the review cycle, and introduced unplanned changes into PRs scoped to specific issues. When a new issue is discovered during an active task, the agent MUST file a GitHub issue and continue the current task \u2014 do not apply an instant fix, even if it seems trivial.\n\n**2. Skill execution MUST stop at the skill's explicit instruction boundary**\n\nAgents continued executing past the final step of a skill into adjacent work \u2014 for example, after completing a review cycle, an agent started fixing unrelated issues it noticed during the review. A skill's final step is an exit condition. When the skill's steps are complete, the agent MUST stop and return to the calling context. Do not drift into adjacent work, even if it seems related. (#198)"
142
+ },
143
+ {
144
+ "id": "skills-scan-before-improvising-2026-04",
145
+ "title": "Skills/ Scan Before Improvising (2026-04)",
146
+ "date": "2026-04",
147
+ "issue_refs": [],
148
+ "tags": [
149
+ "review-cycle"
150
+ ],
151
+ "source": "Issue #200 \u2014 agents improvised multi-step workflows that already existed as skills",
152
+ "body": "**Source:** Issue #200 \u2014 agents improvised multi-step workflows that already existed as skills\n\n**1. MUST scan skills/ for existing coverage before designing a workflow from scratch**\n\nAgents were asked to run a review cycle and improvised a multi-step process from scratch, missing the existing `skills/deft-review-cycle/SKILL.md` that encodes lessons from dozens of prior review rounds. The skills/ directory contains versioned, tested workflows that encode hard-won operational lessons. Before designing any multi-step workflow, the agent MUST scan skills/ for an existing skill that covers the task. If a matching skill exists, use it \u2014 do not reinvent it. (#200)"
153
+ },
154
+ {
155
+ "id": "pr-merge-hygiene-2026-04",
156
+ "title": "PR Merge Hygiene (2026-04)",
157
+ "date": "2026-04",
158
+ "issue_refs": [],
159
+ "tags": [
160
+ "github",
161
+ "debugging"
162
+ ],
163
+ "source": "Issue #167 \u2014 PRs merged but issues not closed and roadmap not updated",
164
+ "body": "**Source:** Issue #167 \u2014 PRs merged but issues not closed and roadmap not updated\n\n**1. Squash merge + closing keywords can silently fail to close issues \u2014 MUST verify after every squash merge**\n\nGitHub processes closing keywords (`Closes #N`, `Fixes #N`) from the PR body when a PR is merged. For regular merge commits, this works reliably. For **squash merges**, GitHub rewrites the commit into a single squash commit and may not always process the closing keywords from the original PR body \u2014 the auto-close can silently fail with no error or notification. The PR shows as merged, but the linked issues remain open.\n\nRoot cause: GitHub's squash merge constructs a new commit message from the PR title and description. If the closing keyword appears only in the PR body (not the squash commit's final message), or if GitHub's keyword parser does not match the rewritten message format, the issue auto-close is skipped silently. This is a known GitHub behavior difference between regular merges and squash merges.\n\n**After every squash merge, MUST verify that referenced issues actually closed:** `gh issue view <N> --json state --jq .state`. If the issue is still open, close it manually with a comment referencing the merged PR: `gh issue close <N> --comment \"Closed by #<PR> (squash merge \u2014 auto-close did not trigger)\"`. (#167)"
165
+ },
166
+ {
167
+ "id": "warp-terminal-multi-line-powershell-string-splitting-2026-04",
168
+ "title": "Warp Terminal Multi-Line PowerShell String Splitting (2026-04)",
169
+ "date": "2026-04",
170
+ "issue_refs": [],
171
+ "tags": [
172
+ "powershell",
173
+ "encoding",
174
+ "github"
175
+ ],
176
+ "source": "Issue #240 -- multi-line PS here-strings pasted into Warp agent input caused syntax errors",
177
+ "body": "**Source:** Issue #240 -- multi-line PS here-strings pasted into Warp agent input caused syntax errors\n\n**1. Warp splits multi-line PowerShell here-strings across separate command blocks -- MUST use temp files**\n\nWhen a multi-line PowerShell string literal (here-string `@\" ... \"@`) is pasted or entered directly into the Warp agent terminal input box, Warp's input handling splits the content across separate command blocks at line boundaries. Each block is sent as a separate command, causing immediate syntax errors (the opening `@\"` is sent without its closing `\"@`) or silent truncation of the string content.\n\n**Root cause:** Warp's terminal input box treats newlines as command separators. A multi-line here-string that spans N lines becomes N separate commands, none of which is syntactically valid on its own.\n\n**Fix:** Always write multi-line PS content to a temp file first (`[System.IO.File]::WriteAllText($tmpFile, $content, [System.Text.UTF8Encoding]::new($false))`), then reference the temp file path in subsequent commands. This avoids the input splitting entirely. (#240)\n\n**Cross-reference:** `scm/github.md` \u2014 Warp Terminal Multi-Line String Handling subsection."
178
+ },
179
+ {
180
+ "id": "duplicate-tab-failure-mode-2026-04",
181
+ "title": "Duplicate-Tab Failure Mode (2026-04)",
182
+ "date": "2026-04",
183
+ "issue_refs": [],
184
+ "tags": [
185
+ "agent-orchestration",
186
+ "swarm",
187
+ "lifecycle"
188
+ ],
189
+ "source": "Issues #261, #263 -- swarm monitor spawned replacement agents while originals were still alive",
190
+ "body": "**Source:** Issues #261, #263 -- swarm monitor spawned replacement agents while originals were still alive\n\n**1. Original Warp agent tabs MUST be assumed alive until confirmed unresponsive via lifecycle events**\n\nDuring a swarm run, the monitor agent observed apparent stalls in sub-agent tabs (no recent commits, no messages) and spawned replacement agents on the same worktrees. The original tabs had not actually crashed -- they resumed shortly after, creating two concurrent agents executing on the same branch. Both agents issued `tool_use` calls and received interleaved `tool_result` responses, causing each agent to act on stale or incorrect state. The `tool_use`/`tool_result` corruption seen in #261 (Phase 5 gate bypass, untested code merged to master) and #263 (monitor crash at message ~158) traces directly to this duplicate-agent root cause.\n\n**Before spawning a replacement agent, MUST verify the original is truly unresponsive by checking for an idle/blocked lifecycle event (no active tool calls, no pending shell commands, no recent output in the original tab). MUST NOT spawn a replacement based solely on message timing or absence of recent commits. If an agent appears stalled, go to its original tab and tell it to resume rather than spawning a new agent.** (#261, #263)"
191
+ },
192
+ {
193
+ "id": "crash-recovery-pattern-2026-04",
194
+ "title": "Crash Recovery Pattern (2026-04)",
195
+ "date": "2026-04",
196
+ "issue_refs": [],
197
+ "tags": [
198
+ "swarm",
199
+ "github",
200
+ "agent-orchestration"
201
+ ],
202
+ "source": "Issue #263 -- monitor crash at message ~158 left merge cascade in ambiguous state",
203
+ "body": "**Source:** Issue #263 -- monitor crash at message ~158 left merge cascade in ambiguous state\n\n**1. Phase 6 merge cascade is safe to recover when steps are idempotent and state is checked before acting**\n\nThe monitor agent crashed mid-cascade (likely due to conversation corruption from accumulated context -- ~158 messages of tool_use/tool_result pairs). On recovery, the new session could not determine which PRs had been merged, which were rebased, and which still needed action. The fix is twofold: (1) make every Phase 6 action idempotent (check state before acting -- already merged? already rebased? already closed?) so re-running any step is safe, and (2) record progress checkpoints at each milestone so a recovery session can reconstruct state via `gh pr list --state all` and `gh pr view <number>`.\n\n**The crash risk is proportional to monitor conversation length. MUST offload rebase, review-watch, and merge sub-tasks to ephemeral sub-agents (per the tiered approach in deft-review-cycle/SKILL.md) to keep the monitor conversation shallow. Target <100 tool-call round-trips in any single monitor conversation before considering a fresh session handoff.** (#263)"
204
+ },
205
+ {
206
+ "id": "rc3-validation-on-windows-2026-04",
207
+ "title": "RC3 Validation on Windows (2026-04)",
208
+ "date": "2026-04",
209
+ "issue_refs": [],
210
+ "tags": [
211
+ "windows",
212
+ "lifecycle",
213
+ "agent-orchestration"
214
+ ],
215
+ "source": "v0.20.0-rc.3 validation on MScottAdams/slizard-rc3-test \u2014 issues #566, #567, #571, #572, #574",
216
+ "body": "**Source:** v0.20.0-rc.3 validation on MScottAdams/slizard-rc3-test \u2014 issues #566, #567, #571, #572, #574\n\n**1. Frameworks that vendor-require a task runner MUST have explicit platform-matrix CI on that runner**\n\nLanguage-level tests (pytest, go test, etc.) do not catch task-runner-specific defects: template-expansion quirks, path normalization on Windows, shell interpretation differences across OSes. A framework that documents \"use go-task\" (or make, just, etc.) MUST include CI jobs that exercise the runner on every supported OS \u2014 at minimum Linux + macOS + Windows if the consumer audience spans those. #566 was a go-task + Windows `GetFullPathNameW` + mixed-separator-normalization interaction; the existing Linux-only CI would never have caught it. Added the `windows-task-dispatch` job in #568 as the regression guard; that pattern MUST be preserved and extended as new render / migration / lifecycle tasks ship.\n\n**2. go-task `vars:` templates re-evaluate at use site in included subfiles \u2014 path vars MUST be defined per-subfile with eager `joinPath`**\n\nA `vars:` entry like `DEFT_ROOT: '{{.TASKFILE_DIR}}'` declared in a root Taskfile does NOT hold the root's TASKFILE_DIR value when referenced from an included subfile \u2014 go-task re-expands the template in the subfile's scope, where TASKFILE_DIR points at the subfile's own directory. To stabilize a path var across the include hierarchy, define it in each subfile that uses it via the eager form `DEFT_ROOT: '{{joinPath .TASKFILE_DIR \"..\"}}'`. `joinPath` is evaluated at template-expansion time with Go's `filepath.Clean`, producing a native-separator, `..`-free absolute path.\n\nCorollary: **pytest guard-rails on task file *content* are insufficient** for template-expansion correctness. A test that verifies `{{joinPath .TASKFILE_DIR \"..\"}}` appears in the file tells you nothing about what it expands to at runtime. MUST pair static content checks with a live subprocess dispatch test that invokes the task through `task --dry-run` or equivalent. This near-miss was caught in #568's pre-push local validation, not by the pytest guard-rail \u2014 which passed green on the wrong configuration.\n\n**3. A task that accepts user recovery flags in CLI_ARGS MUST NOT declare `sources:`/`generates:` incremental-build keys**\n\ngo-task's `sources:` / `generates:` declarations cause the task runner to skip `cmds:` entirely when inputs haven't changed and outputs exist \u2014 printing `Task \"X\" is up to date` without dispatching anything. CLI_ARGS (`-- --force`, `-- --rebuild`, etc.) are relayed only if `cmds:` actually runs, so any user recovery flag documented in a script's error message (e.g. the `#539` \"Re-run with --force\" pattern) will silently no-op when the task is cached. Every task whose script emits \"Re-run with --force\"-style error messages MUST declare neither `sources:` nor `generates:`. See `deft/conventions/task-caching.md` (scaffolded in the #574 fix) for the canonical rule and the regression guard-rail. (#539, #573, #574)\n\n**4. Error messages that prescribe a recovery command are a contract \u2014 they MUST be regression-tested**\n\nWhen a tool emits an error of the shape \"to recover, run X\", following X literally MUST actually recover. A documented recovery command that doesn't work is worse than no recovery message at all \u2014 it sends the operator down a dead-end path believing they've followed the correct fix. MUST add a regression test for every prescriptive error message: reproduce the error, run the command the message suggests literally, assert the expected post-recovery state. Applies equally to operator-facing errors and agent-facing errors. (#539, #574)\n\n**5. Heuristics that detect \"machine-generated\" by fishing for substrings MUST be part of a canonical marker contract shared with the writers**\n\nWhen code like `migrate_vbrief.py::_is_user_customized()` decides preservation vs overwrite based on whether a file contains strings such as `\"Generated by\"` or `\"spec_render.py\"`, those strings MUST be part of a canonical banner contract that the relevant writers (`spec_render.py`, `prd_render.py`, `roadmap_render.py`, `migrate_vbrief.py` deprecation stubs, and any future render / stub emitters) actually emit. A detector-writer asymmetry \u2014 where the detector fishes for text nobody writes \u2014 is a latent correctness bug waiting to misclassify operator edits as auto-generated content or vice versa. MUST co-locate the marker specification (as a `deft/conventions/<topic>.md` doc), the writers that emit it, and the detectors that consume it, with a regression test asserting every writer's output matches what every detector expects. (#572)"
217
+ },
218
+ {
219
+ "id": "github-closing-keyword-false-positive-in-negation-context-2026-04",
220
+ "title": "GitHub Closing-Keyword False-Positive in Negation Context (2026-04)",
221
+ "date": "2026-04",
222
+ "issue_refs": [],
223
+ "tags": [
224
+ "github",
225
+ "context",
226
+ "swarm"
227
+ ],
228
+ "source": "PR #697 -- issue #642 (a tracking umbrella for PR #401) was auto-closed on squash merge despite the PR body intentionally avoiding closing keywords. Recurrence record extended in PR #735 (#737 deterministic encoding gap-closer); see the third bullet below.",
229
+ "body": "**Source:** PR #697 -- issue #642 (a tracking umbrella for PR #401) was auto-closed on squash merge despite the PR body intentionally avoiding closing keywords. Recurrence record extended in PR #735 (#737 deterministic encoding gap-closer); see the third bullet below.\n\n**1. GitHub's closing-keyword parser is substring-based -- the literal token `Closes #N` MUST NOT appear in the PR body even inside a negation, quotation, or example**\n\nThe PR body for #697 contained the parenthetical `` (Intentionally not `Closes #642` -- #642 is a tracking umbrella that should remain open until PR #401's full scope is merged or split into linked follow-up issues.) ``. The text was negating the closing keyword in plain English, but GitHub's auto-close parser operates on token presence, not surrounding semantics. The squash merge processed the literal `Closes #642` substring and closed the issue. The same hazard applies to `Fixes #N`, `Resolves #N`, and the past-tense / lowercase variants (`closed`, `fixed`, `resolved`, etc. -- see GitHub's full keyword list).\n\n**Rule:** When a PR body needs to discuss what it intentionally does NOT close (e.g. tracking umbrellas, partial scope, deferred follow-ups), MUST phrase the disclaimer without using any closing keyword token at all. Use phrasings like:\n\n- \"Intentionally NOT using a closing keyword for #N\" (omits the trigger token entirely)\n- \"Refs #N (tracking umbrella; remains open)\" (use `Refs` only)\n- \"#N stays open as the umbrella anchor for follow-up work\" (no trigger token)\n\n\u2297 MUST NOT write `Closes #N`, `Fixes #N`, `Resolves #N`, or any GitHub closing keyword in a PR body even when negating, quoting, or showing as an example -- the parser does not respect surrounding context.\n\n**2. Post-merge issue-state verification MUST run for every squash merge regardless of intent**\n\nThe existing post-merge verification rule (Lesson: PR Merge Hygiene #1, #167) was framed around closing-keyword failures (issues that should have closed but didn't). The opposite failure mode -- issues that should have stayed open but were auto-closed -- is just as real and is caught by the same check. After every squash merge, MUST verify the state of every issue mentioned in the PR body matches intent: closed if a closing keyword was used; open if only `Refs` was used. If an issue was closed in error, reopen it with a comment referencing the PR and explaining why it should remain open (e.g. tracking umbrella, partial scope). (#697)\n\n**3. Recurrence (#735): the squash-commit body for that PR contained a negation-context clause referencing #734 in a way that auto-closed it on merge despite intent (the issue was the parent for the in-flight #737 work and had to be reopened manually). #737 closes this gap structurally with a deterministic pre-PR lint (`scripts/pr_check_closing_keywords.py`, surfaced via `task pr:check-closing-keywords`) that scans both the PR body AND every commit message for closing-keyword tokens followed by `#\\d+` in negation / quotation / example / code-block contexts and refuses to push when findings surface. The lint is wired into `skills/deft-directive-pre-pr/SKILL.md` Phase 4 (Diff) and cross-referenced from `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1 as the Layer 0 (prevention) surface alongside the existing Layer 3 (recovery) `task pr:check-protected-issues` (#701).**\n\n**Cross-reference:** existing lesson \"PR Merge Hygiene\" #1 (#167); `scm/github.md` PR conventions; `skills/deft-directive-review-cycle/SKILL.md` Post-Merge Verification; `skills/deft-directive-pre-pr/SKILL.md` Phase 4 (Layer 0 prevention, #737); `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1 (Layer 3 recovery, #701)."
230
+ },
231
+ {
232
+ "id": "github-closing-keyword-false-positive-layer-3-persistent-closingissuesreferences-link-2026-04",
233
+ "title": "GitHub Closing-Keyword False-Positive Layer 3 -- Persistent closingIssuesReferences Link (2026-04)",
234
+ "date": "2026-04",
235
+ "issue_refs": [],
236
+ "tags": [
237
+ "github",
238
+ "encoding",
239
+ "lifecycle"
240
+ ],
241
+ "source": "PR #700 squash-merge auto-closed #233; PR #401 squash-merge auto-closed #642 -- both with PR bodies amended to use only `Refs` and no closing keywords in commit messages or the explicit `--subject` / `--body-file` squash payload. Root cause: GitHub's `closingIssuesReferences` link is durable -- it is recorded the moment a closing keyword first appears in a PR body (or an issue is attached via the Development sidebar) and survives every subsequent body / commit-message / squash-payload edit. On squash merge, GitHub iterates the persistent link list and closes every linked issue regardless of the current text.",
242
+ "body": "**Source:** PR #700 squash-merge auto-closed #233; PR #401 squash-merge auto-closed #642 -- both with PR bodies amended to use only `Refs` and no closing keywords in commit messages or the explicit `--subject` / `--body-file` squash payload. Root cause: GitHub's `closingIssuesReferences` link is durable -- it is recorded the moment a closing keyword first appears in a PR body (or an issue is attached via the Development sidebar) and survives every subsequent body / commit-message / squash-payload edit. On squash merge, GitHub iterates the persistent link list and closes every linked issue regardless of the current text.\n\n**Canonical encoding (strongest-applicable layer):** the operative `!` (MUST) rules live in `skills/deft-directive-swarm/SKILL.md` Phase 6 Step 1 -- pre-merge `gh pr view <N> --json closingIssuesReferences` inspection (manually unlink via the PR's Development sidebar before merging if a protected issue is linked) and a post-merge protected-issue reopen sweep. The Anti-Patterns block carries the corresponding `\u2297` entries. Optional second-strongest encoding: `task pr:check-protected-issues` (see `tasks/pr.yml`) which wraps the pre-merge inspection deterministically.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md` (added by PR #401), every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The lessons-consumption umbrella #575 (vBRIEF-backed lessons registry, pre-pr capture, `task lessons:render`, tagged on-demand retrieval) plus the pre-PR deterministic-CI-enforcement umbrella #633 together document why this entry intentionally does not duplicate the rule body in prose -- prose is fallback only.\n\n**Cross-references:** Layer 1 (#167) PR Merge Hygiene; Layer 2 (#698) negation-context substring match; workflow umbrella (#642); incident PRs (#700, #401); lessons-consumption-gap context (#575); pre-PR deterministic-CI umbrella (#633); this lesson (#701). Canonical workflow comment: https://github.com/deftai/directive/issues/642#issuecomment-4330742436."
243
+ },
244
+ {
245
+ "id": "orchestrator-role-separation-canonical-poller-template-2026-04",
246
+ "title": "Orchestrator Role Separation + Canonical Poller Template (2026-04)",
247
+ "date": "2026-04",
248
+ "issue_refs": [],
249
+ "tags": [
250
+ "agent-orchestration",
251
+ "review-cycle",
252
+ "swarm"
253
+ ],
254
+ "source": "#727 -- recurring orchestrator failure across the 2026-04-28 v0.21.0 cut session (#721): three implementation-agent prompts (PR #722, #726, #727) bundled \"watch for Greptile\" instructions into agents that exit at PR-open via the `succeeded` lifecycle (the watch never starts and only happened to work in #722 by `time.sleep(180)` coincidence); plus two recurring poll-script parsing bugs surfaced post-#721 in Agent D's hand-authored poller (markdown-link `Last reviewed commit:` regex never matched; raw `\\b(P0|P1)\\b` substring scan false-positived on Greptile's `No P0 or P1 issues found` clean summary); plus the rm-chaining anti-pattern that hit Agent 1 four times in one session (chained `rm` + `git commit` / `git push` poisons Warp's `is_risky` classification on the whole pipeline).",
255
+ "body": "**Source:** #727 -- recurring orchestrator failure across the 2026-04-28 v0.21.0 cut session (#721): three implementation-agent prompts (PR #722, #726, #727) bundled \"watch for Greptile\" instructions into agents that exit at PR-open via the `succeeded` lifecycle (the watch never starts and only happened to work in #722 by `time.sleep(180)` coincidence); plus two recurring poll-script parsing bugs surfaced post-#721 in Agent D's hand-authored poller (markdown-link `Last reviewed commit:` regex never matched; raw `\\b(P0|P1)\\b` substring scan false-positived on Greptile's `No P0 or P1 issues found` clean summary); plus the rm-chaining anti-pattern that hit Agent 1 four times in one session (chained `rm` + `git commit` / `git push` poisons Warp's `is_risky` classification on the whole pipeline).\n\n**Canonical encoding (strongest-applicable layer):** the operative `!` MUST rules and `\u2297` MUST NOT anti-patterns live in `skills/deft-directive-swarm/SKILL.md` Phase 6 Sub-Agent Role Separation -- post-PR sub-agents embody `skills/deft-directive-review-cycle/SKILL.md` end-to-end as a single coherent role; post-PR monitoring spawns a fresh poller sub-agent via `start_agent`; the canonical poller-prompt template at `templates/swarm-greptile-poller-prompt.md` MUST be used (placeholders `{pr_number}`, `{repo}`, `{poll_interval_seconds}`, `{poll_cap_minutes}`, `{parent_agent_id}`); destructive commands run alone (no rm-chaining); the commit-message temp file is leave-alone. Anti-patterns prohibit parent-turn polling, bundling watch instructions into impl-agent prompts, default pure-pollers, and rm-chaining. Tier 1 deterministic enforcement: `tests/content/test_skills.py` regression-tests that the template exists, all 5 placeholders are present verbatim, both parsing-fix tokens (markdown-link `Last reviewed commit:` regex, badge-based `<img alt=\"P1\"` / negation-aware findings detection) are encoded, the swarm SKILL references the template path, and every `!` rule + `\u2297` anti-pattern carries a stable substring marker.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). This lessons entry exists for discoverability only -- the rule body lives in the swarm SKILL section above and the template artifact, with the content tests as the deterministic enforcement layer.\n\n**Cross-references:** `skills/deft-directive-swarm/SKILL.md` Phase 6 Sub-Agent Role Separation (primary encoding); `templates/swarm-greptile-poller-prompt.md` (template artifact); `skills/deft-directive-review-cycle/SKILL.md` (the skill the poller embodies end-to-end); precedent encoding pattern Layer 3 (#701); session anchor #721; recurrence PRs #722 / #726 / #727; this lesson (#727)."
256
+ },
257
+ {
258
+ "id": "triple-tier-greptile-findings-detector-in-poller-template-2026-05",
259
+ "title": "Triple-Tier Greptile Findings Detector in Poller Template (2026-05)",
260
+ "date": "2026-05",
261
+ "issue_refs": [],
262
+ "tags": [
263
+ "agent-orchestration",
264
+ "review-cycle",
265
+ "swarm"
266
+ ],
267
+ "source": "#910 -- v0.25.1 swarm session 2026-05-04 (4-agent cohort #899/#900/#901/#902 -> PRs #906/#907/#908/#909) produced THREE false-negatives in a single session because the badge-only detector in `templates/swarm-greptile-poller-prompt.md` missed Greptile findings rendered as markdown bullets (#907 first review, #908 first review) and inline prose (#908 retrigger, sentinel-only signal `Not safe to merge until ...`). Each false-negative cost ~30 min poll budget plus a fresh review-cycle agent dispatch.",
268
+ "body": "**Source:** #910 -- v0.25.1 swarm session 2026-05-04 (4-agent cohort #899/#900/#901/#902 -> PRs #906/#907/#908/#909) produced THREE false-negatives in a single session because the badge-only detector in `templates/swarm-greptile-poller-prompt.md` missed Greptile findings rendered as markdown bullets (#907 first review, #908 first review) and inline prose (#908 retrigger, sentinel-only signal `Not safe to merge until ...`). Each false-negative cost ~30 min poll budget plus a fresh review-cycle agent dispatch.\n\n**Failure mode:** Greptile renders findings in at least THREE distinct surface forms across review passes on the same PR (HTML severity badges; markdown-bullet bold like `- **P1 -- ...**`; inline prose like `Three P1 findings ...` or `Not safe to merge`). The pre-#910 detector covered only Tier 1 (badges); the markdown-bullet handling lived in the Notes section but NOT in the prescribed detector code path; inline-prose was not covered at all. Single-tier detection is structurally insufficient against a reviewer that switches rendering modes between passes.\n\n**Canonical encoding (strongest-applicable layer):** the rule body lives in the template artifact -- `templates/swarm-greptile-poller-prompt.md` `### P0/P1 findings detection (TRIPLE-TIER -- #910)` -- which prescribes Tier 1 (badge count), Tier 2 (markdown-bullet bold scan with line-scoped negation guards), and Tier 3 (inline-prose sentinels: `Not safe to merge` substring, `(One|Two|...|\\d+) P[01] findings?` count regex with negation + leading-`0` guards, `^P[01] -- ` line-anchored regex), combined via `has_blocking = (max(tier1_p0, tier2_p0) + max(tier1_p1, tier2_p1)) > 0 or tier3_sentinel`. Tier 1 deterministic enforcement: `tests/content/test_swarm_poller_template.py` ships 14 regression tests covering the six behaviour-matrix cases from the #910 acceptance criteria (markdown-bullet only / `Not safe to merge` only / count-prose only / negation-guard / clean / pure-badge) PLUS eight synchronization tests asserting the template encodes the canonical regex strings + sentinels verbatim AND a `str.format(...)` render guard so a future unescaped `{` in the code block fails CI immediately.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the template artifact + the deterministic content tests; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `templates/swarm-greptile-poller-prompt.md` (rule body); `tests/content/test_swarm_poller_template.py` (deterministic regression coverage); `tests/content/test_skills.py` `test_swarm_greptile_poller_prompt_*` lane (existing #727 placeholder + parsing-fix coverage, kept verbatim); existing `## Orchestrator Role Separation + Canonical Poller Template (2026-04)` lesson (parent context, #727); v0.25.1 swarm session anchor (2026-05-04); recurrence record (#907 first review, #908 first review, #908 retrigger); this lesson (#910). Second-recurrence extension (#1035, PR #1034 -- 2026-05-11): Tier 2.5 SLizard `### P[01] \u00b7` heading detector + confidence-heading parser fallback added under #1035; rule body lives in `templates/swarm-greptile-poller-prompt.md` and the deterministic content tests at `tests/content/test_swarm_poller_template.py`. Third-recurrence extension (#1039, PR #1038 -- 2026-05-11): the canonical poller's (5)-condition CLEAN gate could not distinguish a parse-gap from \"Greptile still working\" -- both kept `has_blocking_combined = False` AND `is_clean = False` and the poller burned its full 30-minute cap on a textbook clean review (poller agent `5794b0e7-...`; maintainer intervened out-of-band). Resolved under #1039 by adding (a) a Tier 1 per-poll instrumentation log line (`[poll i/cap] last_reviewed_sha=... head=... sha_match=... confidence=... has_blocking=... p0=... p1=... errored=... ci_failures=... is_clean=... clean_gate_holdout=...`) so the operator can grep the transcript and see WHICH of the five conditions held the gate; (b) the (5) STALL terminal exit -- a bounded fail-loud exit at ~4.5 min (N=3 consecutive wedged polls at the 90s interval) carrying the canonical subject `PR #<N> poll loop wedged -- terminal-condition detection failure`; (c) Tier 3 per-condition fail-loud -- BOTH (4) TIMEOUT AND (5) STALL exit messages surface `clean_gate_holdout` naming the FIRST failing condition among (1) `sha_match` / (2) `has_blocking` / (3) `confidence` / (4) `ci_failures` / (5) `errored`. Per the Rule Authority [AXIOM] strongest-applicable-layer rule, the rule body lives in `templates/swarm-greptile-poller-prompt.md`'s `## CLEAN gate evaluation, clean_gate_holdout, and per-poll instrumentation (#1039)` + `### (5) STALL` blocks, with deterministic regression coverage in `tests/content/test_swarm_poller_template.py` (behaviour-matrix lane covers AC-1 through AC-4; synchronization-test lane pins the template encoding). The #727 Sub-Agent Role Separation rule that mandates use of this canonical template means a detector gap is a silent false-negative for every conformant sub-agent until landed -- the architectural fix is to surface the gap LOUD via the STALL exit rather than pretend the prescribed parser set is exhaustive across all Greptile / SLizard rendering modes."
269
+ },
270
+ {
271
+ "id": "vbrief-lifecycle-drift-on-release-2026-04",
272
+ "title": "vBRIEF Lifecycle Drift on Release (2026-04)",
273
+ "date": "2026-04",
274
+ "issue_refs": [],
275
+ "tags": [
276
+ "lifecycle",
277
+ "release",
278
+ "ci"
279
+ ],
280
+ "source": "v0.21.0 cut session -- post-publish reconciliation surfaced 13 stranded vBRIEFs (8 cycle-relevant + 5 historical residue) whose origin GitHub issues were closed but whose vBRIEF files still lived in `proposed/` / `pending/` / `active/`. Operators consistently forgot the manual `task scope:complete` move step between merge and release, so each cut accreted lifecycle drift the next release inherited.",
281
+ "body": "**Source:** v0.21.0 cut session -- post-publish reconciliation surfaced 13 stranded vBRIEFs (8 cycle-relevant + 5 historical residue) whose origin GitHub issues were closed but whose vBRIEF files still lived in `proposed/` / `pending/` / `active/`. Operators consistently forgot the manual `task scope:complete` move step between merge and release, so each cut accreted lifecycle drift the next release inherited.\n\n**Canonical encoding (strongest-applicable layer):** the operative `!` MUST rules and `\u2297` MUST NOT anti-patterns live in `skills/deft-directive-release/SKILL.md` Phase 1 (pre-flight) and the deterministic gate at `scripts/release.py::check_vbrief_lifecycle_sync` wired as Step 3 (between branch guard at Step 2 and CI at Step 4) of the 12-step pipeline. The gate refuses the release with `EXIT_VIOLATION` (1) on any Section (c) mismatch (closed-issue vBRIEF in a non-`completed/` folder) unless the operator explicitly passes `--allow-vbrief-drift` (analogous to `--allow-dirty`). The clean recovery path is `task reconcile:issues -- --apply-lifecycle-fixes` (#734) which reads each Section (c) entry, sets `plan.status = \"completed\"`, stamps `vBRIEFInfo.updated`, and `git mv`'s the file into `completed/` -- idempotent on re-run, and tolerant of both legacy bare `github-issue` and canonical `x-vbrief/github-issue` reference shapes.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the deterministic gate (`scripts/release.py::check_vbrief_lifecycle_sync` + the apply-mode helpers in `scripts/reconcile_issues.py`); this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `skills/deft-directive-release/SKILL.md` Phase 1 (primary encoding); `scripts/release.py::check_vbrief_lifecycle_sync` (deterministic gate); `scripts/reconcile_issues.py::apply_lifecycle_fixes` (clean-recovery surface); `scripts/release.py::ReleaseConfig.allow_vbrief_drift` (escape hatch); v0.21.0 cut session anchor; this lesson (#734)."
282
+ },
283
+ {
284
+ "id": "inverted-lookup-scaling-pattern-754",
285
+ "title": "Inverted-lookup scaling pattern (#754)",
286
+ "date": null,
287
+ "issue_refs": [
288
+ "#754"
289
+ ],
290
+ "tags": [
291
+ "lifecycle",
292
+ "ci",
293
+ "release"
294
+ ],
295
+ "source": "v0.22.0 cut session 2026-04-30 -- the vBRIEF-lifecycle-sync release gate (#734) false-positively flagged 32 \"closed\" mismatches on `deftai/directive` once master crossed 200 open issues. Every flagged issue was OPEN on inspection.",
296
+ "body": "**Source:** v0.22.0 cut session 2026-04-30 -- the vBRIEF-lifecycle-sync release gate (#734) false-positively flagged 32 \"closed\" mismatches on `deftai/directive` once master crossed 200 open issues. Every flagged issue was OPEN on inspection.\n\n**Failure mode:** the gate fetched all open issues and filtered for the vBRIEF-referenced subset. `fetch_open_issues` capped at 200 (`--limit 200`); tail issues silently dropped were treated as CLOSED, producing apparent mismatches that did not exist. Cost scaled by O(repo-open-issue-count).\n\n**Fix:** invert the lookup direction. Extract the issue numbers referenced by vBRIEFs, then query just those issues' states via batched `gh api graphql` with aliased nodes (`i100: issue(number: 100) { state }`). Cost now scales by O(vBRIEF-referenced-issue-count); truncation impossible by construction. The Tier 2 truncation-guard surface from the original two-tier proposal is retired -- it was guarding against a problem the new approach cannot have.\n\n**Generalizable heuristic:** whenever a gate cross-references a small subset against a large enumerable set, query the subset's state directly rather than fetching the full set and filtering. The query cost should scale by the property the gate cares about, not by an enumeration property the gate does not.\n\n**Reference:** `scripts/reconcile_issues.py::fetch_issue_states` (helper); `scripts/release.py::check_vbrief_lifecycle_sync` (consumer); #754 (issue + this fix)."
297
+ },
298
+ {
299
+ "id": "greptile-review-stall-detection-2026-04",
300
+ "title": "Greptile Review Stall Detection (2026-04)",
301
+ "date": "2026-04",
302
+ "issue_refs": [],
303
+ "tags": [
304
+ "review-cycle",
305
+ "github",
306
+ "swarm"
307
+ ],
308
+ "source": "rc4 swarm cascade on PR #561 -- a Greptile check run sat in IN_PROGRESS for 21 minutes (~3x the upper bound of normal) on a single `commit.oid` while the polling loop continued silently with no escalation surface.",
309
+ "body": "**Source:** rc4 swarm cascade on PR #561 -- a Greptile check run sat in IN_PROGRESS for 21 minutes (~3x the upper bound of normal) on a single `commit.oid` while the polling loop continued silently with no escalation surface.\n\n**1. Stalls past 3x the expected window MUST escalate to the user, not auto-retrigger**\n\nGreptile reviews typically complete in 2-5 minutes (7 minutes is the upper bound of normal). When the check run remains IN_PROGRESS past 3x expected (~10 minutes) on the SAME `commit.oid`, the agent MUST stop polling silently and surface the situation to the user. The 21-minute observation on PR #561 was the recurrence record: the polling loop kept running, the user had no visibility, and the cycle effectively wasted the operator's attention budget. The Stall Detection Rubric in `skills/deft-directive-review-cycle/SKILL.md` Step 4 codifies the threshold and the deterministic escalation menu (Wait / Re-trigger / Skip / Cancel / Discuss / Back).\n\n**2. Auto-retrigger without explicit user approval is forbidden**\n\nThe naive recovery -- pushing an empty commit, force-pushing, or auto-posting `@greptileai` -- is exactly the wrong move because it resets Greptile's review clock on a NEW SHA and erases the stall evidence. The agent MUST NOT auto-retrigger. The rubric's option-2 (manual `@greptileai` comment) is the only supported re-trigger path AND it requires the user to pick it from the escalation menu. Any user-approved override MUST be documented in a brief PR comment for auditability, so future agents resuming the cycle see why the clock reset.\n\n**3. `startedAt` resets MUST notify the user, not silently re-anchor the clock**\n\nGreptile occasionally drops its prior check run and starts a fresh one without any push from the agent (service-side restart, runner re-roll). The polling loop MUST detect a NEW `startedAt` on the same commit, reset its elapsed-time clock to the new `startedAt`, AND notify the user that an auto-restart was detected. Resetting the clock without notification is forbidden -- the user needs to know the cycle effectively re-started, otherwise a second 10-minute stall can hide behind a silent restart.\n\n**Cross-reference:** `skills/deft-directive-review-cycle/SKILL.md` -- `Stall Detection Rubric (#564)`."
310
+ },
311
+ {
312
+ "id": "greptile-checkrun-success-review-approval-2026-05",
313
+ "title": "Greptile CheckRun SUCCESS != Review Approval (2026-05)",
314
+ "date": "2026-05",
315
+ "issue_refs": [],
316
+ "tags": [
317
+ "review-cycle",
318
+ "ci",
319
+ "swarm"
320
+ ],
321
+ "source": "PR #652 incident 2026-05-01 -- the directive agent attempted to start a merge cascade against `Confidence: 3/5 + 1\u00d7P1 + 2\u00d7P2` because the GitHub `Greptile Review` CheckRun was SUCCESS. Symmetric blind spot to the NEUTRAL-CheckRun case codified in #526: both are wrong-oracle failures where the agent trusts a CheckRun status as proxy for review approval.",
322
+ "body": "**Source:** PR #652 incident 2026-05-01 -- the directive agent attempted to start a merge cascade against `Confidence: 3/5 + 1\u00d7P1 + 2\u00d7P2` because the GitHub `Greptile Review` CheckRun was SUCCESS. Symmetric blind spot to the NEUTRAL-CheckRun case codified in #526: both are wrong-oracle failures where the agent trusts a CheckRun status as proxy for review approval.\n\n**Generalizable pattern:** ANY reviewer-posted CheckRun is a **completion** signal, not an **approval** signal. The CheckRun goes green when the bot finishes its review pass, irrespective of findings or confidence. For Greptile specifically: SUCCESS can hide unresolved P0/P1 + low confidence in the comment body; NEUTRAL can hide a service-side error in the comment body (#526). Both require parsing the body, not the status.\n\n**Canonical encoding (strongest-applicable layer):** the operative `!` MUST rules and `\u2297` MUST NOT anti-patterns live in `skills/deft-directive-swarm/SKILL.md` Phase 5->6 -- programmatic gate (`task pr:merge-ready -- <N>`), atomic-shell-call freshness window (`task pr:merge-ready -- <N> && gh pr merge <N>` chained in the same shell call), SUCCESS-CheckRun-alone prohibition, upstream-batched-readiness-check prohibition. Tier 1 deterministic enforcement: `scripts/pr_merge_readiness.py` parses the Greptile rolling-summary comment body (badge-count P0/P1 detection, structured-section heading fallback, errored-sentinel detection, HEAD-SHA freshness, confidence parse) and exits non-zero on any gate failure; `tests/cli/test_pr_merge_readiness.py` regression-tests the PR #652 incident signature so the same wording cannot pass through the gate again.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the deterministic gate (`scripts/pr_merge_readiness.py`) wrapped by the `task pr:merge-ready` Taskfile target and surfaced in the swarm SKILL section above; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `skills/deft-directive-swarm/SKILL.md` Phase 5->6 (primary encoding); `scripts/pr_merge_readiness.py` (deterministic gate); `tasks/pr.yml::merge-ready` (Taskfile surface); `tests/cli/test_pr_merge_readiness.py` (regression coverage); existing #526 NEUTRAL-CheckRun lesson (symmetric blind spot); #796 (sibling late-arriving-bot-review re-check gap, separate fix); PR #652 incident (recurrence record)."
323
+ },
324
+ {
325
+ "id": "review-cycle-fail-closed-exit-and-non-greedy-sha-2026-06",
326
+ "title": "Review-cycle fail-closed exit + non-greedy Last-reviewed-commit SHA (2026-06)",
327
+ "date": "2026-06",
328
+ "issue_refs": [
329
+ "#1259",
330
+ "#1326"
331
+ ],
332
+ "tags": [
333
+ "review-cycle",
334
+ "github",
335
+ "swarm"
336
+ ],
337
+ "source": "#1259 -- the review-cycle Step 6 exit predicate (confidence > 3 + no P0/P1) could evaluate true against a PARTIAL or STALE Greptile review, letting an agent exit clean and merge un-reviewed code while a P0/P1 finding was still in flight. #1326 -- the poller `Last reviewed commit:` SHA regex used a greedy `[^\\]]*` link-text class that stops at the first `]`, so a commit subject carrying escaped brackets (e.g. `add \\[Unreleased\\] entry`) yielded last_reviewed_sha=None and a false STALL / TIMEOUT on a clean review.",
338
+ "body": "**Source:** #1259 -- the review-cycle Step 6 exit predicate (confidence > 3 + no P0/P1) could evaluate true against a PARTIAL or STALE Greptile review, letting an agent exit clean and merge un-reviewed code while a P0/P1 finding was still in flight. #1326 -- the poller `Last reviewed commit:` SHA regex used a greedy `[^\\]]*` link-text class that stops at the first `]`, so a commit subject carrying escaped brackets (e.g. `add \\[Unreleased\\] entry`) yielded last_reviewed_sha=None and a false STALL / TIMEOUT on a clean review.\n\n**Generalizable takeaway:** a reviewer verdict is only trustworthy when it is SHA-pinned to the current HEAD at read time and backed by a terminal check-run -- a confidence number alone, or a reviewed SHA that lags HEAD, is `unknown`, not a pass. Exit predicates over external review state MUST be fail-closed all-ofs where any missing/ambiguous field resolves to `unknown` (#1259). When parsing markdown-link text whose content is attacker/author-controlled (commit subjects, PR titles), prefer a non-greedy `.*?` bound to the real delimiter over a negated-character class, which silently breaks on escaped delimiters (#1326).\n\n**Canonical encoding (strongest-applicable layer):** the operative rules live in `skills/deft-directive-review-cycle/SKILL.md` Step 6 (fail-closed ReviewerStatus all-of), its Pre-Merge Re-Poll Gate, and the `INCOMPLETE_BUT_RATED` stall signature; the non-greedy SHA regex lives in `templates/swarm-greptile-poller-prompt.md` (inline regex + Implementation Notes) with sync + escaped-bracket regression coverage in `tests/content/test_swarm_poller_template.py`. Per the Rule Authority [AXIOM] block in `main.md` this lessons entry is a discoverability + recurrence-record cross-reference only.\n\n**Cross-references:** `skills/deft-directive-review-cycle/SKILL.md` (Step 6 fail-closed all-of + Pre-Merge Re-Poll Gate + INCOMPLETE_BUT_RATED); `templates/swarm-greptile-poller-prompt.md` (`### Last reviewed commit:` non-greedy regex); `tests/content/test_swarm_poller_template.py` + `tests/content/test_review_cycle_skill.py` (regression coverage); #1259, #1326 (this fix).\n\n**Recurrence (PR #1690 review):** the #1259 terminal-check-run requirement was added to `skills/deft-directive-review-cycle/SKILL.md` Step 6 but NOT initially propagated to the swarm-dispatched poller's `evaluate_clean_gate` -- the PRIMARY review-cycle path. An INCOMPLETE_BUT_RATED review (rolling summary parses clean, SHA matches, confidence > 3, no P0/P1, but the `Greptile Review` check-run is still non-terminal) passed all five legacy gate conditions and returned `(True, None)` -> the poller exited CLEAN prematurely; `ci_failures` is scoped to `CI / *` checks and cannot stand in for the Greptile check-run. Fixed by adding a sixth `terminal_check_run` condition (holdout name `terminal_check_run`) to `evaluate_clean_gate` in `templates/swarm-greptile-poller-prompt.md`, mirroring SKILL.md Step 6, with an INCOMPLETE_BUT_RATED regression test in `tests/content/test_swarm_poller_template.py`. **Meta-takeaway:** when a fail-closed predicate lives on TWO surfaces (the human-readable skill doc AND the executable poller snippet), a fix MUST land on BOTH surfaces in the same change -- the un-propagated surface silently re-opens the gap on the path that actually runs."
339
+ },
340
+ {
341
+ "id": "implementation-intent-inference-is-a-documented-anti-pattern-810",
342
+ "title": "Implementation-intent inference is a documented anti-pattern (#810).",
343
+ "date": null,
344
+ "issue_refs": [
345
+ "#810"
346
+ ],
347
+ "tags": [
348
+ "lifecycle",
349
+ "ci",
350
+ "swarm"
351
+ ],
352
+ "source": "#810 surfacing event -- during the in-flight #801 session the user said \"do full pr process to get it into master use poller agents not local loops\". Intent was scoping / scheduling guidance about where the work would happen and which lifecycle skill would run it. The orchestrator parsed `full PR process` + `poller agents` + `not local loops` as authorization to invoke `start_agent` for implementation. None of the documented skill-routing keywords (`build`, `implement`, `swarm`, `run agents`) appeared in the user's message; the agent inferred them from workflow-shape vocabulary.",
353
+ "body": "**Source:** #810 surfacing event -- during the in-flight #801 session the user said \"do full pr process to get it into master use poller agents not local loops\". Intent was scoping / scheduling guidance about where the work would happen and which lifecycle skill would run it. The orchestrator parsed `full PR process` + `poller agents` + `not local loops` as authorization to invoke `start_agent` for implementation. None of the documented skill-routing keywords (`build`, `implement`, `swarm`, `run agents`) appeared in the user's message; the agent inferred them from workflow-shape vocabulary.\n\n**Failure mode:** lifecycle, branching, and PR-process language are NOT implementation directives. Trigger phrases observed in the wild include \"do the full PR process\", \"poller agents\", \"start the work\", \"not local loops\", \"once the branch is up\". Affirmative continuation phrases (`yes`, `go`, `proceed`, `do it`) similarly do NOT authorize implementation unless the prior turn explicitly proposed it. The honor-system anti-pattern was that AGENTS.md skill-routing is a positive grammar -- it lists trigger keywords for skills but does NOT codify that the **absence** of those keywords forbids implementation -- so an agent inferring from workflow-shape vocabulary slipped through every documented gate.\n\n**Canonical encoding (strongest-applicable layer):** the operative `!` MUST rules and `\u2297` MUST NOT anti-patterns live in `skills/deft-directive-build/SKILL.md` Step 0 (Implementation Preflight) and `skills/deft-directive-swarm/SKILL.md` Phase 0 Step 1, with the structural fail-closed gate at `scripts/preflight_implementation.py` (asserts `vbrief/active/` AND `plan.status == \"running\"`, exits 1 otherwise with the actionable `task vbrief:activate <path>` redirect). The companion `scripts/vbrief_activate.py` (surfaced via `task vbrief:activate`) is the ONLY supported way to satisfy the gate -- idempotent, atomic, status-flipping. The prompt-side guardrail block lands inside the `<!-- deft:managed-section v1 -->` markers in `templates/agents-entry.md` so `cmd_agents_refresh` propagates it to consumer projects, and is mirrored verbatim in repo-level `AGENTS.md`. Tier 1 deterministic enforcement: `tests/cli/test_preflight_implementation.py` covers the (folder, status) matrix + edge cases including the recursively-appropriate self-test against the #810 vBRIEF in `vbrief/pending/`; `tests/content/test_skills_preflight_call.py` regex-asserts both build and swarm skills cite the helper as a `!` rule; `tests/content/test_agents_entry_contract.py` asserts the managed-section carries the `Implementation Intent Gate` anchor + 4 bullets with the `!` / `\u2297` token mix.\n\n**Generalizable takeaway:** workflow-shape vocabulary is not authorization. When the user's intent is ambiguous, ask one targeted question instead of inferring. The fix converts an honor-system rule into a preflight that fails closed -- a structural gate is strictly stronger than a prose anti-pattern because it cannot be silently skipped by a future agent that hasn't internalized the rule.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the deterministic gate (`scripts/preflight_implementation.py`) and the build / swarm / templates / AGENTS.md cross-references; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `scripts/preflight_implementation.py` (deterministic gate); `scripts/vbrief_activate.py` (recovery surface); `tasks/vbrief.yml::activate` (Taskfile surface); `skills/deft-directive-build/SKILL.md` Step 0 (build skill encoding); `skills/deft-directive-swarm/SKILL.md` Phase 0 Step 1 (swarm skill encoding); `templates/agents-entry.md` Implementation Intent Gate (prompt-side guardrail); `AGENTS.md` Development Process (repo-level mirror); #801 (surfacing-event session); this lesson (#810)."
354
+ },
355
+ {
356
+ "id": "windows-cp1252-stdout-default-breaks-non-ascii-prints-2026-05",
357
+ "title": "Windows cp1252 stdout default breaks non-ASCII prints (2026-05)",
358
+ "date": "2026-05",
359
+ "issue_refs": [],
360
+ "tags": [
361
+ "windows",
362
+ "encoding",
363
+ "ci"
364
+ ],
365
+ "source": "#814 -- `scripts/preflight_branch.py` (the #747 branch-protection hook installed by `task setup`) crashed with `UnicodeEncodeError: 'charmap' codec can't encode character '\\u2713'` on Windows whenever Python's stdout default was `cp1252` (the OS default with `PYTHONIOENCODING` unset). The gate had ALREADY APPROVED the commit before the success-path print failed, so the user saw a Python traceback after a successful check and git aborted the commit -- maximally confusing, blocked every commit on a fresh Windows install.",
366
+ "body": "**Source:** #814 -- `scripts/preflight_branch.py` (the #747 branch-protection hook installed by `task setup`) crashed with `UnicodeEncodeError: 'charmap' codec can't encode character '\\u2713'` on Windows whenever Python's stdout default was `cp1252` (the OS default with `PYTHONIOENCODING` unset). The gate had ALREADY APPROVED the commit before the success-path print failed, so the user saw a Python traceback after a successful check and git aborted the commit -- maximally confusing, blocked every commit on a fresh Windows install.\n\n**Generalizable pattern:** any Python script invoked by a deft git hook (or any deft surface that prints non-ASCII glyphs and is run outside the Taskfile's `env: PYTHONUTF8: \"1\"` block, e.g. git's pre-commit invocation) MUST self-reconfigure `sys.stdout` and `sys.stderr` to UTF-8 with `errors='replace'` at `main()` entry, BEFORE any `print()`. Guarded by `hasattr(stream, \"reconfigure\")` so the call is a no-op on non-`TextIOWrapper` streams (e.g. when stdout is captured by a `PIPE`). `errors='replace'` is the belt-and-suspenders fallback so the rare environment that still cannot render UTF-8 sees a printable replacement char rather than an unhandled traceback.\n\n**Canonical encoding (strongest-applicable layer):** the rule body lives in the script -- `scripts/preflight_branch.py::main` (and any future hook-invoked script under `scripts/` referenced from `.githooks/`) reconfigures at entry. Tier 1 deterministic enforcement: `tests/cli/test_hooks_encoding.py` monkeypatches `sys.stdout` to a strict cp1252 `TextIOWrapper` and asserts (a) `main()` returns cleanly with no `UnicodeEncodeError`, (b) `sys.stdout.encoding` is `utf-8` post-reconfigure, (c) the U+2713 glyph reaches the underlying buffer as canonical UTF-8 bytes (`b'\\xe2\\x9c\\x93'`). The audit-only test re-discovers the hook-invoked surface from `.githooks/` at test time, so a future hook script that lands without the reconfigure block fails the contract immediately. `task setup` additionally surfaces an informational warning when it detects a Python interpreter whose stdout defaults to a Windows code page (cp1252 / cp437 / charmap) -- the auto-reconfigure renders the warning advisory rather than required, but it is the discoverable surface that documents the underlying environmental fact.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the script + the contract test; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `scripts/preflight_branch.py::main` (script-level fix); `tests/cli/test_hooks_encoding.py` (deterministic contract); `Taskfile.yml::setup` (cp1252 detection + advisory warning); existing #747 (gate surface this affected); existing #810 (surfacing session -- the chore commit was blocked until `PYTHONIOENCODING=utf-8` was applied); existing Windows File Editing #2 / #4 (sibling PS 5.1 cp1252 corruption pattern at write-time, separate fix surface); this lesson (#814)."
367
+ },
368
+ {
369
+ "id": "cold-start-dual-source-fetch-blind-spot-in-review-cycle-2026-05",
370
+ "title": "Cold-start dual-source fetch blind spot in review-cycle (2026-05)",
371
+ "date": "2026-05",
372
+ "issue_refs": [],
373
+ "tags": [
374
+ "review-cycle",
375
+ "debugging",
376
+ "agent-orchestration"
377
+ ],
378
+ "source": "#796 -- the `skills/deft-directive-review-cycle/SKILL.md` Phase 2 Step 1 dual-source-fetch contract (which correctly catches the `Comments Outside Diff` case via `gh pr view --comments` + MCP `get_review_comments` or `gh api` fallback) does not cover the cold-start case where the agent's first fetch lands BEFORE the bot reviewer (Greptile) has posted. Both sources return zero findings; the Step 6 exit condition false-positively declares the PR review-clean even though the bot review is still in flight. The Step 4 polling loop covers any cycle that includes a push, but the cold-start one-shot review-cycle entry path is uncovered.",
379
+ "body": "**Source:** #796 -- the `skills/deft-directive-review-cycle/SKILL.md` Phase 2 Step 1 dual-source-fetch contract (which correctly catches the `Comments Outside Diff` case via `gh pr view --comments` + MCP `get_review_comments` or `gh api` fallback) does not cover the cold-start case where the agent's first fetch lands BEFORE the bot reviewer (Greptile) has posted. Both sources return zero findings; the Step 6 exit condition false-positively declares the PR review-clean even though the bot review is still in flight. The Step 4 polling loop covers any cycle that includes a push, but the cold-start one-shot review-cycle entry path is uncovered.\n\n**Canonical encoding (strongest-applicable layer):** the operative `~` SHOULD rule (`Late-arriving bot review re-check`) and the matching `\u2297` MUST NOT rule (no exit on a single empty fetch -- re-fetch at least once after a ~60s delay first) live in `skills/deft-directive-review-cycle/SKILL.md` Phase 2 Step 1, before the Step 6 exit-condition evaluation. Tier 1 deterministic enforcement: `tests/content/test_review_cycle_skill.py` regex-asserts the rule presence + canonical phrasing tokens (`re-fetch`, `60s`, `before evaluating`, `re-fetch at least once`) plus a defence-in-depth guard against the cp1252 mojibake form `\u0393\u00e8\u00f9` of the `\u2297` glyph (the corruption fixed in PR #844 review-cycle on the same cohort's pending vBRIEFs). The poller template at `templates/swarm-greptile-poller-prompt.md` already handles the same case for push-driven cycles via its loop body -- the SKILL rule above closes the orthogonal cold-start path.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the SKILL Phase 2 Step 1 rules + the content test; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `skills/deft-directive-review-cycle/SKILL.md` Phase 2 Step 1 (primary encoding); `tests/content/test_review_cycle_skill.py` (deterministic contract); `templates/swarm-greptile-poller-prompt.md` (no-op for the poller -- loop body already handles this; positive guidance for the one-shot review-cycle entry path); user rule `KthiAoZEVMVffm0rv4vxQ6` (qualitative precedent); existing `## PR Review Process (2026-03)` #1 lesson (sibling: dual-source fetching + Comments Outside Diff); this lesson (#796)."
380
+ },
381
+ {
382
+ "id": "ps-5-1-non-ascii-round-trip-corruption-elevated-to-deterministic-tier-2026-05",
383
+ "title": "PS 5.1 Non-ASCII Round-Trip Corruption -- elevated to deterministic tier (2026-05)",
384
+ "date": "2026-05",
385
+ "issue_refs": [],
386
+ "tags": [
387
+ "encoding",
388
+ "powershell",
389
+ "windows"
390
+ ],
391
+ "source": "#798 -- 4th recurrence of PS 5.1 silently corrupting non-ASCII content during read+write round-trips. Recurrence chain: #236 (t1.11.1, scm/github.md `Get-Content -Raw` + BOM-safe write rules), #240 (t1.11.2, multi-line PS here-string rule), #283 (t1.20.1, AGENTS.md `New-Object System.Text.UTF8Encoding $false` rule), and PR #795 (2026-05-01) where a maintainer with all three prose rules loaded ran `Get-Content -Raw | -replace | [System.IO.File]::WriteAllText` on `CHANGELOG.md` and corrupted 132 lines. The corruption happened on the READ side (PS 5.1 `Get-Content -Raw` decoded via the active codepage, typically cp1252 or cp437 on Windows) BEFORE any safe UTF-8 write could preserve the bytes; the existing rules all named write-side smells, not the root cause.",
392
+ "body": "**Source:** #798 -- 4th recurrence of PS 5.1 silently corrupting non-ASCII content during read+write round-trips. Recurrence chain: #236 (t1.11.1, scm/github.md `Get-Content -Raw` + BOM-safe write rules), #240 (t1.11.2, multi-line PS here-string rule), #283 (t1.20.1, AGENTS.md `New-Object System.Text.UTF8Encoding $false` rule), and PR #795 (2026-05-01) where a maintainer with all three prose rules loaded ran `Get-Content -Raw | -replace | [System.IO.File]::WriteAllText` on `CHANGELOG.md` and corrupted 132 lines. The corruption happened on the READ side (PS 5.1 `Get-Content -Raw` decoded via the active codepage, typically cp1252 or cp437 on Windows) BEFORE any safe UTF-8 write could preserve the bytes; the existing rules all named write-side smells, not the root cause.\n\n**Canonical encoding (strongest-applicable layer):** the rule body lives in the deterministic gate -- `scripts/verify_encoding.py` scans tracked text files for U+FFFD replacement chars, the curated CP1252-as-UTF-8 / CP437-as-UTF-8 mojibake bigram set, and unexpected UTF-8 BOM on .md/.json/.yml/.yaml/.txt; three-state exit (0 clean / 1 corruption with `path:line:[label]` diagnostic / 2 config error). Wired into `task check` via `task verify:encoding` and into `.githooks/pre-commit` via `--staged` (alongside the existing #747 branch-protection step). The `## PowerShell` section of `AGENTS.md` carries the root-cause cross-reference covering both reads and writes plus the four-recurrence record. Tier 1 deterministic enforcement: `tests/cli/test_verify_encoding.py` parametrizes over the bigram set, U+FFFD detection, BOM detection, allow-list bypass, three-state exit codes, and the markdown inline-code false-positive guard. Personal rule `3MieNBQjwlObZM1If060iy` on the user's Warp profile encodes the same prohibition for the swarm cohort -- the AGENTS.md rule is the project-side mirror so consumer-installed copies of deft carry the rule even when the personal rule is not loaded.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the deterministic gate + the AGENTS.md root-cause cross-reference; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `scripts/verify_encoding.py` (deterministic gate); `tasks/verify.yml::encoding` + `Taskfile.yml::check` (Taskfile surface); `.githooks/pre-commit` (commit-time enforcement); `AGENTS.md` `## PowerShell` (root-cause rule, project-side mirror of personal rule `3MieNBQjwlObZM1If060iy`); `tests/cli/test_verify_encoding.py` (regression coverage); existing Windows File Editing #2 / #4 (write-side prose-tier rules this gate elevates); existing #814 lesson (sibling PS 5.1 cp1252 stdout fix); recurrence record (#236 / #240 / #283 / PR #795); this lesson (#798).\n\n**Runtime-subprocess variant (#1002, 2026-06):** the same cp1252 root cause recurs at the subprocess-READ surface, not just the file read/write surface that `scripts/verify_encoding.py` covers. `scripts/triage_bootstrap.py::_infer_repo_from_git` captured `git remote get-url origin` via `subprocess.run(..., text=True)` with no explicit `encoding`, so on Windows Python's subprocess reader thread decoded stdout/stderr via the active codepage (cp1252) and raised `UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f` from `subprocess._readerthread` on any non-ASCII byte (surfaced as a `PytestUnhandledThreadExceptionWarning` in the triage_bootstrap suite). The #798 `verify_encoding.py` gate scans tracked text files only -- it cannot see a runtime subprocess decode -- so the subprocess-capture side stays governed by the #1366 `## Safe subprocess capture` rule in `AGENTS.md`. Fixed by routing the capture through `scripts/_safe_subprocess.py::run_text` (#1366), which FORCES `encoding=\"utf-8\", errors=\"replace\"` so undecodable bytes become U+FFFD instead of crashing the read.\n\n**Cross-references (runtime variant):** `AGENTS.md` `## Safe subprocess capture (#1366)` (governing rule); `scripts/_safe_subprocess.py::run_text` (UTF-8-safe helper); `scripts/triage_bootstrap.py::_infer_repo_from_git` (the migrated call site); `tests/test_triage_bootstrap.py` (regression coverage); recurrence chain #236 / #240 / #283 / PR #795 / #798 (file side) + #1366 (subprocess side); this variant (#1002)."
393
+ },
394
+ {
395
+ "id": "gh-cli-graphql-bucket-exhaustion-rest-fallback-utf-8-payload-pattern-2026-05",
396
+ "title": "gh CLI GraphQL Bucket Exhaustion + REST Fallback + UTF-8 Payload Pattern (2026-05)",
397
+ "date": "2026-05",
398
+ "issue_refs": [],
399
+ "tags": [
400
+ "github",
401
+ "encoding",
402
+ "powershell"
403
+ ],
404
+ "source": "mid-session 2026-05-07 \u2014 `gh issue create` against `deftai/webinstaller` returned `GraphQL: API rate limit already exceeded for user ID ...` while `gh api rate_limit` reported `core: 4996/5000 remaining`, `graphql: 0/5000 remaining`. The same operation completed instantly via `gh api repos/<owner>/<repo>/issues --method POST --input <payload.json>`. Same identity drove both surfaces; the rate-limit failure was bucket-specific, not global.",
405
+ "body": "**Source:** mid-session 2026-05-07 \u2014 `gh issue create` against `deftai/webinstaller` returned `GraphQL: API rate limit already exceeded for user ID ...` while `gh api rate_limit` reported `core: 4996/5000 remaining`, `graphql: 0/5000 remaining`. The same operation completed instantly via `gh api repos/<owner>/<repo>/issues --method POST --input <payload.json>`. Same identity drove both surfaces; the rate-limit failure was bucket-specific, not global.\n\n**1. Many `gh` subcommands route through GraphQL \u2014 the `core` REST bucket is independent**\n\n`gh issue create`, `gh issue close`, `gh issue comment`, `gh pr ready`, `gh pr merge`, and most other write operations issue a GraphQL mutation, billing the `graphql` bucket (5000 points/hr per user). The `gh api ...` family (default REST) bills the `core` REST bucket (5000 calls/hr per user). The two are independent. When `graphql` is exhausted, `gh issue ...` fails hard but `gh api repos/<owner>/<repo>/issues --method POST/PATCH/GET ...` continues to work. MUST inspect `gh api rate_limit` per-bucket `remaining` rather than treating the failure message as a global rate-limit signal.\n\n**2. REST fallback for issue mutations**\n\nWhen the `graphql` bucket is contended (parallel agents on shared identity, swarm-shaped polling, etc.) MUST prefer REST equivalents:\n- Create issue: `gh api repos/<owner>/<repo>/issues --method POST --input <payload.json>`\n- Post comment: `gh api repos/<owner>/<repo>/issues/<N>/comments --method POST --input <payload.json>`\n- Close issue: `gh api repos/<owner>/<repo>/issues/<N> --method PATCH -f state=closed -f state_reason=completed`\n- Open PR: `gh api repos/<owner>/<repo>/pulls --method POST --input <payload.json>` with `{title, head, base, body}`\n\nClosing-keyword auto-close on PR squash merge runs server-side and is unaffected by the agent's bucket choice.\n\n**3. UTF-8 safe `gh api --input` payload pattern on PS 5.1**\n\n`gh api --input <file>` reads a JSON file; building that file via PS 5.1 inline string operations corrupts non-ASCII content (em dashes, arrows, smart quotes \u2014 see existing PS 5.1 lessons #236 / #240 / #283 / PR #795 / #798). The canonical UTF-8 safe pattern is:\n\n(a) Write the markdown body to a temp file via the `create_file` tool OR Python `pathlib.Path(p).write_text(text, encoding='utf-8')`.\n(b) Build the JSON wrapper via Python: `import json, pathlib; pathlib.Path(payload).write_text(json.dumps({'body': pathlib.Path(body).read_text(encoding='utf-8')}), encoding='utf-8')`.\n(c) Invoke `gh api ... --input <payload>`.\n\nMUST NOT round-trip non-ASCII content through PS 5.1 `Get-Content` / `Set-Content` / `-replace` / backtick-n interpolation as the JSON wrapper.\n\n**Cross-references:** `scripts/verify_encoding.py` (sibling deterministic gate, #798); existing #236 / #240 / #283 / PR #795 / #798 PS 5.1 chain; existing `## Windows File Editing` #2 / #4 (write-side prose-tier rules); 2026-05-07 mid-session surfacing (deftai/webinstaller#171 filing + deftai/directive#884 closeout via REST)."
406
+ },
407
+ {
408
+ "id": "cross-machine-parallel-agents-single-agent-swarm-pattern-2026-05",
409
+ "title": "Cross-Machine Parallel Agents + Single-Agent Swarm Pattern (2026-05)",
410
+ "date": "2026-05",
411
+ "issue_refs": [],
412
+ "tags": [
413
+ "agent-orchestration",
414
+ "swarm",
415
+ "github"
416
+ ],
417
+ "source": "2026-05-07 session running #884 closeout in parallel with another agent's #947 cache-cap implementation on a different machine. Both agents authenticated as the same GitHub identity. The closeout used `skills/deft-directive-swarm/SKILL.md` with N=1 sub-agent \u2014 not the multi-agent vBRIEF allocation the skill was designed for, but the orchestrator-yields pattern and the REST-only sub-agent dispatch worked cleanly.",
418
+ "body": "**Source:** 2026-05-07 session running #884 closeout in parallel with another agent's #947 cache-cap implementation on a different machine. Both agents authenticated as the same GitHub identity. The closeout used `skills/deft-directive-swarm/SKILL.md` with N=1 sub-agent \u2014 not the multi-agent vBRIEF allocation the skill was designed for, but the orchestrator-yields pattern and the REST-only sub-agent dispatch worked cleanly.\n\n**1. Cross-machine parallel agents share API quota when they share identity**\n\nTwo agents on different machines but the same GitHub user share the `core` (5000/hr) and `graphql` (5000pts/hr) buckets at the personal-account level. Local file overlap is impossible (different filesystems), but API contention is real \u2014 the closeout session's `graphql` exhaustion was plausibly driven by the other agent's polling on the other machine. MUST treat shared-identity multi-machine agents as competing for the same API quota, even though file and branch isolation are automatic. SHOULD prefer GraphQL-light paths (REST mutations, ghx-cached reads, longer poll intervals) when running concurrent shared-identity sessions, and SHOULD agree explicit ownership of shared append-only files (CHANGELOG.md `[Unreleased]`) before launching the parallel work.\n\n**2. Single-agent swarm-skill use is a legitimate steerability primitive**\n\n`skills/deft-directive-swarm/SKILL.md` is documented as \"parallel local agent orchestration\" and the Phase 0\u20136 ladder is sized for N\u22652. But the Phase 6 Sub-Agent Role Separation pattern (#727) \u2014 parent dispatches a fresh sub-agent via `start_agent`, parent yields with no tool calls, sub-agent reports back via messaging \u2014 works as a general orchestration primitive even at N=1, because it preserves the parent's steerability while the sub-agent runs the API-bound work. The Phase 0\u20133 implementation ladder is skipped when the sub-agent's task is non-code (administrative closeout, polling, observation). MAY use single-agent dispatch under this skill when the user explicitly directs it; the swarm-vs-direct decision is about steerability and conversation isolation, not parallelism.\n\n**3. Non-code sub-agents do not require the implementation preflight gate (#810)**\n\nThe implementation preflight gate (`scripts/preflight_implementation.py`, surfaced via `task vbrief:preflight`) is documented as a precondition for \"code-writing tool calls or `start_agent` dispatch for implementation\". Administrative closeout sub-agents (post comment + PATCH state, run smoke commands, verify deliverables, file follow-up issues) are not implementation \u2014 they write no repo files, branch nothing, open no PR. MUST NOT require a scope vBRIEF for a non-code sub-agent. MUST still require an explicit action-verb directive from the user before dispatching a non-code sub-agent that has potential side effects on shared state (issue mutations, force-pushes, deletions, branch deletes).\n\n**4. Constrain sub-agent prompts with `\u2297` MUST-NOT lines around scope expansion**\n\nA closeout sub-agent given freedom to \"expand scope as appropriate\" can drift into an adjacent issue's territory (broader migration, related cache work, unrelated polish) if the prompt does not name the boundaries. MUST encode the boundaries as explicit `\u2297` MUST NOT lines in the dispatch prompt: which files NOT to touch, which adjacent issues are out of scope, which CHANGELOG/branch surfaces are off-limits, and a halt-on-deliverable-missing rule that surfaces the gap to the parent rather than improvising a fix. Explicit constraints are cheaper than retroactive rebasing or scope-creep cleanup.\n\n**Cross-references:** `skills/deft-directive-swarm/SKILL.md` Phase 6 Sub-Agent Role Separation (#727 primary encoding); `scripts/preflight_implementation.py` (#810 implementation gate); 2026-05-07 #884 closeout sub-agent (REST-only, comment + PATCH, halt-on-deliverable-missing pattern); 2026-05-07 #947 sibling implementation on second machine (shared-identity coordination via explicit CHANGELOG ownership disclaim)."
419
+ },
420
+ {
421
+ "id": "ghx-within-session-cache-vs-deft-cache-cross-session-persistence-2026-05",
422
+ "title": "ghx Within-Session Cache vs deft-cache Cross-Session Persistence (2026-05)",
423
+ "date": "2026-05",
424
+ "issue_refs": [],
425
+ "tags": [
426
+ "github",
427
+ "release",
428
+ "encoding"
429
+ ],
430
+ "source": "#884 (ghx adoption) and #883 (deft-cache) both shipped at v0.26.0; #884 closeout 2026-05-07 surfaced confusion about whether the two layers are redundant.",
431
+ "body": "**Source:** #884 (ghx adoption) and #883 (deft-cache) both shipped at v0.26.0; #884 closeout 2026-05-07 surfaced confusion about whether the two layers are redundant.\n\n**1. ghx and deft-cache target orthogonal failure modes**\n\n`ghx` (brunoborges/ghx, adopted via #884) is a `gh` proxy that adds in-memory read cache + singleflight coalescing + auto-invalidation on mutations. State lives only for the life of the daemon; no persistence; no quarantine. It saves the same-process / multi-agent polling case (5 swarm agents calling `gh pr checks` on the same PR collapse to 1 API call). It does NOT save a single mutation's GraphQL cost, since mutations invalidate cache entries rather than consume them.\n\n`deft-cache` (designed in #883, completed at v0.26.0) is a cross-session on-disk cache + quarantine layer. Each entry is split into `raw.json` (immutable audit, never LLM-fed) and `content.md` (post-quarantine, LLM-safe). ETag refresh; mutation-triggered invalidation; versioned scanner with append-only `quarantine-audit.jsonl`. Saves the cross-session re-fetch cost AND provides a uniform security-quarantine surface for ingested content.\n\nThe two layers stack: `scm:*` tasks (#881) \u2192 `ghx` (within-session dedup) \u2192 `cache:put` (cross-session persistence + quarantine) \u2192 consumers (`candidates.jsonl` from triage #845, wiki pages from #610, etc.). MUST NOT treat `ghx` as a substitute for deft-cache or vice versa \u2014 they share zero failure modes.\n\n**2. ghx does not save the rate-limit-during-mutation case**\n\nThe 2026-05-07 session surfaced the `graphql` bucket exhaustion failure mode for `gh issue create`. `ghx` would not have prevented it, because `ghx` caches reads and invalidates on writes \u2014 a single mutation is on the cost path of `ghx`, not the cache-hit path. The right reflex for graphql-bucket exhaustion is REST fallback (see `## gh CLI GraphQL Bucket Exhaustion + REST Fallback + UTF-8 Payload Pattern (2026-05)`), not `ghx` adoption. MUST treat `ghx` adoption as an optimization for the polling / read-heavy case (swarm monitoring, status checks, repeated `pr view` / `pr checks` / `issue view` calls), not a rate-limit panacea.\n\n**Cross-references:** #884 (ghx adoption \u2014 AGENTS.md prefer rule + `task setup` install + CI install pinned to `v1.5.1`); #883 (deft-cache design + completion at v0.26.0); #881 (`scm:*` task namespace, the consumer of both layers); existing `## gh CLI GraphQL Bucket Exhaustion + REST Fallback + UTF-8 Payload Pattern (2026-05)` (sibling lesson on the orthogonal mutation-cost failure mode); brunoborges/ghx (upstream)."
432
+ },
433
+ {
434
+ "id": "rest-fallback-module-surface-2026-05",
435
+ "title": "REST-fallback module surface (2026-05)",
436
+ "date": "2026-05",
437
+ "issue_refs": [],
438
+ "tags": [
439
+ "github",
440
+ "release",
441
+ "encoding"
442
+ ],
443
+ "source": "#961 -- the gh-mutation/read REST-fallback pattern documented in `## gh CLI GraphQL Bucket Exhaustion + REST Fallback + UTF-8 Payload Pattern (2026-05)` (PR #960) was reified at the script layer per the [AXIOM] strongest-applicable-layer rule. New `scripts/gh_rest.py` exposes seven typed Python helpers (5 mutations + 2 reads) that wrap `gh api` against explicit REST endpoints; companion refactor of `scripts/release_publish.py` replaces its two GraphQL `gh release view --json` / `gh release edit ... --draft=false` calls with inline `gh api` REST equivalents (recurrence record: the v0.26.1 publish failed today at the GraphQL bucket exhaustion). The module surface is the deterministic-tier follow-up; this lessons entry exists for discoverability and recurrence-record citation only.",
444
+ "body": "**Source:** #961 -- the gh-mutation/read REST-fallback pattern documented in `## gh CLI GraphQL Bucket Exhaustion + REST Fallback + UTF-8 Payload Pattern (2026-05)` (PR #960) was reified at the script layer per the [AXIOM] strongest-applicable-layer rule. New `scripts/gh_rest.py` exposes seven typed Python helpers (5 mutations + 2 reads) that wrap `gh api` against explicit REST endpoints; companion refactor of `scripts/release_publish.py` replaces its two GraphQL `gh release view --json` / `gh release edit ... --draft=false` calls with inline `gh api` REST equivalents (recurrence record: the v0.26.1 publish failed today at the GraphQL bucket exhaustion). The module surface is the deterministic-tier follow-up; this lessons entry exists for discoverability and recurrence-record citation only.\n\n**Module surface (`scripts/gh_rest.py`):** `rest_create_issue`, `rest_post_comment`, `rest_close_issue`, `rest_open_pr`, `rest_merge_pr`, `rest_issue_view`, `rest_pr_view`. Each returns the raw GitHub REST response dict; raises `GhRestError(stderr, exit_code, endpoint, payload, hint)` on non-zero `gh` exit. JSON payloads are built via `tempfile.mkstemp` + `Path.write_text(text, encoding=\"utf-8\")` so the PS 5.1 mojibake hazard chain (#236 / #240 / #283 / PR #795 / #798) is closed at every gh-mutation call site in one place. Binary routing via `scripts/scm.py::resolve_binary` (ghx -> gh ladder per #884).\n\n**Releases intentionally NOT wrapped:** per issue #961 charter (and the predecessor agent's BLOCKED scope-clarification analysis), releases are out of scope for `scripts/gh_rest.py` -- `task release` (#74) owns that surface, and the companion `scripts/release_publish.py` (#716) ships its own inline REST calls in the same PR rather than extending the cross-cutting helper. Future release-related REST helpers belong in the release pipeline, not in `gh_rest.py`.\n\n**REST-impossible mutations (known limitations, do NOT work around):** two GitHub mutations are GraphQL-only with no REST equivalent. `gh pr ready` (markPullRequestReadyForReview) -- under bucket exhaustion, draft PRs CANNOT be promoted to ready without waiting for reset; workaround: open PRs non-draft when possible. `gh pr review --approve` / `--request-changes` (addPullRequestReview) -- workaround: post a comment via `rest_post_comment` (no approval semantics, but unblocks conversation). The module docstring documents these explicitly so callers do not expect them.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the module + tests + module docstring; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `scripts/gh_rest.py` (deterministic encoding); `scripts/release_publish.py` (sibling concern, inline REST per #74 ownership); `tests/cli/test_gh_rest.py` (regression coverage); `tests/cli/test_release_publish.py::TestRestRegression961` (v0.26.1 publish-failure repro pinned); existing `## gh CLI GraphQL Bucket Exhaustion + REST Fallback + UTF-8 Payload Pattern (2026-05)` lesson (the prose tier this PR elevates); `templates/agent-prompt-preamble.md` S5 (REST-by-default rule); existing #884 ghx adoption + #798 PS 5.1 deterministic gate; this lesson (#961)."
445
+ },
446
+ {
447
+ "id": "surface-conflicts-pick-one-explain-flag-the-other-2026-05",
448
+ "title": "Surface Conflicts: Pick One, Explain, Flag the Other (2026-05)",
449
+ "date": "2026-05",
450
+ "issue_refs": [],
451
+ "tags": [
452
+ "encoding",
453
+ "context",
454
+ "debugging"
455
+ ],
456
+ "source": "#1005 -- external-source rule from @Mnilax (\"Karpathy's 4 CLAUDE.md rules cut mistakes from 41% to 11%. After 30 codebases, I added 8 more\", 6 weeks of testing across 30 codebases / 50 representative tasks). Concrete failure mode from the source: a codebase with two error-handling patterns (async/await with explicit try/catch AND a global error boundary) led an agent to write new code that satisfied BOTH simultaneously -- doubled error handlers, errors swallowed twice, 30 minutes of triage to discover the root cause. No internal deft-side recurrence record at landing time; this entry is forward-looking institutional memory rather than a citation of prior occurrences.",
457
+ "body": "**Source:** #1005 -- external-source rule from @Mnilax (\"Karpathy's 4 CLAUDE.md rules cut mistakes from 41% to 11%. After 30 codebases, I added 8 more\", 6 weeks of testing across 30 codebases / 50 representative tasks). Concrete failure mode from the source: a codebase with two error-handling patterns (async/await with explicit try/catch AND a global error boundary) led an agent to write new code that satisfied BOTH simultaneously -- doubled error handlers, errors swallowed twice, 30 minutes of triage to discover the root cause. No internal deft-side recurrence record at landing time; this entry is forward-looking institutional memory rather than a citation of prior occurrences.\n\n**Canonical encoding (strongest-applicable layer):** the operative `!` MUST rules and `\u2297` MUST NOT anti-patterns live in `coding/hygiene.md` `## Surface Conflicts: Pick One, Explain, Flag the Other (#1005)` (the host section is adjacent to the existing `## Legacy and Deprecated Code` rules because the dropped pattern lands under those rules once flagged) plus a cross-reference in `coding/coding.md` `## Anti-Patterns` (the global anti-pattern index that scope-readers consult before any implementation). The build-skill enforcement surface lives in `skills/deft-directive-build/SKILL.md` Step 1 -- when the skill scans the existing codebase during scope understanding, it MUST surface contradicting patterns before implementation begins and MUST NOT begin implementation against an averaged blend. Tier 1 deterministic enforcement: `tests/content/test_coding_rules.py` regex-asserts the rule presence, the `!` / `\u2297` token mix, the anti-pattern entry, and the skill cross-reference.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in `coding/hygiene.md` + the build-skill cross-reference + the content test; this lessons entry exists for discoverability + future-recurrence-record citation only.\n\n**Cross-references:** `coding/hygiene.md` `## Surface Conflicts` (primary encoding); `coding/coding.md` `## Anti-Patterns` (cross-reference); `skills/deft-directive-build/SKILL.md` Step 1 (skill-side enforcement); `tests/content/test_coding_rules.py` (deterministic contract); sibling `coding/hygiene.md` `## Legacy and Deprecated Code` (the dropped pattern lands here once flagged); related #866 (deep-module-principle -- shallow averaging is the code smell this prevents) and #972 (over-editing -- agents should not introduce new patterns when contradicting patterns already exist); external source: @Mnilax, https://x.com/Mnilax/status/2053116311132155938; this lesson (#1005)."
458
+ },
459
+ {
460
+ "id": "fail-loud-completion-claims-require-outcome-verification-2026-05",
461
+ "title": "Fail Loud: Completion Claims Require Outcome Verification (2026-05)",
462
+ "date": "2026-05",
463
+ "issue_refs": [],
464
+ "tags": [
465
+ "review-cycle",
466
+ "encoding",
467
+ "context"
468
+ ],
469
+ "source": "#1006 -- external-source rule from the same @Mnilax thread. Concrete failure mode from the source: a database migration claimed \"completed successfully\" but had silently skipped 14% of records on a constraint violation; the skip was logged but not surfaced; the bad reports were discovered 11 days later. Other examples cited: \"tests pass\" claimed when tests were skipped, \"feature works\" claimed when only the happy path was verified. No internal deft-side recurrence record at landing time; this entry is forward-looking institutional memory.",
470
+ "body": "**Source:** #1006 -- external-source rule from the same @Mnilax thread. Concrete failure mode from the source: a database migration claimed \"completed successfully\" but had silently skipped 14% of records on a constraint violation; the skip was logged but not surfaced; the bad reports were discovered 11 days later. Other examples cited: \"tests pass\" claimed when tests were skipped, \"feature works\" claimed when only the happy path was verified. No internal deft-side recurrence record at landing time; this entry is forward-looking institutional memory.\n\n**Canonical encoding (strongest-applicable layer):** the operative `!` MUST rules and `\u2297` MUST NOT anti-patterns live in `coding/coding.md` `## Fail Loud: Completion Claims Require Outcome Verification (#1006)` (host section is positioned after `## Quality Standards` because the rule extends the existing `\u2297 Claim checks passed without running them` anti-pattern from process to outcome). The review-cycle enforcement surface lives in `skills/deft-directive-review-cycle/SKILL.md` Phase 2 Step 3 -- when reporting fix-batch completion, the skill MUST surface OUTCOME counts (P0/P1 finding counts addressed, test-collection counts) rather than intent-level claims. The rule is the OPERATIONAL complement to the EPISTEMIC honesty rules in `main.md` morals (don't lie) and the output-side complement to goal-gate-determinism (#852) and machine-verifiable-spec (#973). Tier 1 deterministic enforcement: `tests/content/test_coding_rules.py` regex-asserts the rule presence, the `!` / `\u2297` token mix, the anti-pattern entry, and the review-cycle skill cross-reference.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in `coding/coding.md` + the review-cycle skill cross-reference + the content test; this lessons entry exists for discoverability + future-recurrence-record citation only.\n\n**Cross-references:** `coding/coding.md` `## Fail Loud` (primary encoding); `coding/coding.md` `## Anti-Patterns` (cross-reference); `skills/deft-directive-review-cycle/SKILL.md` Step 3 (skill-side enforcement on fix-batch completion claims); `skills/deft-directive-build/SKILL.md` Step 4 Quality Gates (sibling consumer for task-completion claims); `tests/content/test_coding_rules.py` (deterministic contract); sibling `coding/hygiene.md` `## Error Handling: No Hiding` (the same hiding pattern at the code-write level, not the claim level); related #852 (goal-gate-determinism), #973 (machine-verifiable-spec), #972 (over-editing); external source: @Mnilax, https://x.com/Mnilax/status/2053116311132155938; this lesson (#1006)."
471
+ },
472
+ {
473
+ "id": "install-refresh-contract-self-healing-case-k-install-location-mismatch-classifier-2026-05",
474
+ "title": "Install/Refresh Contract Self-Healing -- Case K Install-Location-Mismatch Classifier (2026-05)",
475
+ "date": "2026-05",
476
+ "issue_refs": [],
477
+ "tags": [
478
+ "swarm",
479
+ "encoding",
480
+ "release"
481
+ ],
482
+ "source": "#1046 -- 2026-05-11 consumer install of `v0.27.1` at `<project>/.deft/core/` put an agent into an unrecoverable Case G refresh loop: `AGENTS.md` referenced `deft/skills/...` paths that did not resolve, `QUICK-START.md` Step 2b detected the staleness, Case G refreshed the managed section with byte-identical bytes (the template had already been flipped to canonical paths in PR #1043), reported success, and the next session re-detected the same staleness. Issue #1046 documented seven framework gaps surfaced by the single install; PR-A of the 3-PR cohort (Choice C ADR per PR #1051) closes the first two (AC-1 canonical-path enforcement / absorbs #1045; AC-2 Case K classifier).",
483
+ "body": "**Source:** #1046 -- 2026-05-11 consumer install of `v0.27.1` at `<project>/.deft/core/` put an agent into an unrecoverable Case G refresh loop: `AGENTS.md` referenced `deft/skills/...` paths that did not resolve, `QUICK-START.md` Step 2b detected the staleness, Case G refreshed the managed section with byte-identical bytes (the template had already been flipped to canonical paths in PR #1043), reported success, and the next session re-detected the same staleness. Issue #1046 documented seven framework gaps surfaced by the single install; PR-A of the 3-PR cohort (Choice C ADR per PR #1051) closes the first two (AC-1 canonical-path enforcement / absorbs #1045; AC-2 Case K classifier).\n\n**Failure mode:** the existing `_classify_install_layout` detector (`run::_classify_install_layout`, #992 PR3) classified consumer installs into A/B/C/D based purely on `deft/` and `.deft/core/` directory presence; it did NOT distinguish the failure class where AGENTS.md's managed section is byte-current but the declared install path does not resolve. Case G's prescribed refresh remediation only fixes byte-different staleness; byte-current + path-unresolved is a different failure class that needs a different remediation (`task framework:doctor`, forthcoming in PR-B). Until PR-A landed, the consumer-side routing had no way to surface \"the install location itself diverges from what the template declares\" as a distinct state.\n\n**Canonical encoding (strongest-applicable layer):** the rule body lives in `run::_classify_install_layout` (extended with the new Case K return value) plus the three new helpers `_install_path_resolves`, `_template_matches_managed_section`, `_agents_md_declared_main_md` (+ module-level regex `_AGENTS_DECLARED_MAIN_MD_PATTERN`). The K-specific auto-prompt branch in `_format_install_layout_prompt` points at `task framework:doctor` (forthcoming in PR-B) rather than the relocator. `templates/agents-entry.md` carries the canonical install-path literals (flipped in PR #1043) and is pinned by the new contract regression at `tests/contract/test_no_legacy_deft_paths_in_agents_template.py`. `QUICK-START.md` Step 2b splits staleness detection three ways (byte-different content -> Case G; byte-current + path-unresolved -> new Case K; legacy `deft/skills/` path missing -> Case G as v0.19 backstop). Tier 1 deterministic enforcement: `tests/cmd_gate/test_case_k.py` ships 28 regression tests across 7 suites covering positive K detection, install-dir-presence priority over K (A/B/C take precedence), the D-vs-K distinction on byte-different vs byte-current AGENTS.md, the helper contracts, the K-specific prompt format, gate-side emission, and the read-only contract (snapshot before/after + poisoned `subprocess.run` / `subprocess.Popen` / `read_yn` / `ask_confirm` MUST NOT be invoked from any K code path). The existing 28 tests in `tests/cmd_gate/test_state_detection.py` continue to pass unmodified -- the K extension is additive.\n\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the classifier + helpers + the deterministic test suite + the QUICK-START dispatch + the contract regression; this lessons entry exists for discoverability + recurrence-record citation only.\n\n**Cross-references:** `run::_classify_install_layout` + helpers (primary encoding); `templates/agents-entry.md` (canonical install-path source of truth); `tests/contract/test_no_legacy_deft_paths_in_agents_template.py` (AC-1 regression pin / absorbs #1045); `tests/cmd_gate/test_case_k.py` (AC-2 deterministic coverage); `QUICK-START.md` Step 2b + Case K (consumer-side dispatch); existing #992 PR3 detector (parent A/B/C/D classifier this PR extends); PR #1043 / commit `985a7f2` (canonical-path flip origin); PR #1051 (cohort ADR; #1046 cohort plan); existing #810 lesson (vBRIEF preflight gate that PR-C will harden); existing #794 lesson (managed-section marker contract this detector reads); this lesson (#1046 PR-A; cohort umbrella #1046 stays OPEN until PR-C lands)."
484
+ },
485
+ {
486
+ "id": "install-refresh-contract-self-healing-vbrief-preflight-install-layout-resolver-fail-closed-2026-05",
487
+ "title": "Install/Refresh Contract Self-Healing -- vBRIEF Preflight Install-Layout Resolver + Fail-Closed (2026-05)",
488
+ "date": "2026-05",
489
+ "issue_refs": [],
490
+ "tags": [
491
+ "lifecycle",
492
+ "ci",
493
+ "encoding"
494
+ ],
495
+ "source": "#1046 PR-C / absorbs #1047 -- the Implementation Intent Gate (#810) is a safety gate, not a routing gate. Issue #1047 documented the safety-significant silent fail-open: the Taskfile target `tasks/vbrief.yml::preflight` wrapped a single `{{.DEFT_ROOT}}/scripts/preflight_implementation.py` path, and on consumer installs where the on-disk layout disagreed with the resolved `DEFT_ROOT` the gate either errored loudly (and the agent treated the gate as unreachable and routed around it) or accidentally returned exit 0 (the gate emitted \"preflight passed\" reasoning without ever evaluating the vBRIEF). The agent-side contract says #810 is in force on every install layout, but the gate was structurally unreachable on misconfigured installs -- materially worse than the gate not existing because the agent's reasoning records \"preflight passed\" in its log.",
496
+ "body": "**Source:** #1046 PR-C / absorbs #1047 -- the Implementation Intent Gate (#810) is a safety gate, not a routing gate. Issue #1047 documented the safety-significant silent fail-open: the Taskfile target `tasks/vbrief.yml::preflight` wrapped a single `{{.DEFT_ROOT}}/scripts/preflight_implementation.py` path, and on consumer installs where the on-disk layout disagreed with the resolved `DEFT_ROOT` the gate either errored loudly (and the agent treated the gate as unreachable and routed around it) or accidentally returned exit 0 (the gate emitted \"preflight passed\" reasoning without ever evaluating the vBRIEF). The agent-side contract says #810 is in force on every install layout, but the gate was structurally unreachable on misconfigured installs -- materially worse than the gate not existing because the agent's reasoning records \"preflight passed\" in its log.\n**Failure mode:** silent fail-open on the canonical safety gate. Per the issue: \"the agent's contract says #810 is in force, the agent will record 'preflight passed' in its reasoning, and the code-writing step proceeds.\" Distinct from the routing-gap failure modes (Findings #1, #2, #3 from #1046 / PR-A + PR-B) because the worst-case outcome is unauthorized code changes through a gate the contract says cannot be bypassed, not stale routing that the agent can subsequently correct.\n**Canonical encoding (strongest-applicable layer):** the rule body lives in the deterministic resolver `scripts/_resolve_preflight_path.py` and the two-step inline `sh:` block in `tasks/vbrief.yml::preflight`. The resolver probes three install layouts in priority order (`.deft/core/scripts/` canonical per #992; `deft/scripts/` legacy; `scripts/` in-repo); returns the resolved absolute path on a hit (exit 0); exits 2 with a structured `gate misconfigured: cannot resolve preflight_implementation.py at any expected path -- run \\`task framework:doctor\\` for diagnostics` error on stderr when no candidate resolves. The Taskfile target's two-step `resolved=$(...) || exit $?` shape propagates the resolver's exit code BEFORE the downstream `uv run python <script>` ever fires, so the gate cannot silently fail open on a misconfigured install. `templates/agents-entry.md` Implementation Intent Gate prose rewritten to drop the path-handling claim and refer to the resolver contract + `task framework:doctor` (the PR-B install-integrity probe from #1057). Tier 1 deterministic enforcement: `tests/cli/test_vbrief_preflight_resolver.py` covers the full state matrix (each layout alone resolves; priority order among multiple candidates; all-three-absent fail-closed with the structured error contract enumerating all three probed layouts + the diagnostic pointer). The existing `tests/cli/test_preflight_implementation.py` (#810 gate-evaluator state matrix) continues to pass unmodified -- PR-C does NOT touch the inner gate's vBRIEF-lifecycle logic, only the wrapping path resolution.\n**Why this is a short cross-reference, not a full prose rule:** per the Rule Authority [AXIOM] block in `main.md`, every rule MUST use the strongest applicable layer (deterministic > Taskfile > vBRIEF > RFC2119 > prose). The rule body lives in the resolver + the Taskfile + the deterministic test suite + the rewritten agents-entry prose; this lessons entry exists for discoverability + recurrence-record citation only.\n**Cross-references:** `scripts/_resolve_preflight_path.py` (primary encoding -- install-layout resolver); `tasks/vbrief.yml::preflight` (Taskfile fail-closed semantic); `templates/agents-entry.md` Implementation Intent Gate (consumer-side prose contract); `tests/cli/test_vbrief_preflight_resolver.py` (deterministic coverage); existing `tests/cli/test_preflight_implementation.py` (sibling #810 gate-evaluator, unchanged by PR-C); existing #810 lesson (the gate this resolver wraps); existing #992 (canonical install layout that made the legacy hardcoded path stale); existing #1046 PR-A lesson (sibling cohort entry: Case K classifier + canonical-path enforcement); #1054 (PR-A merge); #1057 (PR-B merge: `task framework:doctor` -- the diagnostic pointed at by the fail-closed error); this lesson (#1046 PR-C; closes #1047; closes the #1046 cohort)."
497
+ },
498
+ {
499
+ "id": "changelog-entry-style-brief-release-notes-not-implementation-detail-2026-05",
500
+ "title": "CHANGELOG entry style -- brief release-notes, not implementation detail (2026-05)",
501
+ "date": "2026-05",
502
+ "issue_refs": [],
503
+ "tags": [
504
+ "release",
505
+ "ci",
506
+ "github"
507
+ ],
508
+ "source": "Issue #1242. Recurrence anchor: v0.32.0 Phase 3 e2e rehearsal on 2026-05-19 -- `gh release create` exited HTTP 422 `body is too long (maximum is 125000 characters)` because the auto-flowed `[Unreleased]` section was ~140K chars. ~22 Wave-2d entries had drifted into multi-paragraph engineering-log walkthroughs (per-file line counts, per-test assertions, schema fragments, helper signatures).",
509
+ "body": "**Source:** Issue #1242. Recurrence anchor: v0.32.0 Phase 3 e2e rehearsal on 2026-05-19 -- `gh release create` exited HTTP 422 `body is too long (maximum is 125000 characters)` because the auto-flowed `[Unreleased]` section was ~140K chars. ~22 Wave-2d entries had drifted into multi-paragraph engineering-log walkthroughs (per-file line counts, per-test assertions, schema fragments, helper signatures).\n\n**Why this is a structural recurrence, not a one-off:** the release pipeline (`scripts/release.py::_section_for_version`) auto-promotes `[Unreleased]` -> `[<version>]` and pushes the section verbatim into the GitHub release body. The cap is a GitHub server-side hard limit; no client-side workaround clears it without either truncating release notes or shortening the entries themselves. Truncation hides changes from operators / consumers (the audience that actually reads the release body); shortening discipline keeps every change visible. Same anti-pattern is banned at the report layer by the personal ship-report rule (`Pfb0EDkthFmfzp2kLhs0Lc`) -- this lesson institutionalises it on the CHANGELOG side too.\n\n**Rule (canonical encoding in `CONTRIBUTING.md` `## CHANGELOG entry style (#1242)` + `AGENTS.md` `## CHANGELOG entry style (#1242)`):**\n\n- `[Unreleased]` and promoted-version entries MUST be brief release-notes (2-4 sentences, ~300-800 chars), not implementation detail.\n- Each entry MUST reference its canonical PR / issue number(s); preserve `Closes #N` / `Refs #N` tails.\n- Each entry MUST describe the user-visible change in plain English; no conventional-commit subjects, no internal change names.\n- Entries MUST NOT inline file paths, test counts, schema fragments, function signatures, or implementation walkthroughs.\n- Entries MUST NOT exceed roughly 800 chars; split user-visible bullets or move detail to PR body.\n- Entries SHOULD lead with user-visible benefit, then mechanism, then link.\n\n**v0.32.0 fix-forward:** the same #1242 PR that introduced this rule rewrote every existing `[Unreleased]` entry against it; the resulting section is ~15K chars (well under 80K target, ~88% under the 125K hard cap). `task release` dry-run passes; the release ships clean.\n\n**Why this is a prose-tier lesson, not a deterministic gate:** the rule landed v1 as prose (CONTRIBUTING.md + AGENTS.md + this lessons entry) because a robust char-count + per-entry gate needs a stable definition of \"entry boundary\" in markdown and an `--allow-list` for legitimate multi-paragraph cases (release-note migration guides, breaking-change blocks). A deterministic-tier lint gate that scans `CHANGELOG.md [Unreleased]` for over-length entries is tracked as a separate follow-up (#1239 / N5 pattern) so the rule and the gate evolve independently.\n\n**Cross-references:** `CONTRIBUTING.md` `## CHANGELOG entry style (#1242)` (primary reference for human contributors); `AGENTS.md` `## CHANGELOG entry style (#1242)` (agent-load surface); issue #1242 (RFC2119 rule body + acceptance criteria); release skill `skills/deft-directive-release/SKILL.md`; personal rule `Pfb0EDkthFmfzp2kLhs0Lc` (ship-report convention)."
510
+ },
511
+ {
512
+ "id": "debugging-and-root-cause-investigation-2026-06",
513
+ "title": "Debugging and root-cause investigation (2026-06)",
514
+ "date": "2026-06",
515
+ "issue_refs": [],
516
+ "tags": [
517
+ "debugging",
518
+ "ci",
519
+ "encoding"
520
+ ],
521
+ "source": "Issue #1621 (consolidates the stale #659 + #1173). Directive had no debugging capability; agents defaulted to guess-and-fix, thrashing on retries and treating the first plausible hypothesis as correct. The design is grounded in the obra/superpowers `systematic-debugging` pattern and the vendored `forensic-research` reference bundle (mandatory falsification, claim ledger, validator).",
522
+ "body": "**Source:** Issue #1621 (consolidates the stale #659 + #1173). Directive had no debugging capability; agents defaulted to guess-and-fix, thrashing on retries and treating the first plausible hypothesis as correct. The design is grounded in the obra/superpowers `systematic-debugging` pattern and the vendored `forensic-research` reference bundle (mandatory falsification, claim ledger, validator).\n\n**Canonical encoding (strongest-applicable layer):** the rule body lives in `coding/debugging.md` (Iron Law, four phases, the 3-fix architecture gate, evidence discipline, Fact vs Hypothesis labeling, observability-gap loop); the sustained multi-agent investigation posture lives in the `deft-directive-debug` skill; the investigation-ledger validator is the deterministic `task verify:investigation` gate (`scripts/verify_investigation.py`). `coding/coding.md` carries the short-form cross-reference + the #1621 anti-pattern entry. The vendored reference design sits under `docs/reference/forensic-research/`.\n\n**Why this lessons entry is a short cross-reference:** per the Rule Authority [AXIOM] in `main.md`, the rule body lives at the strongest layer (coding standard + skill + deterministic gate); this entry exists for discoverability + recurrence-record citation.\n\n**Cross-references:** `coding/debugging.md` (rule body); `skills/deft-directive-debug/SKILL.md` (workflow); `scripts/verify_investigation.py` + `tasks/verify.yml::investigation` (validator gate); `docs/reference/forensic-research/` (vendored design); #1580 (Fact vs Judgment labeling -- shared vocabulary); superseded #659 + #1173."
523
+ },
524
+ {
525
+ "id": "agentic-disposability-decisions-not-deadends-2026-06",
526
+ "title": "Disposable code & decaying negatives: record decisions, not dead-ends (2026-06)",
527
+ "date": "2026-06",
528
+ "issue_refs": [
529
+ "#584"
530
+ ],
531
+ "tags": [
532
+ "lifecycle",
533
+ "context"
534
+ ],
535
+ "source": "Issue #584 -- consumer request to keep intentionally-disabled / sub-optimal code in place so agents neither remove nor re-enable it. Closed won't-fix.",
536
+ "body": "**Source:** #584 -- consumer request to leave intentionally-disabled / sub-optimal code in place so agents don't delete it or \"helpfully\" re-enable it. Closed won't-fix after a design discussion.\n\n**Decision:** deft will NOT add an in-tree \"parked code\" mechanism or a project-local negative-results store. The delete-dead-code rule in `coding/hygiene.md` stands; no relaxation.\n\n**Principle (the reusable part):** In an agentic codebase, both *code* and *experiment-level negative results* are cheap to regenerate and decay quickly, so preserving them has low -- often negative -- ROI. A stale \"don't try X\" note actively misleads future agents away from approaches that later become viable (Chesterton's Fence in reverse). Record *stable design decisions* (ADR-grade), not disposable dead-ends. Genuinely-deferred work belongs in the tracker (a vBRIEF), optionally with a one-line `TODO(#ref)` pointer at the site -- never as commented-out or flag-disabled code.\n\n**Exception (already covered, needs no new machinery):** stable, expensive-to-rediscover, high-stakes negatives (licensing / legal constraints, hard API limits, incident post-mortems) DO warrant durable records -- but ADRs / post-mortems / the issue tracker already serve them.\n\n**Why prose-tier:** a governance / won't-build decision with no code rule to enforce; this entry exists for discoverability so the #584 discussion does not recur. Future duplicates close as duplicate-of-#584."
537
+ },
538
+ {
539
+ "id": "usermd-content-read-enforcement-2026-06",
540
+ "title": "USER.md content-read enforcement -- existence checks are insufficient (2026-06)",
541
+ "date": "2026-06",
542
+ "issue_refs": [
543
+ "#696"
544
+ ],
545
+ "tags": [
546
+ "context",
547
+ "lifecycle"
548
+ ],
549
+ "source": "Issue #696. Observed 2026-04-27: a returning session emitted the alignment confirmation, ran `Test-Path` on USER.md (-> True), declared all config present, and addressed the user by a name injected via a Warp Drive notebook instead of the name in USER.md. USER.md was never read; its `Personal (always wins)` precedence rule was never applied because that rule lives inside the file the agent skipped.",
550
+ "body": "**Source:** Issue #696. Observed 2026-04-27: a returning session emitted the alignment confirmation, ran `Test-Path` on USER.md (-> True), declared all config present, and addressed the user by a name injected via a Warp Drive notebook instead of the name in USER.md. USER.md was never read; its `Personal (always wins)` precedence rule was never applied because that rule lives inside the file the agent skipped.\n\n**Key insight:** A presence / existence check on USER.md is NOT a content read. An alignment confirmation that can be emitted without reading USER.md leaves a substitution gap: when external context (Warp Drive notebooks, MCP server outputs, prompt-injected preferences) defines the same fields as USER.md, the agent silently adopts the external value. External context sits OUTSIDE deft's precedence hierarchy -- USER.md `Personal (always wins)` must win, but only a real content read can apply that rule.\n\n**Rule:** The alignment confirmation MUST include data drawn from USER.md content -- specifically the addressing-name -- so the read is unfakeable (`Deft Directive active -- AGENTS.md loaded. Addressing you as: {Name}.`). Returning Sessions is promoted from unmarked prose to an enforced `!`/`\u2297` rule with an ordered read list (main.md -> USER.md -> PROJECT-DEFINITION) and an inline external-context precedence rule. A `Test-Path` substitution for a content read is an explicit anti-pattern.\n\n**Canonical encoding (strongest-applicable layer):** the rule body lives in `AGENTS.md` + `templates/agents-entry.md` (`## Returning Sessions` + `### Deft Alignment Confirmation`), propagated to the consumer managed-section via `task agents:refresh` (#1309). Deterministic shape-coverage: `tests/content/test_agents_md.py` (must-marker, Test-Path anti-pattern, name-echo, external-context precedence wording). The behavioral eval that actually catches the substitution failure class (conflicting USER.md vs external-context names) is deferred to its own issue (Fix D); the greet-skill structural cleanup is Fix E.\n\n**Cross-references:** `AGENTS.md` / `templates/agents-entry.md` `## Returning Sessions` + `### Deft Alignment Confirmation` (rule body); `tests/content/test_agents_md.py` (shape coverage); #163 (CLI gate parity, complementary); #258 (Warp Drive inventory, documentation half of the same root cause); #78 (adjacent bootstrap-update offer)."
551
+ }
552
+ ]
553
+ }