@tenkara/afm-linux-arm64 0.0.0-stage → 4.0.0-alpha.11

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 (283) hide show
  1. package/bin/afm +4 -0
  2. package/content/.coupling-baseline.tsv +1 -0
  3. package/content/.pending-commands.txt +2 -0
  4. package/content/CHANGELOG-archive.md +806 -0
  5. package/content/CHANGELOG.md +4330 -0
  6. package/content/PRINCIPLES.md +119 -0
  7. package/content/README-light.md +158 -0
  8. package/content/README.md +150 -0
  9. package/content/code-style/_any/file-size.md +62 -0
  10. package/content/code-style/_any/imports.md +68 -0
  11. package/content/code-style/_any/naming.md +72 -0
  12. package/content/code-style/_any/test-placement.md +57 -0
  13. package/content/code-style/nextjs-app-router/imports.md +50 -0
  14. package/content/code-style/nextjs-app-router/naming.md +50 -0
  15. package/content/code-style/prisma-postgres/error-boundary.md +55 -0
  16. package/content/code-style/prisma-postgres/imports.md +61 -0
  17. package/content/code-style/shadcn-ui/naming.md +48 -0
  18. package/content/code-style/stripe-billing/naming.md +76 -0
  19. package/content/code-style/trigger-dev-v4/imports.md +58 -0
  20. package/content/code-style/trigger-dev-v4/naming.md +49 -0
  21. package/content/code-style/trigger-dev-v4/test-placement.md +61 -0
  22. package/content/code-style/trpc-v11/error-boundary.md +51 -0
  23. package/content/code-style/trpc-v11/file-size.md +44 -0
  24. package/content/code-style/trpc-v11/naming.md +60 -0
  25. package/content/code-style/trpc-v11/test-placement.md +52 -0
  26. package/content/core/ach.md +260 -0
  27. package/content/core/afm.md +514 -0
  28. package/content/core/gotchas.md +18 -0
  29. package/content/core/prd.md +141 -0
  30. package/content/core/protocol.md +158 -0
  31. package/content/core/ust.md +65 -0
  32. package/content/guardrails/README.md +55 -0
  33. package/content/guardrails/dsl-com-ordem-obrigatoria.md +47 -0
  34. package/content/guardrails/exaustividade-de-union.md +48 -0
  35. package/content/guardrails/fronteira-de-import.md +57 -0
  36. package/content/hooks/README.md +70 -0
  37. package/content/interview/bootstrap.md +417 -0
  38. package/content/interview/promotion.md +115 -0
  39. package/content/interview/refactor.md +379 -0
  40. package/content/interview/triggers.md +107 -0
  41. package/content/modules/nextjs-app-router/ach.patch.md +44 -0
  42. package/content/modules/nextjs-app-router/gotchas.seed.md +78 -0
  43. package/content/modules/nextjs-app-router/rules.patch.md +35 -0
  44. package/content/modules/prisma-postgres/ach.patch.md +65 -0
  45. package/content/modules/prisma-postgres/rules.patch.md +28 -0
  46. package/content/modules/shadcn-ui/ach.patch.md +42 -0
  47. package/content/modules/stripe-billing/ach.patch.md +46 -0
  48. package/content/modules/stripe-billing/gotchas.seed.md +73 -0
  49. package/content/modules/stripe-billing/rules.patch.md +30 -0
  50. package/content/modules/trigger-dev-v4/ach.patch.md +64 -0
  51. package/content/modules/trigger-dev-v4/gotchas.seed.md +53 -0
  52. package/content/modules/trigger-dev-v4/rules.patch.md +35 -0
  53. package/content/modules/trpc-v11/ach.patch.md +56 -0
  54. package/content/modules/trpc-v11/rules.patch.md +34 -0
  55. package/content/modules/vireya-ui/ach.patch.md +85 -0
  56. package/content/modules/vireya-ui/gotchas.seed.md +98 -0
  57. package/content/modules/vireya-ui/rules.patch.md +36 -0
  58. package/content/ops/adr.md +54 -0
  59. package/content/ops/analyze.md +130 -0
  60. package/content/ops/clarify.md +86 -0
  61. package/content/ops/deliver.md +209 -0
  62. package/content/ops/diagnose.md +143 -0
  63. package/content/ops/evolve.md +366 -0
  64. package/content/ops/export-template.md +97 -0
  65. package/content/ops/generalize.md +117 -0
  66. package/content/ops/gotcha.md +46 -0
  67. package/content/ops/obligations.md +145 -0
  68. package/content/ops/plan.md +61 -0
  69. package/content/ops/policy.md +61 -0
  70. package/content/ops/promote.md +72 -0
  71. package/content/ops/protocol.md +51 -0
  72. package/content/ops/reconcile.md +44 -0
  73. package/content/ops/reflect.md +182 -0
  74. package/content/ops/remediate.md +143 -0
  75. package/content/ops/restructure.md +142 -0
  76. package/content/ops/rule.md +170 -0
  77. package/content/ops/specify.md +62 -0
  78. package/content/ops/tasks.md +67 -0
  79. package/content/ops/update.md +252 -0
  80. package/content/ops/verify.md +67 -0
  81. package/content/patterns/adapter.md +46 -0
  82. package/content/patterns/facade.md +47 -0
  83. package/content/patterns/factory.md +46 -0
  84. package/content/patterns/null-object.md +55 -0
  85. package/content/patterns/result-vs-exception.md +52 -0
  86. package/content/patterns/strategy.md +45 -0
  87. package/content/patterns/template-method-vs-composition.md +50 -0
  88. package/content/questions/adr.questions.yaml +14 -0
  89. package/content/questions/analyze.questions.yaml +12 -0
  90. package/content/questions/bootstrap-mapping.questions.yaml +14 -0
  91. package/content/questions/bootstrap-rules.questions.yaml +10 -0
  92. package/content/questions/bootstrap-stack.questions.yaml +23 -0
  93. package/content/questions/clarify.questions.yaml +10 -0
  94. package/content/questions/code-style.questions.yaml +12 -0
  95. package/content/questions/confirm.questions.yaml +12 -0
  96. package/content/questions/deliver.questions.yaml +23 -0
  97. package/content/questions/evolve.questions.yaml +15 -0
  98. package/content/questions/export-template-apply.questions.yaml +11 -0
  99. package/content/questions/export-template.questions.yaml +23 -0
  100. package/content/questions/gates.questions.yaml +27 -0
  101. package/content/questions/generalize.questions.yaml +13 -0
  102. package/content/questions/gotcha.questions.yaml +11 -0
  103. package/content/questions/init-template.questions.yaml +10 -0
  104. package/content/questions/init.questions.yaml +41 -0
  105. package/content/questions/obligations.questions.yaml +17 -0
  106. package/content/questions/plan.questions.yaml +14 -0
  107. package/content/questions/refactor.questions.yaml +23 -0
  108. package/content/questions/reflect.questions.yaml +13 -0
  109. package/content/questions/remediate.questions.yaml +27 -0
  110. package/content/questions/restructure.questions.yaml +15 -0
  111. package/content/questions/rf-mapping.questions.yaml +12 -0
  112. package/content/questions/rule-scope.questions.yaml +12 -0
  113. package/content/questions/rule.questions.yaml +12 -0
  114. package/content/questions/specify.questions.yaml +23 -0
  115. package/content/questions/update-artifacts.questions.yaml +10 -0
  116. package/content/questions/update-strings.questions.yaml +14 -0
  117. package/content/questions/update.questions.yaml +10 -0
  118. package/content/questions/verify.questions.yaml +9 -0
  119. package/content/roles/executor.md +18 -0
  120. package/content/roles/explorer.md +22 -0
  121. package/content/roles/reviewer.md +23 -0
  122. package/content/rubrics/enum-vs-union-vs-branded.md +40 -0
  123. package/content/rubrics/episodic-vs-semantic-boundary.md +37 -0
  124. package/content/rubrics/error-classification.md +98 -0
  125. package/content/rubrics/failure-classification.md +111 -0
  126. package/content/rubrics/negative-filters.md +38 -0
  127. package/content/rubrics/solid-triggers.md +119 -0
  128. package/content/rubrics/template-vs-streaming-precedence.md +34 -0
  129. package/content/rubrics/validation-boundary.md +94 -0
  130. package/content/rubrics/when-to-create-dsl.md +65 -0
  131. package/content/rubrics/when-to-create-lib.md +49 -0
  132. package/content/rubrics/when-to-create-module.md +39 -0
  133. package/content/rubrics/when-to-evolve-methodology.md +62 -0
  134. package/content/skills/adr.md +42 -0
  135. package/content/skills/analyze.md +28 -0
  136. package/content/skills/clarify.md +28 -0
  137. package/content/skills/deliver.md +26 -0
  138. package/content/skills/diagnose.md +42 -0
  139. package/content/skills/evolve.md +43 -0
  140. package/content/skills/export-template.md +37 -0
  141. package/content/skills/generalize.md +31 -0
  142. package/content/skills/gotcha.md +40 -0
  143. package/content/skills/init.md +77 -0
  144. package/content/skills/obligations.md +27 -0
  145. package/content/skills/plan.md +28 -0
  146. package/content/skills/policy.md +25 -0
  147. package/content/skills/promote.md +39 -0
  148. package/content/skills/protocol.md +41 -0
  149. package/content/skills/reconcile.md +45 -0
  150. package/content/skills/refactor.md +79 -0
  151. package/content/skills/reflect.md +43 -0
  152. package/content/skills/remediate.md +33 -0
  153. package/content/skills/restructure.md +46 -0
  154. package/content/skills/rule.md +43 -0
  155. package/content/skills/specify.md +28 -0
  156. package/content/skills/tasks.md +24 -0
  157. package/content/skills/update.md +58 -0
  158. package/content/skills/verify.md +29 -0
  159. package/content/templates/adr.template.md +51 -0
  160. package/content/templates/evolution-report.template.md +132 -0
  161. package/content/templates/feature-plan.template.md +84 -0
  162. package/content/templates/feature-research.template.md +56 -0
  163. package/content/templates/feature-spec.template.md +86 -0
  164. package/content/templates/feature-tasks.induced.template.md +91 -0
  165. package/content/templates/feature-tasks.template.md +82 -0
  166. package/content/templates/focus.template.md +30 -0
  167. package/content/templates/gotcha.template.md +31 -0
  168. package/content/templates/guardrail-runner.template.md +97 -0
  169. package/content/templates/guardrail.template.md +63 -0
  170. package/content/templates/learning.template.md +40 -0
  171. package/content/templates/procedure.template.md +47 -0
  172. package/content/templates/protocols/p1.md +29 -0
  173. package/content/templates/protocols/p2.md +28 -0
  174. package/content/templates/protocols/p3.md +47 -0
  175. package/content/templates/research.template.md +50 -0
  176. package/content/templates/session-handoff.template.md +58 -0
  177. package/content/templates/us.template.md +39 -0
  178. package/content/tools/gate/core-rules/PLUGIN-13-secrets.sh +36 -0
  179. package/content/tools/gate/core-rules/PLUGIN-21-code-language.sh +27 -0
  180. package/content/tools/gate/core-rules/PLUGIN-35-gate-complete.sh +19 -0
  181. package/content/tools/gate/core-rules/PLUGIN-36-absolute-path.sh +38 -0
  182. package/content/tools/gate/core-rules/PLUGIN-37-prose-language.sh +56 -0
  183. package/content/tools/gate/core-rules/PLUGIN-38-parser-marker.sh +82 -0
  184. package/content/tools/gate/core-rules/PLUGIN-39-frontmatter-ops.sh +325 -0
  185. package/content/tools/gate/core-rules/PLUGIN-40-dead-reference.sh +74 -0
  186. package/content/tools/gate/core-rules/README.md +55 -0
  187. package/content/tools/gate/guardrails.sh +740 -0
  188. package/content/tools/gate/lib/ratchet.sh +119 -0
  189. package/content/tools/gate/lib/scope.sh +85 -0
  190. package/content/tools/gate/ratchet.seed.tsv +1 -0
  191. package/content/tools/gate/rules/README.md +50 -0
  192. package/package.json +19 -5
  193. package/plugin/agy/hooks.json +29 -0
  194. package/plugin/agy/mcp_config.json +7 -0
  195. package/plugin/agy/plugin.json +6 -0
  196. package/plugin/agy/skills/afm-adr/SKILL.md +48 -0
  197. package/plugin/agy/skills/afm-analyze/SKILL.md +34 -0
  198. package/plugin/agy/skills/afm-clarify/SKILL.md +34 -0
  199. package/plugin/agy/skills/afm-deliver/SKILL.md +30 -0
  200. package/plugin/agy/skills/afm-diagnose/SKILL.md +46 -0
  201. package/plugin/agy/skills/afm-evolve/SKILL.md +49 -0
  202. package/plugin/agy/skills/afm-export-template/SKILL.md +43 -0
  203. package/plugin/agy/skills/afm-generalize/SKILL.md +37 -0
  204. package/plugin/agy/skills/afm-gotcha/SKILL.md +46 -0
  205. package/plugin/agy/skills/afm-init/SKILL.md +81 -0
  206. package/plugin/agy/skills/afm-obligations/SKILL.md +31 -0
  207. package/plugin/agy/skills/afm-plan/SKILL.md +34 -0
  208. package/plugin/agy/skills/afm-policy/SKILL.md +29 -0
  209. package/plugin/agy/skills/afm-promote/SKILL.md +43 -0
  210. package/plugin/agy/skills/afm-protocol/SKILL.md +45 -0
  211. package/plugin/agy/skills/afm-reconcile/SKILL.md +49 -0
  212. package/plugin/agy/skills/afm-refactor/SKILL.md +83 -0
  213. package/plugin/agy/skills/afm-reflect/SKILL.md +49 -0
  214. package/plugin/agy/skills/afm-remediate/SKILL.md +39 -0
  215. package/plugin/agy/skills/afm-restructure/SKILL.md +52 -0
  216. package/plugin/agy/skills/afm-rule/SKILL.md +49 -0
  217. package/plugin/agy/skills/afm-specify/SKILL.md +34 -0
  218. package/plugin/agy/skills/afm-tasks/SKILL.md +28 -0
  219. package/plugin/agy/skills/afm-update/SKILL.md +64 -0
  220. package/plugin/agy/skills/afm-verify/SKILL.md +35 -0
  221. package/plugin/claude-code/.claude-plugin/marketplace.json +15 -0
  222. package/plugin/claude-code/.claude-plugin/plugin.json +9 -0
  223. package/plugin/claude-code/.mcp.json +7 -0
  224. package/plugin/claude-code/agents/afm-executor.md +18 -0
  225. package/plugin/claude-code/agents/afm-explorer.md +20 -0
  226. package/plugin/claude-code/agents/afm-reviewer.md +21 -0
  227. package/plugin/claude-code/hooks/hooks.json +85 -0
  228. package/plugin/claude-code/skills/adr/SKILL.md +48 -0
  229. package/plugin/claude-code/skills/analyze/SKILL.md +34 -0
  230. package/plugin/claude-code/skills/clarify/SKILL.md +34 -0
  231. package/plugin/claude-code/skills/deliver/SKILL.md +30 -0
  232. package/plugin/claude-code/skills/diagnose/SKILL.md +46 -0
  233. package/plugin/claude-code/skills/evolve/SKILL.md +49 -0
  234. package/plugin/claude-code/skills/export-template/SKILL.md +43 -0
  235. package/plugin/claude-code/skills/generalize/SKILL.md +37 -0
  236. package/plugin/claude-code/skills/gotcha/SKILL.md +46 -0
  237. package/plugin/claude-code/skills/init/SKILL.md +81 -0
  238. package/plugin/claude-code/skills/obligations/SKILL.md +31 -0
  239. package/plugin/claude-code/skills/plan/SKILL.md +34 -0
  240. package/plugin/claude-code/skills/policy/SKILL.md +29 -0
  241. package/plugin/claude-code/skills/promote/SKILL.md +43 -0
  242. package/plugin/claude-code/skills/protocol/SKILL.md +45 -0
  243. package/plugin/claude-code/skills/reconcile/SKILL.md +49 -0
  244. package/plugin/claude-code/skills/refactor/SKILL.md +83 -0
  245. package/plugin/claude-code/skills/reflect/SKILL.md +49 -0
  246. package/plugin/claude-code/skills/remediate/SKILL.md +39 -0
  247. package/plugin/claude-code/skills/restructure/SKILL.md +52 -0
  248. package/plugin/claude-code/skills/rule/SKILL.md +49 -0
  249. package/plugin/claude-code/skills/specify/SKILL.md +34 -0
  250. package/plugin/claude-code/skills/tasks/SKILL.md +28 -0
  251. package/plugin/claude-code/skills/update/SKILL.md +64 -0
  252. package/plugin/claude-code/skills/verify/SKILL.md +35 -0
  253. package/plugin/opencode/afm/index.js +245 -0
  254. package/plugin/opencode/afm/tui.js +21 -0
  255. package/plugin/opencode/agents/afm-executor.md +39 -0
  256. package/plugin/opencode/agents/afm-explorer.md +92 -0
  257. package/plugin/opencode/agents/afm-reviewer.md +93 -0
  258. package/plugin/opencode/skills/afm-adr/SKILL.md +48 -0
  259. package/plugin/opencode/skills/afm-analyze/SKILL.md +34 -0
  260. package/plugin/opencode/skills/afm-clarify/SKILL.md +34 -0
  261. package/plugin/opencode/skills/afm-deliver/SKILL.md +30 -0
  262. package/plugin/opencode/skills/afm-diagnose/SKILL.md +46 -0
  263. package/plugin/opencode/skills/afm-evolve/SKILL.md +49 -0
  264. package/plugin/opencode/skills/afm-export-template/SKILL.md +43 -0
  265. package/plugin/opencode/skills/afm-generalize/SKILL.md +37 -0
  266. package/plugin/opencode/skills/afm-gotcha/SKILL.md +46 -0
  267. package/plugin/opencode/skills/afm-init/SKILL.md +81 -0
  268. package/plugin/opencode/skills/afm-obligations/SKILL.md +31 -0
  269. package/plugin/opencode/skills/afm-plan/SKILL.md +34 -0
  270. package/plugin/opencode/skills/afm-policy/SKILL.md +29 -0
  271. package/plugin/opencode/skills/afm-promote/SKILL.md +43 -0
  272. package/plugin/opencode/skills/afm-protocol/SKILL.md +45 -0
  273. package/plugin/opencode/skills/afm-reconcile/SKILL.md +49 -0
  274. package/plugin/opencode/skills/afm-refactor/SKILL.md +83 -0
  275. package/plugin/opencode/skills/afm-reflect/SKILL.md +49 -0
  276. package/plugin/opencode/skills/afm-remediate/SKILL.md +39 -0
  277. package/plugin/opencode/skills/afm-restructure/SKILL.md +52 -0
  278. package/plugin/opencode/skills/afm-rule/SKILL.md +49 -0
  279. package/plugin/opencode/skills/afm-specify/SKILL.md +34 -0
  280. package/plugin/opencode/skills/afm-tasks/SKILL.md +28 -0
  281. package/plugin/opencode/skills/afm-update/SKILL.md +64 -0
  282. package/plugin/opencode/skills/afm-verify/SKILL.md +35 -0
  283. package/README.md +0 -3
