@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,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: core
|
|
3
|
+
description: 'The product: the job it does, the pain it removes and who it is for.'
|
|
4
|
+
load: warm
|
|
5
|
+
materializes_to: 'docs/prd.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# PRD — {{nome do produto}}
|
|
9
|
+
|
|
10
|
+
> Product Requirements Document. This is a living document. Edit it in place.
|
|
11
|
+
> Scope changes must pass through the product owner before merge.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## 1. Summary
|
|
15
|
+
|
|
16
|
+
{{The product in 1 paragraph. What it does, who uses it, what the concrete gain is. No marketing jargon. Explain it the way you would explain it to the CTO of another team, in 30 seconds.}}
|
|
17
|
+
|
|
18
|
+
Value proposition in one sentence: *"{{single sentence that captures why this exists}}"*
|
|
19
|
+
|
|
20
|
+
## 2. Problem & Opportunity
|
|
21
|
+
|
|
22
|
+
- **Observed pain:** {{who feels it, in what context, what they do today to work around it}}.
|
|
23
|
+
- **Why now:** {{technology/market/regulation that unlocked this now. Existing alternatives do not solve it, or got worse}}.
|
|
24
|
+
- **Size of the gain:** {{eliminate X class of problem, reduce Y latency, gain Z compliance}}.
|
|
25
|
+
|
|
26
|
+
## 3. Target User & Jobs-to-be-done
|
|
27
|
+
|
|
28
|
+
**Primary persona:** {{specific role at a specific type of company}}.
|
|
29
|
+
|
|
30
|
+
**Typical companies:**
|
|
31
|
+
- {{type A with symptom}}.
|
|
32
|
+
- {{type B with symptom}}.
|
|
33
|
+
|
|
34
|
+
**Jobs-to-be-done:**
|
|
35
|
+
- When {{context}}, I want {{result}} so that {{outcome}}.
|
|
36
|
+
|
|
37
|
+
**Typical scenarios:**
|
|
38
|
+
- "{{1-sentence description of the trigger}}"
|
|
39
|
+
|
|
40
|
+
## 4. Functional Requirements (RF)
|
|
41
|
+
|
|
42
|
+
| ID | Requirement | Priority |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| RF-01 | {{1-sentence description}} | P0 |
|
|
45
|
+
| RF-02 | {{description}} | P0 |
|
|
46
|
+
|
|
47
|
+
*[TBD — fill in as features are built.]*
|
|
48
|
+
|
|
49
|
+
## 5. Non-Functional Requirements (RNF)
|
|
50
|
+
|
|
51
|
+
| ID | Type | Criterion |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| RNF-01 | Availability | {{e.g. 99.5% monthly for the dashboard}} |
|
|
54
|
+
| RNF-02 | Latency | {{e.g. p95 < 500ms on operation X}} |
|
|
55
|
+
| RNF-03 | Security | {{e.g. tokens encrypted at rest}} |
|
|
56
|
+
|
|
57
|
+
*[TBD.]*
|
|
58
|
+
|
|
59
|
+
## 6. Goals by Stage
|
|
60
|
+
|
|
61
|
+
### 6.1 MVP — *"hello world end-to-end"* [PROPOSAL — confirm]
|
|
62
|
+
|
|
63
|
+
**Objective:** {{1 paragraph on what must work to reach MVP}}.
|
|
64
|
+
|
|
65
|
+
**Minimum scope:** {{list of RFs included}}.
|
|
66
|
+
|
|
67
|
+
**Out of the MVP:** {{list of what comes later}}.
|
|
68
|
+
|
|
69
|
+
**Acceptance criteria:** {{reproducible steps}}.
|
|
70
|
+
|
|
71
|
+
**Success metric:** {{numeric and measurable}}.
|
|
72
|
+
|
|
73
|
+
### 6.2 GTM — *"design partners"* [PROPOSAL — confirm]
|
|
74
|
+
|
|
75
|
+
**Objective:** {{N design partners in production with regular feedback}}.
|
|
76
|
+
|
|
77
|
+
**Positioning:** *"{{tagline}}"*. Focus on {{specific segment}}.
|
|
78
|
+
|
|
79
|
+
**Additional scope:** {{new RFs in GTM}}.
|
|
80
|
+
|
|
81
|
+
**Success metric:** {{NPS, retention, or another indicator}}.
|
|
82
|
+
|
|
83
|
+
### 6.3 First N — *"operational sustainability"* [PROPOSAL — confirm]
|
|
84
|
+
|
|
85
|
+
**Objective:** {{scale that runs without human heroics}}.
|
|
86
|
+
|
|
87
|
+
**Additional scope:** {{billing, multi-tenant isolation, SLA, etc.}}.
|
|
88
|
+
|
|
89
|
+
**Guardrails:** {{churn, NPS, uptime}}.
|
|
90
|
+
|
|
91
|
+
## 7. Metrics & North Stars [PROPOSAL — confirm]
|
|
92
|
+
|
|
93
|
+
**North Star:** **{{single metric}}** — {{why this one, not another}}.
|
|
94
|
+
|
|
95
|
+
**Input metrics (the team moves these directly):**
|
|
96
|
+
- {{metric A}}.
|
|
97
|
+
- {{metric B}}.
|
|
98
|
+
|
|
99
|
+
**Guardrails (must not get worse):**
|
|
100
|
+
- {{metric X with threshold}}.
|
|
101
|
+
|
|
102
|
+
## 8. Risks & Mitigations [PROPOSAL — confirm]
|
|
103
|
+
|
|
104
|
+
| Risk | Impact | Probability | Mitigation |
|
|
105
|
+
| --- | --- | --- | --- |
|
|
106
|
+
| {{risk}} | High/Medium/Low | High/Medium/Low | {{how we mitigate it}} |
|
|
107
|
+
|
|
108
|
+
## 9. Out of Scope (v1)
|
|
109
|
+
|
|
110
|
+
- **{{item explicitly out of scope}}.** {{Reason.}}
|
|
111
|
+
|
|
112
|
+
## 10. Glossary
|
|
113
|
+
|
|
114
|
+
- **{{term}}** — {{1-sentence definition}}.
|
|
115
|
+
|
|
116
|
+
## 11. Naming — product vs technical identifiers
|
|
117
|
+
|
|
118
|
+
- **User-facing product name:** "{{canonical name}}" — use it in UI, emails, titles, and external copy.
|
|
119
|
+
- **Technical identifiers do NOT change.** They are URL-safe and part of the infra:
|
|
120
|
+
- {{e.g. cookies, buckets, localStorage keys, folder paths — list which ones stay stable, and why}}.
|
|
121
|
+
|
|
122
|
+
### 11.1 App-facing string language
|
|
123
|
+
|
|
124
|
+
- **The language this product shows its own users:** {{language, or `[TBD]`}}.
|
|
125
|
+
|
|
126
|
+
This is a **product** decision and it lives here, not in `afm.md`. It is declared, never enforced.
|
|
127
|
+
|
|
128
|
+
The methodology writes every artifact in English — the docs, the code, the comments, the tests,
|
|
129
|
+
the rule text. This field exists because the strings your users read are the one exception. Without
|
|
130
|
+
this field, the exception is only prose. A language migration then has no way to tell an artifact
|
|
131
|
+
from a product string. It would translate your UI.
|
|
132
|
+
|
|
133
|
+
**Who reads it.** `afm path ops/update.md` reads it before a language migration and excludes those strings
|
|
134
|
+
by construction. `reconcile` reads it before it signals an artifact written in another language.
|
|
135
|
+
|
|
136
|
+
**`[TBD]` is a valid answer** and it is the default. It makes the migration stop and ask through
|
|
137
|
+
`afm ask init`. It never lets the migration choose a language for you.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
*Current state and living history are in `/docs/ust.md` (backlog) and `/docs/ach.md` (architecture). Process decisions are in `/docs/afm.md`.*
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: core
|
|
3
|
+
description: The autonomy posture, declared per job, that parametrizes the loop.
|
|
4
|
+
load: hot
|
|
5
|
+
materializes_to: 'docs/protocols/'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Protocol — the autonomy posture, declared per job
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
A **protocol** declares how the agent and the human split decisions in a delivery. Before v4, this posture was global and implicit. It was the same for a throwaway POC and for a production refactor, with one on/off switch (`"go"`).
|
|
12
|
+
|
|
13
|
+
A protocol is **data**, not code. It sets parameters for operations that already exist. It does not create a new execution path.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 1. The fields
|
|
18
|
+
|
|
19
|
+
| # | Field | What it decides |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| 1 | `plan` | **planning** posture: `auto` \| `assisted` |
|
|
22
|
+
| 2 | `exec` | **execution** posture: `auto` \| `assisted` |
|
|
23
|
+
| 3 | `policy` | the **escalation contract** — the mechanical conditions that force a stop |
|
|
24
|
+
| 4 | `cadence` | when `reflect` / `evolve` trigger |
|
|
25
|
+
| 5 | `delegation` | subagent delegation ceiling (§ 5) |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. The parameters
|
|
30
|
+
|
|
31
|
+
Each row is a literal that used to live scattered, and now has a name. **The `default` column reproduces pre-v4 behavior.** No protocol present means no change.
|
|
32
|
+
|
|
33
|
+
| Parameter | Where it applies | default | P1 | P2 | P3 |
|
|
34
|
+
| --- | --- | --- | --- | --- | --- |
|
|
35
|
+
| `RETRY_CAP` | `core/afm.md § 5.1` — retries before escalation | `2` | `2` | `1` | `2` |
|
|
36
|
+
| `MAX_DISCOVERY_ROUNDS` | `afm path ops/deliver.md` Phase 0 | `2` | `2` | `3` | `3` |
|
|
37
|
+
| `QUESTION_CAP` | `PRINCIPLES.md` #8 — questions per call | `4` | `4` | `4` | `4` |
|
|
38
|
+
| `ROUND_CAP` | `PRINCIPLES.md` #8 — rounds in the flow | `2` | `0` | `4` | `2` |
|
|
39
|
+
| `HALT_PER_PHASE` | `afm path ops/deliver.md` Phase 3 | chosen at the gate | `off` | `on` | `off` |
|
|
40
|
+
| `SUPPRESS_SUBGATES` | `deliver` suppression contract | `on` under orchestration | `on` | `off` | `on` |
|
|
41
|
+
| `PLAN_THRESHOLD` | `core/afm.md § 2` step 3 — when to require a plan file | heuristic `§ 2.1` | `§ 2.1` | report before editing | `§ 2.1` |
|
|
42
|
+
| `UNKNOWN_POLICY` | what to do with an irreducible unknown | ask | `[ASSUMIDO:]` | ask | ask in phase 1 |
|
|
43
|
+
| `MAX_PARALLEL_SUBAGENTS` | `afm path ops/deliver.md` 0.C — research topics per round | `4` | `4` | `2` | `6` |
|
|
44
|
+
| `REFLECT_EVERY` | `hooks/afm-session-end.sh` — deliverables closed before `reflect` becomes mandatory | `1` | `1` | `1` | `1` |
|
|
45
|
+
| `EVOLVE_EVERY` | `hooks/afm-session-end.sh` — deliverables closed before `evolve` becomes mandatory; also reported by `hooks/afm-health.sh` | `3` | `3` | `3` | `3` |
|
|
46
|
+
| `REFLECT_TASKS` | `hooks/afm-session-end.sh` — per-task floor, when work does not open a feature folder | `20` | `20` | `20` | `20` |
|
|
47
|
+
|
|
48
|
+
> **The three cadence parameters are declared on one line**, in `docs/_focus.md`: `cadence: reflect=1 evolve=3 tasks=20`. Every key accepts `off`, which turns off that arm and leaves the other one standing. An absent key keeps the default in this table. `hooks/lib/afm-cadence.sh` is the single reader (hard rule 34).
|
|
49
|
+
>
|
|
50
|
+
> **`20`, not the `4` this table used to carry — measured, not guessed.** `hooks/lib/afm-cadence.sh` hardcoded the default two ways at once: `4` here, `20` there, since 3.9.0-rc.9. The code's number is the one with a measurement behind it: tasks closed per session, read off this repository's own ledger on 2026-08-29, were 8, 17, 18, 29 and 150 — a median of 18, rounded up to 20. At `4`, the threshold came due four times inside a median session, and `Stop`'s cap of one block per session (G2) saturated it by construction — it fired on every session that did any work, which reads as noise and gets ignored. `20` comes due about once per median session, which is what G2 was sized for. This row now matches the code; no runtime behavior changed.
|
|
51
|
+
|
|
52
|
+
`ROUND_CAP = 0` in P1 makes "no questions" mechanical, not a promise. With zero rounds available, every irreducible unknown falls to `UNKNOWN_POLICY`.
|
|
53
|
+
|
|
54
|
+
> **`MAX_PARALLEL_SUBAGENTS` is the first of two rows whose `default` does NOT reproduce pre-v4 behavior.** Before, fan-out had no limit. The exception is deliberate. The delegation ceiling in § 5 always governed *what* gets delegated (`none`/`read-only`/`full`), never *how many*. A fan-out with no ceiling multiplies cost and latency without multiplying signal. Each subagent re-establishes context, re-explores, and returns a report the parent must reread. An explicit number costs less than the prose "delegate sparingly," which is not verifiable.
|
|
55
|
+
|
|
56
|
+
> **`REFLECT_EVERY`/`EVOLVE_EVERY` are the second exception, and it is deliberate.** Before, the threshold (`3-4`) lived **hardcoded in `afm-health.sh`** and produced a nudge at `SessionStart` — the worst possible spot. It sat thousands of tokens before the decision moment, and out of context when the delivery ended. The measured result: `reflect` triggered almost never, with the operator reporting whole protocols with zero reflections.
|
|
57
|
+
>
|
|
58
|
+
> This repo names the cause: *a primitive whose trigger is "remember to do it" has no trigger.* Moving the threshold into the project artifact lets `Stop` evaluate it (§ 6, **hard** category) instead of only reporting it.
|
|
59
|
+
>
|
|
60
|
+
> **The unit is the deliverable**, not the task. It is what the ledger already counts (`afm-capture.sh` appends a `deliverable` event), and it is the granularity the operator stated — *"reflect at the end of each protocol, evolve every N protocols"*. `REFLECT_EVERY = 1` is literally that.
|
|
61
|
+
>
|
|
62
|
+
> **`REFLECT_TASKS` exists because `deliverable` alone would be a dead trigger.** The `deliverable` event fires only when every task of the `tasks:` list of a `docs/features/*/tasks.md` is done. Work that never opens a feature folder — most work, per `§ 2.1` — never emits it. Measured in the plugin's own repo while building this feature: **25 `task` events, zero `deliverable` events**, over two months of real work. A gate anchored only on the first counter would never fire. It would be indistinguishable from a working gate, which is the worst property a gate can have.
|
|
63
|
+
>
|
|
64
|
+
> So the condition is a **disjunction**: `NDEL >= REFLECT_EVERY` **OR** `NTASK >= REFLECT_TASKS`. The first catches work that went through the delivery loop. The second catches the rest.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 3. The three presets
|
|
69
|
+
|
|
70
|
+
| | P1 — Autonomous | P2 — Assisted | P3 — Planned Autonomous |
|
|
71
|
+
| --- | --- | --- | --- |
|
|
72
|
+
| `plan` | `auto` | `assisted` | `assisted` |
|
|
73
|
+
| `exec` | `auto` | `assisted` | `auto` |
|
|
74
|
+
| For | POC, MVP — speed over depth | pair programming, human as senior dev | plans together, executes alone |
|
|
75
|
+
| Gate | none (bright lines only) | per phase | **one**, at the plan→exec transition |
|
|
76
|
+
| Cadence | `REFLECT_EVERY`/`EVOLVE_EVERY` (§ 2) — default `1`/`3` deliverables | same | same — phase 2 closes with mandatory `reflect` |
|
|
77
|
+
|
|
78
|
+
**What P1 does NOT relax:** a hard rule with an executable trigger stays 0/1. Security posture is not negotiable (`core/afm.md § 1.3` — hardening does not trade for speed). What relaxes is **depth**: coverage, edge-case exhaustiveness, naming refinement.
|
|
79
|
+
|
|
80
|
+
**P2 is compositional, not introspective.** P2 does not call `afm ask` merely because it is unsure. Self-reported confidence is not an executable trigger, per principle #3 (CRITIC). P2 is exactly three knobs: `SUPPRESS_SUBGATES=off`, `RETRY_CAP=1`, `HALT_PER_PHASE=on`.
|
|
81
|
+
|
|
82
|
+
**P2 floor.** Below the feature-folder heuristic (`§ 2.1`), the assisted posture degrades to **"report before commit"**. Calling `afm ask` at every step for a trivial change is noise that makes people turn the protocol off. Above the heuristic, the change stops being a loose task and falls into the runner's path.
|
|
83
|
+
|
|
84
|
+
**P3 transition gate** — a mechanical criterion, not judgment: `plan.md` and `tasks.md` with no open `[NEEDS CLARIFICATION:]` (`grep`, 0/1).
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 4. What the protocol does NOT govern
|
|
89
|
+
|
|
90
|
+
Invariants that hold in **every** protocol, including P1:
|
|
91
|
+
|
|
92
|
+
- **Bright lines** (`PRINCIPLES.md` #6): destructive action, editing the canonical plugin, `git push`, hard rule 11.
|
|
93
|
+
- **`reconcile`** — always on, it proposes and never applies. A passive signal is not approved scope.
|
|
94
|
+
- **Self-evolution operations** (`evolve` and its mechanisms) — a **mandatory gate**, a first-class exception alongside the bright lines. (This used to read "`evolve` has no `AUTONOMY=high` path." The flag is now replaced by a protocol. The sentence had to stop denying a flag that no longer exists.)
|
|
95
|
+
- **`RETRY_CAP` minimum of 1** — zero retries would turn every transient failure into an escalation.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 5. Subagent: delegation ceiling, not inheritance
|
|
100
|
+
|
|
101
|
+
The harness already propagates permission **downward, with no override**. A subagent is at most as loose as its parent, and plugin subagents ignore `permissionMode`. So the protocol **is not inherited**. It declares a **ceiling**.
|
|
102
|
+
|
|
103
|
+
| `delegation` | Means |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `none` | does not delegate writes — the gate stays with the parent (P2 default) |
|
|
106
|
+
| `read-only` | delegates only search/read (`Explore`) |
|
|
107
|
+
| `full` | delegates writes — `SubagentStart` injects the hard rules (default for P1 and P3-exec) |
|
|
108
|
+
|
|
109
|
+
The escalation contract (field 3) **does not flow down**. A subagent has no useful `afm ask` channel — the question would die in its own transcript.
|
|
110
|
+
|
|
111
|
+
**Two dimensions, not one.** `delegation` answers *what* can be delegated. `MAX_PARALLEL_SUBAGENTS` (§ 2) answers *how many at once*. The two are independent — `read-only` with no ceiling can still fire N parallel searches. The ceiling applies to the fan-out of a single turn, not to the session total. A sequence of rounds is still governed by `MAX_DISCOVERY_ROUNDS`.
|
|
112
|
+
|
|
113
|
+
When the desired fan-out exceeds the ceiling: **prioritize, do not split into batches.**
|
|
114
|
+
|
|
115
|
+
- Running 6 topics under a ceiling of 4 means investigating the 4 that move the decision most.
|
|
116
|
+
- Log the other 2 as `[NEEDS CLARIFICATION:]`.
|
|
117
|
+
- Do not run two rounds of 3. That pays the spawn cost twice for the same signal.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 6. Hard, counted, and soft
|
|
122
|
+
|
|
123
|
+
| Category | Mechanism | Who enforces |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| **Hard** | cadence counters derived from the ledger (`afm-capture.sh` appends → `hooks/lib/afm-cadence.sh` derives), 0/1 gates in `test/`, **`REFLECT_EVERY` at `Stop`** (blocks session end until the reflection exists as an artifact) | the harness |
|
|
126
|
+
| **Counted** | `RETRY_CAP` and `QUESTION_CAP` (self-observable by the model within its own context window — a hook here would duplicate information already present: YAGNI) · `MAX_PARALLEL_SUBAGENTS` (reportable only *after the fact* — the fan-out goes out in a single message, before the first `SubagentStart` fires. The v3.5.0 blocking carve-out is **named and singular**: it applies only to `Stop`, not to `SubagentStart` — see `hooks/README.md`) · `ROUND_CAP` (a counter, not a promise — see note below) | no one — it is counted, and overflow is observable, but nothing blocks |
|
|
127
|
+
| **Soft** | the posture described in prose in the protocol file | the model cooperates |
|
|
128
|
+
|
|
129
|
+
> **Design correction (2026-07-25).** The plan called for denying the ask tool as the hard mechanism for P1, through a per-skill tool denylist. It **does not fit this architecture**. A tool denylist is static, **per-skill** frontmatter that not every harness has, and the protocol is **data** layered on a **shared** runner (`deliver` serves all three presets). A separate skill just for P1 would multiply the listing — the exact problem v4 exists to solve.
|
|
130
|
+
> The real P1 mechanism is **`ROUND_CAP = 0`** — **counted**, in the § 6 category, not hard. With no question round available, every irreducible unknown falls to `UNKNOWN_POLICY` and becomes `[ASSUMIDO:]`. It is a counter, not a promise — and a counter is exactly the form that survives the Claude 5 generation cut.
|
|
131
|
+
|
|
132
|
+
A declarative artifact in the repo **does not bind the harness**. The official precedent: Claude Code ignores `defaultMode: "auto"` coming from the project's `.claude/settings.json`, *"so a repository cannot grant itself auto mode"*. Two design consequences follow:
|
|
133
|
+
|
|
134
|
+
1. **Every field declares which category it lives in.** A soft-only field with no matching gate is prescriptive prose. It is exactly what Claude 5 generation guidance says to cut.
|
|
135
|
+
2. **P1 is not granted by the repo.** `docs/protocols/p1.md` can exist and be versioned. But P1 becomes **current** only through an explicit act in the session. A `git pull` never changes the autonomy posture.
|
|
136
|
+
|
|
137
|
+
> **Note — `permissions.deny`.** It is left out of the table above because it is a harness mechanism class, not something AFM configures (cited once in the repo, with no `settings.json` that configures it).
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 7. Current protocol
|
|
142
|
+
|
|
143
|
+
**`afm protocol` is the skill that reads and writes this layer.** With no argument it reports the current posture. With an argument (`afm protocol p2`) it materializes the preset into `docs/protocols/` and writes the `protocol:` line below. Editing the line by hand does the same thing. The skill exists so the presets stay data instead of prose someone retypes.
|
|
144
|
+
|
|
145
|
+
A line in `docs/_focus.md`:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
protocol: p3
|
|
149
|
+
cadence: reflect=1 evolve=3
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The `cadence:` line is optional. It overrides the § 2 defaults — `reflect=off` turns off the entire `Stop` block (guard G3 in `afm-session-end.sh`). When absent, the current preset's defaults apply.
|
|
153
|
+
|
|
154
|
+
Read by `SessionStart` (marginal cost ~0 — the hook already injects `_focus.md`). Also read by step 1 (READ) of `core/afm.md § 2`, which already lists this file. **A project with no such line runs pre-v4 behavior** — the default for each parameter in the § 2 table.
|
|
155
|
+
|
|
156
|
+
**The config is the floor of both lines.** `[protocol] preset` and `[cadence]` in the layered config (`afm config get protocol.preset`) are in force where `docs/_focus.md` says nothing: a preset the config sets stands in for a missing `protocol:` line, and a cadence value replaces the default for a key the `cadence:` line omits. A key the organisation locks (`[org] locked`) ignores the focus file: `afm protocol <name>` refuses another preset (exit 1) and the hooks read the locked value. A project config can never grant p1; only a person's `afm protocol p1 --i-mean-it` does.
|
|
157
|
+
|
|
158
|
+
An already-bootstrapped instance receives `protocol: [TBD]` from `afm update`. Assuming the posture the owner wants would be invention (principle #4).
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: core
|
|
3
|
+
description: The user stories, which are the oracle for every acceptance criterion.
|
|
4
|
+
load: warm
|
|
5
|
+
materializes_to: 'docs/ust.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# UST — User Stories
|
|
9
|
+
|
|
10
|
+
> Living backlog of stories. Every new feature or change starts as a US.
|
|
11
|
+
> Status flows: `draft` → `ready` → `in_progress` → `done` (or `cancelled` with a reason).
|
|
12
|
+
|
|
13
|
+
## Conventions
|
|
14
|
+
|
|
15
|
+
- **ID:** `US-NNN` (3 digits, zero-padded — `US-001` to `US-999`).
|
|
16
|
+
- **Cross-reference:** always by ID (`RF-NN`, `RNF-NN`, `US-NNN`).
|
|
17
|
+
- **Criteria:** Gherkin (`Given/When/Then`). 1 or more scenarios per US.
|
|
18
|
+
- **Status:** update in place, in the commit that changes the real state.
|
|
19
|
+
|
|
20
|
+
## Summary
|
|
21
|
+
|
|
22
|
+
| ID | Title | Category | Priority | Status |
|
|
23
|
+
| --- | --- | --- | --- | --- |
|
|
24
|
+
| US-001 | {{Título}} | {{categoria}} | P0 | ready |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Epic {{Nome do Épico}}
|
|
29
|
+
|
|
30
|
+
### US-001 — {{título objetivo, "[Persona] [verbo] [outcome]"}}
|
|
31
|
+
|
|
32
|
+
- **Persona:** {{quem do PRD §3}}.
|
|
33
|
+
- **Pain point:** {{1 sentence on the workaround they use today}}.
|
|
34
|
+
- **Story:** As a {{persona}}, I want {{ação}} so that {{outcome mensurável}}.
|
|
35
|
+
|
|
36
|
+
**Acceptance criteria:**
|
|
37
|
+
|
|
38
|
+
```gherkin
|
|
39
|
+
Scenario: {{nome do cenário happy path}}
|
|
40
|
+
Given {{precondição}}
|
|
41
|
+
And {{precondição adicional se houver}}
|
|
42
|
+
When {{ação do user}}
|
|
43
|
+
Then {{resultado esperado observável}}
|
|
44
|
+
And {{efeito colateral verificável}}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```gherkin
|
|
48
|
+
Scenario: {{cenário de erro mais provável}}
|
|
49
|
+
Given {{precondição}}
|
|
50
|
+
When {{ação do user em estado inválido}}
|
|
51
|
+
Then {{erro esperado, classificação tipada}}
|
|
52
|
+
And {{nada de side-effect de DB}}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Metadata:**
|
|
56
|
+
- **Priority:** P0
|
|
57
|
+
- **Status:** ready
|
|
58
|
+
- **RF:** RF-NN
|
|
59
|
+
- **RNF:** RNF-NN (if applicable)
|
|
60
|
+
- **Success metric:** {{how to measure that the user job is done}}
|
|
61
|
+
- **Dependencies:** US-NNN, US-NNN (if any)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
*To add a new US: copy the block above and place it below the matching epic. If the epic does not exist, create a new H2. Number the ID in sequence after the last one used.*
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Guardrail catalog
|
|
2
|
+
|
|
3
|
+
> **This is data, not a skill.** Each file here describes a gate that AFM can **suggest** when a
|
|
4
|
+
> signal matches the project. Suggesting is all it does on its own. Installing needs the
|
|
5
|
+
> operator's OK. Writing outside `docs/` is a bright line.
|
|
6
|
+
|
|
7
|
+
The catalog grows, but the mechanism that reads it does not. `interview/triggers.md` has **one**
|
|
8
|
+
line for the whole catalog. That line says a guardrail whose `signal` matches this project is not
|
|
9
|
+
installed yet. One line per guardrail would repeat the same thing N times.
|
|
10
|
+
|
|
11
|
+
## The two origins
|
|
12
|
+
|
|
13
|
+
The catalog in this directory is **gate suggestion by project signal**. The guardrails that the
|
|
14
|
+
consumer materializes in `<consumer>/docs/guardrails/` arrive through two doors. Both produce the
|
|
15
|
+
same artifact.
|
|
16
|
+
|
|
17
|
+
| `origin:` | Comes from | Procedure |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `recurring-failure` | `sig=` recurred 2 or more times (or 1 user correction) in the substrate | [`../rubrics/failure-classification.md`](../rubrics/failure-classification.md) → [`../ops/remediate.md`](../ops/remediate.md) |
|
|
20
|
+
| `hard-rule` | rule in `afm.md § 3` with a 0/1 trigger that no one ran | [`../ops/rule.md` § Install the gate](../ops/rule.md) |
|
|
21
|
+
|
|
22
|
+
Installation itself is **one procedure**, in `ops/rule.md`. The two doors compose into it.
|
|
23
|
+
|
|
24
|
+
## Format
|
|
25
|
+
|
|
26
|
+
YAML frontmatter plus prose. The fields the mechanism reads:
|
|
27
|
+
|
|
28
|
+
| Field | What it is |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `id` | stable and citable. Never reuse a retired id. The historical rationale points to it |
|
|
31
|
+
| `signal` | the command that detects whether **this project** would benefit. Exit 0 means match. It triggers the suggestion |
|
|
32
|
+
| `trigger` | the 0/1 check the gate runs after install. Without this, it is advice, not a guardrail |
|
|
33
|
+
| `surface` | where it **can** run. Where it **will** run is the operator's choice |
|
|
34
|
+
| `params` | what the operator fills in. Never hardcode an operator constant. That was the bug that started this catalog |
|
|
35
|
+
| `cost` | `low`/`medium`/`high`. High does not suggest itself. It runs only when the operator asks |
|
|
36
|
+
|
|
37
|
+
## The burden of refutation
|
|
38
|
+
|
|
39
|
+
Every file ends in **`## What would refute this`**, and this field is not decorative.
|
|
40
|
+
|
|
41
|
+
An installed guardrail becomes permanent friction. Friction without a visible rationale is the
|
|
42
|
+
first thing someone removes on a tight Friday. This section states **what evidence would overturn
|
|
43
|
+
the recommendation**. It is not "when not to use this," which is opinion. It is the observable
|
|
44
|
+
fact that makes the gate wrong for this project. Anyone who removes a guardrail should be able to
|
|
45
|
+
cite the line that refuted it.
|
|
46
|
+
|
|
47
|
+
## What does NOT go in
|
|
48
|
+
|
|
49
|
+
- **A guardrail without a 0/1 `trigger`.** Without a command, it is not a gate. If the idea is
|
|
50
|
+
good but does not mechanize, it belongs in `core/afm.md § 1.3` as a principle, not in this
|
|
51
|
+
directory.
|
|
52
|
+
- **A guardrail whose constant AFM chooses.** The framework supplies the form. The operator
|
|
53
|
+
supplies the value. A catalog that hardcodes `300` repeats the bug it exists to fix.
|
|
54
|
+
- **A guardrail that blocks only by heuristic.** A gate that rejects correct work gets bypassed.
|
|
55
|
+
A bypassed gate protects nothing.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: guardrail-suggestion
|
|
3
|
+
description: A fluent builder whose call order matters should refuse the wrong order at compile time.
|
|
4
|
+
load: cold
|
|
5
|
+
id: dsl-com-ordem-obrigatoria
|
|
6
|
+
title: A builder whose order matters must refuse the wrong order at compile time
|
|
7
|
+
signal: |
|
|
8
|
+
grep -rqE 'return this;?$' --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=build --exclude-dir=vendor --exclude-dir=target --exclude-dir=.git . 2>/dev/null
|
|
9
|
+
trigger: tsc --noEmit
|
|
10
|
+
surface: [tsconfig-or-lint, script-local]
|
|
11
|
+
params: [tipo-do-builder]
|
|
12
|
+
cost: high
|
|
13
|
+
source: patterns/16-type-state-builder-faseado.md
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Mandatory order in a fluent chain
|
|
17
|
+
|
|
18
|
+
A builder with mandatory order — `.authorize()` before `.refund()` — is a deferred bug
|
|
19
|
+
**when it only fails at runtime**. It wears the disguise of an elegant API.
|
|
20
|
+
The person who writes the wrong chain finds out in production.
|
|
21
|
+
|
|
22
|
+
The pattern that closes this gap is **type-state**. Each method returns a type.
|
|
23
|
+
That type exposes only the next legal methods. The wrong order stops compiling.
|
|
24
|
+
No path to runtime remains.
|
|
25
|
+
|
|
26
|
+
**This guardrail carries `cost: high` on purpose.** The cheap exit is part of the
|
|
27
|
+
recommendation. Type-state often gets expensive in TypeScript. When it does,
|
|
28
|
+
**do not build the builder.** Use one function with all arguments required.
|
|
29
|
+
It solves the same problem without the technique. The rubric
|
|
30
|
+
`rubrics/when-to-create-dsl.md` prefers this exit by default.
|
|
31
|
+
|
|
32
|
+
**The proof must stay committed.** Add a `@ts-expect-error` above the call that uses
|
|
33
|
+
the illegal order. Place it inside the test file. This turns a one-time audit into
|
|
34
|
+
a gate. If the protection regresses, the `@ts-expect-error` becomes an unused
|
|
35
|
+
directive. Then `tsc` fails the build.
|
|
36
|
+
|
|
37
|
+
## What would refute this
|
|
38
|
+
|
|
39
|
+
- **The order is not truly mandatory.** Suppose `.b()` before `.a()` only produces
|
|
40
|
+
a different result, not an illegal state. Then there is nothing to protect.
|
|
41
|
+
Type-state here is dead weight.
|
|
42
|
+
- **The chain has two steps.** The cost of the pattern does not pay off. Two
|
|
43
|
+
arguments in one function solve the same problem.
|
|
44
|
+
- **You measured the ergonomics and they are bad.** Type-state error messages in
|
|
45
|
+
TypeScript are famously unclear. An error nobody understands can cost more than
|
|
46
|
+
the bug it prevents. This is legitimate evidence against the pattern.
|
|
47
|
+
It explains why `cost: high` exists here.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: guardrail-suggestion
|
|
3
|
+
description: A new union case should break every caller that does not handle it.
|
|
4
|
+
load: cold
|
|
5
|
+
id: exaustividade-de-union
|
|
6
|
+
title: A new case in a union must break every caller that does not handle it
|
|
7
|
+
signal: |
|
|
8
|
+
grep -rqE 'as const' --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=build --exclude-dir=vendor --exclude-dir=target --exclude-dir=.git . 2>/dev/null
|
|
9
|
+
trigger: tsc --noEmit
|
|
10
|
+
surface: [tsconfig-or-lint, script-local, ci]
|
|
11
|
+
params: []
|
|
12
|
+
cost: low
|
|
13
|
+
source: patterns/03-exaustividade-com-never.md
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Exhaustiveness with `never`
|
|
17
|
+
|
|
18
|
+
A `switch` over a union of literals does **not** guarantee coverage in the caller. A new code
|
|
19
|
+
compiles clean in every existing caller. The `switch` falls through in silence, and the
|
|
20
|
+
`if`-chain returns `undefined`. Only a `default` that assigns the rest to `never` gives you that
|
|
21
|
+
guarantee.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
default: {
|
|
25
|
+
const _exhaustive: never = result.code;
|
|
26
|
+
throw new Error(`unhandled code: ${_exhaustive}`);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Marginal cost is zero.** This does not install a new tool. The gate is the `tsc --noEmit` the
|
|
31
|
+
project already runs. What changes is that the code now produces the error instead of swallowing
|
|
32
|
+
the case.
|
|
33
|
+
|
|
34
|
+
**What changes with an agent:** it adds the new member to the union because the task asked for it.
|
|
35
|
+
It does not visit the callers, because nothing forced it to. The compiler is the only reviewer
|
|
36
|
+
that never forgets.
|
|
37
|
+
|
|
38
|
+
## What would refute this
|
|
39
|
+
|
|
40
|
+
- **The union is open by design** (a third-party error code, a protocol string that grows outside
|
|
41
|
+
your control). Exhaustiveness is the wrong model here. If you force it, someone silences the
|
|
42
|
+
`default` with `as never`. That is the same hole, with one more line.
|
|
43
|
+
- **The project has no type-check.** Then there is no gate to install here. Turn the type-check on
|
|
44
|
+
first.
|
|
45
|
+
|
|
46
|
+
> AFM already fixed this exact case in itself. `rubrics/error-classification.md` claimed
|
|
47
|
+
> that an exhaustive switch guarantees caller coverage, and the claim was **false**. A test with
|
|
48
|
+
> `tsc --noEmit --strict` proved it false, on a union of three codes with a switch that covered two.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: guardrail-suggestion
|
|
3
|
+
description: A layer import boundary should be a command that fails, not a paragraph in the README.
|
|
4
|
+
load: cold
|
|
5
|
+
id: fronteira-de-import
|
|
6
|
+
title: A layer boundary is a command, not a paragraph in the README
|
|
7
|
+
signal: |
|
|
8
|
+
test -d packages -o -d apps && ! grep -q '"references"' tsconfig.json 2>/dev/null
|
|
9
|
+
trigger: tsc -b
|
|
10
|
+
surface: [tsconfig-or-lint, ci, script-local]
|
|
11
|
+
params: [camadas, quem-pode-importar-quem]
|
|
12
|
+
cost: medium
|
|
13
|
+
source: guardrails/code-quality/05-fronteiras-de-arquitetura-executaveis.md
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Executable import boundary
|
|
17
|
+
|
|
18
|
+
The rule "`billing` does not import from `ui`" needs a command. The command must
|
|
19
|
+
return a non-zero exit code when someone breaks the rule. While the rule lives in
|
|
20
|
+
the README, a human must enforce it by inspection. Human inspection does not scale.
|
|
21
|
+
It cannot match the volume of PRs a team with agents produces.
|
|
22
|
+
|
|
23
|
+
The wrong import compiles. It fixes the bug of the day. It passes a forty-second
|
|
24
|
+
review. Months later it is circular coupling, with five modules tangled instead of
|
|
25
|
+
one.
|
|
26
|
+
|
|
27
|
+
**What changes with an agent:** a senior human has social hesitation. They think
|
|
28
|
+
"this import looks strange, let me ask". An agent is the most literal executor of
|
|
29
|
+
"make the test pass". It imports from whatever module solves the task fastest.
|
|
30
|
+
|
|
31
|
+
Human review is good at local complexity in a diff. It is bad at fan-in/fan-out
|
|
32
|
+
drift, because that drift does not show in the diff. It shows in the graph.
|
|
33
|
+
|
|
34
|
+
## How to install
|
|
35
|
+
|
|
36
|
+
Add `composite: true` and `references` to each package's `tsconfig.json`. A passing
|
|
37
|
+
`tsc -b` (exit 0) then becomes **impossible** with a broken boundary. This is not a
|
|
38
|
+
lint that warns. It is a build that does not close.
|
|
39
|
+
|
|
40
|
+
Outside TypeScript, use the stack's declarative config equivalent
|
|
41
|
+
(`dependency-cruiser`, `import-linter`, `ArchUnit`). One thing does **not** work:
|
|
42
|
+
`grep` for a forbidden import. `grep` does not track transitivity, so a boundary
|
|
43
|
+
break two hops away passes clean.
|
|
44
|
+
|
|
45
|
+
## What would refute this
|
|
46
|
+
|
|
47
|
+
- **The project has only one package.** With no graph, there is no boundary to
|
|
48
|
+
cross, and `references` is ceremony.
|
|
49
|
+
- **The layers are still forming.** Code that changes shape every week does not
|
|
50
|
+
support a frozen boundary. A frozen boundary there produces a gate that rejects
|
|
51
|
+
legitimate work. The team then learns to edit the config instead of respecting
|
|
52
|
+
the boundary. That outcome is worse than no gate at all.
|
|
53
|
+
- **`tsc -b` ran and missed the violation you have.** Then the problem is not the
|
|
54
|
+
import boundary. This guardrail is not the fix. (Precedent: a catalog listed
|
|
55
|
+
`tsc -b --dry` as proof of a boundary check, but it **runs no type-check at all**.
|
|
56
|
+
Someone built the monorepo with the violation in place, and measured exit 0.
|
|
57
|
+
Measure before you trust it.)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# AFM hooks — shared contract
|
|
2
|
+
|
|
3
|
+
The hooks of AFM 4.0 are one command, `afm hook <Event>`, not a set of scripts. Each harness adapter
|
|
4
|
+
(claude-code, opencode, agy) wires its own native events to that command and translates the payload
|
|
5
|
+
in and the decision out. The logic is TypeScript in `packages/core/src/features/hook/domain/`, so it is
|
|
6
|
+
tested once for every harness. The git hooks (`afm install git`) run the gate with no AI at all.
|
|
7
|
+
The 3.x bash scripts and their history live in `CHANGELOG-archive.md`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Contract
|
|
12
|
+
|
|
13
|
+
- **cwd** = consumer project root (reads `./docs/...`).
|
|
14
|
+
- **Silent no-op** outside an AFM project (no `docs/.afm-version`): the decision is `noop`, exit 0, no stdout.
|
|
15
|
+
- **A bug fails open, a policy decision fails closed.** A crash, a timeout or an unreadable payload exits 0 and the operation goes on. A decision the hook made (a red gate on a commit) refuses. **Exit 2 means "I did not decide"** and never counts as a pass.
|
|
16
|
+
- **A hook never blocks an operation by default.** The one rule has **two named exceptions** (below). The list is nominal: no third hook inherits the permission, and a test fails if one does (see the carve-outs below).
|
|
17
|
+
- **Factual phrasing, never imperative.** The injected text describes the state of the project; what to do with it is judgment. An order triggers the model's anti-injection defenses.
|
|
18
|
+
- **Every handler is a pure function** `payload -> decision`. A decision is one of `inject | deny | block | noop`, and the adapter renders it in the harness's native format.
|
|
19
|
+
- **No secret is ever written.** A captured command is redacted before it reaches the state store.
|
|
20
|
+
|
|
21
|
+
## Events
|
|
22
|
+
|
|
23
|
+
The adapters normalize every harness to six events.
|
|
24
|
+
|
|
25
|
+
| Event | Handler | What it does |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| `session.start` | digest | Injects the § 3 rule headlines, the doctrine, and a pending handoff or focus. The focus carries its age ("last updated N days ago") and is flagged possibly stale after 7 days; an unset `protocol: [TBD]` points at `afm protocol`. It does not run `afm doctor`. |
|
|
28
|
+
| `tool.pre` on a write | pre-edit | Injects the rule, gotcha or learning whose `Trigger:` matches the file by path segment or by content (`afm detect --for <file>`). |
|
|
29
|
+
| `tool.pre` on a shell command | pre-commit | When the command is a `git commit` (read by a token grammar, never a substring), runs `afm gate --staged` and refuses on red. |
|
|
30
|
+
| `tool.post` | capture | Appends one line to the ledger for an edit to a normative doc, and for the op that ran. It also warns once per file and session when a `docs/*.md` write is over `max-doc-bytes`. |
|
|
31
|
+
| `tool.fail` | failure capture | Records a failure of an oracle (test, typecheck, lint, build) with its `sig=`. The second occurrence of a `sig=` is what `afm failures promotable` lists. |
|
|
32
|
+
| `subagent.start` | headlines | Hands a sub-agent that writes the hard-rule headlines, because it never sees the parent's digest. A read-only role gets nothing. |
|
|
33
|
+
| `session.stop` | handoff | Writes the mechanical handoff of a session with work. The judgment fields are the agent's (`reflect`). |
|
|
34
|
+
|
|
35
|
+
Matching is **by content for gotchas and rubrics, by path segment for hard rules**. `afm detect`
|
|
36
|
+
evaluates `trigger_path`, `trigger_basename`, `trigger_ext`, `trigger_grep`, `trigger_min_lines`,
|
|
37
|
+
`trigger_max_consumers` and `trigger_new_module`, conjunctive unless `trigger_any: yes`. A gotcha keyed
|
|
38
|
+
on an API call fires only if it declares `fires_on: file-shape` and a `trigger_grep`.
|
|
39
|
+
|
|
40
|
+
> **The cap starves a narrow trigger.** `MAX_RUBRICS` and `AFM_MAX_GOTCHAS` default to 2, and matches are
|
|
41
|
+
> emitted in filename order, not by specificity. A broad entry with no content condition wins the slot over
|
|
42
|
+
> a precise one that sorts later. The cap is deliberate (an always-loud channel gets ignored); the order is
|
|
43
|
+
> a known limit.
|
|
44
|
+
|
|
45
|
+
## The Stop carve-out and the pre-commit carve-out
|
|
46
|
+
|
|
47
|
+
- **`session.stop` may `block` for a cadence.** The reflection arm and the evolve arm are independent: each has its own threshold, its own opt-out key (`cadence: reflect=off`, `cadence: evolve=off` in `docs/_focus.md`) and its own ceiling of one block per threshold crossing. A stop that is already the result of a block never blocks again, which is what prevents an infinite loop. Blocking a stop postpones the end of a turn; it never stops work. The counter is **derived** from the ledger and `docs/evolution/`, never stored, so it resets only when a real cycle is on disk.
|
|
48
|
+
- **`tool.pre` on a shell command may `deny` a `git commit`** when the gate is red and the project has no native pre-commit surface (no husky, lefthook, `pre-commit`, or `core.hooksPath`). Blocking a commit only postpones it: the work stays on disk.
|
|
49
|
+
|
|
50
|
+
The commit detector is a **nudge for the common path, not a boundary.** It reads `git commit` after a
|
|
51
|
+
separator, inside `(...)` or `{...}`, behind `VAR=value`, behind an enumerated wrapper (`command`, `env`,
|
|
52
|
+
`sudo`, `nohup`, `nice`, `time`, `stdbuf`) or shell (`sh -c`, `bash -c`), quoted, and with the global
|
|
53
|
+
options that take a value. It cannot see `eval`, a variable holding the command, or a script on disk.
|
|
54
|
+
The boundary is `afm install git`, which writes git's own `pre-commit`; git runs it however the command
|
|
55
|
+
is spelled. That is why the nudge goes quiet once a native surface exists.
|
|
56
|
+
|
|
57
|
+
`block` carries the only imperative prose a hook may emit, because saying what to do is the field's
|
|
58
|
+
meaning: `{ "decision": "block", "reason": "<what to do, with both paths>" }`.
|
|
59
|
+
|
|
60
|
+
## Where state lives
|
|
61
|
+
|
|
62
|
+
Nothing a hook records goes into the repository. The ledger, the handoffs, the captured failures and the
|
|
63
|
+
dedup flags live under `AFM_HOME` (`~/.afm/state/<project-hash>/`), with a TTL on the volatile ones. The
|
|
64
|
+
repository keeps only the structure of `docs/` and its markers. `afm gc` deletes the expired keys.
|
|
65
|
+
|
|
66
|
+
## Changing a hook
|
|
67
|
+
|
|
68
|
+
- A handler is a pure function under `packages/core/src/features/hook/domain/` named `domain_<name>`, with its test beside it.
|
|
69
|
+
- If you change the `docs/` format a handler parses (the `## 3.` heading, a rule's `*Verification:*` field, a gotcha's `trigger_path` key), change the handler and its fixture in the same commit.
|
|
70
|
+
- A hook that depends on a harness event must go through the adapter. No handler branches on a harness name.
|