@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,379 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: interview
|
|
3
|
+
description: The brownfield roadmap that infers an instance from code that already exists.
|
|
4
|
+
load: cold
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Refactor — retroactive AFM adoption roadmap
|
|
8
|
+
|
|
9
|
+
> This doc is an instruction **for the agent** when the operator runs `afm refactor` (or asks for something
|
|
10
|
+
> equivalent, like "adopt AFM here", "build retroactive docs for the project", "infer docs from the code").
|
|
11
|
+
> The CLI runs the **autonomous code archaeology** and materializes the skeleton of `docs/`; the agent
|
|
12
|
+
> fills the drafts from the report, runs **a short gap interview** and writes the forward-only rules.
|
|
13
|
+
>
|
|
14
|
+
> Difference from `afm path interview/bootstrap`: bootstrap interviews the operator in 7 steps about a new
|
|
15
|
+
> project. Refactor scans existing code, proposes drafts, and asks only what the code cannot reveal
|
|
16
|
+
> (persona, pain, intentional decisions).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Invariant principles (do not violate)
|
|
21
|
+
|
|
22
|
+
**AFM universals**: see the single canonical source, `afm path PRINCIPLES`. Refactor applies **all** of
|
|
23
|
+
them: § Universals (§1-8) + § Additional (#9, a retroactive ADR is load-bearing-only). Especially
|
|
24
|
+
relevant here:
|
|
25
|
+
|
|
26
|
+
- **#2** (discover-first): Part A is the literal application. The CLI scans stack, folders, entities, git
|
|
27
|
+
and rules on its own. Do not ask "what is the stack" or "where is X".
|
|
28
|
+
- **#4** (`[TBD]` beats invention): refactor never infers persona, pain or intent from code. Code does not
|
|
29
|
+
reveal that.
|
|
30
|
+
- **#5** (do not invent an entity or module): refactor does not materialize an entity that is not in the
|
|
31
|
+
schema (or its equivalent).
|
|
32
|
+
- **#6** (autonomy-first): invoking `afm refactor` **is** the approval. There is no "start?" gate and no
|
|
33
|
+
per-file confirmation. Part B materializes the docs and **reports** (summary + paths). Review happens
|
|
34
|
+
over the `git diff` of the whole set, not a stop at each write. The same applies to D.10 (forward-only
|
|
35
|
+
default) and Closing (`[TBD]` goes to reconcile).
|
|
36
|
+
- **#8** (questions are data, recommendation-first): every closed confirmation or selection goes through
|
|
37
|
+
`afm ask <op>`. Free text is only for genuinely open questions (pain, persona, justify N/A).
|
|
38
|
+
- **#9** (a retroactive ADR is load-bearing): this sets refactor apart from bootstrap, which forbids one.
|
|
39
|
+
|
|
40
|
+
**Refactor-specific rules** (how the retroactive adoption runs, not the methodology itself):
|
|
41
|
+
|
|
42
|
+
1. **Materialize and report, no gate.** Write all the docs and report a summary + paths. The operator
|
|
43
|
+
reviews via `git diff`. No "start?", "want to review?" or "adjust granularity?": the invocation is the
|
|
44
|
+
approval (#6).
|
|
45
|
+
2. **Inherited hard rules are forward-only by default.** Legacy code does not need to retroactively pass a
|
|
46
|
+
strict type check or 100% coverage. Forward-only is documented in `afm.md § 3.1` with a cutoff date.
|
|
47
|
+
3. **Do not ask what is discoverable.** Stack, folders, entities and broken rules all come from the scan.
|
|
48
|
+
**DoD and pre-push** also come from the scan (the CI and hooks it reports). The interview spends turns
|
|
49
|
+
only on **intent** the code cannot reveal: pain, persona, problem/MVP, load-bearing ADR, lived gotcha.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Pre-check (before Part A)
|
|
54
|
+
|
|
55
|
+
1. **Is this really brownfield?** `afm refactor --mechanical` checks all of it and refuses with the reasons
|
|
56
|
+
(exit 1) when any is false: `docs/.afm-version` does not exist (if it does, the project is already
|
|
57
|
+
bootstrapped: use `afm update` to raise the version, and the atomic ops stay available); a manifest
|
|
58
|
+
with real dependencies exists; the source root holds more than 0 non-test files; the git history has
|
|
59
|
+
more than 20 commits (a sign of a project with history, not a scaffold). A project with `docs/.template`
|
|
60
|
+
needs `afm init` (its from-template branch). Not brownfield: suggest `afm init`.
|
|
61
|
+
2. **Ask the two decisions the mechanical pass needs:** `afm ask init` (the harnesses whose instruction
|
|
62
|
+
files the project covers, and the string language of the product). A brownfield project already HAS
|
|
63
|
+
strings on screen, so scan first (#2): read the UI strings the code carries, and tell the operator what
|
|
64
|
+
you found before they answer. The language is a **product** decision, declared and never enforced; it
|
|
65
|
+
goes to `prd.md § 11.1`. `afm update` reads it before a language migration and excludes those strings
|
|
66
|
+
by construction. Without it, a migration cannot tell an artifact from a string the user reads, and it
|
|
67
|
+
would translate the UI.
|
|
68
|
+
3. **Announce the plan in one line and go straight in, no gate.** Invoking `afm refactor` is already the
|
|
69
|
+
approval (#6). Print: *"Running retroactive adoption: **A** — autonomous scan (stack, folders,
|
|
70
|
+
entities, README, git, CI and hooks, rule audit) · **B** — materialize the docs · **C** — short
|
|
71
|
+
interview, intent only (pain, persona, ADR, gotcha) · **D** — § 3.1 forward-only. Review at the end via
|
|
72
|
+
`git diff`."* If the operator wants to adjust or cancel, they interrupt.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Part A — Code archaeology (autonomous, no operator turns)
|
|
77
|
+
|
|
78
|
+
**Run `afm refactor --mechanical`** (add `--dry-run` first to see the plan; `--answers <file>` runs it
|
|
79
|
+
headless). One command does every mechanical operation of this part and prints the facts, one
|
|
80
|
+
tab-separated line each. Keep the report: Part B writes it all at once.
|
|
81
|
+
|
|
82
|
+
### A.1 Stack scan
|
|
83
|
+
|
|
84
|
+
The command enumerates the manifests first and reads each one, because a workspace root declares the
|
|
85
|
+
tooling and none of the stacks. Reading only the root manifest answers "no stacks here" for a repository
|
|
86
|
+
full of them; measured on 2026-09-04 across nine real JS/TS workspaces, 63 root-level probes were all
|
|
87
|
+
empty while the same probes fired correctly inside the packages. The report records **where** each
|
|
88
|
+
dependency was found, not only that it exists: a stack present in one package out of eight is a true
|
|
89
|
+
finding and a different fact from a stack present throughout. `afm detect --stacks` repeats the scan.
|
|
90
|
+
|
|
91
|
+
Map the stacks to the opt-in modules with `afm index --triggers` (one row per module with its `detect`
|
|
92
|
+
command), never with a table kept here. A stack with no module (Nest, FastAPI, Bun, BullMQ, Sidekiq) is
|
|
93
|
+
`[TBD]` in the component types of `core/ach`; consider future promotion with `afm promote`.
|
|
94
|
+
|
|
95
|
+
### A.2 Folder tree scan
|
|
96
|
+
|
|
97
|
+
The report lists `component<TAB><type><TAB><convention><TAB><evidence>` lines: the dominant folder patterns
|
|
98
|
+
mapped to the universal component types (Procedure-like route handlers, Domain-like pure functions,
|
|
99
|
+
Task-like jobs, Lib-like isolated packages, Adapter-like port implementations). The **observed** shapes stay
|
|
100
|
+
literal on purpose: it is how this op recognises an existing project. Only the convention it writes into the
|
|
101
|
+
consumer's `ach.md` is parameterised by the measured source root. Output: a draft of `ach.md` §1 (macro
|
|
102
|
+
diagram), §2 (folder rules) and §3.1 (the component-type to concrete-stack mapping).
|
|
103
|
+
|
|
104
|
+
### A.3 Entity scan
|
|
105
|
+
|
|
106
|
+
The report lists `entity<TAB><name><TAB><relations>` lines read from the project schema (a Prisma schema or
|
|
107
|
+
its equivalent). For each entity: the name goes to `prd.md` §10 (Glossary) with a one-line inferred role,
|
|
108
|
+
and the declared relations (foreign keys, relation attributes) go to a mini ER in ASCII in `ach.md` §1.
|
|
109
|
+
**Do not invent attributes** the schema does not have: if it only has an id, a creation date and a name,
|
|
110
|
+
the glossary says "Entity with a name (details in the schema file)".
|
|
111
|
+
|
|
112
|
+
### A.4 README + git log scan
|
|
113
|
+
|
|
114
|
+
Read the README and the recent history yourself: they are prose, and judgment is the point.
|
|
115
|
+
|
|
116
|
+
- **PRD §1 (Summary):** the first one or two paragraphs of the README, if they describe the product. If the
|
|
117
|
+
README is only "how to install", mark §1 `[TBD]`.
|
|
118
|
+
- **PRD §4 (implicit RFs):** derived from the commits that open features. List 3-5 candidates as
|
|
119
|
+
`RF-01..RF-N` with the note *(inferred from commit `<sha>`)*.
|
|
120
|
+
- **PRD §2 (Problem), §3 (Persona), §6 (MVP/success criteria):** typically NOT inferable from code. Mark
|
|
121
|
+
`[TBD]` directly and ask in Part C.
|
|
122
|
+
|
|
123
|
+
### A.5 Hard-rule audit
|
|
124
|
+
|
|
125
|
+
The report lists `audit<TAB><rule><TAB><violations><TAB><recommendation>` lines: the mechanical checklist
|
|
126
|
+
of the hard-rule slots run against the current code. Four slots (3, 8, 9, 14) are demoted principles with
|
|
127
|
+
no trigger: they are skipped, and the report says so. A rule that cannot be audited mechanically (idempotent
|
|
128
|
+
tasks, saga ordering, error classification, boundary validation) gets a quick visual check on sample files,
|
|
129
|
+
or is marked **N/A in the audit and applied forward-only by default**.
|
|
130
|
+
|
|
131
|
+
**The path constants are measured first** (`src-path`, `domain-path`, `ext`, then the file-size limit and the
|
|
132
|
+
domain prefix), the same way as in bootstrap: skipping this swaps a wrong literal for a placeholder nobody
|
|
133
|
+
fills, which fails silently, this repository's most expensive defect class. The report prints
|
|
134
|
+
`measured<TAB><constant><TAB><value><TAB><evidence>`. Propose each as `<constant> = <measured value>` with
|
|
135
|
+
the count attached, and record `[TBD]` for any the code does not answer (#4). The file-size audit is
|
|
136
|
+
reported against the measured p90, never against a number assumed here: *"p90 = 240; 7 files over the
|
|
137
|
+
proposal"*.
|
|
138
|
+
|
|
139
|
+
Output: a `rule | trigger | violations found | recommendation` table, for example:
|
|
140
|
+
|
|
141
|
+
| Rule | Trigger | Violations | Recommendation |
|
|
142
|
+
| --- | --- | --- | --- |
|
|
143
|
+
| 1 — TDD | tests/src ratio | 12 / 184 (6%) | forward-only from `<DATE>` |
|
|
144
|
+
| 2 — file ≤ limit | file lines | p90 = 240; 7 files over the proposal | propose the p90 rounded up, forward-only + split tickets |
|
|
145
|
+
| 4 — zero `any` | typed-escape count | 23 occurrences | forward-only from `<DATE>` |
|
|
146
|
+
| 11 — ADR for arch change | ADR count | 0 ADRs | apply now |
|
|
147
|
+
|
|
148
|
+
This table feeds Part D (it writes § 3.1 forward-only).
|
|
149
|
+
|
|
150
|
+
### A.6 Decisions inferable as retroactive ADRs (candidate list)
|
|
151
|
+
|
|
152
|
+
The report lists `adr-candidate<TAB><stack>` lines. Decisions that **were probably intentional** and still
|
|
153
|
+
load-bear: the chosen stack ("tRPC instead of REST", where switching breaks every typed client), the ORM
|
|
154
|
+
("Prisma instead of Drizzle", declarative schema plus generated migrations), the runtime or deploy target
|
|
155
|
+
("Trigger.dev instead of BullMQ", ergonomics plus observability) and the adoption of a custom design
|
|
156
|
+
system. This list feeds C.9.4.
|
|
157
|
+
|
|
158
|
+
**Do NOT list as candidates:** naming conventions (emergent patterns, not decisions), a dependency version
|
|
159
|
+
(state, not a decision), folder structure (it goes in `ach.md` § 2, not an ADR).
|
|
160
|
+
|
|
161
|
+
### A.7 CI and hooks scan (input for DoD + pre-push)
|
|
162
|
+
|
|
163
|
+
The report lists `check<TAB><check><TAB><stage><TAB><source>` lines, and `pr-flow<TAB>yes` when recurring
|
|
164
|
+
merges or a CODEOWNERS file show a PR flow. Each check is classified (`test`, type check, `lint`, `build`)
|
|
165
|
+
and by the stage it runs at: **CI on a PR or on a push to a protected branch** is a candidate for the
|
|
166
|
+
**DoD**, a **pre-push hook** for **pre-push**, and a PR flow points to a protected main branch, a candidate
|
|
167
|
+
for the "no direct commits to main" rule (`afm path interview/bootstrap` § Step 5). Output: two
|
|
168
|
+
pre-marked sets for C.9.6. Discovery-by-evidence is not invention (#5): what gates today is a fact. Whether
|
|
169
|
+
it *should* gate is what the operator confirms.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Part B — Draft materialization + presentation
|
|
174
|
+
|
|
175
|
+
### B.7 — Fill the docs from the report
|
|
176
|
+
|
|
177
|
+
`afm refactor --mechanical` already wrote the skeleton, and never over a file the operator has: `prd.md`,
|
|
178
|
+
`ach.md`, `afm.md` and `ust.md` with the measured constants filled and `[TBD]` where the code answers
|
|
179
|
+
nothing; `adr/0000-template.md`; empty `gotchas/` and `rules/`; every rubric and pattern; `AGENTS.md` with a
|
|
180
|
+
block recording the retroactive date; the per-harness stubs; and `docs/.afm-version`. Fill the gaps from
|
|
181
|
+
Part A, marking `[TBD]` for every one left:
|
|
182
|
+
|
|
183
|
+
- **`docs/prd.md`:** §1 from A.4; §2 and §3 `[TBD]`; §4 from the A.4 commits with an origin note; §6
|
|
184
|
+
`[TBD]`; §10 from the A.3 entities.
|
|
185
|
+
- **`docs/ach.md`:** §1 from A.2 and the A.3 mini ER; §2 from the observed tree; §3.1 the A.2 mapping; §3.2+
|
|
186
|
+
empty or `[TBD]`. Apply `modules/<stack>/ach.patch` (read it with `afm path`) for each stack detected.
|
|
187
|
+
- **`docs/afm.md`:** §1.1 and §1.2 from the universals; §1.3 `[TBD]` (project-specific principles come from
|
|
188
|
+
the interview); §3 carries **all** the rules (the file numbers slots up to 21; never copy a prefix, since
|
|
189
|
+
17-20 and the INALIENABLE 21 live at the end and a "1-16" copy drops them in silence); §3.1 is filled in
|
|
190
|
+
Part D; §6 and §6.1 are `[TBD]` until C.9.6. Apply `modules/<stack>/rules.patch` for each stack.
|
|
191
|
+
- **`docs/ust.md`:** reverse-engineer from existing tests whose `describe`/`it` reads like a story,
|
|
192
|
+
US-001 onward, with the origin (`*test ref:* path/to/test`). With no narrative tests leave it empty with
|
|
193
|
+
the note "*USs to be created as features evolve*". The persona in each US is `[TBD]` (it comes from
|
|
194
|
+
Part C).
|
|
195
|
+
- **`docs/gotchas/`:** empty. For each detected stack with a `gotchas.seed`, **do not copy it as active**:
|
|
196
|
+
add a commented block at the end with the seeds, for the operator to review.
|
|
197
|
+
- **The gate** is the CLI, so nothing is copied into `docs/tools/`. **In a brownfield project run
|
|
198
|
+
`afm gate --baseline` as soon as the first runner exists**: the code predates the rule by definition, so
|
|
199
|
+
the gate would start red, and a gate that starts red is disabled the next day. The baseline turns what
|
|
200
|
+
HEAD already breaks into declared debt that only decreases. `afm install git` arms it when the
|
|
201
|
+
operator asks.
|
|
202
|
+
- **`docs/rubrics/` and `docs/patterns/`** were copied from the lists `afm index --type rubric` and
|
|
203
|
+
`afm index --type pattern` give: no hand-written names (a hand-written set of eight rubric names shipped
|
|
204
|
+
until 3.40.0 while twelve existed, and four never reached a refactored project). The path-constants
|
|
205
|
+
measurement of A.5 filled their placeholders. A pattern only ever *suggests*; it becomes a convention
|
|
206
|
+
when the operator accepts one in C.9.7.
|
|
207
|
+
- **`docs/code-style/`:** **not written here.** Every code-style seed is opt-in item by item, asked in
|
|
208
|
+
C.9.7. Writing it during this no-gate step would apply what the operator has not yet seen. (`evolution/`
|
|
209
|
+
is not materialized either: it appears on demand at the first `afm evolve` cycle.)
|
|
210
|
+
|
|
211
|
+
### B.8 — Report
|
|
212
|
+
|
|
213
|
+
Report a summary + paths, **no review gate** (#6). Review happens via `git diff`, since the docs are
|
|
214
|
+
already on disk:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
I materialized the docs (+ rubrics/ + patterns/) in docs/ based on the scan. Summary:
|
|
218
|
+
|
|
219
|
+
- prd.md: N [TBD] (problem, persona, MVP, "why now").
|
|
220
|
+
- ach.md: diagram + inferred component types. 1 [TBD] in § 3.2 project-specific conventions.
|
|
221
|
+
- afm.md: all hard rules + the forward-only ones (filled in the next part). DoD/pre-push pre-filled from A.7.
|
|
222
|
+
- ust.md: N stories inferred from tests. Persona in [TBD].
|
|
223
|
+
- gotchas/: empty. K seeds from detected modules available in a comment.
|
|
224
|
+
- rubrics/, patterns/: copied. Nothing in patterns/ is a rule until you accept one, next in C.9.7.
|
|
225
|
+
- code-style/: nothing yet. Coming up next, item by item.
|
|
226
|
+
|
|
227
|
+
Review: git diff. To see a whole doc, just ask. Moving on to intent questions.
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Go straight to Part C. No need to ask "want to review?".
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Part C — Short gap interview (turns with the operator)
|
|
235
|
+
|
|
236
|
+
**Principles** (mirror bootstrap): ONE question per turn. Validate by rephrasing ("I understood: X.
|
|
237
|
+
Correct?"), then `afm ask confirm --scope <slug>`. If the operator says "I don't know", accept `[TBD]`. Do
|
|
238
|
+
not guess.
|
|
239
|
+
|
|
240
|
+
### C.9 — Questions
|
|
241
|
+
|
|
242
|
+
- **C.9.1 Pain (free text):** *"In 1 sentence: what pain does this product solve?"* Rephrase, validate with
|
|
243
|
+
`afm ask confirm --scope refactor-pain`, fill `prd.md` § 2.
|
|
244
|
+
- **C.9.2 Persona (free text):** *"Who is the primary user, in 1 sentence (role + context)?"* Rephrase,
|
|
245
|
+
validate with `afm ask confirm --scope refactor-persona`, fill `prd.md` § 3.
|
|
246
|
+
- **C.9.3 Validate the README summary:** state *"I inferred the product summary from the README as: «X»"*,
|
|
247
|
+
then `afm ask confirm --scope refactor-summary`. Correct `prd.md` § 1 per the answer.
|
|
248
|
+
- **C.9.3b App-facing string language:** already answered in the pre-check by `afm ask init`: fill
|
|
249
|
+
`prd.md` § 11.1 from it. Nothing is asked twice.
|
|
250
|
+
- **C.9.4 Retroactive ADR candidates:** list the A.6 decisions that still load-bear, then `afm ask refactor`. Criterion: if the operator swapped it tomorrow, would someone need a doc to explain why? Yes
|
|
251
|
+
means ADR, no means skip. For each chosen one: `afm adr new <slug> --title "<title>" --description "<one sentence>"`, then fill the sections from the A.6 reasoning and mark the status
|
|
252
|
+
`Accepted (retroactive, created by afm refactor)`. For each unchosen one: leave it out (a dead decision
|
|
253
|
+
does not become an ADR later).
|
|
254
|
+
- **C.9.5 Gotcha seeds:** the same `afm ask refactor` round asks which seeds of the detected stacks the
|
|
255
|
+
team has already been bitten by (read each with `afm path modules/<stack>/gotchas.seed`). Seeds are
|
|
256
|
+
*candidates*: they activate only if the operator confirms "this has already happened here". For each
|
|
257
|
+
confirmed one: `afm gotcha new <slug> --basis bitten-twice ...` (see `afm path ops/gotcha`), and drop it
|
|
258
|
+
from the commented block. Then ask in free text: *"Do you remember other gotchas that are not on this
|
|
259
|
+
list?"* and register each through `afm gotcha`.
|
|
260
|
+
- **C.9.6 DoD + pre-push, recommendation-first with the A.7 defaults:** the checks found in A.7 come
|
|
261
|
+
**already checked**. What gates today may not be the desired target, so the question is a confirmation
|
|
262
|
+
("is this what *should* gate, or do I adjust it?"). `afm ask gates` fills `afm.md` § 6 and § 6.1. With A.7
|
|
263
|
+
empty **and** nothing checked: `[TBD]` plus the note "define before the first merge after adoption".
|
|
264
|
+
- **C.9.7 Code-style seeds, one round per detected stack:** for each stack, list the files
|
|
265
|
+
`afm index --type code-style` reports under `code-style/<stack>/` plus `code-style/_any/`. Each is a house
|
|
266
|
+
convention the module's author observed in one project (naming, test placement, a file-size ceiling, an
|
|
267
|
+
error-boundary shape, an import order), never a fact of the stack. State in one line what each decides,
|
|
268
|
+
then `afm ask code-style`. Materialize only the marked items, verbatim, into
|
|
269
|
+
`docs/code-style/<stack>/<concern>.md`. An unmarked item is written nowhere. **Default = nothing marked,
|
|
270
|
+
nothing applies**: the opposite default from the rubrics and modules already copied, and deliberately so.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Part D — Forward-only rules
|
|
275
|
+
|
|
276
|
+
### D.10 — Write `afm.md` § 3.1
|
|
277
|
+
|
|
278
|
+
From the A.5 table, materialize the section in `docs/afm.md`:
|
|
279
|
+
|
|
280
|
+
```md
|
|
281
|
+
## 3.1 Forward-only rules
|
|
282
|
+
|
|
283
|
+
Retroactive adoption through `afm refactor` on <YYYY-MM-DD>. The rules below apply to **new code from this date on** and to **files you modify** (boy-scout rule). Legacy code that violates them is tracked tech debt, not a PR blocker. **This holds even for `INALIENABLE` rules.** Inalienable means the operator cannot switch the rule off. It does **not** mean the rule rewrites legacy code. A retroactive requirement would make adoption impossible in any codebase that did not start under it.
|
|
284
|
+
|
|
285
|
+
| Rule | Why forward-only | Tech debt tracked in |
|
|
286
|
+
| --- | --- | --- |
|
|
287
|
+
| 1 — TDD | Current coverage: <X>%. A retroactive sweep would take months. | `[TBD — open an issue or an ADR]` |
|
|
288
|
+
| 4 — zero `any` | <N> occurrences across <M> files. Incremental migration. | `[TBD]` |
|
|
289
|
+
|
|
290
|
+
**Boy-scout criterion:** when you edit a legacy file that violates a forward-only rule, bring it into conformance in the same PR if the PR scope justifies it. Do not force a 500-line refactor to fix a typo. Otherwise open a separate issue and link it.
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Granularity: autonomous default (#6), no gate.** Materialize the table with **forward-only from today +
|
|
294
|
+
boy-scout**, the right default in most cases, and report it with a non-blocking note: *"Forward-only from
|
|
295
|
+
today + boy-scout by default. If you want a mandatory sweep (with a deadline) or rule-by-rule granularity,
|
|
296
|
+
tell me."* The operator adjusts it in review if they want. Do not block materialization with a question.
|
|
297
|
+
|
|
298
|
+
### D.11 — `docs/.afm-version`
|
|
299
|
+
|
|
300
|
+
Written by `afm refactor --mechanical` with the version in use. Check that it is there; do not hand-write
|
|
301
|
+
the version.
|
|
302
|
+
|
|
303
|
+
### D.12 — Instruction files
|
|
304
|
+
|
|
305
|
+
`AGENTS.md` is the canonical, tool-agnostic file and `afm refactor --mechanical` wrote it, plus a stub per
|
|
306
|
+
answered harness that imports it. If the repo already had an `AGENTS.md`, only the managed AFM block was
|
|
307
|
+
merged in: never duplicate a rule. The block carries the **retroactive adaptations**: the note *"This
|
|
308
|
+
project adopted AFM retroactively on `<YYYY-MM-DD>`, through afm v`<VERSION>`"*, `afm.md` flagged as
|
|
309
|
+
including § 3.1 forward-only (inherited rules apply only to new code plus boy-scout edits), and `adr/`
|
|
310
|
+
flagged as including the retroactive ADRs from adoption. Add the ADR note once C.9.4 created any.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Closing
|
|
315
|
+
|
|
316
|
+
1. **Report the `[TBD]` items left, no gate; reconcile takes over.** List each open item (file + section,
|
|
317
|
+
for example `prd.md § 3 Persona`) in the closing summary and in the commit note. Do not force filling
|
|
318
|
+
it now: `afm reconcile` (always-on) watches the `[TBD]` items and proposes filling them as the product
|
|
319
|
+
evolves. If the operator wants to close one right away, they ask. It is not a gate.
|
|
320
|
+
2. **Generate the indexes and check:** `afm index --materialize`, `afm index --check`, then `afm doctor`.
|
|
321
|
+
The PRD has § 1 + § 2 + § 3 + at least 1 RF; the ACH has a diagram + mapped component types; the AFM has
|
|
322
|
+
rules + § 3.1 forward-only + DoD (or `[TBD]`); the UST has at least 1 US (even from an existing test; if
|
|
323
|
+
not, empty with a note is fine); `.afm-version` points to the version in use; the retroactive ADRs chosen
|
|
324
|
+
in C.9.4 are in `docs/adr/`.
|
|
325
|
+
3. **Suggested commit:**
|
|
326
|
+
```
|
|
327
|
+
docs(afm): retroactive adoption through afm refactor — afm v<VERSION>
|
|
328
|
+
|
|
329
|
+
Materializes PRD/ACH/AFM/UST/gotchas from code archaeology + a gap interview.
|
|
330
|
+
Forward-only rules from <YYYY-MM-DD>: <list>.
|
|
331
|
+
Retroactive ADRs: <numbered list>.
|
|
332
|
+
Open [TBD] items: <list>.
|
|
333
|
+
```
|
|
334
|
+
4. **Reminder for the operator:** the refactor is closed. `docs/` is now a living source that evolves
|
|
335
|
+
alongside the code through passive reconcile: every session watches the signals in
|
|
336
|
+
`afm path interview/triggers` and proposes an edit to the right file, with no explicit command.
|
|
337
|
+
Forward-only in § 3.1 is not an excuse to stop: boy-scouting an edited file keeps the improvement
|
|
338
|
+
gradient going. The ops `afm gotcha`, `afm adr`, `afm rule` and `afm promote` stay available on demand.
|
|
339
|
+
The single test for memory versus doc: *if another dev needs to know it to avoid breaking things, it is
|
|
340
|
+
a doc.*
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Anti-patterns
|
|
345
|
+
|
|
346
|
+
- ❌ **Inferring persona or pain from code.** Code does not reveal this. Always `[TBD]` + ask in Part C.
|
|
347
|
+
- ❌ **Inventing an entity not in the schema.** If the schema has only 3 entities, the glossary has 3.
|
|
348
|
+
- ❌ **Applying a retroactive hard rule without a forward-only flag.** This will lock up PRs.
|
|
349
|
+
- ❌ **Writing an ADR for every inferred decision.** Only for decisions with a present symptom
|
|
350
|
+
(load-bearing). Does swapping it hurt? ADR. Does swapping it not hurt? A note in `ach.md`, or skip it.
|
|
351
|
+
- ❌ **Materializing step by step and asking for confirmation.** Bootstrap does this because the answers
|
|
352
|
+
come from the operator. Refactor does an autonomous scan and materializes-and-reports. Per-file
|
|
353
|
+
confirmation is needless friction (#6).
|
|
354
|
+
- ❌ **A "start?", "want to review?" or "adjust granularity?" gate.** Invoking `afm refactor` is already the
|
|
355
|
+
approval (#6). Announce, materialize, report. Review happens via `git diff`.
|
|
356
|
+
- ❌ **Asking DoD/pre-push blind.** What gates merge and push is in CI and hooks, and the report lists it.
|
|
357
|
+
Confirm what was found (#2). A blank selection is only for when A.7 finds nothing.
|
|
358
|
+
- ❌ **Forcing `[TBD]` items to be filled at closing.** Report the list and let `afm reconcile` propose it
|
|
359
|
+
later. `[TBD]` beats pressure for a rushed answer (#4).
|
|
360
|
+
- ❌ **Skipping Part A to go straight to the interview.** Without the scan this becomes a badly-run
|
|
361
|
+
bootstrap ("tell me what you remember about the product"). The operator adopted AFM retroactively because
|
|
362
|
+
the doc did not exist; they probably do not remember everything the code does, either.
|
|
363
|
+
- ❌ **Activating gotcha seeds without the operator's confirmation.** Seeds are *candidates*. They activate
|
|
364
|
+
only if the operator confirms "this has already happened here".
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Refactor success criteria
|
|
369
|
+
|
|
370
|
+
- The generated doc is useful **without** the operator having to edit it by hand (except filling `[TBD]`
|
|
371
|
+
and validating retroactive ADRs).
|
|
372
|
+
- No section contains invention beyond what the code shows or the operator answered.
|
|
373
|
+
- Another dev can clone the repo, read `docs/`, and understand the product and the architecture, including
|
|
374
|
+
the rules in force and what is forward-only, without having to ask.
|
|
375
|
+
- Forward-only never becomes an excuse to never improve legacy code. The boy-scout rule in § 3.1 keeps the
|
|
376
|
+
improvement gradient going.
|
|
377
|
+
- **Only genuine intent spends a turn.** Zero ceremony gates (start, want-to-review, granularity, pending
|
|
378
|
+
items). Nothing discoverable (stack, folders, entities, DoD, pre-push) is asked: it comes from the scan.
|
|
379
|
+
What is left to ask is pain, persona, problem/MVP, load-bearing ADR and lived gotcha.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: interview
|
|
3
|
+
description: The signals that reconcile watches to know a doc edit is due.
|
|
4
|
+
load: cold
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Triggers — Signals for Doc Evolution
|
|
8
|
+
|
|
9
|
+
> This doc gives instructions to the agent. The agent reads it every session, detects a signal and proposes an edit. The agent does not apply the edit alone.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Single Principle
|
|
14
|
+
|
|
15
|
+
Invariant principle #1 (see [`../PRINCIPLES.md`](../PRINCIPLES.md)): the agent's memory holds no project rule. It holds only the user's style. Here is the direct application:
|
|
16
|
+
|
|
17
|
+
Use this one hard criterion to decide where something goes:
|
|
18
|
+
|
|
19
|
+
> "If another developer (or another agent without my memory) needs this fact to avoid breaking something, it belongs in a doc, not in memory."
|
|
20
|
+
|
|
21
|
+
Memory holds only:
|
|
22
|
+
- The user's communication style (terse vs. verbose, no emoji, and so on).
|
|
23
|
+
- A durable preference (small PRs vs. large bundles).
|
|
24
|
+
- Personal context (role, years of experience, current focus).
|
|
25
|
+
|
|
26
|
+
Project knowledge always goes into `/docs/`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Trigger Table
|
|
31
|
+
|
|
32
|
+
Every session, watch for these signals. When one fires, propose the edit to the user. Do not apply it alone. The user approves, then the agent edits.
|
|
33
|
+
|
|
34
|
+
| Signal observed during the session | Affected doc | The agent's action (ready-made line) |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| A new structural decision (a new layer, a new first-class component, a new cross-module contract, a folder refactor) | `ach.md` + new `adr/NNNN-*.md` | "I just proposed X, and that is an architectural decision. Do you want me to draft an ADR and update ACH before we merge?" |
|
|
37
|
+
| The same runtime/SDK surprise blocked us 2 times | `gotchas/` | "This behavior confused me before. I will log it in gotchas with an explicit trigger: 'If you edit X, read this first.'" |
|
|
38
|
+
| The user corrected the approach with a reusable rule ("no, never do Z") | `afm.md` § 3 (hard rule) or § 5 (off-track signal) | "I will add this as a hard rule. What mechanical trigger detects the violation? (`grep`, a checklist, and so on)" |
|
|
39
|
+
| **A catalog guardrail whose `signal` field matches this project** — run the `signal` field from `../guardrails/*.md` (it returns 0/1), and the gate is not yet installed. **One line covers the whole catalog**: the catalog grows, this row does not | `../guardrails/<id>.md` (read) + an install proposal | "This project matches guardrail `<id>`'s signal: `<the command that matched>`. Once installed, it becomes trigger `<trigger>`. Installing writes **outside `docs/`**, so I will not do it alone (bright line). I will bring you the constant to fill in and the possible surfaces, so you can choose where it runs. Want to see it?" |
|
|
40
|
+
| **A hard rule whose trigger is an ad hoc command where a versioned script already exists** — the `*Verification:*` field of a § 3 rule rewrites in prose what a repo `test/*.sh` already does | `afm.md` § 3 (the rule) | "Rule N verifies with a hand-written command, and `<script>` already does this in a versioned form. Should I point `*Verification:*` at the script? Rewriting the command in prose creates a second source of truth that drifts on its own." |
|
|
41
|
+
| The user accepted a counterintuitive approach ("the single bundled PR was right here") | `afm.md` § 5 (validated) or ADR | "Do you want me to log this as a validated decision? (So another agent or developer does not undo it.)" |
|
|
42
|
+
| A new component appeared (a 3rd caller of a helper, a new module, a promoted library) | `ach.md` § 3 + naming convention | "I promoted X from inline code to `lib/`. Should I update ACH § 3.2 and add a canonical example?" |
|
|
43
|
+
| **Primitive boilerplate grew** — the same fixed-order sequence of primitive calls repeats ≥3 times, AND the wiring hides the intent at those sites, AND a reusable grammar exists (composition, ordering, branching) | candidate `adr/NNNN-*.md` + check `rubrics/when-to-create-dsl.md` | "I noticed the same wiring sequence repeated in N places, and the boilerplate hides the intent. A DSL (builder or combinators) may help. This is an architectural change (rule 11), so **I will not build it alone**. I will bring you an API sketch and a candidate ADR for you to decide. Want to see it?" |
|
|
44
|
+
| **A doc gave bad guidance at apply time** (the `reflect` behavior accumulator) — I applied a rule or section from **one** core doc, and it was ambiguous (I had to reread it, or read it two ways), it led me to an error, or the user corrected me against that specific doc ≥2 times in the session. **This accumulates** (do not propose on every turn — the default threshold is ≥2 events per session, but an explicit user complaint unlocks it right away) | suggest `afm reflect` (instructiveness refinement) | "Rule/section X guided me poorly today (ambiguous, stale, or I hit this 2 times). Running `afm reflect` lets me propose making it more assertive. I add nothing new, I only refine what exists. Want me to?" |
|
|
45
|
+
| **A lived contradiction between docs** (Class B of evolve) — I followed **two** core docs that contradict each other in the same task | suggest `afm evolve` (health cycle) | "The docs contradict each other on X (I hit this 2 times in this session). This is not a one-off fix. Running `afm evolve` can diagnose the whole instance's health and propose a package. Want me to?" |
|
|
46
|
+
| **Perceived bloat at apply time** (Class B of evolve) — while loading context, a notably redundant or duplicated core doc got in the task's way | suggest `afm evolve` (health cycle) | "`afm.md` or `ach.md` looks bloated or duplicated, and it got in the way just now. If you want, `afm evolve` can audit the instance (with `wc`/grep, real evidence) and propose a trim at the gate. I apply nothing without your approval." |
|
|
47
|
+
| **≥2 similar gotchas accumulated** (the `generalize` mechanism) — I saw 2+ gotchas/rules in `gotchas/` that share the same abstract cause (one more general instruction would have prevented all N) | suggest `afm generalize` (semantic consolidation) | "These N gotchas say the same thing in different cases. Running `afm generalize` can merge them into one abstract rule (with a tombstone on the originals, no lost provenance). This cuts bloat at the root. Want me to?" |
|
|
48
|
+
| **Staleness or misplaced info** (the `restructure` mechanism) — a doc references code that changed and was not updated (likely, per `git log`), OR I searched for guidance that **exists** but sat in the wrong doc or section | suggest `afm restructure` (context architecture) | "`<doc>` may be stale versus `<path>` (it changed since the doc's last edit), or guidance X was buried where I did not look. `afm restructure` proposes an update or a move at the gate. (If the info does **not** exist anywhere, that is a case for `afm gotcha`, not restructure.)" |
|
|
49
|
+
| **A failure crossed the § 5.1 terminal condition** (a test/`tsc`/command returned non-zero and stayed unresolved past the retry cap, OR the user explicitly corrected the approach) | the failure store of the AFM state directory (the failure hook logs it) + suggest `afm remediate` | "This failure crossed the retry cap, or you corrected me. The failure hook already recorded it with the external signal (`sig=`). If it recurs (≥2 times) or you confirm, I will run `afm remediate` to turn it into a procedure (mechanical) or a learning (conceptual)." |
|
|
50
|
+
| **The same failure `sig=` recurred ≥2 times** (recurrence: `afm failures promotable` lists it) | suggest `afm remediate` (CRITIC loop) | "This failure recurred. Running `afm remediate` can resolve it for good — a runbook (`procedures/`) for a mechanical sequence, or a `learnings/` entry for future agent sessions if it is conceptual. I apply nothing without your approval." |
|
|
51
|
+
| **A repeatable operational sequence ran ≥2 times** (a release, deploy, or migration done by hand again) | `docs/procedures/` via `afm remediate` | "You did this sequence (release, deploy, or migration) by hand ≥2 times. I want to turn it into a `docs/procedures/` runbook with a 0/1 check per step. May I?" |
|
|
52
|
+
| **An artifact was written in a language other than English**, and `prd.md` § 11.1 does not say that language is the product's own | `prd.md` § 11.1 (read first) + the artifact | "I wrote `<file>` in `<language>`. Artifacts are English (the rule), and the ONE exception is the strings your users read, which `prd.md` § 11.1 declares. That field says `<value>`, so this file is not covered by it. Should I rewrite it in English, or is this a product string that belongs in the exception?" **Read § 11.1 before you fire this.** With `[TBD]` there, ask which of the two it is — never assume the file is a violation. |
|
|
53
|
+
| A feature started with no matching US | `ust.md` | "I found no matching US. Do you want me to create US-NNN before I start implementing?" |
|
|
54
|
+
| A product scope change (an RF cut or addition) | `prd.md` § 4 | "This changes RF-NN. Should I update the table and note in the PRD?" |
|
|
55
|
+
| The user asks to DELIVER a feature ("build feature X", "implement X", "deliver X end to end") | `deliver` flow (`afm.md` § 2.2) | "I will run the autonomous delivery: scan the code, research libraries through nasca, ask you only what I cannot infer, build the plan for one approval, and execute to the end. May I? (Say 'go' and I go straight through.)" |
|
|
56
|
+
| An in-progress change reaches >200 lines / >3 files, OR it crosses layers, OR it adds a new external boundary | new `docs/features/NNN-*/` via `deliver` | "Should I promote this to a feature folder and deliver it via `afm deliver`? It runs discovery, builds a plan for one approval, and executes to the end." |
|
|
57
|
+
| The user is about to start T001 in `tasks.md` for a feature, and `analyze` has not run this session | `analyze` (suggestion) | "Before starting T001 for this feature, should I run `afm analyze` to catch coverage gaps?" — suggest once per feature, do not repeat. |
|
|
58
|
+
| I learned a pattern about the user (a durable style preference) | memory | save it in the agent's memory as a durable preference |
|
|
59
|
+
| I learned technical knowledge about the project | doc | edit the doc (`afm`, `ach`, `prd`, `gotchas`, `adr`) |
|
|
60
|
+
| The learning generalizes to any project | propose a change to the AFM template content | see `promotion.md` |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Concrete Examples (Training)
|
|
65
|
+
|
|
66
|
+
### Example 1 — A Gotcha Emerges
|
|
67
|
+
|
|
68
|
+
**Session:** the user and the agent debug for 30 minutes: "this tsdown subprocess breaks with `type: commonjs`." They fix it. It works.
|
|
69
|
+
|
|
70
|
+
**Signal detected:** "the same runtime surprise blocked us 2 times." The user mentioned it had confused another developer before.
|
|
71
|
+
|
|
72
|
+
**Proposed action:** "I will add a `gotchas/` entry. Read it before you edit emit/package-json.ts or move the tsdown binary. `type` stays `module`, even in format=cjs. Do you also want ADR-0003 with the full decision?"
|
|
73
|
+
|
|
74
|
+
### Example 2 — A New Rule Emerges from a Correction
|
|
75
|
+
|
|
76
|
+
**Session:** the agent implemented `domain_X`, which imports `TRPCError`. The user flags it: "Domain code never imports a transport error. Map code to TRPCError in the procedure."
|
|
77
|
+
|
|
78
|
+
**Signal detected:** "the user corrected the approach with a reusable rule."
|
|
79
|
+
|
|
80
|
+
**Proposed action:** "I will add hard rule 15 to `afm.md` § 3: 'Domain code never imports TRPCError. Use DomainError({ code: ... as const }). The procedure maps it at the boundary.' Mechanical trigger: `grep -rn 'TRPCError' {{src-path}}/server/modules/**/domain/` returns 0. Correct?" (`{{src-path}}` is the project's measured source root — see `bootstrap.md` § Step 5 "Rule constants" or `refactor.md` § A.5. Pasting this example with a literal `src/` would hardcode a house layout as a consumer's hard rule for a project whose real root is `app/` or `lib/`.)
|
|
81
|
+
|
|
82
|
+
### Example 3 — Promotion to the Template
|
|
83
|
+
|
|
84
|
+
**Session:** a new project learned that a Stripe webhook can arrive 2 times, and it needs an idempotent UPSERT. The user confirms the rule.
|
|
85
|
+
|
|
86
|
+
**Signal detected:** "the learning generalizes to any project" — this applies to every project that uses Stripe.
|
|
87
|
+
|
|
88
|
+
**Proposed action:** "This rule (`idempotent webhook UPSERT via unique stripeSubscriptionId`) applies to any Stripe project. I just added it to `/docs/gotchas/`. Do you also want me to propagate it to the `modules/stripe-billing/gotchas.seed.md` of the AFM template content? Then your next project gets it for free."
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Anti-Patterns
|
|
93
|
+
|
|
94
|
+
- ❌ **Apply an edit without proposing it first.** The user decides what goes into the doc.
|
|
95
|
+
- ❌ **Save a project rule in memory.** This violates the single principle. Memory is not a doc.
|
|
96
|
+
- ❌ **Invent a gotcha without the 2-time trigger.** A gotcha comes from real use, not imagination.
|
|
97
|
+
- ❌ **Promote to the template before the user confirms.** The learning may be project-specific.
|
|
98
|
+
- ❌ **Propose an edit on every turn.** Accumulate signal across the session, then propose when it carries weight.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## When NOT to Propose
|
|
103
|
+
|
|
104
|
+
- A trivial change (a variable rename, a lint fix, a typo).
|
|
105
|
+
- A refactor with no new learning (code moves, but behavior does not change).
|
|
106
|
+
- A bug fix that reveals no rule (it was a typo, not a decision).
|
|
107
|
+
- An exploratory session that has not converged — wait for it to settle.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: module
|
|
3
|
+
description: 'Adds the Server/Client Component split to the materialized ACH. The folder tree, the UI-primitive location, and the route-group/handler-delegation convention are opt-in seeds — see the pointers below.'
|
|
4
|
+
load: cold
|
|
5
|
+
stack: nextjs-app-router
|
|
6
|
+
patches: 'docs/ach.md'
|
|
7
|
+
detect: |
|
|
8
|
+
if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("next")' package.json >/dev/null 2>&1; else grep -q '"next"' package.json 2>/dev/null; fi
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Patch — ACH for Next.js App Router projects
|
|
12
|
+
|
|
13
|
+
> This patch applies to `/docs/ach.md`. Insert the sections below at the marked points.
|
|
14
|
+
|
|
15
|
+
## §2 — Add to the folder tree
|
|
16
|
+
|
|
17
|
+
> Whether the App Router root sits at the repo root (`app/`) or under `src/` (`src/app/`) — Next.js
|
|
18
|
+
> accepts both — and how a route group under it gets named, are house choices, not a Next.js
|
|
19
|
+
> requirement. They now live as an opt-in seed at `code-style/nextjs-app-router/naming.md`
|
|
20
|
+
> (concern: naming). Materialize it only if the operator accepts it.
|
|
21
|
+
|
|
22
|
+
## §3 — Add the UI primitives component
|
|
23
|
+
|
|
24
|
+
> The `src/components/` location this patch used to state as fact for UI primitives is the same
|
|
25
|
+
> house choice as the folder root above — Next.js has no opinion on where a component lives. It
|
|
26
|
+
> now lives in the same opt-in seed, `code-style/nextjs-app-router/naming.md` (concern: naming).
|
|
27
|
+
|
|
28
|
+
## §6 — Add dashboard routing
|
|
29
|
+
|
|
30
|
+
> The route-group split this patch used to state as fact (`(protected)/` vs `auth/`) and the
|
|
31
|
+
> "route handlers stay thin and delegate to `server/`" rule are both house convention, not a
|
|
32
|
+
> framework requirement. They now live as opt-in seeds at
|
|
33
|
+
> `code-style/nextjs-app-router/naming.md` (concern: naming, for the route-group split) and
|
|
34
|
+
> `code-style/nextjs-app-router/imports.md` (concern: imports, for the delegate-to-`server/`
|
|
35
|
+
> rule). Materialize either only if the operator accepts it.
|
|
36
|
+
|
|
37
|
+
## Additional hard rules
|
|
38
|
+
|
|
39
|
+
(number these starting from the next free slot in `afm.md` §3)
|
|
40
|
+
|
|
41
|
+
- **Server Component is the default. Use "use client" only where you need interactivity.**
|
|
42
|
+
*Verification:* `grep -rn "use client" src/app app 2>/dev/null` reviews each context. Each match must justify itself (a form, a state hook, a DOM event).
|
|
43
|
+
- **Do not fetch directly in a Server Component when a typed client is available**. Use the direct server client (`trpcServer`) on the server, and React Query on the client.
|
|
44
|
+
*Verification:* `grep -rn "fetch(" --include=page.tsx src/app app 2>/dev/null` finds no raw fetch call in a page.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: module
|
|
3
|
+
description: 'Seed gotchas for the App Router, starting with cookies() and headers() being valid only in a Server Component or a route handler.'
|
|
4
|
+
load: cold
|
|
5
|
+
stack: nextjs-app-router
|
|
6
|
+
patches: 'docs/gotchas/*.md'
|
|
7
|
+
detect: |
|
|
8
|
+
if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("next")' package.json >/dev/null 2>&1; else grep -q '"next"' package.json 2>/dev/null; fi
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Gotchas seed — Next.js App Router
|
|
12
|
+
|
|
13
|
+
> Each entry below is a complete gotcha file. Copy one to `docs/gotchas/NNN-slug.md` (the first `---` of the entry as line 1) only when it bites in real use. Do not copy everything proactively. Re-date `last_bite` on copy.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
type: gotcha
|
|
17
|
+
description: 'cookies() and headers() from next/headers work only in a request context (Server Component, route handler, server action) and throw everywhere else.'
|
|
18
|
+
load: warm
|
|
19
|
+
fires_on: event
|
|
20
|
+
fires_when: 'you use cookies() or headers() from next/headers in a pure library or in a Client Component.'
|
|
21
|
+
bites: 1
|
|
22
|
+
last_bite: 2026-10
|
|
23
|
+
refs:
|
|
24
|
+
- 'node_modules/next/dist/docs/01-app/02-api-reference/04-functions/cookies.mdx'
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Next.js — `cookies()` / `headers()` work only in a Server Component or route handler
|
|
28
|
+
|
|
29
|
+
## Behavior
|
|
30
|
+
|
|
31
|
+
Both functions work only in a request context: Server Component, route handler, or server action. Everywhere else, they throw an error at runtime.
|
|
32
|
+
|
|
33
|
+
## Solution
|
|
34
|
+
|
|
35
|
+
Read cookies in the Server Component or route handler. Pass the value to the client component as a prop. Never import `next/headers` in code that can run in the browser.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
type: gotcha
|
|
39
|
+
description: 'revalidatePath and revalidateTag invalidate the Next.js cache only; React Query keeps its own client cache, so a mutation must also invalidate the client query.'
|
|
40
|
+
load: warm
|
|
41
|
+
fires_on: event
|
|
42
|
+
fires_when: 'you are in a route handler or server action and want to refresh the client cache.'
|
|
43
|
+
bites: 1
|
|
44
|
+
last_bite: 2026-10
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Next.js — `revalidatePath` / `revalidateTag` do not invalidate React Query
|
|
48
|
+
|
|
49
|
+
## Behavior
|
|
50
|
+
|
|
51
|
+
`revalidatePath` invalidates the Next.js cache (RSC + fetch). React Query keeps its own cache on the client. A server action that mutates data must also invalidate the client query. This does not happen automatically.
|
|
52
|
+
|
|
53
|
+
## Solution
|
|
54
|
+
|
|
55
|
+
Return a signal to the client that triggers `queryClient.invalidateQueries(...)`. Or use a stack client that already integrates this, for example `@trpc/tanstack-react-query`, which invalidates automatically in `onSuccess`.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
type: gotcha
|
|
59
|
+
description: 'A root layout under a dynamic segment (app/[lang]/layout.tsx) leaves the internal _not-found route with no root layout, so the build fails or serves a raw 404.'
|
|
60
|
+
load: warm
|
|
61
|
+
fires_on: event
|
|
62
|
+
fires_when: 'you delete app/layout.tsx and move <html>/<body> to app/[lang]/layout.tsx (an i18n pattern, for the correct lang at SSR).'
|
|
63
|
+
bites: 1
|
|
64
|
+
last_bite: 2026-10
|
|
65
|
+
refs:
|
|
66
|
+
- 'node_modules/next/dist/docs/.../file-conventions/not-found.md (section global-not-found.js)'
|
|
67
|
+
- 'node_modules/next/dist/docs/.../layout.md (root layout under a dynamic segment)'
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Next.js — a root layout under a dynamic segment orphans `_not-found`
|
|
71
|
+
|
|
72
|
+
## Behavior
|
|
73
|
+
|
|
74
|
+
Next.js generates an internal `/_not-found` route that lives **outside** the `[lang]` segment. It then has no root layout to compose the document. The build fails (the `_not-found` page has no root layout) or serves a raw 404. Non-locale paths that escape the proxy redirect (for example `/foo.bar`, with a dot) fall into the same gap.
|
|
75
|
+
|
|
76
|
+
## Solution
|
|
77
|
+
|
|
78
|
+
Add `app/global-not-found.tsx`. It returns its own complete HTML document (`<html><body>…`, default lang, provider-free, because it bypasses layouts). Enable it with `experimental.globalNotFound: true` in the Next.js config (Next.js 15.4 or later). The `notFound()` of localized routes still resolves in `app/[lang]/not-found.tsx` (inside the layout, with the correct html lang and theme).
|