@tenkara/afm-linux-arm64 0.0.0-stage → 4.0.0-alpha.10
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.
- package/bin/afm +4 -0
- package/content/.coupling-baseline.tsv +1 -0
- package/content/.pending-commands.txt +2 -0
- package/content/CHANGELOG-archive.md +806 -0
- package/content/CHANGELOG.md +4320 -0
- package/content/PRINCIPLES.md +119 -0
- package/content/README-light.md +158 -0
- package/content/README.md +150 -0
- package/content/code-style/_any/file-size.md +62 -0
- package/content/code-style/_any/imports.md +68 -0
- package/content/code-style/_any/naming.md +72 -0
- package/content/code-style/_any/test-placement.md +57 -0
- package/content/code-style/nextjs-app-router/imports.md +50 -0
- package/content/code-style/nextjs-app-router/naming.md +50 -0
- package/content/code-style/prisma-postgres/error-boundary.md +55 -0
- package/content/code-style/prisma-postgres/imports.md +61 -0
- package/content/code-style/shadcn-ui/naming.md +48 -0
- package/content/code-style/stripe-billing/naming.md +76 -0
- package/content/code-style/trigger-dev-v4/imports.md +58 -0
- package/content/code-style/trigger-dev-v4/naming.md +49 -0
- package/content/code-style/trigger-dev-v4/test-placement.md +61 -0
- package/content/code-style/trpc-v11/error-boundary.md +51 -0
- package/content/code-style/trpc-v11/file-size.md +44 -0
- package/content/code-style/trpc-v11/naming.md +60 -0
- package/content/code-style/trpc-v11/test-placement.md +52 -0
- package/content/core/ach.md +260 -0
- package/content/core/afm.md +514 -0
- package/content/core/gotchas.md +18 -0
- package/content/core/prd.md +141 -0
- package/content/core/protocol.md +158 -0
- package/content/core/ust.md +65 -0
- package/content/guardrails/README.md +55 -0
- package/content/guardrails/dsl-com-ordem-obrigatoria.md +47 -0
- package/content/guardrails/exaustividade-de-union.md +48 -0
- package/content/guardrails/fronteira-de-import.md +57 -0
- package/content/hooks/README.md +70 -0
- package/content/interview/bootstrap.md +417 -0
- package/content/interview/promotion.md +115 -0
- package/content/interview/refactor.md +379 -0
- package/content/interview/triggers.md +107 -0
- package/content/modules/nextjs-app-router/ach.patch.md +44 -0
- package/content/modules/nextjs-app-router/gotchas.seed.md +78 -0
- package/content/modules/nextjs-app-router/rules.patch.md +35 -0
- package/content/modules/prisma-postgres/ach.patch.md +65 -0
- package/content/modules/prisma-postgres/rules.patch.md +28 -0
- package/content/modules/shadcn-ui/ach.patch.md +42 -0
- package/content/modules/stripe-billing/ach.patch.md +46 -0
- package/content/modules/stripe-billing/gotchas.seed.md +73 -0
- package/content/modules/stripe-billing/rules.patch.md +30 -0
- package/content/modules/trigger-dev-v4/ach.patch.md +64 -0
- package/content/modules/trigger-dev-v4/gotchas.seed.md +53 -0
- package/content/modules/trigger-dev-v4/rules.patch.md +35 -0
- package/content/modules/trpc-v11/ach.patch.md +56 -0
- package/content/modules/trpc-v11/rules.patch.md +34 -0
- package/content/modules/vireya-ui/ach.patch.md +85 -0
- package/content/modules/vireya-ui/gotchas.seed.md +98 -0
- package/content/modules/vireya-ui/rules.patch.md +36 -0
- package/content/ops/adr.md +54 -0
- package/content/ops/analyze.md +130 -0
- package/content/ops/clarify.md +86 -0
- package/content/ops/deliver.md +209 -0
- package/content/ops/diagnose.md +143 -0
- package/content/ops/evolve.md +366 -0
- package/content/ops/export-template.md +97 -0
- package/content/ops/generalize.md +117 -0
- package/content/ops/gotcha.md +46 -0
- package/content/ops/plan.md +61 -0
- package/content/ops/policy.md +61 -0
- package/content/ops/promote.md +72 -0
- package/content/ops/protocol.md +51 -0
- package/content/ops/reconcile.md +44 -0
- package/content/ops/reflect.md +182 -0
- package/content/ops/remediate.md +143 -0
- package/content/ops/restructure.md +142 -0
- package/content/ops/rule.md +170 -0
- package/content/ops/specify.md +62 -0
- package/content/ops/tasks.md +67 -0
- package/content/ops/update.md +252 -0
- package/content/ops/verify.md +67 -0
- package/content/patterns/adapter.md +46 -0
- package/content/patterns/facade.md +47 -0
- package/content/patterns/factory.md +46 -0
- package/content/patterns/null-object.md +55 -0
- package/content/patterns/result-vs-exception.md +52 -0
- package/content/patterns/strategy.md +45 -0
- package/content/patterns/template-method-vs-composition.md +50 -0
- package/content/questions/adr.questions.yaml +14 -0
- package/content/questions/analyze.questions.yaml +12 -0
- package/content/questions/bootstrap-mapping.questions.yaml +14 -0
- package/content/questions/bootstrap-rules.questions.yaml +10 -0
- package/content/questions/bootstrap-stack.questions.yaml +23 -0
- package/content/questions/clarify.questions.yaml +10 -0
- package/content/questions/code-style.questions.yaml +12 -0
- package/content/questions/confirm.questions.yaml +12 -0
- package/content/questions/deliver.questions.yaml +23 -0
- package/content/questions/evolve.questions.yaml +15 -0
- package/content/questions/export-template-apply.questions.yaml +11 -0
- package/content/questions/export-template.questions.yaml +23 -0
- package/content/questions/gates.questions.yaml +27 -0
- package/content/questions/generalize.questions.yaml +13 -0
- package/content/questions/gotcha.questions.yaml +11 -0
- package/content/questions/init-template.questions.yaml +10 -0
- package/content/questions/init.questions.yaml +41 -0
- package/content/questions/plan.questions.yaml +14 -0
- package/content/questions/refactor.questions.yaml +23 -0
- package/content/questions/reflect.questions.yaml +13 -0
- package/content/questions/remediate.questions.yaml +27 -0
- package/content/questions/restructure.questions.yaml +15 -0
- package/content/questions/rf-mapping.questions.yaml +12 -0
- package/content/questions/rule-scope.questions.yaml +12 -0
- package/content/questions/rule.questions.yaml +12 -0
- package/content/questions/specify.questions.yaml +23 -0
- package/content/questions/update-artifacts.questions.yaml +10 -0
- package/content/questions/update-strings.questions.yaml +14 -0
- package/content/questions/update.questions.yaml +10 -0
- package/content/questions/verify.questions.yaml +9 -0
- package/content/roles/executor.md +18 -0
- package/content/roles/explorer.md +22 -0
- package/content/roles/reviewer.md +23 -0
- package/content/rubrics/enum-vs-union-vs-branded.md +40 -0
- package/content/rubrics/episodic-vs-semantic-boundary.md +37 -0
- package/content/rubrics/error-classification.md +98 -0
- package/content/rubrics/failure-classification.md +111 -0
- package/content/rubrics/negative-filters.md +38 -0
- package/content/rubrics/solid-triggers.md +119 -0
- package/content/rubrics/template-vs-streaming-precedence.md +34 -0
- package/content/rubrics/validation-boundary.md +94 -0
- package/content/rubrics/when-to-create-dsl.md +65 -0
- package/content/rubrics/when-to-create-lib.md +49 -0
- package/content/rubrics/when-to-create-module.md +39 -0
- package/content/rubrics/when-to-evolve-methodology.md +62 -0
- package/content/skills/adr.md +42 -0
- package/content/skills/analyze.md +28 -0
- package/content/skills/clarify.md +28 -0
- package/content/skills/deliver.md +26 -0
- package/content/skills/diagnose.md +42 -0
- package/content/skills/evolve.md +43 -0
- package/content/skills/export-template.md +37 -0
- package/content/skills/generalize.md +31 -0
- package/content/skills/gotcha.md +40 -0
- package/content/skills/init.md +77 -0
- package/content/skills/plan.md +28 -0
- package/content/skills/policy.md +25 -0
- package/content/skills/promote.md +39 -0
- package/content/skills/protocol.md +41 -0
- package/content/skills/reconcile.md +45 -0
- package/content/skills/refactor.md +79 -0
- package/content/skills/reflect.md +43 -0
- package/content/skills/remediate.md +33 -0
- package/content/skills/restructure.md +46 -0
- package/content/skills/rule.md +43 -0
- package/content/skills/specify.md +28 -0
- package/content/skills/tasks.md +24 -0
- package/content/skills/update.md +58 -0
- package/content/skills/verify.md +29 -0
- package/content/templates/adr.template.md +51 -0
- package/content/templates/evolution-report.template.md +132 -0
- package/content/templates/feature-plan.template.md +84 -0
- package/content/templates/feature-research.template.md +56 -0
- package/content/templates/feature-spec.template.md +86 -0
- package/content/templates/feature-tasks.induced.template.md +91 -0
- package/content/templates/feature-tasks.template.md +82 -0
- package/content/templates/focus.template.md +30 -0
- package/content/templates/gotcha.template.md +31 -0
- package/content/templates/guardrail-runner.template.md +97 -0
- package/content/templates/guardrail.template.md +63 -0
- package/content/templates/learning.template.md +40 -0
- package/content/templates/procedure.template.md +47 -0
- package/content/templates/protocols/p1.md +29 -0
- package/content/templates/protocols/p2.md +28 -0
- package/content/templates/protocols/p3.md +47 -0
- package/content/templates/research.template.md +50 -0
- package/content/templates/session-handoff.template.md +58 -0
- package/content/templates/us.template.md +39 -0
- package/content/tools/gate/core-rules/PLUGIN-13-secrets.sh +36 -0
- package/content/tools/gate/core-rules/PLUGIN-21-code-language.sh +27 -0
- package/content/tools/gate/core-rules/PLUGIN-35-gate-complete.sh +19 -0
- package/content/tools/gate/core-rules/PLUGIN-36-absolute-path.sh +38 -0
- package/content/tools/gate/core-rules/PLUGIN-37-prose-language.sh +56 -0
- package/content/tools/gate/core-rules/PLUGIN-38-parser-marker.sh +82 -0
- package/content/tools/gate/core-rules/PLUGIN-39-frontmatter-ops.sh +325 -0
- package/content/tools/gate/core-rules/PLUGIN-40-dead-reference.sh +74 -0
- package/content/tools/gate/core-rules/README.md +55 -0
- package/content/tools/gate/guardrails.sh +740 -0
- package/content/tools/gate/lib/ratchet.sh +119 -0
- package/content/tools/gate/lib/scope.sh +85 -0
- package/content/tools/gate/ratchet.seed.tsv +1 -0
- package/content/tools/gate/rules/README.md +50 -0
- package/package.json +19 -5
- package/plugin/agy/hooks.json +29 -0
- package/plugin/agy/mcp_config.json +7 -0
- package/plugin/agy/plugin.json +6 -0
- package/plugin/agy/skills/afm-adr/SKILL.md +48 -0
- package/plugin/agy/skills/afm-analyze/SKILL.md +34 -0
- package/plugin/agy/skills/afm-clarify/SKILL.md +34 -0
- package/plugin/agy/skills/afm-deliver/SKILL.md +30 -0
- package/plugin/agy/skills/afm-diagnose/SKILL.md +46 -0
- package/plugin/agy/skills/afm-evolve/SKILL.md +49 -0
- package/plugin/agy/skills/afm-export-template/SKILL.md +43 -0
- package/plugin/agy/skills/afm-generalize/SKILL.md +37 -0
- package/plugin/agy/skills/afm-gotcha/SKILL.md +46 -0
- package/plugin/agy/skills/afm-init/SKILL.md +81 -0
- package/plugin/agy/skills/afm-plan/SKILL.md +34 -0
- package/plugin/agy/skills/afm-policy/SKILL.md +29 -0
- package/plugin/agy/skills/afm-promote/SKILL.md +43 -0
- package/plugin/agy/skills/afm-protocol/SKILL.md +45 -0
- package/plugin/agy/skills/afm-reconcile/SKILL.md +49 -0
- package/plugin/agy/skills/afm-refactor/SKILL.md +83 -0
- package/plugin/agy/skills/afm-reflect/SKILL.md +49 -0
- package/plugin/agy/skills/afm-remediate/SKILL.md +39 -0
- package/plugin/agy/skills/afm-restructure/SKILL.md +52 -0
- package/plugin/agy/skills/afm-rule/SKILL.md +49 -0
- package/plugin/agy/skills/afm-specify/SKILL.md +34 -0
- package/plugin/agy/skills/afm-tasks/SKILL.md +28 -0
- package/plugin/agy/skills/afm-update/SKILL.md +64 -0
- package/plugin/agy/skills/afm-verify/SKILL.md +35 -0
- package/plugin/claude-code/.claude-plugin/marketplace.json +15 -0
- package/plugin/claude-code/.claude-plugin/plugin.json +9 -0
- package/plugin/claude-code/.mcp.json +7 -0
- package/plugin/claude-code/agents/afm-executor.md +18 -0
- package/plugin/claude-code/agents/afm-explorer.md +20 -0
- package/plugin/claude-code/agents/afm-reviewer.md +21 -0
- package/plugin/claude-code/hooks/hooks.json +85 -0
- package/plugin/claude-code/skills/adr/SKILL.md +48 -0
- package/plugin/claude-code/skills/analyze/SKILL.md +34 -0
- package/plugin/claude-code/skills/clarify/SKILL.md +34 -0
- package/plugin/claude-code/skills/deliver/SKILL.md +30 -0
- package/plugin/claude-code/skills/diagnose/SKILL.md +46 -0
- package/plugin/claude-code/skills/evolve/SKILL.md +49 -0
- package/plugin/claude-code/skills/export-template/SKILL.md +43 -0
- package/plugin/claude-code/skills/generalize/SKILL.md +37 -0
- package/plugin/claude-code/skills/gotcha/SKILL.md +46 -0
- package/plugin/claude-code/skills/init/SKILL.md +81 -0
- package/plugin/claude-code/skills/plan/SKILL.md +34 -0
- package/plugin/claude-code/skills/policy/SKILL.md +29 -0
- package/plugin/claude-code/skills/promote/SKILL.md +43 -0
- package/plugin/claude-code/skills/protocol/SKILL.md +45 -0
- package/plugin/claude-code/skills/reconcile/SKILL.md +49 -0
- package/plugin/claude-code/skills/refactor/SKILL.md +83 -0
- package/plugin/claude-code/skills/reflect/SKILL.md +49 -0
- package/plugin/claude-code/skills/remediate/SKILL.md +39 -0
- package/plugin/claude-code/skills/restructure/SKILL.md +52 -0
- package/plugin/claude-code/skills/rule/SKILL.md +49 -0
- package/plugin/claude-code/skills/specify/SKILL.md +34 -0
- package/plugin/claude-code/skills/tasks/SKILL.md +28 -0
- package/plugin/claude-code/skills/update/SKILL.md +64 -0
- package/plugin/claude-code/skills/verify/SKILL.md +35 -0
- package/plugin/opencode/afm/index.js +245 -0
- package/plugin/opencode/afm/tui.js +21 -0
- package/plugin/opencode/agents/afm-executor.md +39 -0
- package/plugin/opencode/agents/afm-explorer.md +92 -0
- package/plugin/opencode/agents/afm-reviewer.md +93 -0
- package/plugin/opencode/skills/afm-adr/SKILL.md +48 -0
- package/plugin/opencode/skills/afm-analyze/SKILL.md +34 -0
- package/plugin/opencode/skills/afm-clarify/SKILL.md +34 -0
- package/plugin/opencode/skills/afm-deliver/SKILL.md +30 -0
- package/plugin/opencode/skills/afm-diagnose/SKILL.md +46 -0
- package/plugin/opencode/skills/afm-evolve/SKILL.md +49 -0
- package/plugin/opencode/skills/afm-export-template/SKILL.md +43 -0
- package/plugin/opencode/skills/afm-generalize/SKILL.md +37 -0
- package/plugin/opencode/skills/afm-gotcha/SKILL.md +46 -0
- package/plugin/opencode/skills/afm-init/SKILL.md +81 -0
- package/plugin/opencode/skills/afm-plan/SKILL.md +34 -0
- package/plugin/opencode/skills/afm-policy/SKILL.md +29 -0
- package/plugin/opencode/skills/afm-promote/SKILL.md +43 -0
- package/plugin/opencode/skills/afm-protocol/SKILL.md +45 -0
- package/plugin/opencode/skills/afm-reconcile/SKILL.md +49 -0
- package/plugin/opencode/skills/afm-refactor/SKILL.md +83 -0
- package/plugin/opencode/skills/afm-reflect/SKILL.md +49 -0
- package/plugin/opencode/skills/afm-remediate/SKILL.md +39 -0
- package/plugin/opencode/skills/afm-restructure/SKILL.md +52 -0
- package/plugin/opencode/skills/afm-rule/SKILL.md +49 -0
- package/plugin/opencode/skills/afm-specify/SKILL.md +34 -0
- package/plugin/opencode/skills/afm-tasks/SKILL.md +28 -0
- package/plugin/opencode/skills/afm-update/SKILL.md +64 -0
- package/plugin/opencode/skills/afm-verify/SKILL.md +35 -0
- package/README.md +0 -3
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: op
|
|
3
|
+
description: Turns a failure that repeats into a durable artifact, recognized by an external signal.
|
|
4
|
+
load: warm
|
|
5
|
+
writes: ["docs/procedures/*.md", "docs/learnings/*.md", "docs/guardrails/*.md"]
|
|
6
|
+
composes: []
|
|
7
|
+
ancestors: "CRITIC (arXiv 2305.11738) — failure recognition is anchored in an **external tool or signal**. Self-critique with no tool degrades (PRINCIPLES #3). This op complements the short in-task loop in `afm.md § 5.1` (ephemeral Reflexion, cap 2). `remediate` is the **long loop**: when a failure crosses the § 5.1 terminal (non-transient) and recurs, it becomes a **durable** remediation."
|
|
8
|
+
---
|
|
9
|
+
# Sub-flow: remediation of a recognized failure (`remediate`) — CRITIC loop
|
|
10
|
+
|
|
11
|
+
> **Form** — effect: `guardrails/`, `procedures/`, or `learnings/` · trigger: `afm remediate`, composition · precondition: a failure in the state store that `afm failures promotable` lists · composed by: `evolve` 1.3, `reconcile` · **degree of freedom: medium**
|
|
12
|
+
|
|
13
|
+
Triggers: `afm remediate`.
|
|
14
|
+
It also triggers on natural language that asks to **resolve a failure for good**.
|
|
15
|
+
Example phrases:
|
|
16
|
+
|
|
17
|
+
- "this failed again, let's fix it for real"
|
|
18
|
+
- "make a runbook for this"
|
|
19
|
+
- "record this lesson for the next agents"
|
|
20
|
+
- "why do I keep getting this wrong?"
|
|
21
|
+
|
|
22
|
+
> **Installing a guardrail uses one procedure.** When the rubric classifies the failure as *mechanizable* and the target is `docs/guardrails/`, follow [`rule.md` § Install the gate](./rule.md) — it covers discovery of the surface the project already runs, the runner built from [`../templates/guardrail-runner.template.md`](../templates/guardrail-runner.template.md), **measuring HEAD before turning the gate on** (a gate that starts red gets turned off the next day), measured cost, doc-runner sync, and recording with `origin: recurring-failure`. This op does not repeat those steps (rule 34): the origin differs, but the install path is the same.
|
|
23
|
+
|
|
24
|
+
> **Target = this project's local docs (`<consumer>/docs/`), never the plugin.** Like every AFM write family, `remediate` writes to `<consumer>/docs/` (guardrails/procedures/learnings) and **proposes** — the human approves (#6). No op writes to the plugin: `afm promote` only records a finding for it.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## What `remediate` is / is not
|
|
29
|
+
|
|
30
|
+
**Is:** the loop **recognize → understand/classify → remediate** over the failures the failure-capture hook recorded in the state store (`afm failures promotable` lists the signatures that recurred).
|
|
31
|
+
It reads the failures that are still open: not closed by a record or by `afm failures close`.
|
|
32
|
+
It classifies each one using [`../rubrics/failure-classification.md`](../rubrics/failure-classification.md).
|
|
33
|
+
Then it **proposes** the correct durable artifact: **guardrail** (mechanizable — takes precedence), **procedure** (mechanical), or **learning** (conceptual).
|
|
34
|
+
Gate #5 and the negative filters control this step.
|
|
35
|
+
|
|
36
|
+
**Is not:**
|
|
37
|
+
- A new failure **detector**. The `afm.md § 5.1` terminal recognizes failures (external signal). `remediate` consumes what was already recorded.
|
|
38
|
+
- The short in-task loop (`§ 5.1`). That loop retries GREEN right away (ephemeral). `remediate` runs cross-turn and cross-session (durable).
|
|
39
|
+
- A plugin editor (#7) or an auto-applier (#6 — it proposes, the human approves).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Pre-check
|
|
44
|
+
|
|
45
|
+
1. **Does `<consumer>/docs/.afm-version` exist?** If not, stop and suggest `afm init` or `afm refactor`.
|
|
46
|
+
2. **Is there an open failure?** Run **`afm failures promotable`**. It lists each `sig=` that recurred (twice or more) with its `count`, `first` and `last` date, minus those already closed. With `no failure is promotable`, and with no explicit user correction to record, report "nothing to remediate" and stop.
|
|
47
|
+
An empty result is a valid outcome — do not invent a failure.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Procedure
|
|
52
|
+
|
|
53
|
+
1. **Recognize (already done — consume the record)**.
|
|
54
|
+
Each failure is an event in the state store (`~/.afm/state/<project>/`, never the repository), keyed by `sig=<signature>` (the recurrence key).
|
|
55
|
+
The failure-capture hook writes it, or the `§ 5.1` terminal does, or the agent does when it records an explicit user correction.
|
|
56
|
+
The record carries an **external signal** (oracle plus command plus output) and a `task/feature`.
|
|
57
|
+
A failure with no external signal does not exist (CRITIC).
|
|
58
|
+
Do not invent one.
|
|
59
|
+
|
|
60
|
+
2. **Promotion gate (#5 + negative-filters)**. For each `open` failure, decide whether it becomes an artifact now:
|
|
61
|
+
- **Explicit user correction → promote at count 1** (the user is the oracle).
|
|
62
|
+
- **Mechanical → requires recurrence:** the `sig=` must appear in `afm failures promotable` (its `count` is 2 or more).
|
|
63
|
+
- **Transient → do not promote:** the § 5.1 retry resolved it, or no durable antidote exists.
|
|
64
|
+
Also reject a claim that dies once you fix the root cause — "tool X was broken" is a fossil.
|
|
65
|
+
Close it with `afm failures close --sig <sig> --as transient` and move on. A closed signature stops counting: only failures after the close are counted again.
|
|
66
|
+
This is the negative filter — Phase 4 formalizes the checklist.
|
|
67
|
+
- A failure that does not clear the gate stays open (waits for recurrence). It becomes nothing yet.
|
|
68
|
+
|
|
69
|
+
3. **Classify (executable discriminator)** by applying the `failure-classification.md` rubric to failures that passed the gate.
|
|
70
|
+
The first question is the **precedence** question: *can a 0/1 check detect this with no judgment call*?
|
|
71
|
+
If yes, the target is **guardrail**.
|
|
72
|
+
A document that relies on memory loses to a gate that fires on its own.
|
|
73
|
+
If no, choose between two targets: **procedure** is a repeatable sequence with 0/1 gates and a start trigger.
|
|
74
|
+
**Learning** is a conceptual lesson, valuable for its cause.
|
|
75
|
+
|
|
76
|
+
3.5. **Read the mechanical surface the project already has**.
|
|
77
|
+
Do this before you propose a guardrail, never before.
|
|
78
|
+
That way you do not pay for the read when nobody needs it.
|
|
79
|
+
Find out what **already runs** in this repo:
|
|
80
|
+
|
|
81
|
+
- linter config (`eslint*`, `biome*`, `ruff*`, `.golangci*`, or the stack's equivalent).
|
|
82
|
+
- type-check (`tsconfig.json`, `mypy.ini`, `go vet`).
|
|
83
|
+
- CI (`.github/workflows/*`, `.gitlab-ci.yml`).
|
|
84
|
+
- git hooks (`.husky/`, `.git/hooks/`, `lefthook*`, `pre-commit`).
|
|
85
|
+
- verification scripts already versioned (`test/`, `scripts/`, `Makefile`, `package.json` scripts).
|
|
86
|
+
|
|
87
|
+
The proposal **extends what exists**. A new tool is a **last resort**.
|
|
88
|
+
State it explicitly, with its cost declared.
|
|
89
|
+
A gate that needs one more tool often costs more than the error it catches.
|
|
90
|
+
If nothing mechanical exists in the project, that gap is the finding.
|
|
91
|
+
Propose the minimal surface, not the complete one.
|
|
92
|
+
|
|
93
|
+
> **Context economy is the point.** The failure that triggered the guardrail used to be caught by non-mechanical analysis — reading, ad-hoc `grep`, review. That costs context **every time the code is touched**. A check costs once. This is the economic case for the precedence target, and it applies to both the human and the agent.
|
|
94
|
+
|
|
95
|
+
> **Red on the old code.** Before you accept any assertion that guards the fix (a guardrail check, a regression test), run it against the old code: `git show HEAD:<file>` or the parent commit. It MUST fail there. A regression test that passes on the old code proves nothing. If it passes, the assertion does not catch the failure: rewrite it before you propose it.
|
|
96
|
+
|
|
97
|
+
4. **Propose the artifact (a delta, never a rewrite — rule 17)**.
|
|
98
|
+
- **Guardrail** → new `docs/guardrails/<slug>.md`, written by `afm new guardrail <slug> --origin recurring-failure --surface <surface> --cost <low|medium|high> --description "..."` (the generator fills the frontmatter from the template; fix findings with `afm lint --fix`).
|
|
99
|
+
The `origin` key cites the `sig=`, and the `## What would refute this` section is **mandatory**.
|
|
100
|
+
Installing the check (editing the linter config, the CI workflow, or a git hook) writes outside `docs/`.
|
|
101
|
+
Because of that, the agent **proposes it to the operator**.
|
|
102
|
+
It offers surface options — linter, CI, git hook, or free text — and never applies the check itself.
|
|
103
|
+
Until the operator installs it, the `installed_on` key stays `[TBD]`.
|
|
104
|
+
- **Procedure** → new `docs/procedures/<slug>.md`, written by `afm new procedure <slug> --fires-on <event|file-shape> --description "..."`.
|
|
105
|
+
The `origin` key cites the `sig=` value(s) for traceability (**hard rule 19**).
|
|
106
|
+
The folder appears on demand, mirroring `adr/`.
|
|
107
|
+
- **Learning** → new `docs/learnings/<slug>.md`, written by `afm new learning <slug> --fires-on file-shape --trigger-path "<glob>" --description "..."`. The `trigger_path` key uses a path (for injection through the pre-edit hook). The `origin` key cites the `sig=`.
|
|
108
|
+
- Before creating, **`grep` for an equivalent** (retrieval-before-create). If a guardrail, procedure, or learning already covers this `sig=`, **update it** (append or update-in-place) instead of duplicating it. If 2 or more learnings converge, suggest `afm generalize`.
|
|
109
|
+
|
|
110
|
+
**Gate (à la carte).** Show the evidence (the `sig=`, the count, the oracle output) and the proposed delta, then run `afm ask remediate --scope <sig>`. The `artifact` question confirms the target with the classification as the recommendation (`guardrail`, `procedure`, `learning`, or `not now`). For a guardrail, the `surface` question asks where the check is installed (linter, CI, git hook, or free text). The scope is the `sig=` turned into a slug (lowercase words joined by `-`, starting with a letter), so each failure keeps its own answers.
|
|
111
|
+
|
|
112
|
+
5. **Close the failure**.
|
|
113
|
+
Once the agent proposes the artifact and the user approves it, close the signature: `afm failures close --sig <sig> --as remediated --detail "<path of the artifact>"`.
|
|
114
|
+
Do **not** delete the failure (provenance — append-only, rule 18). The close is itself one more event, so the history stays.
|
|
115
|
+
|
|
116
|
+
6. **Report the outcome**.
|
|
117
|
+
List which failures became a guardrail (and on which surface), which became a procedure, and which became a learning.
|
|
118
|
+
List which failures stayed open or were closed as `transient`, and why.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Composition (suppression contract)
|
|
123
|
+
|
|
124
|
+
| Who uses it | Reuses | Suppresses |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `reconcile` (passive) | reads `afm failures promotable` at end of turn and **proposes** the remediation | nothing — reconcile already proposes-not-applies (#6) |
|
|
127
|
+
| `evolve` (cycle) | the failure events are **evidence** for `reflect` (a lived failure) → **final** remediation happens at the cycle's gate | `remediate`'s own gate (folds into the single gate) |
|
|
128
|
+
|
|
129
|
+
À-la-carte (`afm remediate`) has its own gate: one `afm ask remediate` question per artifact proposed (`questions/remediate.questions.yaml`). Under `reconcile` or `evolve`, approval moves up to their flow.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Anti-patterns
|
|
134
|
+
|
|
135
|
+
- ❌ **Recognizing a failure with no external signal** (self-judgment) — CRITIC: this degrades quality (#3).
|
|
136
|
+
- ❌ **Promoting a single mechanical failure**, or a **transient** one — only 2+ occurrences (or 1 user correction) qualify. Transient failures never qualify (negative filter).
|
|
137
|
+
- ❌ **Writing a procedure or learning that omits the `sig=`** — this violates rule 19's traceability requirement.
|
|
138
|
+
- ❌ **Deleting the failure when remediating it** — close it with `afm failures close --as remediated`, never delete it (provenance).
|
|
139
|
+
- ❌ **Auto-applying the fix** — `remediate` proposes, the human approves (#6).
|
|
140
|
+
- ❌ **Forcing everything into one target** — guardrail, procedure, and learning are distinct (see the rubric).
|
|
141
|
+
- ❌ **Writing a procedure or learning for what a check can catch** — the mechanizable target takes precedence. A document that relies on memory loses to a gate that fires on its own.
|
|
142
|
+
- ❌ **Proposing a guardrail without reading the existing surface** (step 3.5). A new tool by default is the fastest way for the operator to turn the mechanism off.
|
|
143
|
+
- ❌ **Installing the check alone** — installing writes outside `docs/`. The agent must propose it instead (#6).
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: op
|
|
3
|
+
description: Checks the health of the context architecture and finds what went stale.
|
|
4
|
+
load: warm
|
|
5
|
+
writes: ["docs/evolution/*.md"]
|
|
6
|
+
composes: []
|
|
7
|
+
ancestors: Codified-Context (arXiv 2602.20478).
|
|
8
|
+
---
|
|
9
|
+
# Sub-flow: context architecture health (`restructure`)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
> **Form** — effect: edits + opt-in ADR · trigger: `afm restructure`, composition · pre-condition: git + `bites`/`last_bite` · composed by: `evolve` 1.3 · **degree of freedom: medium**
|
|
13
|
+
|
|
14
|
+
Triggers: `afm restructure`, or natural language about **structure/staleness** ("the doc is out of date vs. the code", "this should load only on demand", "reorganize what is always-read vs. on-demand", "I cannot find the guidance in the doc, but it exists").
|
|
15
|
+
|
|
16
|
+
> **Target = this project's local docs (`<consumer>/docs/`), never the plugin.** `restructure` updates, relocates, or re-partitions **only** the docs materialized in this repo. It never touches the canonical framework (the bundled content, bright line #7). A loading strategy that fits any instance is only **recorded** through `afm promote` (a finding with evidence). See [`evolve.md § The target of evolution`](./evolve.md).
|
|
17
|
+
>
|
|
18
|
+
> **Mechanism, not conductor.** `restructure` is the mechanism skill that checks whether the docs are **structured to load efficiently** and **keep pace with the code**. It runs **a-la-carte** (own gate) OR inside the `evolve` cycle (which composes it when `diagnose` flags staleness or a badly partitioned doc — the own gate is then suppressed).
|
|
19
|
+
>
|
|
20
|
+
> CodifiedContext(doc, path):
|
|
21
|
+
> 1. Tier(content) := its memory temperature: hot-always (every turn) | per-task | on-demand (deep reference)
|
|
22
|
+
> 2. GitEvidence(claim) := a `git log` comparison that confirms or refutes claim # PRINCIPLES.md #2
|
|
23
|
+
> 3. Staleness(doc, path) := doc cites `path` AND path's last commit postdates doc's last commit
|
|
24
|
+
> AND doc stayed unchanged for more than N commits # PRINCIPLES.md #3
|
|
25
|
+
> 4. NullRetrieval(query) := an answer to query exists in the docs, but retrieval missed it
|
|
26
|
+
> 5. Gate := opens only after GitEvidence and self-verify confirm the finding # PRINCIPLES.md #6, #8
|
|
27
|
+
> 6. if NullRetrieval -> create or relocate a doc
|
|
28
|
+
>
|
|
29
|
+
> A doc versions its content by memory temperature. Two documented cases show a stale spec
|
|
30
|
+
> misleading the agent — the paper's only confirmed failure mode. See [`../PRINCIPLES.md`](../PRINCIPLES.md).
|
|
31
|
+
|
|
32
|
+
> **Maturity (honest YAGNI).** The paper's full tiering (108k lines of code, 3 tiers, a retrieval MCP) is **future** robustness — an AFM consumer's docs are small today. The **actionable core now** is **staleness** (git trigger) plus **findability/partition** (information exists but sits in the wrong layer). **Tiering by temperature** (marking hot-always vs. on-demand) enters only as an **opt-in ADR proposal**, never as enforcement — only when a doc has actually grown too large.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## What `restructure` is / is not
|
|
37
|
+
|
|
38
|
+
**It is:** the check of the instance's **context architecture**. It covers four items.
|
|
39
|
+
|
|
40
|
+
1. **Staleness** — a doc references code that changed. The doc was not updated.
|
|
41
|
+
2. **Findability** — guidance exists but sits in the wrong layer or place. Retrieval failed even though the content was present.
|
|
42
|
+
3. **Partition by temperature** — what is always-read vs. deep on-demand reference. This is an opt-in loading-strategy proposal.
|
|
43
|
+
4. **Dead-gotcha pruning** — a gotcha whose last-bite date is old and whose count has stopped. A dry incidence is a tombstone candidate. The `fade` axis of `afm doctor` generalizes this: a cold orphan doc with no inject, read or edit in the window and no recent bite is a tombstone candidate too.
|
|
44
|
+
|
|
45
|
+
**It is not:**
|
|
46
|
+
- `diagnose` axis A1 (**dead reference** = the path does not exist). `restructure` staleness means the path **still exists but changed** while the doc did not. The two do not overlap.
|
|
47
|
+
- `reconcile` "missing doc": if information is **genuinely absent** (never documented) → that is `reconcile`/`gotcha`/`rule` (adds new knowledge). `restructure` targets information that **exists but was not found** because it sits in the wrong place (relocation, not creation).
|
|
48
|
+
- `reflect` (a doc misled because it was **ambiguous**). `restructure` targets **location/freshness**, not writing clarity.
|
|
49
|
+
- `generalize` (merges gotchas). `restructure` reorganizes/updates. It does not consolidate content.
|
|
50
|
+
- A content rewriter. `restructure` moves, updates, and re-partitions. Missing product **content** becomes `[TBD]` (#4) or goes to `reconcile` — it is never invented (#5).
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Pre-check
|
|
55
|
+
|
|
56
|
+
1. **Does `<consumer>/docs/.afm-version` exist?** If not, stop and suggest `afm init` or `afm refactor`.
|
|
57
|
+
2. **Does `afm preflight` exit 0?** A non-zero exit says why: the docs are not bootstrapped, the bundled content does not resolve, or the pin does not match. Report it. Do not fall back silently.
|
|
58
|
+
3. **Is git available?** The staleness scan depends on the git history. Without git, `afm doctor` reports the staleness axis as "not covered: no git" (never omit it). Findability and partition still run.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 1. Staleness scan (git trigger — the paper's confirmed failure mode)
|
|
63
|
+
|
|
64
|
+
**`afm doctor --json` runs this scan** and returns each candidate as a finding with axis `staleness` (command, `file:line` and the path's commits as evidence). Read those findings; do not redo the scan by hand. What it checks, for each core doc that **references code** (`ach.md § 3.1` component-to-file map, gotchas with a path, ADRs with a path):
|
|
65
|
+
|
|
66
|
+
- Extract the paths cited (in backticks). For each path, find the **date of the last commit that touched it** (`git log -1 --format=%cd -- <path>`). Compare it with the **date of the last commit that touched the doc** (`git log -1 --format=%cd -- <doc>`).
|
|
67
|
+
- **Path changed after the doc** (code evolved, doc stayed still) → **staleness candidate**. Report the doc's `file:line` and the path's commit range. Default threshold: the path changed in at least 1 commit after the doc. The doc must also have gone untouched for more than N commits (default 20, override with `<!-- evolve: stale-commits=N -->` in `docs/.afm-version`).
|
|
68
|
+
|
|
69
|
+
> Staleness is a **signal**, not a verdict. The doc may still be correct while the code change is unrelated to the documented part. The finding goes to the gate as "this doc may be out of date vs. `<path>` (it changed in N commits since the doc's last update) — review it?" It never rewrites the doc on its own. The correct content requires reading the current code, so it becomes a proposal, not an auto-fix.
|
|
70
|
+
|
|
71
|
+
### 1.1 Dead-gotcha pruning (last-bite — incidence, not avoidance)
|
|
72
|
+
|
|
73
|
+
This applies the same staleness logic to a gotcha's **usefulness**. It uses a **falsifiable** signal: the frontmatter `bites` and `last_bite` fields (from `templates/gotcha.template.md`, filled in by `ops/gotcha.md`). `afm doctor --json` checks it for each gotcha in `gotchas/` and reports the candidates with axis `dead-gotcha`.
|
|
74
|
+
|
|
75
|
+
- **Old last bite** (`last_bite` more than N months ago — a conservative default of 12, override with `<!-- evolve: stale-gotcha-months=N -->`; the `bites` line must also be unmoved in git, and a line whose age is unknown counts as unmoved) **and** `N` unchanged since then → **prune candidate**. It goes to the gate as "this gotcha may be dead (last bite `<date>`, count `N` unchanged) — archive it?"
|
|
76
|
+
- Apply the approval as a **tombstone**: comment out the block, do not delete it. This preserves history and *adds* lines, so it does **not** trigger hard rule 17 (the same pattern as `generalize`). Never hard-delete.
|
|
77
|
+
|
|
78
|
+
> **Why incidence, not avoidance:** the signal is "how many times the gotcha **bit**" (observable in git or in the session), never "how many times it **avoided** an error." Avoidance is counterfactual, unfalsifiable, and *reward-hackable* (the agent could freely self-report avoidance — exactly what the STOP guard in `PRINCIPLES.md` #3 forbids). Pruning by dry incidence is what the Reflexion paper approximates without violating that hardening.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 2. Findability (information exists but is misplaced)
|
|
83
|
+
|
|
84
|
+
**Behavioral plus structural** signal: during the session, the agent looked for guidance in `ach.md` or `gotchas/`/rubrics and **did not find it**. The information **does exist** in another doc or section, proven by a grep that finds it outside the expected place. This is the paper's "null-retrieval" seen from the **structure** angle, not the absence angle:
|
|
85
|
+
|
|
86
|
+
- If the information **exists but sits buried or in the wrong doc** → propose a fix. Relocate it, or add a pointer to it. Move it to the expected section, or add the pointer there. Cite both places (`where I looked` vs. `where it was`).
|
|
87
|
+
- If the information **does not exist anywhere** → **this is not `restructure`**. Route it to `reconcile` (adding new knowledge is `gotcha`/`rule`/a new section). `restructure` does not create missing knowledge (#5).
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. Partition by temperature (opt-in loading-strategy proposal)
|
|
92
|
+
|
|
93
|
+
Apply this only when a doc has **actually grown too large** (a `diagnose` A6 signal: inflation). Part of it must also be **deep reference rarely needed**.
|
|
94
|
+
|
|
95
|
+
- Propose a **loading strategy** mark. *Hot-always* content is needed every turn — conventions, commands, § 3 rules. *On-demand* content is deep subsystem reference. It becomes an appendix, read only when needed. The paper's split is hot / per-task / on-demand.
|
|
96
|
+
- The change is **an ADR proposal** (a doc architecture decision), **not** an automatic cut. Moving content to an on-demand appendix keeps 100% of the content (only re-partitions it) and follows the minimal-change discipline.
|
|
97
|
+
- **Opt-in:** without a real inflated doc, **do not propose** tiering. Small docs do not need it (YAGNI), and forced tiering is ceremony. This is the only `restructure` axis that can have zero findings in a healthy instance. The evidence for a tier change is the `tier-drift` axis of `afm doctor` (a cold doc used often, an idle hot or warm doc, an on-demand section read in most runs of its op). It stays a proposal at the gate: nothing changes `load:` on its own.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 4. Own gate (a-la-carte) + application — suppressed under `evolve`
|
|
102
|
+
|
|
103
|
+
Follow the **shared proposal discipline** in `evolve.md § Proposal discipline (shared)`. Read it and follow it. Do not duplicate it — the numbered procedure lives only there.
|
|
104
|
+
|
|
105
|
+
The findings carry **weight**. Staleness changes what the agent believes. Relocation changes where information lives. Tiering is an architecture decision. For this reason, the findings need an item-by-item, earned gate through `afm ask restructure` (`questions/restructure.questions.yaml`, one `--scope <item-slug>` per finding).
|
|
106
|
+
|
|
107
|
+
A-la-carte opens its own gate with that command. Under `evolve`, the process suppresses it.
|
|
108
|
+
|
|
109
|
+
On approval, apply the change. Update the stale doc after reading the current code. Relocate the information, and materialize the loading-strategy ADR if approved. Use the write ops where applicable (`afm adr new <slug>` for the loading-strategy ADR), and suppress their `afm ask`.
|
|
110
|
+
|
|
111
|
+
**Post-condition:** `afm doctor --json`, `afm index --check` and `afm detect --for <file>` over the touched docs must preserve the format the hooks parse. If it does not, report a rollback. The two-branch check in [`evolve.md § Phase 4.3`](./evolve.md#phase-4--application--postconditions--commit-κ-applied) must also report a green boundary.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 5. Record + handoff
|
|
116
|
+
|
|
117
|
+
1. **Materialize or append** to `docs/evolution/<date>-NNN.md` (create it with `afm new evolution-report --mechanism <...>`; fix findings with `afm lint --fix`): the staleness candidates (with git evidence), the relocations, and the tiering decision.
|
|
118
|
+
2. **Does it generalize?** If the loading strategy fits any instance, **record** it through `afm promote`. Give it a finding with evidence. Never apply it directly (#7).
|
|
119
|
+
3. **Hand off to `reconcile`** to watch whether the relocated or updated doc gets found in later sessions.
|
|
120
|
+
|
|
121
|
+
> **The report name is `YYYY-MM-DD-NNN.md` and its frontmatter MUST carry `mechanism: restructure`.**
|
|
122
|
+
> Three mechanisms write into `docs/evolution/`, and each one's cadence window is "since MY last
|
|
123
|
+
> report". A report with no `mechanism:` is invisible to every window, and an ordinal-first name
|
|
124
|
+
> makes the selector order by ordinal before date. Both were live defects until 3.13.0.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Suppression contract (back-pointer)
|
|
129
|
+
|
|
130
|
+
`restructure` **reuses** the proposal discipline, the bucket partition, and the post-conditions from `evolve.md`. Read and follow them. It also reuses the write ops: `adr.md` for the loading strategy, and it suppresses that op's `afm ask`. Under `evolve`, the process suppresses the § 4 gate. The authoritative table is in `evolve.md § Suppression contract`.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Anti-patterns
|
|
135
|
+
|
|
136
|
+
- ❌ **Staleness without git evidence** — "it looks out of date" is not a finding. `git log` on the path vs. the doc is one (#5).
|
|
137
|
+
- ❌ **Rewriting the stale doc alone** — staleness is a signal. The correct content requires reading the current code, so it goes up as a proposal, not an auto-fix.
|
|
138
|
+
- ❌ **Creating missing knowledge** — information that does not exist anywhere belongs to `reconcile`/`gotcha`, not `restructure`. `restructure` only relocates what already exists (#5).
|
|
139
|
+
- ❌ **Forcing tiering on a small doc** — partition by temperature is opt-in. Use it only when the doc has actually grown too large (YAGNI). Small docs do not need it.
|
|
140
|
+
- ❌ **Hard-deleting a gotcha or pruning by avoidance** — gotcha pruning is a **signal**. The signal is an old last bite plus an unchanged `N`. Send it to the gate. Apply it as a **tombstone** (rule 17), never an auto-delete. The signal must be observable incidence, never a self-reported "avoided N errors" claim. That claim is unfalsifiable and conflicts with STOP/#3.
|
|
141
|
+
- ❌ **Cutting content while re-partitioning** — moving content to an on-demand appendix keeps 100% of it. That is re-partition, not pruning. Pruning is a `diagnose`/A6 decision that goes to the gate.
|
|
142
|
+
- ❌ **Editing the bundled content** — **record** a loading strategy for the plugin through `promote`. Never apply it directly (absolute boundary #7). The boundary post-condition is in `evolve.md § Phase 4.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.
|