@tenkara/afm-linux-arm64 0.0.0-stage → 4.0.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +4330 -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/obligations.md +145 -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/obligations.questions.yaml +17 -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/obligations.md +27 -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-obligations/SKILL.md +31 -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/obligations/SKILL.md +31 -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-obligations/SKILL.md +31 -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,514 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: core
|
|
3
|
+
description: 'The methodology itself: the task loop, the hard rules and the cadence.'
|
|
4
|
+
load: hot
|
|
5
|
+
materializes_to: 'docs/afm.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# AFM — Agent Flow Methodology
|
|
9
|
+
|
|
10
|
+
> Operational playbook for the agent (human or LLM) in this project.
|
|
11
|
+
> It mixes **Extreme Programming** (Kent Beck) + **Pragmatic Programmer** (Hunt/Thomas) + practices specific to {{nome do projeto}}.
|
|
12
|
+
>
|
|
13
|
+
> This is the **canonical source** for how to do tasks here. If `CLAUDE.md` and this doc disagree, this doc wins.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Principles
|
|
19
|
+
|
|
20
|
+
### 1.1 Inherited from XP
|
|
21
|
+
|
|
22
|
+
> The XP and Pragmatic-Programmer canon is assumed, not restated. What follows is only the part
|
|
23
|
+
> this project **decides** — a stance an agent would not adopt on its own. DRY lives in § 1.3 with
|
|
24
|
+
> the Rule of Three, and the tracer bullet in § 4.1, each at its point of use.
|
|
25
|
+
|
|
26
|
+
- **TDD** — no new code without a test that fails first. Loop: red → green → refactor.
|
|
27
|
+
- **Continuous refactor** — after green, improve the design right away. Do not let debt build up.
|
|
28
|
+
- **Simple design (YAGNI/DRY/KISS)** — use the smallest solution for the current problem. Duplicate only after the second caller (Rule of Three). Inline beats abstract.
|
|
29
|
+
|
|
30
|
+
### 1.3 Specific to this project
|
|
31
|
+
|
|
32
|
+
- **One responsibility per file**. A file does **one** thing well. A vague name like "and"/"Manager"/"Utils" hides two responsibilities — split the file. A file is a unit of reasoning. Reading it fully must fit in one mental diff.
|
|
33
|
+
- **File ≤ {{limite-linhas}} lines**. Hard limit, with strict exceptions (generated file, full schema, large fixture). Over the limit, split the file.
|
|
34
|
+
- **Explicit types at boundaries, inference elsewhere**. Exported function parameters and return types: write the types. Local variables and internal helpers: let the type checker infer.
|
|
35
|
+
- **Zero `any` / `unknown` in your own helpers**. `tsc --noEmit` is part of the red/green loop. `unknown` is allowed only at the boundary of external data, before you validate it with Zod and narrow it.
|
|
36
|
+
- **DRY after the third caller** (Rule of Three). Two callers can be coincidence. Three is a pattern. Never abstract before the third caller. Never leave duplication alive after it.
|
|
37
|
+
- **KISS beats YAGNI beats DRY**. Simple duplication beats an elegant abstraction with a mental footnote. Fewer layers, fewer generics, fewer config options.
|
|
38
|
+
- **DSL-first when primitive boilerplate stops scaling — propose it, do not build it**. This complements the Rule of Three. It does not oppose KISS. Do not abstract early. But watch for wiring that hides the intent and repeats. There, a DSL pays for itself — a fluent builder, combinators, a declarative object, or a mini-language.
|
|
39
|
+
|
|
40
|
+
**Trigger — both signals together:** (1) 3 or more call sites repeat the same *sequence* of primitives. The order stays fixed, and this one signal alone is a sequence Rule of Three. (2) At those sites, the boilerplate dominates the real logic. One more condition separates a DSL from a helper: a **reusable grammar** must exist (composition, ordering, branching). With no grammar, a named helper wins instead.
|
|
41
|
+
|
|
42
|
+
Proposing a DSL is an architectural change — it adds a first-class layer or abstraction. So **hard rule 11 applies: stop and ask**. The agent brings a proposal, an API sketch, and a candidate ADR. The architecture owner decides. The decision procedure covers the lightest form that captures the grammar, counterexamples, and self-review. It lives in [`rubrics/when-to-create-dsl.md`](rubrics/when-to-create-dsl.md).
|
|
43
|
+
- **A comment is the exception**. Default is zero comments. Add one only when the logic is genuinely complex (a hidden invariant, a workaround for a bug, a counterintuitive choice). Before you comment: rename, extract, or simplify first.
|
|
44
|
+
- **MVP scopes the features, not the security posture**. Hardening (encryption at rest, saga ordering, key rotation, single-use tokens) ships before launch, not after. Scope decisions never trade security posture for speed.
|
|
45
|
+
- **Saga ordering — the irreversible external signal comes before the DB commit**. A change can
|
|
46
|
+
combine an external signal with an irreversible commit. The signal is an email, a charge, a
|
|
47
|
+
publish or a tag flip. The commit is a soft-delete, a status update or a pointer flip.
|
|
48
|
+
|
|
49
|
+
When it does, the order is fixed. First send the external signal. Then log and abort on
|
|
50
|
+
failure, or commit on success. The reverse order — commit before the signal — is banned. A failed
|
|
51
|
+
signal then leaves the DB already mutated, with the user unaware. Review checklist: "if signal
|
|
52
|
+
X failed, what state is the DB in now?"
|
|
53
|
+
- **A doc is a living playbook that evolves by delta, not by monolithic rewrite (ACE)**. An update to `/docs/` is a **structured append** to the right section, or an **update-in-place** of one named item. It never rewrites the whole doc. This posture avoids two failure modes.
|
|
54
|
+
|
|
55
|
+
*Context collapse*: a rewrite erodes the doc into a short summary and loses load-bearing detail. *Brevity bias*: the urge to "summarize" and discard domain heuristics. The mechanizable half of this posture becomes **hard rule 17**. Curing bloat means **consolidating** (`generalize`/`restructure`), never cutting for size.
|
|
56
|
+
|
|
57
|
+
- **Ingest fans out across cardinality — it does not append to one place (Karpathy LLM-wiki)**. Sometimes you **redefine** a term, concept, or contract that already appears in several docs. When you do, the update must touch **every** page where it lives (`grep -rl "<term>" docs/`), not just the obvious one. Otherwise `/docs/` becomes internally contradictory, and `diagnose A7` catches it later.
|
|
58
|
+
|
|
59
|
+
This rule is **orthogonal to rule 17**: the *granularity* of each touch stays a **delta** (never a rewrite). The *cardinality* is the **fan-out** (how many pages). `reconcile` covers the fan-out. There is no clean 0/1 trigger for "this was a redefinition." It stays a principle, not a hard rule (#3).
|
|
60
|
+
- **Promoting a doc as durable goes through the negative filter**. Before a signal becomes a `gotcha`/`learning`/`rule`/`procedure`, it passes the checklist in [`rubrics/negative-filters.md`](rubrics/negative-filters.md). Four things **never** become docs: a negative tool claim, a transient failure, a one-off narrative, and a user-visible status. A false constraint is poison — the negative face of #5. This is the entry gate for `reconcile`/`gotcha`/`remediate`.
|
|
61
|
+
|
|
62
|
+
- **In a fan-out of agents, the model tier follows the task tier**. A workflow, or any batch of parallel subagents, does not give all of them the same model. Give a **light** model the mechanical, high-volume stages — search, read, collect, `grep`, extract, label. Give a **capable** model the stages where a wrong answer contaminates everything downstream. Examples: synthesis, judgment, design, adversarial verification, the final report. A middle tier covers structured work with a clear contract.
|
|
63
|
+
|
|
64
|
+
The reason is **cost per unit of risk**, not cost alone. A research agent that misses one file costs one more search. A synthesis agent that reasons wrong writes the conclusion the human then acts on. Spend the capability where the error is expensive, not where the volume is high.
|
|
65
|
+
|
|
66
|
+
**How to apply:** set the model **per agent call**, never once for the whole run. Omit it only when the stage really inherits the session model. Record the tier of each phase in the workflow metadata, so the choice is auditable afterwards.
|
|
67
|
+
|
|
68
|
+
There is no 0/1 trigger that separates a mechanical stage from a synthesis stage — the split is judgment. So this stays a principle, not a hard rule (#3).
|
|
69
|
+
|
|
70
|
+
{{Add project-specific principles here — e.g. "Tasks are idempotent", "S3 is the source of truth for artifacts", etc.}}
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 2. Task flow
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
1. READ → 2. UNDERSTAND → 3. PLAN → 4. RED
|
|
78
|
+
↓
|
|
79
|
+
8. REPEAT ← 7. COMMIT ← 6. REFACTOR ← 5. GREEN
|
|
80
|
+
↓
|
|
81
|
+
8.5 RECONCILE
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
In a feature folder (§ 2.1) one TDD cycle is two or three tasks of the frontmatter `tasks:` list of `tasks.md` (ADR-0020): a **red** task (step 4, `scenario: N` of the spec), then a **green** task that closes it (step 5, `closes:` the red), then an optional **refactor** (step 6). Step 8.5 is the one **reconcile** task, the last of the list. The agent adds a task with `afm task add` and closes it with `afm task done`, never by editing the list; `afm feature lint` checks the order.
|
|
85
|
+
|
|
86
|
+
### 1. READ
|
|
87
|
+
|
|
88
|
+
- `/docs/prd.md` for product context (which RF the task serves).
|
|
89
|
+
- `/docs/ust.md` for the matching story (US-NNN) — acceptance criteria are the oracle.
|
|
90
|
+
- `/docs/ach.md` for structure (where the code goes, which component).
|
|
91
|
+
- `/docs/gotchas/` if the touched area has a registered trigger (the hook injects the matching entry).
|
|
92
|
+
- `/docs/_focus.md` (if it exists) — the current focus, plus the `protocol:` line. The **active protocol** parametrizes steps 3, 5.1, and 8.5 (see [`protocol.md`](protocol.md)). This is work state, not a normative doc. `SessionStart` also injects it. Without the line, the defaults apply.
|
|
93
|
+
- Existing code in the files that will change, plus their neighbors.
|
|
94
|
+
|
|
95
|
+
### 2. UNDERSTAND
|
|
96
|
+
|
|
97
|
+
- Restate in 2 sentences: *"what will change"* and *"why."*
|
|
98
|
+
- If you cannot do this in 2 sentences, use the rubber duck, or ask.
|
|
99
|
+
- List the 3-5 files that will be touched.
|
|
100
|
+
|
|
101
|
+
### 3. PLAN
|
|
102
|
+
|
|
103
|
+
- Small change (< ~50 lines in 1-2 files): a mental plan plus a commit comment is enough.
|
|
104
|
+
- Medium or large change: write an explicit plan file before you touch code.
|
|
105
|
+
- Architectural change: **stop and ask** (hard rule 11).
|
|
106
|
+
|
|
107
|
+
`PLAN_THRESHOLD` (a protocol field) shifts only the **first** line. Under the assisted-planning posture, a small change gets **reported before the first RED** instead of resolved silently. It does not turn into a per-step question. Below the § 2.1 heuristic, that would be noise (the P2 floor, `protocol.md` § 3). Rule 11 stays invariant across all three protocols.
|
|
108
|
+
|
|
109
|
+
### 4. RED
|
|
110
|
+
|
|
111
|
+
- Write the test (unit / integration / e2e) that fails for the right reason.
|
|
112
|
+
- Or write the type signature that does not compile.
|
|
113
|
+
- Confirm it fails for the expected reason before you write code.
|
|
114
|
+
|
|
115
|
+
### 5. GREEN
|
|
116
|
+
|
|
117
|
+
- Write the minimum implementation that passes.
|
|
118
|
+
- Do not optimize, do not anticipate. Just make it pass.
|
|
119
|
+
- Run `tsc --noEmit` plus the affected tests.
|
|
120
|
+
|
|
121
|
+
### 5.1 GREEN fails → capped reflect-retry (in-task Reflexion)
|
|
122
|
+
|
|
123
|
+
GREEN can fail to turn green: the test still fails, or `tsc --noEmit` breaks. When it does, run a short self-correction loop **before** you halt or ask. This works here because two Reflexion requirements (arXiv:2303.11366) are met. First, a restartable episode (re-run GREEN). Second, a cheap, reliable Evaluator (the test or `tsc` itself, which already ran).
|
|
124
|
+
|
|
125
|
+
1. **State the failure**, grounded in the **real output** of the test or `tsc`. Example: "the test failed because I assumed X, the fixture returns Y". So I will do Z, grounded in that tool output. Never write "I reread it and it looks fine" (CRITIC, arXiv:2305.11738 — self-critique with no tool degrades). That is why § 3 requires a mechanical trigger.
|
|
126
|
+
2. **Retry GREEN**, conditioned on that reflection, against **the same RED**. Rule 1 stays intact: do not relax the test to make it pass.
|
|
127
|
+
3. **Cap = `RETRY_CAP` retries** (default **2**, so 3 attempts total) — the Ω 1-3 window from Reflexion. A *new* failure on each attempt within the cap means keep trying. `RETRY_CAP` is a field of the active protocol ([`protocol.md` § 2](protocol.md)). Under the assisted posture it drops to 1 — the first red escalates right away. The minimum is 1. Zero would turn every transient failure into an escalation.
|
|
128
|
+
4. **Anti-oscillation (Self-Refine, arXiv:2303.17651):** watch for the same failure signature twice — the loop undoing and redoing the same edit. That means **stop right away and escalate**. Do not spend the cap hitting the same wall.
|
|
129
|
+
5. **Cap exhausted OR oscillation** → **log the failure to the substrate before halting**. Then report to the human. Give the task, the failure, and what you tried. Record it with `afm failures record --sig <sig> --detail "<oracle, command, real output, task>"`. The command writes to the state store under `AFM_HOME`, never to the repository.
|
|
130
|
+
|
|
131
|
+
The record carries the **external signal** — the oracle, the command and the real output — plus the `task/feature`. Give it a **stable** `sig=<signature>`. The signature is the oracle plus the normalized file or error type, and it is the recurrence key. This is recognition by **tool**, not self-judgment (CRITIC, arXiv:2305.11738 — #3).
|
|
132
|
+
|
|
133
|
+
The human gate is still the terminal step, the replacement for the paper's Commit step. The attempts are *earned* first. An explicit user correction against the approach also logs a failure here, since the user is the oracle.
|
|
134
|
+
|
|
135
|
+
The in-task reflection is **ephemeral short-term memory** — it lives only in this session's context, the Reflexion trajectory. It is **not** a doc. A failure **resolved on retry** never crossed the terminal, so it is **transient**. It does not become an artifact (negative filter, #5: a claim that dies once its cause is fixed is poison).
|
|
136
|
+
|
|
137
|
+
A failure that **crossed the terminal** becomes a failure record in the state store (`afm failures promotable` lists the signatures that recurred). From there the **CRITIC loop** (`afm path ops/remediate.md`) remediates it for good, as a **procedure** (mechanical) or a **learning** (conceptual). The gate is *one user correction OR `sig=` recurring ≥2×*. Cost: zero on the happy path. It fires only for a task that fails.
|
|
138
|
+
|
|
139
|
+
### 6. REFACTOR
|
|
140
|
+
|
|
141
|
+
- Remove duplication, improve names, extract constants or types where it adds clarity.
|
|
142
|
+
- Run the type check, the affected tests, and lint.
|
|
143
|
+
|
|
144
|
+
### 7. COMMIT
|
|
145
|
+
|
|
146
|
+
- The message explains **why** (the diff already shows the what).
|
|
147
|
+
- Reference the story: `US-NNN: ...` or the requirement: `RF-NN: ...`.
|
|
148
|
+
- One commit equals one coherent change.
|
|
149
|
+
|
|
150
|
+
### 8. REPEAT
|
|
151
|
+
|
|
152
|
+
- Go back to step 4 for the next slice.
|
|
153
|
+
|
|
154
|
+
### 8.5 RECONCILE
|
|
155
|
+
|
|
156
|
+
In a feature folder this step is the reconcile task, the last of the `tasks:` list: `afm task done` refuses it while another task is open.
|
|
157
|
+
|
|
158
|
+
Before you close the task, ask:
|
|
159
|
+
|
|
160
|
+
- **Did I learn something another dev without this session needs, to avoid breaking things?** If yes, update `/docs/`:
|
|
161
|
+
- New architectural decision → `/docs/adr/NNNN-title.md` plus a line in `ach.md`.
|
|
162
|
+
- Counterintuitive surprise (trigger from an external SDK/runtime/lib) → one file under `/docs/gotchas/`.
|
|
163
|
+
- Reusable rule the user corrected me on → a new hard rule in `afm.md` (with a mechanical trigger).
|
|
164
|
+
- New component (3rd caller, new module) → update `ach.md` §3.
|
|
165
|
+
- Product scope change → `prd.md` §4.
|
|
166
|
+
- **Does the learning generalize to any project?** If yes, `afm promote` **logs** the finding (with mechanical evidence) in the project's state store under `AFM_HOME`, never in the repository. The plugin maintainer then collects it from there. The plugin is never edited from here — this boundary is absolute (see `afm path ops/promote.md` + `afm path interview/promotion.md`).
|
|
167
|
+
|
|
168
|
+
**Cadence**. The hook already logs task closure mechanically in the ledger (`task` on commit, `deliverable` when `tasks.md` closes). The counter is **derived** — no one writes it by hand. When the active protocol's threshold is crossed, `SessionStart` reports it and calls the operation (`reflect` every 3-4 tasks, `evolve` every 3-4 deliverables). This is what keeps `reflect`/`evolve` from being work that "runs only if the human remembers". A primitive whose trigger is human memory has no trigger.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 2.1 When a feature needs its own folder
|
|
173
|
+
|
|
174
|
+
The loop in § 2 covers a small change. For a feature that hits **any** of the criteria below, open `docs/features/NNN-slug/`:
|
|
175
|
+
|
|
176
|
+
- > 1 day of work OR > 200 lines OR > 3 files.
|
|
177
|
+
- Touches a new layer or creates a new component (triggers hard rule 11).
|
|
178
|
+
- Has a new external boundary (API, webhook, CLI, pub/sub contract).
|
|
179
|
+
- Has > 2 unknowns that would need `[NEEDS CLARIFICATION:]`.
|
|
180
|
+
|
|
181
|
+
**Autonomous delivery (default):** `afm deliver <feature>` orchestrates discovery → plan → single approval → execution to completion — see § 2.2. **À la carte** (run one step alone): `afm specify <slug>` → `afm plan` → `afm tasks`. The standalone ops are also autonomous by default (principle #6 — autonomy-first). They infer from the scan (#2, discover-first), materialize their output, and report it, reviewed through `git diff`. They ask no question about state they can discover on their own. Each item in `tasks.md` runs as one cycle of § 2.
|
|
182
|
+
|
|
183
|
+
Auxiliaries (run when needed, not part of the required sequence):
|
|
184
|
+
|
|
185
|
+
- `afm clarify [<feature>]` — a structured ambiguity scan across 9 categories. The agent resolves what has evidence through discovery (#2), and asks only the irreducible product questions (max 5, through `afm ask clarify`, recommendation-first). It updates `spec.md` inline, with a dated session, in § 7 Clarifications.
|
|
186
|
+
- `afm analyze [<feature>]` — cross-artifact validation (spec ↔ plan ↔ tasks ↔ ach ↔ afm § 3 ↔ PRINCIPLES). It detects coverage gaps, constitution violations, and terminology drift. It gives a severity-triaged, read-only report in chat (CRITICAL/HIGH/MEDIUM/LOW), and offers remediation on request. Suggested between `tasks` and T001.
|
|
187
|
+
- External technical research is not an AFM op. Use nasca (`nasca-research` skill; `nasca-campaign` for a deep multi-front study) and cite its stored sources in the spec, plan or ADR.
|
|
188
|
+
|
|
189
|
+
Otherwise (a small change that hits no criteria): skip the folder. Go straight to § 2.
|
|
190
|
+
|
|
191
|
+
> **The `[NEEDS CLARIFICATION:]` marker differs from `[TBD]`**. The first means the doc needs this info to be executable. Someone must resolve it before moving on, it blocks `tasks`, and `afm clarify` resolves it. The second means the user consciously defers it — a posture, and it blocks nothing.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 2.2 Autonomous delivery mode (orchestrated)
|
|
196
|
+
|
|
197
|
+
The user can ask to **deliver** a feature: "build feature X", "implement X", "deliver X end to end". The default path is the orchestrated autonomous delivery (`afm deliver <feature>` — canonical in `afm path ops/deliver.md`). It is not sending the user to run `specify` → `plan` → `tasks` by hand. The agent self-provisions (invariant principle #2 — discover-first):
|
|
198
|
+
|
|
199
|
+
1. **Gap-triggered discovery** (principle #2) — identify what is missing for delivery. Route each gap by source, and skip the scan if no gap is discoverable. (a) Code or `docs/` can answer: scan it (`grep`/glob/`Read`/Explore). (b) The web can answer (library or tech): run the nasca research flow (`nasca-research` skill, or `nasca-campaign` for a deep multi-front study) and cite its stored sources; if nasca is not installed, ask the operator instead of searching the web ad hoc. (c) No source can answer, or it is a product decision: ask (≤4 questions per call, ≤2 rounds). The loop converges: it doubles down on answers, and digs deeper only where a gap remains.
|
|
200
|
+
2. **Synthesis plus self-verification** — materialize spec and plan from the discovery. Never ask in the blind for what the code or `ach.md` already implies. Then run an **adversarial self-critique** on them (principle #6 — the verification corollary). Partition the findings into (a) self-correct with evidence, (b) irreducible and load-bearing, or (c) cosmetic. Escalate (b) to the gate, and mark (c) as `[TBD]`. *Produce → self-verify → report.*
|
|
201
|
+
3. **Single gate = the verification residue.** The gate is not a plain approve-the-plan question. It is the **bucket (b)**: the earned questions verification did not close. Each one carries evidence, a recommendation, and a note on what breaks if it is wrong. Answering it **is** approval (`afm approve` records it with the sha256 of the plan, after `afm ask deliver` collected the answers). With no residue (b) and no CRITICAL finding, there is no gate. An explicit `Go` then skips it (principle #6, the checkpoint corollary).
|
|
202
|
+
4. **Execute to completion** — build the `tasks:` list of `tasks.md` with `afm task add`. Run the tasks in list order: each red task, the green task that closes it, an optional refactor, then COMMIT (§ 2), and close each one with `afm task done`. Halt only on three things: a failed task that is not `parallel`, an architectural surprise (hard rule 11), or the push boundary. § 6.1 says never push alone.
|
|
203
|
+
5. **Reconcile** (§ 8.5) and close with a summary.
|
|
204
|
+
6. **Post-delivery self-reflection for large deliveries (under "go"):** two conditions apply together for this step. The delivery is **large**, which means it hits the feature-folder heuristic. It also ran under **autonomous planning**: `plan=auto`, protocol P1/P3, or you said "go". Then run `afm reflect`.
|
|
205
|
+
|
|
206
|
+
This is the **behavioral** self-reflection: where the docs misled you during delivery. It proposes sharpening them and **keeps its gate** — never self-applies — under `plan=assisted` (already reviewed inline) this step is **suggested**. Rationale: "go" means less inline review, so a reflection pass over the docs catches friction you did not see. It closes the loop `reconcile` does not cover — the doc that failed as harness, not new knowledge.
|
|
207
|
+
|
|
208
|
+
Resumable: if spec/plan/tasks already exist, resume from the right phase. This includes "tasks already exist → run them directly," which covers the case of running implementation alone. The sub-flows in § 2.1 stay available à la carte, to run one step alone. `deliver` **reuses them underneath** — it reads and follows them, and does not duplicate the procedure.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 3. Hard rules
|
|
213
|
+
|
|
214
|
+
> **An organisation policy is CONTEXT, never a gate (3.11.0).** When `[org] dir` in the global config (`~/.afm/config.toml`) names a
|
|
215
|
+
> policy bundle, its rules and gotchas reach the session as injected text, named and marked as not
|
|
216
|
+
> editable here. Nothing in that bundle is ever executed, so nothing in it can block a commit. An
|
|
217
|
+
> organisation that wants a rule enforced writes a runner into this project's own
|
|
218
|
+
> `docs/tools/gate/rules/`, where it is versioned and reviewed. Run `afm policy` to see what
|
|
219
|
+
> applies here.
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
Every rule below has an **executable trigger** the agent runs from the keyboard (`grep`, `wc -l`, `git diff`, `tsc`). It is a binary pass/fail. It does not depend on "code review" or a "PR template" — a human process, not a mechanical one. A rule with only manual review **is not a hard rule** — it lives in §1.3 as a guiding principle. A rule marked **INALIENABLE** never appears in the interview's opt-out question. `afm rule` refuses to remove it or mark it N/A, and `diagnose` flags its absence from the instance as CRITICAL.
|
|
223
|
+
|
|
224
|
+
**The limit, stated plainly:** `docs/afm.md` is a file in the operator's repo, and the operator can edit it. AFM **detects** its removal. It does not **prevent** it. Calling it "impossible to change" would be the same empty promise `protocol.md § 6` used to carry. Inalienable also does **not** mean retroactive. In a brownfield project it enters forward-only (§ 3.1) like any other rule, otherwise adoption would be impossible.
|
|
225
|
+
|
|
226
|
+
Rules 1, 12, and 21 have a trigger that covers only **part** of what the rule requires. There, the `*Verification:*` line states plainly which half it proves, and which half stays judgment. A partial trigger is honest about its own reach, and what it does not reach is never claimed as proven. A rule or guardrail file may carry an optional `does_not_cover` key: what its gate does not check (for example "checks that a frontmatter exists, not the per-type schema"). A green result is then not read as more than it proves. Never boilerplate such as "edge cases". (The plugin's `PreToolUse` hook injects the rules in this section whose path prefix matches the edited file — see `hooks/`.)
|
|
227
|
+
|
|
228
|
+
PLUGIN-1. **No new code without a test**. Includes a type test.
|
|
229
|
+
|
|
230
|
+
*Verification:* `git diff --staged --name-only | grep -qE '\.(test|spec)\.'` — a commit that adds code brings a test with it. The command proves only half the rule. It proves a test **exists** in the commit, not that it **covers the new path**. That stays judgment, and it lives in § 5. It gives a false positive on a pure refactor, a rename, a revert, or a config-only commit. The rule does not apply there, and the trigger is not the oracle.
|
|
231
|
+
|
|
232
|
+
PLUGIN-2. **Zero `any` / `unknown` in your own helpers**.
|
|
233
|
+
|
|
234
|
+
*Verification (two tiers, native first):* does the project already configure the rule? Discover it (#2) in the ESLint config as the no-explicit-any rule, or in the Biome config as `noExplicitAny`. If it does, that command is the trigger. If it does not, the trigger stays `grep -nE "\bany\b|\bunknown\b" {{src-path}}` on non-boundary files. The native tool tells `any` from `Company` and reads the type-checker config. A `grep` does neither, so it is the floor and never the ceiling.
|
|
235
|
+
|
|
236
|
+
The rule ids above are spelled out rather than quoted as paths, on purpose. This line feeds the A5 vital sign, which resolves every backticked path on it against the disk. A package-scoped rule id looks exactly like a path, and would be reported as missing.
|
|
237
|
+
|
|
238
|
+
3. *(principle — lives in §1.3 "Explicit types at boundaries." Demoted: no trigger distinguishes inference from a missing annotation. The type checker passes both. Manual review is not a trigger.)*
|
|
239
|
+
PLUGIN-4. **Never commit with a broken type check**.
|
|
240
|
+
|
|
241
|
+
*Verification:* `npx tsc --noEmit` (or the stack's equivalent).
|
|
242
|
+
|
|
243
|
+
PLUGIN-5. **One responsibility per file**. A vague name ("manager", "utils", "helpers" with no domain prefix) means split it.
|
|
244
|
+
|
|
245
|
+
*Verification:* `find src -type f \( -iname "*manager*" -o -iname "*utils*" -o -iname "*helpers*" \)` returns 0 with no prefix.
|
|
246
|
+
|
|
247
|
+
PLUGIN-6. **File ≤ {{limite-linhas}} lines**. Exceptions need a header that explains why.
|
|
248
|
+
|
|
249
|
+
*Verification:* `find {{src-path}} -type f -name "*.{{ext}}" -not -name "*.test.*" | xargs wc -l | awk '$1 > {{limite-linhas}}'`. (Project with more than one extension: expand with `\( -name "*.ts" -o -name "*.tsx" \)`. Do **not** use the brace glob `*.{ts,tsx}`, which `find` does not expand.)
|
|
250
|
+
|
|
251
|
+
PLUGIN-7. **{{Componente Domain-like}} exports exactly ONE `{{prefixo-dominio}}<name>` function**. Domain holds business rules. Query builders, DTO maps, and transport glue **do not belong here**.
|
|
252
|
+
|
|
253
|
+
*Verification:* `grep -c "^export.*{{prefixo-dominio}}" {{src-path}}/{{domain-path}}/*.{{ext}}` returns `1` per file.
|
|
254
|
+
8. *(principle — lives in §1.3 "DRY after the third caller." Demoted: counting callers is judgment, with no binary mechanical trigger.)*
|
|
255
|
+
9. *(this slot stays open — KISS is a guiding principle. It lives in §1.3, and does not earn a hard-rule slot.)*
|
|
256
|
+
PLUGIN-10. **No backwards-compat shim**. If the caller is gone, delete the code. A `// removed for X` comment adds noise.
|
|
257
|
+
|
|
258
|
+
*Verification:* `grep -rn "removed\|deprecated\|legacy" {{src-path}}`.
|
|
259
|
+
|
|
260
|
+
PLUGIN-11. **An architectural change stops and asks**. A new layer, a first-class component, a contract between modules, or a folder refactor needs the architecture owner's approval. This is bright line #6(d) — it applies **mid-flight**, before any PR (`deliver` triggers it during execution).
|
|
261
|
+
|
|
262
|
+
*Verification (mid-flight, not PR):* `git status --porcelain` shows an `A` for a **new first-class directory** under `{{src-path}}` — a new layer or module. Or the diff moves folders, or introduces a new cross-module import. Either signal means STOP and ask before continuing. This is a binary signal from the working tree, not a review question.
|
|
263
|
+
|
|
264
|
+
PLUGIN-12. **{{Componente Task-like}} is idempotent**. A rerun does not corrupt state (early return at terminal states, immutable external identifiers).
|
|
265
|
+
|
|
266
|
+
*Verification:* `grep -qiE 'rerun|idempot' {{caminho da task}}.test.{{ext}}` — proves the task's test file **names** the rerun case. Whether the test really exercises the second run is judgment, not grep.
|
|
267
|
+
|
|
268
|
+
PLUGIN-13. **Tokens and secrets never leak — in code, docs, logs or the state store**.
|
|
269
|
+
|
|
270
|
+
Never log a token in plain text. Redact secrets in errors. Never commit `.env`. This covers every record the agent writes about its own work (a failure detail, a finding, a handoff): the agent quotes the command it ran, and that command held the key.
|
|
271
|
+
|
|
272
|
+
*Verification:* `git diff --staged | grep -nE "(token|secret|api[_-]?key|password|bearer)\s*[:=]\s*['\"][^'\"]+"` returns 0 (a secret literal in the diff). `git diff --staged --name-only | grep -E "(^|/)\.env"` is empty. And `git diff --staged | grep -nE "(sk-[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|AIza[A-Za-z0-9_-]{30,}|xox[baprs]-[A-Za-z0-9-]{10,}|eyJ[A-Za-z0-9_-]{20,}\.)"` returns 0 (a known credential prefix anywhere in the staged text).
|
|
273
|
+
14. *(principle — lives in §1.3 "Saga ordering". Demoted: correct effect ordering is a design choice, with no grep that verifies it. It is a review checklist, not a trigger.)*
|
|
274
|
+
|
|
275
|
+
PLUGIN-15. **Error classification — Domain, Procedure, and Infra stay separate**. Domain returns `Result<T, DomainErrorCode>` or `throw DomainError({ code: "..." as const })`. Procedure maps the code to `{{ProcedureError}}` at the boundary. Domain **never** imports the transport error type.
|
|
276
|
+
|
|
277
|
+
*Verification:* `grep -rn "{{ProcedureError}}" {{src-path}}/{{domain-path}}/` returns 0.
|
|
278
|
+
|
|
279
|
+
PLUGIN-16. **Validation at the boundary — schema only at entry**. The schema parser validates in (a) procedure input, (b) parsing of an external payload (webhook, crawled source, env var). Domain receives an already-validated shape. Re-validating inside domain duplicates the source of truth.
|
|
280
|
+
|
|
281
|
+
*Verification:* `grep -rnE "z\.|zod|{{schema-lib}}" {{src-path}}/{{domain-path}}/` returns 0 outside `*.types.{{ext}}`.
|
|
282
|
+
|
|
283
|
+
PLUGIN-17. **An edit to `/docs/` never collapses the doc (ACE — anti-context-collapse)**.
|
|
284
|
+
|
|
285
|
+
This universal rule targets the materialized AFM instance, not the code tree it documents. Rewriting a doc in `/docs/` and removing **over half** its lines at once (floor of 20 removed lines) **stops, requiring review**. Reconcile proposes a delta instead: an append, or an update-in-place of a named item. It never proposes a rewrite that turns the doc into a summary (§ 1.3 "delta, not rewrite"). Legitimate consolidation by `generalize` uses a **tombstone**: it comments the entry out and *adds* lines, so this never triggers the rule.
|
|
286
|
+
|
|
287
|
+
*Verification:* `git diff --numstat -- docs/ | grep -vE '(^|/)_focus\.md$' | awk '$2 > 20 && $2/($1+$2+1) > 0.5 {print}'` returns empty (no **normative** doc lost >50% of its lines in one edit). **Path exclusion (BY DESIGN — not a loosened threshold):** one slot stays outside rule 17, the slot-state `docs/_focus.md` (rule 20). The exclusion works by **closed path**, never by a flag a normative doc could set. A settable flag would reopen the context collapse rule 17 exists to close.
|
|
288
|
+
|
|
289
|
+
18. *(superseded — the event ledger lives in the state store under `AFM_HOME`, never in the repository (ADR-0001). The old append-only check on the in-repository ledger has nothing left to check.)*
|
|
290
|
+
|
|
291
|
+
19. *(superseded — failures live in the state store under `AFM_HOME` (ADR-0001), and `afm failures promotable` lists the signatures that recurred. The old trigger read an in-repository failures folder, which no longer exists.)*
|
|
292
|
+
|
|
293
|
+
PLUGIN-20. **`/docs/_focus.md` is a small, overwritable slot-state file**. The current focus is **OVERWRITE**, not append-growth (an unwanted append is a bug). This is why it is exempt from rule 17 (path exclusion). It stays small (work state, not normative knowledge — outside the diagnose A2-A7 axes; only the A1 dead-reference check reads it, since a dead path there misleads the next session).
|
|
294
|
+
|
|
295
|
+
*Verification:* `{ [ -f docs/_focus.md ] && wc -l < docs/_focus.md || echo 0; } | awk '$1 > 40 {print "TOO LARGE"}'` returns empty.
|
|
296
|
+
|
|
297
|
+
PLUGIN-21. **INALIENABLE — code and tests are written in English**.
|
|
298
|
+
|
|
299
|
+
Identifiers, function names, test names, and comments stay in English, in any language and any project. The trigger below covers identifiers and test names, and comments are part of the rule but stay in judgment. **User-facing strings follow the product's own language** — this rule is about the code, not what the end user reads. This is not a style preference. An identifier split across two languages breaks search, autocomplete, and the agent's own reasoning, since most code is English.
|
|
300
|
+
|
|
301
|
+
*Verification:* `grep -rqP '\b(?:function|class|const|let|var|def|interface|type|enum|describe|it|test)\b\s*\(?\s*["\x27]?[A-Za-z_$][\w$ ]*[^\x00-\x7F]' {{src-path}}` returns **1** (no hit) — an identifier or test name with a non-ASCII character (`criarPedidoComEndereço`, `it("devolve o pedido válido")`). **What it does NOT prove, and why:** (a) unaccented Portuguese (`criarPedido`) passes — that stays judgment, § 5. (b) **A comment falls outside its scope**. English prose uses em dashes, curly quotes, and ellipses, so non-ASCII in a comment is noise, not signal. The `\b` in the alternation is load-bearing: without it, `it` could match inside "delimitam" for the wrong reason. This regex requires `grep -P` (GNU).
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
PLUGIN-35. **A hard rule with a 0/1 trigger needs a versioned runner in the gate**.
|
|
306
|
+
|
|
307
|
+
# Hard rule PLUGIN-35 — A hard rule with a 0/1 trigger needs a versioned runner in the gate.
|
|
308
|
+
|
|
309
|
+
A trigger written only as prose in the doc works only as long as someone remembers to run it. The agent forgets exactly when context gets tight. A rule that promises verification with no runner is a **phantom gate**. It is worse than no gate, because it buys trust it cannot back up.
|
|
310
|
+
|
|
311
|
+
*Verification (pre-commit):* `afm gate` (builtin runner `PLUGIN-35`) runs the same audit as `afm gate --audit`. This cross-checks `afm.md § 3` against the builtin runners and `docs/tools/gate/rules/` **in both directions**. A rule with `pre-commit`/`ci` surface and no runner fails. A runner with no matching rule fails too, a builtin included: the rule of every builtin ships in this section, so an up-to-date project declares it. A rule with `agente` (agent) surface — a judgment call, like rule 11 — is not covered.
|
|
312
|
+
|
|
313
|
+
PLUGIN-36. **A doc under `docs/` carries no FILESYSTEM absolute path**.
|
|
314
|
+
|
|
315
|
+
# Hard rule PLUGIN-36 — A doc under `docs/` carries no FILESYSTEM absolute path.
|
|
316
|
+
|
|
317
|
+
A leading `/` means the bundle root, which is `docs/` itself. That is the link form OKF v0.2 recommends, because it survives a file move. A runbook with `/home/<someone>` runs only on the machine that wrote it. An agent reading it from a different clone either obeys the wrong path or hallucinates a fix. A path relative to the repo root keeps the markdown executable from any checkout. It also saves the context that explaining the path would cost.
|
|
318
|
+
|
|
319
|
+
*Verification (pre-commit):* `afm gate` (builtin runner `PLUGIN-36`). **One structural exclusion:** the pattern requires a segment **after** the user (`/home/<user>/<something>`). Otherwise, a doc that describes this very trigger would flag itself.
|
|
320
|
+
|
|
321
|
+
PLUGIN-37. **The prose of an artifact is English**.
|
|
322
|
+
|
|
323
|
+
# Hard rule PLUGIN-37 — The prose of an artifact is English.
|
|
324
|
+
|
|
325
|
+
Rule 21 covers only an identifier or a test name with a non-ASCII character. It is blind to prose: a `.md` written entirely in Portuguese passes it clean. That is how this repo reached 2026-08 with 175 Portuguese doc files while the gate said green. Every artifact — doc, code, DB, comment, test, skill `description`, hook-injected text — is written in **English under ASD-STE100**. The **chat reply follows the language the user speaks**, and a reply is not an artifact. **A request made in Portuguese is not a request FOR Portuguese** — conflation is measured failure, not hypothetical (2026-08-27).
|
|
326
|
+
|
|
327
|
+
The rule has two tiers, and they are not the same strength:
|
|
328
|
+
|
|
329
|
+
- **Code, test, and comment: inalienable.** No explicit request overrides it, in any language and any project. This is rule 21 restated for the surface rule 21 left in judgment (the comment). An identifier split across two languages breaks search, autocomplete, and the agent's own reasoning.
|
|
330
|
+
- **Document: English by default, and the operator may override it explicitly**. A doc has one human audience and the operator owns the choice of language for it. The override has to be stated, not inferred from the language of the request.
|
|
331
|
+
|
|
332
|
+
The **end-user-facing strings of a consumer app are the exception to both**. The app language is a product decision that lives in that project's `prd.md`, and it is never forced.
|
|
333
|
+
|
|
334
|
+
A project whose docs are not English records it with `afm config set language.artifacts <tag>`. The gate then skips PLUGIN-37 (prose) and PLUGIN-38 (Portuguese literal in a script) for it. With no declaration the default is English. Code, test and comment (rule 21) stay English whatever is declared.
|
|
335
|
+
|
|
336
|
+
*Verification (two moments, one measurement):* the threshold lives in `afm lang` and nowhere else (rule 34). The `PreToolUse` hook (`afm hook`) runs it on the text about to be written and names the tier. The verdict arrives while the text is still a proposal. `afm gate` (builtin runner `PLUGIN-37`) runs the same measure at commit. Two copies of a measured threshold drift. The commit gate would then contradict the hook that had just approved the write. The signal is the **density** of Portuguese function words in prose, not their presence. Presence is a guaranteed false positive: every doc here quotes `**Gatilho:**`, `agente` and `### Migração de consumer` — literals the gates need to stay Portuguese. **The threshold is measured, not chosen** (§ 1.3, "a number that enters a decision carries its provenance"). The English population (n=117) peaks at **0.69**, and the Portuguese population (n=29) bottoms at **1.52**. So **1.0** falls in the gap between them. `MIN_HITS=3` removes the two 2-hit outliers at the top of the English population. **One structural exclusion:** `CHANGELOG-archive.md` — frozen historical record, not a live artifact. It is counter-proved: remove it from the runner and the archive is reported. Put it back, and it goes silent.
|
|
337
|
+
|
|
338
|
+
PLUGIN-38. **A Portuguese literal never survives inside a script or inside a quoted marker**.
|
|
339
|
+
|
|
340
|
+
# Hard rule PLUGIN-38 — A Portuguese literal never survives inside a script or inside a quoted marker.
|
|
341
|
+
|
|
342
|
+
This is the remediation for the most frequent failure of the english-first migration. **The data was translated and the parser was not** — 14 times. The symptom never varies: a literal stays Portuguese while the target already changed spelling. The match returns zero, and every gate stays green.
|
|
343
|
+
|
|
344
|
+
Three of the 14 killed a mechanism in silence. The 3.x health script matched `Substituída por ADR-` while the ADRs said something else (**vital sign A4 found zero, always**). `afm path ops/restructure.md` read `Mordidas:`/`última:` while the template wrote `Bites:`/`last:` (**the dead-gotcha prune never found anything**). And `afm path guardrails/README.md` documented the keys `sinal`/`gatilho` while the files used `signal:`/`trigger:` (**a new guardrail would be born invisible**). None of the 14 was caught by a gate. They were caught by reading.
|
|
345
|
+
|
|
346
|
+
*Verification (pre-commit):* `afm gate` (builtin runner `PLUGIN-38`). Because the repo is english-first (rule 37), an accented word inside a shell script is a candidate. So is one inside a backtick span in a PLUGIN `.md`. It does not prove drift — it says where drift lives, and a human triages. It complements rule 37, which measures **prose density** in `.md` and misses shell and a single stray literal in English documentation. **Two implementation notes are themselves paid bugs, from the bash prototype.** (a) The detector uses explicit alternation, never a `[À-ÿ]` range. That range is invalid outside UTF-8 and aborts `grep`, and the prototype swallowed that error and reported "0 findings" against a corpus with 6 known cases. (b) Backticks are paired by index (even-indexed fields), never with a naive `` `[^`]*…[^`]*` `` pattern. That pattern matches the **gap between** two code spans too, so the first version flagged "à la carte" as Portuguese.
|
|
347
|
+
|
|
348
|
+
*(Slots 18 and 19 are superseded (ADR-0001: the ledger and the failures live in the state store under `AFM_HOME`). Slot 20, `_focus` slot-state, stays active: `docs/_focus.md` carries the `protocol:` line that `afm protocol` writes.)*
|
|
349
|
+
|
|
350
|
+
PLUGIN-39. **An indexed file carries the OKF contract, and an op declares what it writes and composes**.
|
|
351
|
+
|
|
352
|
+
# Hard rule PLUGIN-39 — An indexed file carries the OKF contract, and an op declares what it writes and composes.
|
|
353
|
+
|
|
354
|
+
Every type has its own schema on top of the three keys that follow (`afm lint` judges it, ADR-0014). The common contract is three keys. `type:` is the one key OKF v0.2 requires. `description:` is one sentence, and the index prints it instead of opening the body. `load:` is `hot`, `warm` or `cold`: a `hot` doc is listed at session start by headline and path, a `warm` one reaches a write by the path it names, a `cold` one only when its declared file-shape trigger matches, and `afm index --load <level>` lists one level. `afm index` reads frontmatter and nothing else. A file without it is invisible to the index.
|
|
355
|
+
|
|
356
|
+
The agent asks who writes `spec.md`, gets a list with that file missing, and receives no signal. Missing metadata does not look like an error. It looks like a shorter list. A `composes:` that names a dead op fails from the other side. The reader trusts it because the frontmatter is the cheap thing it read instead of the body.
|
|
357
|
+
|
|
358
|
+
**`description:` reverses an earlier decision, and the trade is named.** Until 3.9.0 this rule refused a `summary:` key. The reason was sound. Every other key is a property of the file, so no edit to the body can make the frontmatter false. A summary is a derived duplicate, and it goes stale in silence.
|
|
359
|
+
|
|
360
|
+
The objection stands, and this rule accepts it. It buys one thing. Without a stated description the index cannot say what a file is without opening it. That read is the cost the whole retrieval layer exists to remove.
|
|
361
|
+
|
|
362
|
+
Two facts limit the damage. A description states the purpose of the file, which changes at rename scale, not at edit scale. The key is `description`, not `summary`, because OKF v0.2 names it and index generators read it.
|
|
363
|
+
|
|
364
|
+
*Verification (pre-commit):* `afm gate` (builtin runner `PLUGIN-39`) runs `afm lint` over every `.md` under the roots the index reads. Each type has one strict schema (ADR-0014): the keys it requires, the keys it forbids and the shape of each value, so an unknown key, a retired `kind:` or an unknown `type:` is an error. A family declared blocking (adr, rule, gotcha, feature spec, plan and tasks, guardrail) is refused at write time by the PreToolUse hook, and `afm lint --fix` repairs what has one exact answer. The op pass checks that each `composes:` resolves to a real file; the presence of `writes:` and `composes:` is the op schema's job. The directory list must stay in sync with `INDEXED` in the index itself. A directory the index reads but the gate skips is where frontmatter goes missing silently.
|
|
365
|
+
|
|
366
|
+
PLUGIN-40. **A normative doc cites no path that does not exist**.
|
|
367
|
+
|
|
368
|
+
# Hard rule PLUGIN-40 — A normative doc cites no path that does not exist
|
|
369
|
+
|
|
370
|
+
A rename is finished when the **writers** and the **readers** both move. The sentence was already
|
|
371
|
+
written in this repository, in `docs/_focus.md` and in a gotcha. On 2026-08-30 it failed twice
|
|
372
|
+
inside the release that exists because of it. First a runner glob kept matching only the old name
|
|
373
|
+
shape. That left a whole directory of runners invisible while the gate reported green. Then seven
|
|
374
|
+
rule files kept showing a label that pointed at files which had just moved.
|
|
375
|
+
|
|
376
|
+
`afm doctor` **detected** the second one and reported it at SessionStart. It was read, and the
|
|
377
|
+
work continued. Detection at the start of a session is not the same as refusal at the commit.
|
|
378
|
+
Only the second one arrives while the change is still in the hands of whoever made it.
|
|
379
|
+
|
|
380
|
+
**Scope: the normative families only** — `afm.md`, `ach.md`, `prd.md`, `ust.md`, `_focus.md`, the ADR index, and
|
|
381
|
+
`rules/`, `gotchas/`, `learnings/`, `guardrails/`, `procedures/`. The ADRs themselves are out of scope: they are immutable records and cite what
|
|
382
|
+
stopped existing. A rubric teaches with an
|
|
383
|
+
illustrative path, and a research dossier quotes a hypothetical one. A feature's `tasks.md` names
|
|
384
|
+
paths as they were the day it was written. Aiming the gate at those produced 44 findings that were
|
|
385
|
+
all correct notation.
|
|
386
|
+
|
|
387
|
+
`patterns/` and `code-style/` are excluded for the same reason. Both teach through illustrative code
|
|
388
|
+
paths inside their examples, never one the reader is meant to follow. A gate that starts red gets
|
|
389
|
+
switched off the next day, taking every other rule in the aggregator with it.
|
|
390
|
+
|
|
391
|
+
**A cited path resolves from the repository root OR from the directory of the file that cites it**.
|
|
392
|
+
Both conventions are alive in a real instance. A checker that knows only one invents findings.
|
|
393
|
+
That is the same way a gate gets switched off, arrived at from the other side.
|
|
394
|
+
|
|
395
|
+
Four things are not a violation. A line that announces a removal — an ADR's job is to cite what
|
|
396
|
+
stopped existing. A placeholder shape: `features/NNN/spec.md`, `rules/NN-slug.md`, `YYYY-MM.md`.
|
|
397
|
+
A dot-dir path: `.seo/config.json` is the notation for a materialized instance. And `/docs/x.md`,
|
|
398
|
+
which is AFM's notation for the consumer's directory, not an absolute path.
|
|
399
|
+
|
|
400
|
+
A citation that is deliberately historical goes in the ratchet, with the reason: a user story, for
|
|
401
|
+
instance, that quotes a path **because** it stopped existing. An ADR needs no row, since ADRs
|
|
402
|
+
are out of scope.
|
|
403
|
+
|
|
404
|
+
*Verification (pre-commit):* `afm gate` (builtin runner `PLUGIN-40`) checks every backticked path in a normative doc. It runs `test -e` from the repository root and from the citing file's directory. Then a `git ls-files` fallback runs. **The fallback matches by SUFFIX**. A path written relative to a component root is accepted, even while the tracked file sits further up the tree. That is deliberate: a doc naming a path relative to a component root is not lying. But the rule only proves the file EXISTS somewhere, not that the label leads a reader to it. Seven labels passed this way on the day the rule was written, and were made explicit by hand. Exit 1 on any finding. The aggregator then decides against the ratchet.
|
|
405
|
+
|
|
406
|
+
PLUGIN-41. **No import cycle between features**.
|
|
407
|
+
|
|
408
|
+
# Hard rule PLUGIN-41 — No import cycle between features.
|
|
409
|
+
|
|
410
|
+
The directed graph "feature A imports feature B" has no cycle. A feature is a directory that one glob of `architecture.features.roots` matches (for example `src/features/*`, `src/shared/*`, `src/app`). A shared need moves to a shared folder, never into a mutual import. An import inside one feature is not an edge, and a test file is not a node.
|
|
411
|
+
|
|
412
|
+
*Verification (pre-commit):* `afm gate` (builtin runner `PLUGIN-41`) builds the import graph of the `.ts` files under the declared roots and reports each strongly connected component of more than one feature. Set `roots` in `.afm/config.toml`, under `[architecture.features]`. With no `roots` declared the runner skips and says why; it never fails a project that did not opt in.
|
|
413
|
+
|
|
414
|
+
PLUGIN-42. **A source folder holds at most `architecture.limits.folder_files` files**.
|
|
415
|
+
|
|
416
|
+
# Hard rule PLUGIN-42 — A source folder holds at most `architecture.limits.folder_files` files.
|
|
417
|
+
|
|
418
|
+
A folder that directly holds a non-generated `.ts` file counts the files directly inside it (tests and data included, not recursive). More than the limit (30 by default) is a finding: group the files in themed subfolders of the feature. A folder of data files only is not judged.
|
|
419
|
+
|
|
420
|
+
*Verification (pre-commit):* `afm gate` (builtin runner `PLUGIN-42`). Set `folder_include`, the repo-relative Bun.Glob patterns of the paths to count, under `[architecture.limits]` in `.afm/config.toml`, together with `folder_files`. With no `folder_include` the runner skips and says why. `[architecture.limits.folder_ignore]` says what else is left out: `detectors`, a subset of `generated` (generated paths) and `fixtures` (a directory named `fixtures`), both by default, and `globs`, none by default.
|
|
421
|
+
|
|
422
|
+
> **Two namespaces, and no reserved number range (3.20.0).** A rule the plugin ships is
|
|
423
|
+
> `PLUGIN-<n>` — it is the contract of the `docs/` that AFM materializes, and it arrives with
|
|
424
|
+
> its runner built into `afm gate`. A rule THIS project writes about its own code is
|
|
425
|
+
> a plain `<n>`, and you may start at 1. The two namespaces never collide, so nothing has to be
|
|
426
|
+
> reserved. An organisation policy rule is numbered 100 or higher — a third namespace, see
|
|
427
|
+
> `afm policy`.
|
|
428
|
+
>
|
|
429
|
+
> Before 3.20.0 this block reserved 18-29 for "template universal rules, which grow with each
|
|
430
|
+
> release" and told a project to start at 30. A reservation is a promise about numbers that
|
|
431
|
+
> nobody can keep. The plugin's own repository had already spent 35-39 on rules that turned out
|
|
432
|
+
> to be universal, and the moment those shipped, any consumer's own rule 35 collided with them.
|
|
433
|
+
|
|
434
|
+
{{Add project-specific hard rules here, plain-numbered. Each one MUST have a mechanical trigger. Otherwise it goes to §1.3 as a principle.}}
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 4. Guidelines by change type
|
|
439
|
+
|
|
440
|
+
### 4.1 New feature
|
|
441
|
+
|
|
442
|
+
1. Identify the matching US. If it does not exist, write it first (`/docs/ust.md`).
|
|
443
|
+
2. The Gherkin acceptance criterion becomes the first test.
|
|
444
|
+
3. Tracer bullet: link UI → procedure → domain → DB with the minimum. See the flow end to end.
|
|
445
|
+
4. Thicken the layers through TDD.
|
|
446
|
+
5. Commit per slice.
|
|
447
|
+
|
|
448
|
+
### 4.2 Refactor
|
|
449
|
+
|
|
450
|
+
1. The suite is green before you start. Run the **full** suite after, not only the affected
|
|
451
|
+
tests. A fix or a refactor often exposes a second failure elsewhere.
|
|
452
|
+
2. Do not change observable behavior. If it changes, this is a feature or fix, not a refactor.
|
|
453
|
+
3. Small, frequent changes.
|
|
454
|
+
4. If you find a missing test in an area you are about to touch, write a **characterization test**. Capture the **CURRENT** observed behavior — run it and record the real output, never the desired one.
|
|
455
|
+
5. Commit: `refactor: <the why>`.
|
|
456
|
+
|
|
457
|
+
### 4.3 New dependency
|
|
458
|
+
|
|
459
|
+
1. Justify it in 2 lines in the commit or PR (what, why, and the alternative you considered).
|
|
460
|
+
2. Check the license (MIT/Apache/BSD are fine, warn on GPL/AGPL).
|
|
461
|
+
3. Check the size (bundle size for client-side deps).
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## 5. Signs you are off-track
|
|
466
|
+
|
|
467
|
+
> Only the signs that carry a project number or a non-obvious reason are listed. A sign an agent
|
|
468
|
+
> already recognises unprompted was cut in 3.30.0 — restating it cost every session and changed
|
|
469
|
+
> nothing.
|
|
470
|
+
|
|
471
|
+
- **A mock that simulates more than needed**. You are testing the mock.
|
|
472
|
+
- **Adding `eslint-disable`, `@ts-ignore`, `@ts-expect-error` with no ticket**. A broken window.
|
|
473
|
+
- **Creating a `utils.ts` function with no second caller**. Inline it.
|
|
474
|
+
- **Duplicating business logic across transports**. Move it to pure domain.
|
|
475
|
+
- **A test that exists only to raise coverage %**. Delete it. Coverage is a metric, not a goal.
|
|
476
|
+
- **A file over {{limite-linhas}} lines**. "But it's cohesive!" — split it anyway.
|
|
477
|
+
- **A generic name with "and"/"manager"/"helper"/"utils"**. Rename it or split it in two.
|
|
478
|
+
- **`any` / `as any` showing up "because of TS."** Investigate the inference instead.
|
|
479
|
+
- **Code copied 3 times**. Abstract it.
|
|
480
|
+
- **An abstraction with 1 caller**. Inline it.
|
|
481
|
+
- **{{Componente Domain-like}} with 2+ exports**. Split it.
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## 6. Definition of Done
|
|
486
|
+
|
|
487
|
+
A change is done only when **every** item below is true:
|
|
488
|
+
|
|
489
|
+
- [ ] New tests cover the added or changed behavior, and they run green.
|
|
490
|
+
- [ ] The full suite runs green.
|
|
491
|
+
- [ ] The type check passes.
|
|
492
|
+
- [ ] Lint passes with no new warnings.
|
|
493
|
+
- [ ] No new `any`/`unknown`/`@ts-ignore` without justification.
|
|
494
|
+
- [ ] No touched file went over {{limite-linhas}} lines (or the exception is documented).
|
|
495
|
+
- [ ] No file gained a second responsibility.
|
|
496
|
+
- [ ] No duplication with 3+ occurrences was left behind.
|
|
497
|
+
- [ ] The commit references US/RF and explains the *why*.
|
|
498
|
+
- [ ] If a contract with another layer changed: ACH is updated. If scope changed: PRD is updated. If observable behavior changed: UST is updated.
|
|
499
|
+
- [ ] If I learned something another dev needs to know: the doc is updated (ADR/gotcha/rule/feature).
|
|
500
|
+
- [ ] If the learning generalizes: I proposed a change to the AFM content (`afm promote` records it).
|
|
501
|
+
|
|
502
|
+
### 6.1 Pre-push validation (mandatory ritual)
|
|
503
|
+
|
|
504
|
+
Before any `git push`, run in sequence:
|
|
505
|
+
|
|
506
|
+
1. The full test suite.
|
|
507
|
+
2. The type check.
|
|
508
|
+
3. A production build.
|
|
509
|
+
|
|
510
|
+
All three must pass, or do not push. On failure: stop and report. Never try a quick "fix" without checking in first.
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
*Changes to this doc follow rule 11 (stop and ask) when they affect process. Style updates (a new off-track sign, a new per-type guideline) can go through a normal PR.*
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: core
|
|
3
|
+
description: DEPRECATED seed. A gotcha is one file under docs/gotchas/ since 3.9.1.
|
|
4
|
+
load: cold
|
|
5
|
+
status: deprecated
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Gotchas — DEPRECATED as a single file
|
|
9
|
+
|
|
10
|
+
> **Nothing materializes this file any more.** Since 3.9.1 a gotcha is one file under
|
|
11
|
+
> `docs/gotchas/`, and `afm path interview/bootstrap.md` creates that directory empty. The per-entry
|
|
12
|
+
> template is [`../templates/gotcha.template.md`](../templates/gotcha.template.md), and the
|
|
13
|
+
> procedure that writes one is [`../ops/gotcha.md`](../ops/gotcha.md), which also carries the
|
|
14
|
+
> entry rule this file used to state.
|
|
15
|
+
>
|
|
16
|
+
> It is kept as a tombstone rather than deleted, because deleting a canonical file is a bright
|
|
17
|
+
> line that stops and asks (PRINCIPLES #7). **Proposed for removal.** An instance that still
|
|
18
|
+
> holds `docs/gotchas.md` has not migrated — `afm update` § Structural migrations splits it.
|