@@ -0,0 +1,170 @@
1
+ ---
2
+ type: op
3
+ description: Creates a hard rule, and refuses one that has no mechanical trigger.
4
+ load: warm
5
+ writes: ["docs/rules/*.md", "docs/afm.md", "docs/tools/gate/rules/*.sh", "docs/tools/gate/rules/*.ts", "docs/guardrails/*.md"]
6
+ composes: []
7
+ ---
8
+ # Sub-flow: new hard rule
9
+
10
+ > **Form** — effect: one file under `docs/rules/` plus its index line in `afm.md § 3` (or a principle in `§ 1.3`) · trigger: `afm rule`, composition · precondition: executable 0/1 trigger · composed by: `evolve`, `reflect`, `generalize` · **degree of freedom: low**
11
+
12
+ Triggers: `afm rule`, or natural language ("new hard rule", "this should be a rule").
13
+
14
+ The CLI owns the mechanics: the next number, the rule file, the `N. **title** — [link]` index line in
15
+ `afm.md § 3` (a parsing contract two hooks read), the gate runner and the index. This op owns the
16
+ judgment the CLI cannot make: is the trigger honest, is the constant the operator's, where does it run.
17
+
18
+ ## Procedure
19
+
20
+ 1. `afm preflight`. Exit 3: no instance, run `afm init` or `afm refactor` first. Read `afm.md § 3`
21
+ (format and numbering) and `afm path interview/triggers` § "hard rule".
22
+ 2. **Confirm an executable trigger** the agent can run, for example `grep`, `wc -l`, `git diff` or
23
+ `tsc`, each returning 0 or 1. Ask for it as free text: the trigger needs description, not closed
24
+ options. "Code review", "PR template" and "manual review" do **not** count, because they describe a
25
+ human process no agent session has. `afm rule new` refuses them.
26
+
27
+ No executable trigger means the item is a **principle**, not a hard rule. Put it in `afm.md § 1.3`
28
+ instead (invariant principle #3 in `afm path PRINCIPLES`). **Insist hard here:** a rule nobody can
29
+ run is a rule nobody applies.
30
+ - **Anti-Goodhart guard (arXiv:2310.02304):** does the trigger measure the thing that matters, or a
31
+ proxy that is easy to satisfy? Test it before you accept it: can a diff pass the trigger while it
32
+ breaks the rule's intent? If yes, the trigger is reward-hackable. **Rewrite** it to target what
33
+ matters, or **downgrade** it to a principle. A `grep "test"` passes with an empty test, because
34
+ it counts a string and not coverage. A line count under 300 passes with one giant line. A
35
+ gameable trigger is worse than an honest principle.
36
+ 3. **Check whether the trigger already exists as a versioned script**: a `test/*.sh` file, a package
37
+ script or a project hook may already do the check. If so, the rule cites the script, not the full
38
+ command. Two sources of truth for the same check drift apart: the one the human reads stops being
39
+ the one that runs. Also check the catalog under `afm path guardrails/README`: an entry whose
40
+ `sinal` matches this project has a trigger better tested than one improvised now.
41
+ 4. **The rule's constant belongs to the operator, not to you**: a numeric limit, a name prefix, a
42
+ directory list. **Measure the repo, and propose the value with the measurement** ("your file p90
43
+ is 240 lines, I propose 300"). Never set a "best practice" value yourself. An unanswered question
44
+ becomes `[TBD]`, and the rule stays without a trigger until someone fills it in (#4). An invented
45
+ number creates a rule the owner disables at the first friction, because he never chose it.
46
+ 5. **Choose the surface before you write the rule.** A 0/1 trigger that stays only as prose works only
47
+ while someone remembers to run it, and the agent forgets exactly when context gets tight. Ask
48
+ `afm ask rule` (questions in `content/questions/rule.questions.yaml`). The recommendation follows
49
+ what the project already runs (#2): `pre-commit` unless there is a reason against it.
50
+ - `pre-commit`: blocks every commit, including one made outside an agent session.
51
+ - `ci`: blocks the merge, not the local commit. Only if the project already has CI.
52
+ - `agent`: stays in the doc, run by judgment. Honest for a heuristic trigger that could reject
53
+ correct work. **Not** honest for a precise trigger nobody automated.
54
+ 6. **Write the rule**: `afm rule new <slug> --trigger '<command returning 0 or 1>' --title "<title>" --statement "<one sentence>" --surface <pre-commit|ci|agent> [--runner ts|sh] [--does-not-cover '<text>']`. The command writes
55
+ `docs/rules/NN-<slug>.md` (frontmatter `type: rule`, `description`, `load: warm`, `rule_number`),
56
+ the `*Verification (<surface>):*` line, the index line in `afm.md § 3`, and, for `pre-commit` and
57
+ `ci`, a runner skeleton. `NN` in the file name IS the identity and never renumbers. A rule written
58
+ only as a file never reaches a session, and one written only as an index line has no body: the
59
+ command writes both. It prints each path and refreshes the index.
60
+ When the Verification command is narrower than the statement, the statement names that scope, or the
61
+ two are made to match. State what the gate does not check with `--does-not-cover "<text>"` (for
62
+ example "checks that a frontmatter exists, not the per-type schema"), so a green result is not read
63
+ as more than it proves. Never write boilerplate such as "edge cases". The option is optional.
64
+ 7. **Report the paths** (invariant principle #6, autonomy-first). The operator reviews with `git diff`.
65
+ For `pre-commit` and `ci` continue with the next section.
66
+
67
+ ## Install the gate
68
+
69
+ > Canonical procedure for "hard rule → executed guardrail". Composed of this op (step 5) and of
70
+ > `afm remediate` when the recurring failure is mechanizable. **Do not duplicate these steps in
71
+ > another file** (rule 34): point to this one.
72
+
73
+ 0. **The aggregator is the CLI.** `afm gate` is the aggregator, the libraries, the ratchet reader and
74
+ the hook body. Do not write an aggregator or copy one from another project. You write the runner
75
+ body only. A project that maintains the plugin keeps its gate under `./test/`, and `afm gate`
76
+ resolves that fork itself. A project whose gate directory is missing is behind the plugin: it runs
77
+ `afm update`.
78
+ 1. **Find the surface the project ALREADY has** (#2, mandatory before any proposal): linter config,
79
+ type-check, CI steps, git hooks, the test script. The proposal **extends what already runs**. A new
80
+ tool is a last resort, stated explicitly with its cost. A guardrail that needs a new tool to catch
81
+ one error usually costs more than the error.
82
+ 2. **Pick the runner form: TypeScript is the default.** `afm rule new` writes
83
+ `docs/tools/gate/rules/NN-<slug>.ts`, a skeleton that throws (exit 2, never a pass) until you write the rule:
84
+ `({ rule }) => rule("NN").in(glob).except(glob).skipping(re).forbid(re, message)` (or `.require(re, message)`).
85
+ It needs no shell and no execute bit, runs on Windows, and `afm gate` runs it in a child process
86
+ (`afm gate ts-runner`) under the same exit contract. Check it with `afm gate --dry-run --only NN`. Full API:
87
+ `docs/tools/gate/rules/README.md`. Logic the builder cannot say (a diff, a count against the ratchet, another
88
+ tool) needs a bash runner: pass `--runner sh` to `afm rule new` in step 6 and the rest of this step applies to it.
89
+ Finish the runner that `afm rule new` generated. Its template (both forms) is
90
+ `afm path templates/guardrail-runner.template.md`. Decide the **class** there. A *diff* rule measures the index
91
+ with a zero budget. A *count* rule measures the whole repo against the ratchet. Swapping the two is
92
+ a silent false negative. Exit 0 compliant, 1 violated (one `NN<TAB>path:line<TAB>message` line),
93
+ 2 could not decide. Exit 2 is mandatory: "could not verify" must never become "failed".
94
+ The generated runner takes each line the trigger PRINTS as a violation (a grep with no match exits 1
95
+ and passes), so the trigger prints its violations: `grep -rn`, never `grep -q` or a bare `test`.
96
+ 3. **Measure HEAD before you turn the gate on**: `afm gate --baseline`. It runs every runner that has
97
+ no ratchet row, records what it measures as declared debt, and says which rules it skipped. **A gate
98
+ that starts red gets disabled the next day**, and it takes every other rule in the aggregator with
99
+ it. The mode only ever appends: it never touches a rule somebody already decided about.
100
+ 4. **Measure the cost** and record it. A runner above the operator's budget drops to the `agent`
101
+ surface. The whole gate does **not** become optional because one runner is expensive.
102
+ 5. **Doc-to-runner sync is mechanical:** `afm gate --audit` cross-references the rule and the runner in
103
+ both directions. A rule with a gate surface and no runner is a **phantom gate**, worse than no gate
104
+ because it buys trust it cannot back up. A runner with no rule is an orphan.
105
+ 6. **Record the guardrail** with `afm new guardrail <slug> --origin hard-rule --surface <surface> --cost <low|medium|high> --description "..."`
106
+ (never by hand from the template). Use the `surface` chosen and the `cost` measured in step 4, then fill the
107
+ mandatory `## What would refute this` section. Fix any finding with `afm lint --fix`.
108
+ 7. **Activate it: find the manager the project already uses, and propose that one.** Never ask the
109
+ operator to edit git config by hand. A repository cannot declare its own hooks (if it could, a
110
+ clone would be remote code execution), so one opt-in step is irreducible. Escalate in this order
111
+ (#2, the existing surface comes first):
112
+
113
+ | Tier | Signal in the repo | Where the gate lives |
114
+ | --- | --- | --- |
115
+ | 1 | `.husky/`, `lefthook.y*ml`, `.pre-commit-config.yaml` | **in the manager already there**: add `afm gate --staged` to its config |
116
+ | 2 | `package.json` (and the operator agrees) | propose **husky** (or **lefthook**, for a binary with no Node) and run its install |
117
+ | 3 | neither one nor the other | `afm install git`, which writes the pre-commit, commit-msg and post-commit hooks into the repository's hooks directory |
118
+ | 4 | none of these fit (no local git, CI-only) | a CI step that runs `afm gate --worktree` |
119
+
120
+ The table is a recommendation, not a rail. **Introducing a new manager into a project with none is a
121
+ new tool** (step 1): state the cost explicitly, and it loses to tier 3 when the project is not Node.
122
+
123
+ **Running the install is allowed. Auto-installing is not.** The activation writes outside `docs/`,
124
+ so it runs only when the operator asks. When they ask, the agent runs it. What stays forbidden is
125
+ the gate arming itself inside a session-start or inside another hook: an opt-in that installs
126
+ itself is not opt-in.
127
+
128
+ 7.5. **If no native surface exists** (no husky, no `core.hooksPath`), the harness hook that runs
129
+ `afm gate --staged` before a shell commit is the last resort. It works only as tier 3, and it steps
130
+ back as soon as a native surface appears: a duplicate gate reports the same finding twice and
131
+ teaches the operator to ignore the output. The blocking carve-out is justified by **damage
132
+ asymmetry**: blocking a commit delays work, blocking an edit disables the whole session.
133
+
134
+ > **The limit, stated in full.** An installed gate can be bypassed (`--no-verify`, `# noqa`,
135
+ > `eslint-disable`). That is not a design failure: friction is the intended effect, and a gate with no
136
+ > escape gets uninstalled. What the record from step 6 buys is that **bypassing leaves a trace, and
137
+ > removing it requires refuting the reason**, not that bypassing is impossible.
138
+
139
+ ## Inalienable rule
140
+
141
+ A rule marked `INALIENABLE` in `afm.md § 3` **cannot be removed or marked N/A** by this op: `afm rule na` and `afm rule remove` refuse, under explicit invocation and under a suppressed sub-gate alike. A
142
+ request to do so **stops and reports**. This op is not the right channel; the right channel is the
143
+ plugin maintainer, with the repo open (`afm promote` records the argument with evidence).
144
+
145
+ What this op **can** do to an inalienable rule: fill in its `{{placeholder}}` (the `{{src-path}}` of
146
+ rule 21, for example) and adapt the `*Verification:*` line to the stack. The **form** belongs to the
147
+ framework, and the **constant** belongs to the operator (step 4).
148
+
149
+ **The limit, stated in full:** `docs/afm.md` is a file in the operator's repo, and nothing stops them
150
+ from editing it by hand. This op refuses to do so. The `afm doctor` check A5b flags the absence as
151
+ CRITICAL. Detection is not prevention, and promising prevention would misstate what the architecture
152
+ delivers.
153
+
154
+ An ordinary rule that no longer applies: `afm rule na <id> --reason "<why>"` keeps the number and
155
+ marks the index line N/A. `afm rule remove <id>` deletes the rule file and its runner, and its line in `docs/afm.md` § 3.
156
+
157
+ ## Additional for a refactored project
158
+
159
+ > Applies only if the project was adopted through `afm refactor`. For a greenfield project skip it:
160
+ > every new rule goes straight into § 3.
161
+
162
+ **Consider whether the rule enters as forward-only** (`afm.md § 3.1`), because applying it
163
+ retroactively would break a lot of existing code. Ask `afm ask rule-scope` (questions in
164
+ `content/questions/rule-scope.questions.yaml`). The rule's scope is a matter of intent, not a
165
+ discoverable state.
166
+
167
+ - `universal`: § 3, applies to all code including legacy. May generate a large cleanup change.
168
+ - `forward-only`: add the rule to the § 3.1 table by hand, with the reason and where the debt is
169
+ tracked. New code and boy-scout edits only; tracked tech debt, not a PR blocker.
170
+ - `cancel`: undo with `afm rule remove <id>` if the rule was already written.
@@ -0,0 +1,62 @@
1
+ ---
2
+ type: op
3
+ description: Opens the feature folder and writes the spec, the what and the why with no technology.
4
+ load: warm
5
+ writes: ["docs/features/*/spec.md", "docs/ust.md"]
6
+ composes: []
7
+ ---
8
+ # Sub-flow: open feature folder (`specify`)
9
+
10
+ > **Form** — effect: `features/NNN/spec.md` · trigger: `afm specify`, composition · pre-condition: `.afm-version`, `ust.md` · composed by: `deliver` 1.B · **degree of freedom: low**
11
+
12
+ Triggers: `afm specify <slug>`, or natural language ("open feature", "start new feature", "spec for feature X").
13
+
14
+ > **Autonomy-first (#6):** running this op alone IS the approval to open the feature. The agent computes the heuristic and the linked US from the scan (discover-first #2), writes the spec and reports the result. Review happens through `git diff`. **Under the assisted-planning protocol** (`plan=assisted`, run `afm protocol` to read it) this rule does NOT apply and the op keeps its own gate. With no protocol declared, the default applies (autonomous). See [`core/protocol.md`](../core/protocol.md) § 2. **In orchestrated mode** (called by [`deliver.md`](deliver.md)) approval moves to the single gate; see its suppression contract.
15
+
16
+ The fixed questions of this op are data: `questions/specify.questions.yaml`. Ask them with `afm ask specify --scope <slug>` and record one answer with `afm answer specify <id> <label> --scope <slug>`. A free-text value (the slug, a US number) is not a question-file entry: ask for it in plain words.
17
+
18
+ Procedure:
19
+
20
+ 1. **Pre-check.** Run `afm preflight`. Exit 3 means no `docs/.afm-version`: stop and redirect to `afm init` (greenfield) or `afm refactor` (brownfield). Exit 4 or 5: report the line it printed and stop.
21
+
22
+ 2. **Trigger heuristic — discover-first (#2): compute it from the scan, do not ask a blank question.** Run a quick scan (search plus the estimated change size). Does **any** criterion match: `>1 day OR >200 lines OR >3 files OR new layer/component (rule 11) OR new external boundary OR >2 unknowns`?
23
+ - Clearly **not** a match: stop and suggest the `core/afm.md` § 2 loop directly, with no feature folder. This applies KISS to the method itself.
24
+ - Clearly a match: go to step 3.
25
+ - **Only** if the case is borderline, ask the question `heuristic`.
26
+
27
+ 2.5. **Retrieval before generate (Voyager/AWM): before you open a new folder, check whether this extends something.** Run `grep -rliE "<feature-tokens>" docs/features/*/spec.md docs/adr/ 2>/dev/null`. This is the same check as [`deliver.md` Phase 0.A0](deliver.md): a link, not a copy. If an equivalent feature or ADR exists, **propose extending it** and do not open a new number. An empty result means proceed.
28
+
29
+ 3. **Next number.** Run `afm next-id feature`. It prints the zero-padded `NNN` (`001` when `docs/features/` does not exist yet).
30
+
31
+ 4. **Slug.** The kebab-case of the `<slug>` the operator gave, or derived from the linked US. If it is empty, ask in plain words.
32
+
33
+ 5. **Linked US — discover-first: search `docs/ust.md` before you ask.** Look for a US that matches the feature (title, persona, criteria). If there is an obvious candidate, propose it. Then ask the question `us-link`:
34
+ - `candidate` (recommended when a candidate was detected): confirms the link.
35
+ - `other-existing`: a different existing US; ask for its number in plain words.
36
+ - `create-first`: run the mini US flow, which mirrors `interview/bootstrap.md` § 3 to write a Gherkin criterion, and add the new US to `docs/ust.md`.
37
+ - `later`: mark `[NEEDS CLARIFICATION: link the US]` in the spec.
38
+
39
+ 6. **Create `docs/features/NNN-slug/spec.md`** with `afm new feature-spec <slug> --description "..."` (add `--us US-NNN` when step 5 linked one; the command takes the next feature number and writes the frontmatter), then fill in the body:
40
+ - the title and the date.
41
+ - § 1 from the US description (if linked); with no US, mark `[NEEDS CLARIFICATION:]`.
42
+ - § 3 Gherkin scenarios copied from the US (if linked).
43
+ - § 5 with at least one `[NEEDS CLARIFICATION:]` that states the main gap. Do not invent it.
44
+ - Keep the other `{{placeholders}}` for later.
45
+
46
+ **The generator writes the frontmatter the template's own `frontmatter_for_output:` names** (hard rule 39: an op declares the keys it writes): `type: feature-spec`, `description`, `load: cold`, `status: draft` (the folder's ONE lifecycle value, no Status line in the body), `for_paths: []`, and `us: US-NNN` when step 5 linked one. `for_paths` starts empty: `plan.md` fills it once the components are known. `afm index --for <path>` routes a code path to the feature through it, and `status` tells the reader whether a route still applies (a `done` or `cancelled` feature no longer governs). A feature born without those keys stays invisible to the index.
47
+
48
+ 7. **Write and report** (#6): write `spec.md`, print a summary, the path and the open `[NEEDS CLARIFICATION:]` items. Review happens through `git diff`; no blocking question.
49
+
50
+ 8. **Update `docs/ust.md`**: add the line `Spec: docs/features/NNN-slug/spec.md` to the linked US metadata (if any). Write it directly (#6).
51
+
52
+ 9. **Output to the operator:** the path of the spec, the numbered list of open `[NEEDS CLARIFICATION:]` items, and the suggested next step: "Resolve the clarifications and run `afm plan`, now or later." Check where the delivery stands with `afm feature status <NNN>`.
53
+
54
+ ## Anti-patterns
55
+
56
+ - ❌ Do not invent a success criterion the operator did not state (universal #5, see [`../PRINCIPLES.md`](../PRINCIPLES.md)).
57
+ - ❌ Do not create a spec with no linked US **and** no `[NEEDS CLARIFICATION: link the US]`.
58
+ - ❌ Do not skip the trigger heuristic. A feature folder for a trivial change is overhead.
59
+ - ❌ Do not ask a blank question about the heuristic or the US. The scan or `ust.md` already show it (#2).
60
+ - ❌ Do not block with a question before each write. Autonomy-first (#6): write and report.
61
+ - ❌ Do not decide the stack or a function name in the spec. That is `plan.md` territory.
62
+ - ❌ Do not pick the number by hand. `afm next-id feature` is the only source.
@@ -0,0 +1,67 @@
1
+ ---
2
+ type: op
3
+ description: Breaks a plan into the tasks list of tasks.md in TDD order, added with afm task add.
4
+ load: warm
5
+ writes: ["docs/features/*/tasks.md"]
6
+ composes: []
7
+ needs: ["feature"]
8
+ ---
9
+ # Sub-flow: make tasks for the feature (`tasks`)
10
+
11
+ > **Form** — effect: `features/NNN/tasks.md` · trigger: `afm tasks`, composition · precondition: `plan.md` has no marker · composed by: `deliver` 1.D · **degree of freedom: low**
12
+
13
+ Triggers: `afm tasks`, or natural language ("make the tasks", "break the feature into tasks").
14
+
15
+ > **Autonomy-first (#6):** running this op IS the approval to generate `tasks.md`. The agent materializes and reports (review through `git diff`), with no per-artifact question. **Under the assisted-planning protocol** (`plan=assisted`, run `afm protocol` to read it) this rule does NOT apply and the op keeps its own gate. With no declared protocol, the default applies (autonomous). See [`core/protocol.md`](../core/protocol.md) § 2. The **HARD block** for `[NEEDS CLARIFICATION:]` (step 2) STAYS: it is a mechanical refusal, not a question. **Orchestrated mode** (called by [`deliver.md`](deliver.md)): the HARD block becomes a CRITICAL finding at the single gate; see its suppression contract.
16
+
17
+ This op asks nothing a question file could hold. Choosing among several candidate features is a dynamic list: ask it in plain words.
18
+
19
+ Procedure:
20
+
21
+ 1. **Pre-check.** Run `afm preflight` (exit 3: no `docs/.afm-version`, redirect to `afm init` or `afm refactor`). Find the feature: with zero plans, stop and suggest `afm plan` first. With several, **auto-detect the active feature** (discover-first #2): the branch `feat/NNN-*`, then the operator's hint, then the one `afm feature status <NNN> --json` reports with `phase` `tasks`. Ask in plain words only when two or more candidates are genuinely ambiguous.
22
+
23
+ 2. **HARD block on an open `[NEEDS CLARIFICATION:]` in the plan.** `afm feature status <NNN> --json` lists them in `blockers`. When it is not empty, refuse to run and answer:
24
+
25
+ > "The plan has N open `[NEEDS CLARIFICATION:]` marker(s):
26
+ > - plan.md:LINE: «question text»
27
+ >
28
+ > Resolve these first (edit the plan, or run `afm plan` again to answer them), then call me back. Tasks generated with open clarifications come out incoherent."
29
+
30
+ There is no "proceed anyway" override: this is a hard gate. (`[TBD]` in the plan does **not** block. It only shows up as a flag in the output.)
31
+
32
+ 3. **Read `spec.md` and `plan.md`** in full, for the feature.
33
+
34
+ 4. **Make the tasks.** Create the file with `afm new feature-tasks --feature NNN-slug --description "..."` (it writes the frontmatter and the template body), then add every task with `afm task add <NNN>`. The tasks live in the frontmatter `tasks:` list, never as a checklist in the body. The CLI assigns the id, validates the item and refuses one that breaks a rule `afm feature lint` grades (exit 1, the findings printed); you fill only the fields. The body is free prose (phases, notes). Build the list in this order:
35
+ - **Setup:** empty files with a signature and the test fixtures: `--kind setup --files <paths> --refs <US/RF> --text "..."`, typically `--parallel`.
36
+ - **Foundation:** for each Gherkin scenario `N` of spec § 3, a red task (`--kind red --scenario N --files <test path>`), then the green task that closes it (`--kind green --closes <the red's id> --files <code path>`). A refactor (`--kind refactor`) follows a green or another refactor, when it adds expected clarity.
37
+ - **Boundary:** each new component of plan § 2 is in the `--files` of one task, with the exact path of the plan cell. For each boundary of plan § 5 (`### Boundary <name>`), a red task and the green task with the Zod validation (rule 16), added with `--validates <name>`: the lint matches the boundary by that field, never by the task text.
38
+ - **Reconciliation (8.5):** one task, `--kind reconcile`, always the last (`afm task add` keeps it last, and reopens it when a task lands after it was done): update `ach.md § 3` if a component is new, add an ADR (`afm adr new <slug>`, see [`adr.md`](adr.md)) if the work materializes an architectural decision, set the US status to `done`, and set the `status:` frontmatter key of `spec.md`, `plan.md` and `tasks.md` to `done` (or `cancelled` for a feature dropped; hard rule 39). No body line carries the status. It is last because those edits change the hashed documents, so the delivery approval (`afm approve --check`) goes stale by design and nothing may run after it. `afm task done` refuses it while another task is open, a green while its red is open, and a refactor while the task before it is open.
39
+
40
+ 5. **Mark `--parallel`** where a task has no dependency on the task immediately before it. Heuristic:
41
+ - Fixture tasks, tasks that touch different test files and doc tasks are typically parallel.
42
+ - A green task depends on the red task it closes: never parallel.
43
+ - A refactor depends on its green task: never parallel.
44
+
45
+ 6. **Every task references `US-NNN` or `RF-NN`** (`--refs`; the CLI refuses a task without one, except the reconcile task). The rule is `core/afm.md` § 2, step 7 COMMIT: a commit references a US or RF. If the feature has no linked US, fail earlier, at the pre-check.
46
+
47
+ 7. **Mechanical validation before you report** (auto-correct, do not ask). Run `afm feature lint <NNN>`. It reads the `tasks:` list and checks: each spec scenario has a red task with its number and a later green that closes it, a green closes an earlier red (CRITICAL), a refactor follows a green or a refactor, no green or refactor is parallel, each new plan component is in the `files` of a task, each plan boundary has a task whose `validates` is its name, there is exactly one reconcile task and it is last, every `US-NNN` exists in `ust.md` and every `RF-NN` in `prd.md`, and no checklist line is left in the body. It exits 1 on CRITICAL or HIGH. Fix a finding with `afm task add` (a missing task), `afm task set <NNN> <Txxx> [--kind] [--text] [--files] [--refs] [--scenario] [--closes] [--validates] [--parallel|--no-parallel] [--clear <field>]` (the field the finding names; `--clear scenario|closes|validates|files|refs` clears one), or `afm task remove <NNN> <Txxx>` (a task that should not exist; refused while a green closes it), then re-run until the exit is 0. `afm task set` and `afm task remove` run the same rules as `afm task add` and write only the lines they change; a new `--kind` brings its own needs (a green `--closes`, a red `--scenario`). Step 6 is the declarative rule; this step is the safety net for an attention lapse.
48
+
49
+ 8. **Materialize and report** (#6, see [`../PRINCIPLES.md`](../PRINCIPLES.md)): `tasks.md` is written by step 4; print the summary (step 9) with the path. Review happens through `git diff`; no blocking question.
50
+
51
+ **Check the frontmatter the template's `frontmatter_for_output:` names** (hard rule 39): `type: feature-tasks`, `description`, `load: cold`, and `for_paths`/`us`/`rf` carried from `plan.md`, unchanged, `status: planned`, and the `tasks:` list. `status` moves to `in_progress` with the first task and to `done` only in the reconcile task (step 4), never earlier.
52
+
53
+ 9. **Output to the operator:**
54
+ - The path of `tasks.md`.
55
+ - The summary `N tasks total / M parallel / red, green, refactor and reconcile counts`.
56
+ - The `[TBD]` markers still in the plan (a flag, not a block).
57
+ - **Next step:** "Before you start T001, consider `afm analyze` to cross-check coverage between scenarios, tasks and hard rules. It is a read-only report in chat, a safety net and not a gate. After that, run the tasks in list order: a red task, the green task that closes it, an optional refactor (`core/afm.md` § 2). When a task is done, close it with `afm task done <NNN> <Txxx>`."
58
+
59
+ ## Anti-patterns
60
+
61
+ - ❌ Skip the red task. This violates rule 1 (TDD).
62
+ - ❌ Create a task with no US or RF reference.
63
+ - ❌ Write a subjective task ("clean up code", "improve naming") with no mechanical criterion.
64
+ - ❌ Mark a green task `--parallel`: it depends on the red task it closes.
65
+ - ❌ Block with a question before writing. Materialize and report; review through `git diff`.
66
+ - ❌ Tolerate an open `[NEEDS CLARIFICATION:]` in the plan. The block is hard: a mechanical refusal, not a question.
67
+ - ❌ Write a task as a checklist line in the body, or edit the `tasks:` list by hand. `afm task add` adds a task, `afm task set` corrects one (its kind included), `afm task remove` drops one and `afm task done` closes it, atomically. When the CLI cannot write an edit it prints `held:` and a `task:` line with its verify; follow that task.
@@ -0,0 +1,252 @@
1
+ ---
2
+ type: op
3
+ description: Brings a materialized instance up to the current plugin version.
4
+ load: warm
5
+ writes: ["docs/.afm-version", "docs/evolution/*.md"]
6
+ composes: []
7
+ ---
8
+ # Sub-flow: sync of the materialized instance with the current plugin version (`update`)
9
+
10
+ > **Form** — effect: deltas in the instance + bump · trigger: `afm update`, composition · precondition: pin ≠ the shipped version · composed by: `evolve` 2.2 · **degree of freedom: low**
11
+
12
+ Triggers: `afm update`, or natural language for **version bump / sync with the plugin**. Examples:
13
+ "update AFM here", "bump the methodology version", "sync `docs/` with the new plugin", "migrate to the new
14
+ AFM version", "my `.afm-version` is behind".
15
+
16
+ > **The write target is the local docs (`docs/`), NEVER the plugin.** `afm update` reads the shipped
17
+ > content only to **discover the deltas**. It **materializes** the consumer-facing ones
18
+ > in the local instance. It never edits the plugin (bright line #7). See `afm path ops/evolve` § The
19
+ > target of evolution.
20
+ >
21
+ > **Sync mechanism, not orchestrator.** `update` resolves the **instance-to-plugin version drift** (axis
22
+ > A3, which `afm diagnose` *detects* but does not fix). It runs à la carte (this doc, with its own gate)
23
+ > or inside the `evolve` cycle, which composes it when `afm diagnose` flags A3, with the own gate
24
+ > suppressed (see § Suppression contract).
25
+
26
+ The command owns everything mechanical: reading the pin, comparing versions, running the **codemod
27
+ registry** of every release between the pin and the binary (ADR-0013), proving each op, keeping the manifest
28
+ and moving the pin at the end. This op owns the three things it cannot do: the agent tasks, the language
29
+ migration and the post-conditions.
30
+
31
+ ## When it is another op instead
32
+
33
+ - `init` / `refactor` (bootstrap) **create** the instance from zero. `update` assumes the instance
34
+ **already exists** and only brings it forward. `afm refactor` refuses a project with `.afm-version`;
35
+ `update` is the correct path for that case.
36
+ - `diagnose` only **detects** A3 (read-only). `update` **resolves** it. Diagnose finds it, update fixes it.
37
+ - `evolve` is the full health cycle. `update` is **one lens** (version sync), composed when A3 fires.
38
+ - `promote` **logs** a finding meant for the plugin. `update` goes plugin to **instance**. Neither op
39
+ touches the shipped content: an absolute boundary (#7).
40
+ - Not a resolver of `[TBD]`. Not an inventor of product content (#4/#5). It materializes only what the
41
+ plugin already provides: stack-agnostic rubrics, hygiene rules, template sections. It never fills in a
42
+ product decision.
43
+
44
+ ## Procedure
45
+
46
+ 1. **Preview:** `afm update --dry-run`. It runs the pre-check and prints every codemod with its result
47
+ against an overlay of the docs, writing nothing:
48
+ - Exit 3: no `docs/.afm-version`. Stop and run `afm init` (greenfield) or `afm refactor` (brownfield).
49
+ - Exit 5: the pin and the binary differ by a major version. Run `afm migrate` (the 3.x to 4.0 bridge),
50
+ then `afm update` again.
51
+ - *already up to date*: no drift means nothing to do, and an empty diagnosis is a valid result.
52
+ - *anomaly*: the pin is **ahead** of the binary. Someone edited the instance by hand, or the binary
53
+ is old. Report it, upgrade the binary, and never roll the pin back.
54
+ 2. **Read the preview as one package, not N loose items.** The tally counts what each op did:
55
+ - *applied*: the op wrote (a literal replacement, a move, a created or synced file, a frontmatter key,
56
+ a section, a regenerated surface).
57
+ - *already applied or absent*: the proof of need failed (the old text is not there), so the delta was
58
+ already applied or never existed in this instance. Silent, and not work done.
59
+ - *held*: `[TBD]` content protects the text (#4), or the owner edited a file a codemod would delete.
60
+ **Stop and report it**: the owner's stance wins.
61
+ - *failed*: a proof of application did not hold. The pin stays; fix the cause and run again.
62
+ - *agent task(s) pending*: work only judgment can do (below).
63
+ - a release with nothing to materialize counts toward the bump and prints no line. Do not invent work
64
+ where the plugin declared there is none.
65
+ 3. **Own gate (à la carte).** Show the preview as evidence (which version to which version, what each
66
+ codemod changes in the instance), then ask `afm ask update` (questions in
67
+ `content/questions/update.questions.yaml`). Under the `evolve` cycle this gate is **suppressed**: the
68
+ migration item moves up to the orchestrator's single gate.
69
+ 4. **Apply:** `afm update`. It runs every codemod, proves each one, prints the tally and **moves
70
+ `docs/.afm-version` only at the end**. The exit code tells what is left:
71
+ - Exit 0: done, the pin moved.
72
+ - Exit 1: a codemod failed (or the pin is ahead). The pin stays (marker honesty: never bump as if the
73
+ migration were complete). Fix it and run again.
74
+ - **Exit 6: agent tasks are pending.** Do them (step 5), then run `afm update` again. The pin stays until
75
+ every task passes its check.
76
+ - **Protected surfaces** are never materialized by an update: the shipped content itself,
77
+ `PRINCIPLES`, `[TBD]` content, the format the hooks parse, the rubric that decides when the evolution
78
+ cycle runs (`when-to-evolve-methodology`) and the `triggers` entries that fire the evolution
79
+ mechanisms. That is self-reference: the sync does not materialize the document that decides when
80
+ the cycle runs. New stack-agnostic rubrics, yes.
81
+ 5. **Do each agent task** listed after the tally. A line reads `agent <codemod>: <brief> (scope: <paths>)`
82
+ followed by the lines of its check that still fail. Work only inside the scope, through the write ops
83
+ (`afm adr`, `afm gotcha`, `afm rule`, `afm new <type>`), suppressing each op's own per-artifact
84
+ question. Run `afm lint <path>` to see the frontmatter findings and `afm lint --fix <path>` to fix
85
+ the ones that have one exact answer. Never invent content: what the operator did not say stays `[TBD]`.
86
+ A `[TBD]` placeholder alone lints clean and does not hold the pin.
87
+ An agent task is mechanical disambiguation: fan it out on the light model tier, per the fan-out principle
88
+ of `afm.md` § 1, and let the codemod do the write (re-run `afm update --dry-run` until the file is no longer
89
+ held; never write a `tasks:` list by hand).
90
+ When the run prints `commit .afm/config.toml: it is the project's AFM config`, commit that file with the update: `.afm/` holds nothing else (machine state lives in `~/.afm/state`).
91
+ 6. **Close a sync on an edited file** with `afm update --accept <path>`. When a codemod would overwrite a
92
+ materialized file the owner edited (its hash in the manifest no longer matches), it hands the merge to
93
+ the agent instead. Merge the new shipped text into the owner's file, then run
94
+ `afm update --accept docs/<path>`: it records the file as it is now as the accepted version. It never
95
+ edits the file.
96
+ 7. **Run `afm update` again** until it prints `pin: <from> -> <to>` (or *already up to date*). Then the
97
+ **post-conditions** (below) and the **log**.
98
+
99
+ ## What the codemods can do
100
+
101
+ A release is a module in `packages/core/src/features/codemod-releases/data/` with an ordered list of typed ops. The rules that
102
+ make them safe are 0/1 and none depends on judgment:
103
+
104
+ | Op | What it does | Proof of need | When it hands over |
105
+ | --- | --- | --- | --- |
106
+ | `replace` | literal substitution in one file | the old text is in the file | `[TBD]` there: *held* |
107
+ | `move` | renames a file or a folder | the source exists | a target that already exists |
108
+ | `create` | writes a shipped file the instance lacks | the file is absent | never overwrites |
109
+ | `sync` | overwrites a materialized file with the new shipped text | the manifest hash still equals the file | an edited file becomes an agent task (`--accept`) |
110
+ | `delete` | removes a file the plugin retired | the manifest hash still equals the file | an edited file is *held* |
111
+ | `frontmatter` | sets or renames a key | the key is missing or old | `[TBD]` there: *held* |
112
+ | `section` | adds a section to a doc | the heading is absent | never rewrites an existing one |
113
+ | `regenerate` | rebuilds a generated surface (agents block, stubs, index) | the surface is stale | none |
114
+ | `custom` | a program for what the others cannot say (the OKF lift) | its own | its own agent tasks |
115
+ | `agent` | what only judgment can do (a rewrite, a merge) | its own | always, with a mechanical check |
116
+
117
+ **The rules:**
118
+
119
+ 1. **Proof of need before a file is touched.** Zero matches means already applied: skip it silently.
120
+ 2. **Literal substitution, never a regular expression.** A pattern on an instance the owner edited by
121
+ hand is how a sync destroys text nobody asked to change.
122
+ 3. **Proof of application afterwards.** The old text is gone **and** the new text appears at least as often.
123
+ 4. **A file missing from the instance is skipped.** A doc the consumer never materialized does not arrive
124
+ through the back door, except through an explicit `create`.
125
+ 5. **`[TBD]` is untouchable** (#4): text inside a block the owner marked this way is *held*.
126
+ 6. **The manifest is the memory of what AFM wrote.** `docs/.afm-manifest.json` maps each file AFM wrote to
127
+ the hash of the text it wrote. `sync` and `delete` act only on a file whose hash still matches. A file
128
+ the owner changed is never overwritten or removed silently: the old "update never deletes" is now
129
+ "update never deletes what the owner changed". Commit the manifest with the docs.
130
+ 7. **The proof of need decides, not only the pin.** A codemod whose precondition still holds runs even when
131
+ the pin is past its release.
132
+ 8. **The pin moves last**, and only when every agent task passes its check.
133
+
134
+ > **Discover-first (#2):** the proof of need is the discover-first rule. A delta the operator already copied
135
+ > by hand never reappears.
136
+
137
+ ## Instances older than 4.0
138
+
139
+ `afm update` refuses to cross a major version (exit 5). A 3.x instance goes through `afm migrate` first
140
+ (`--dry-run` to preview): the bridge to 4.0, the split of `docs/gotchas.md` into `docs/gotchas/` and of
141
+ `afm.md` section 3 into `docs/rules/`, and the report names. Run it once and **commit before anything
142
+ else** (a structural migration mixed with a content edit hides both in the diff). Then run `afm update`
143
+ to receive the codemods of the 4.x releases (the OKF frontmatter lift among them), and
144
+ `afm index --materialize` (hard rule 39 fails on a missing or stale index).
145
+
146
+ ## 1.6 Language migration (runs when the instance is not English)
147
+
148
+ Artifacts are English: the docs, the code, the comments, the tests, the rule text. The ONE exception is
149
+ the strings the product shows its own users, and that is a product decision.
150
+
151
+ **The order below is the mechanism, and reversing it is the defect.** Read the field. Derive the excluded
152
+ scope. Only then read a file. "Scan first, remember to skip" makes the exclusion depend on the agent
153
+ noticing during the work, and that is the branch that damages a product.
154
+
155
+ 1. **Read `docs/prd.md` § 11.1 FIRST.** It holds the app-facing string language. An instance
156
+ materialized before this field existed has no such section, which reads the same as `[TBD]`.
157
+ 2. **`[TBD]` or absent: stop and ask `afm ask update-strings`** (questions in
158
+ `content/questions/update-strings.questions.yaml`). Never choose a language. The irreducible mistake
159
+ is "no field, so nothing is a product string, so migrate everything". A translated UI is not something
160
+ `git revert` gives back to the user who saw it.
161
+ 3. **Derive the excluded scope from the answer, before any file is read.** The exclusion is a file and
162
+ directory list computed once, not a judgment made per file. An English app language makes the excluded
163
+ scope empty, and the rest is unchanged. `afm lang` reports the language each file is written in.
164
+ 4. **Ask ONE question about the artifacts: `afm ask update-artifacts`** (questions in
165
+ `content/questions/update-artifacts.questions.yaml`). Ask it once, never per file. `Keep the current language` records the choice, because the next update must not ask again.
166
+ Record the answer with `afm config set language.artifacts <tag>` (for example `pt-BR`). The command
167
+ validates the tag and edits `.afm/config.toml` in place. Never write that key by hand. A language agent task the
168
+ update prints (`artifact-language:*`) names the exact command; run it, then its 0/1 check.
169
+ 5. **On migrate, fan out over the artifacts outside the excluded scope**: the local `docs/`, the code
170
+ comments, the test names. Each file gets a rewrite of its language and writing style. Then an
171
+ adversarial check asks what fact, number, path, marker or caveat disappeared. The checker reports; it
172
+ never rewrites.
173
+
174
+ **Run the fan-out as one orchestrated batch, never file by file in the main session.** A file-by-file
175
+ rewrite in the session context spends the capable model on mechanical volume and fills the context
176
+ with text it reads once. Use the orchestration primitive of the harness: a workflow script (an
177
+ ultracode run, where the harness has one) or, failing that, its batch of parallel subagents. Build
178
+ the file list from step 3 BEFORE the batch starts, and pass it to the batch as data. A file the list does not name is never touched.
179
+
180
+ **The model tier follows the task tier** (`afm.md` § 1, the fan-out principle). Translation and
181
+ style rewrite under a fixed contract is mechanical, high-volume work:
182
+ - **Rewrite: the light tier** (a Haiku-class model, Haiku 5.5 today). Set the model on each
183
+ agent call, never once for the whole run. Give each agent a chunk of files, not one file, so the
184
+ concurrency stays inside `MAX_PARALLEL_SUBAGENTS`.
185
+ - **Check: the light tier too.** Its contract is a comparison, not a judgment: list the facts,
186
+ numbers, paths, markers (`[TBD]`, `[NEEDS CLARIFICATION:]`), code spans and caveats of the original,
187
+ and report each one the rewrite lost. Escalate ONLY a file the check flags to a capable model, for
188
+ one more rewrite of that file.
189
+ - **The orchestrator keeps the judgment**: the excluded scope, the final gate and the report to the
190
+ operator. Those stay on the session model.
191
+
192
+ Each rewrite brief carries the same contract: translate the prose, keep every code span, path,
193
+ identifier, frontmatter key and marker byte for byte, keep the headings structure, and never shorten
194
+ (hard rule 17). After the batch, `afm lang` reports the language of each file: a file still in the
195
+ old language goes back into the batch once, then to the operator.
196
+ 6. **Never touch the excluded scope.** Not the UI strings, not the user-facing error messages, not the
197
+ email copy, not the seed data a user reads. An ambiguous file stays.
198
+
199
+ **Post-condition.** `prd.md` § 11.1 is unchanged by this step. The migration reads that field and never
200
+ writes it. The product language is the owner's decision, not a consequence of a version bump.
201
+
202
+ ## Opt-in deltas: a release that adds a capability, not a requirement
203
+
204
+ A capability delta is not a migration. Nothing breaks when the operator ignores it, and `update` must not
205
+ apply it silently, because opting in is the operator's decision. The release notes (`content/CHANGELOG.md`) name it, and no codemod applies it.
206
+ Report an opt-in delta, and never apply it. A project that has not opted in has not decided against the
207
+ feature: it has not been asked.
208
+
209
+ | Release | Capability | How to opt in | What happens if you do nothing |
210
+ | --- | --- | --- | --- |
211
+ | 3.10.0 | organisation policy | set `[org] dir` in the global config, pointing at a bundle that holds `policy.md` that your user owns (it was the `AFM_GLOBAL_DIR` variable before 4.0) | nothing. Every reader returns before any file test when no bundle is set |
212
+ | 3.11.0 | `policy_blocking` is REMOVED | delete the key if an instance added it | nothing. It decided nothing while it existed |
213
+
214
+ ## Post-conditions
215
+
216
+ 1. **Parseable format.** Re-extraction by the hooks over the touched docs must preserve the format:
217
+ `afm lint` exits 0, `afm index --check` returns 0 and `afm gate --audit` is no worse than before. Otherwise roll back the
218
+ delta and report it. Never fail silently.
219
+ 2. **The instance/plugin boundary is MECHANICAL.** Run the two-branch check in `afm path ops/evolve`
220
+ § Phase 4.3 at the end. The boundary must come back clean: any change in the shipped content means
221
+ **rollback + report CRITICAL**. `update` only reads the plugin.
222
+
223
+ ## Log and handoff
224
+
225
+ - **À la carte:** report in chat what migrated (applied deltas, pending deltas, the newly pinned version).
226
+ A simple sync does not need its own `docs/evolution/` entry.
227
+ - **Under `evolve`:** it feeds the cycle report (the A3 migration becomes one line in
228
+ `docs/evolution/<date>-NNN.md`).
229
+ - **Handoff to `afm reconcile`** to watch, in the following sessions, whether the materialized delta now
230
+ guides well.
231
+
232
+ ## Suppression contract (back-pointer)
233
+
234
+ `evolve` **composes `update`** on axis A3. Under the cycle, `evolve` suppresses the gate of step 3 and
235
+ moves the item up to its single gate; the log feeds the cycle report. The authoritative table lives in
236
+ `afm path ops/evolve` § Suppression contract. À la carte, `update` runs on its own, with its own gate.
237
+
238
+ ## Anti-patterns
239
+
240
+ - ❌ **Editing the shipped content.** `update` only **reads** the plugin to discover deltas. It writes
241
+ only to the instance.
242
+ - ❌ **Materializing a consumer-noop release.** A plugin-internal patch with no content to materialize
243
+ only bumps the version. Do not invent work.
244
+ - ❌ **Re-materializing a delta that is already in the instance.** The proof of need exists for this.
245
+ - ❌ **Overwriting or deleting a file the owner edited.** The manifest hash is the guard: merge by hand and close it with `afm update --accept <path>`.
246
+ - ❌ **Materializing a protected surface:** the evolution cycle's own rubric, the evolution `triggers`,
247
+ and `[TBD]`. This is self-reference, and it is the operator's own stance.
248
+ - ❌ **Bumping `.afm-version` as if the migration were complete.** If a delta was left out, the marker
249
+ must show the gap honestly.
250
+ - ❌ **Inventing product content.** `update` materializes only what the plugin already provides
251
+ (stack-agnostic). A missing product decision stays `[TBD]`, for `afm reconcile` to handle (#4/#5).
252
+ - ❌ **Running as bootstrap.** A missing instance calls for `init` or `refactor`, not `update`.
@@ -0,0 +1,67 @@
1
+ ---
2
+ type: op
3
+ description: Stamps a human verification on a doc, the only writer of the OKF `verified` field.
4
+ load: warm
5
+ writes: ["docs/**/*.md"]
6
+ composes: []
7
+ ---
8
+ # Sub-flow: stamp a human verification (`verify`)
9
+
10
+ > **Form** — effect: adds `verified:` to the frontmatter of one file · trigger: `afm verify <path>` · precondition: `.afm-version` · **degree of freedom: low**
11
+
12
+ Triggers: `afm verify <path>`. Also natural language that states a person checked a document.
13
+ Examples: "I reviewed this rule", "mark this gotcha as verified", "sign off on this ADR".
14
+
15
+ ## What `verify` is, and is not
16
+
17
+ OKF v0.2 §5.2 keeps two facts apart. `generated` records what PRODUCED the content. `verified`
18
+ records who CONFIRMED it. They are separate because the writer and the checker are different
19
+ actors, and a document can change without re-confirmation.
20
+
21
+ From those two fields OKF derives a trust tier (§5.3):
22
+
23
+ | `verified` | Tier |
24
+ | --- | --- |
25
+ | absent | **unverified** |
26
+ | present, no `human:` actor | **machine-confirmed** |
27
+ | present with a `human:<id>` actor | **human-reviewed** |
28
+
29
+ AFM is autonomy-first. `reflect`, `reconcile` and `remediate` all WRITE documentation. Without this
30
+ operation, "an agent wrote this and no person read it" and "a person approved it" look the same.
31
+
32
+ ## Why only a human, and why only here
33
+
34
+ An automatic writer would destroy the field. The agent commits under the operator's git identity. A
35
+ hook that stamps `verified` on every commit would claim human review of every agent commit. That is
36
+ worse than no field: it converts an honest absence into a confident lie.
37
+
38
+ So the rule is one line. **Absence means unverified, and only an explicit human stamp writes it.**
39
+ The command enforces it: it refuses when `AFM_ACTOR` names a role or a machine instead of a person.
40
+
41
+ ## Procedure
42
+
43
+ 1. `afm preflight`. Exit 3: no instance, stop.
44
+ 2. **Confirm that a person read the file.** Show it, or ask `afm ask verify` (questions in
45
+ `content/questions/verify.questions.yaml`). Never infer review from ordinary work. `No` stops.
46
+ 3. **Stamp it:** `afm verify <path under docs/>`. The actor is `human:<id>`, where `<id>` comes from
47
+ `--as <id>`, from `AFM_ACTOR=human:<id>` or from the git `user.name` in lowercase with no space.
48
+ The command appends `verified: [{ by: "human:<id>", at: <ISO 8601 UTC> }]`, keeps every other
49
+ key, and adds an entry when `verified` is already a list. A second check is a second event; the
50
+ tier reads the latest `at`.
51
+ 4. **Report the tier that resulted**, and say what it means. The command prints it.
52
+
53
+ A file with no frontmatter carries no contract to stamp, and the command says so.
54
+
55
+ ## What it must never do
56
+
57
+ - Never stamp a file the person did not read.
58
+ - Never write `verified` from a hook, a gate, a commit, or another op.
59
+ - Never remove a verification. A doc that changed after a verification stays verified at that date,
60
+ and the date is the signal. Silently dropping it hides that the content moved.
61
+ - Never write `generated` here. That belongs to whoever produced the content.
62
+
63
+ ## Invariant principles
64
+
65
+ The single canonical source is `afm path PRINCIPLES`. Universals §1-8 apply. #5 applies hardest:
66
+ this operation records a fact about a person. Inventing one is the worst kind of invention, because
67
+ nothing downstream can tell.