@tenkara/afm-linux-arm64 0.0.0-stage → 4.0.0-alpha.6
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 +4256 -0
- package/content/PRINCIPLES.md +121 -0
- package/content/README-light.md +160 -0
- package/content/README.md +152 -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 +265 -0
- package/content/core/afm.md +496 -0
- package/content/core/gotchas.md +18 -0
- package/content/core/prd.md +143 -0
- package/content/core/protocol.md +160 -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 +72 -0
- package/content/interview/bootstrap.md +415 -0
- package/content/interview/promotion.md +117 -0
- package/content/interview/refactor.md +381 -0
- package/content/interview/triggers.md +109 -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 +129 -0
- package/content/ops/clarify.md +86 -0
- package/content/ops/deliver.md +211 -0
- package/content/ops/diagnose.md +146 -0
- package/content/ops/evolve.md +317 -0
- package/content/ops/export-template.md +97 -0
- package/content/ops/generalize.md +120 -0
- package/content/ops/gotcha.md +46 -0
- package/content/ops/plan.md +61 -0
- package/content/ops/policy.md +63 -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 +180 -0
- package/content/ops/remediate.md +146 -0
- package/content/ops/restructure.md +145 -0
- package/content/ops/rule.md +165 -0
- package/content/ops/specify.md +62 -0
- package/content/ops/tasks.md +67 -0
- package/content/ops/update.md +223 -0
- package/content/ops/verify.md +69 -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 +28 -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 +113 -0
- package/content/rubrics/negative-filters.md +38 -0
- package/content/rubrics/solid-triggers.md +121 -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 +34 -0
- package/content/skills/clarify.md +34 -0
- package/content/skills/deliver.md +42 -0
- package/content/skills/diagnose.md +50 -0
- package/content/skills/evolve.md +67 -0
- package/content/skills/export-template.md +40 -0
- package/content/skills/generalize.md +47 -0
- package/content/skills/gotcha.md +40 -0
- package/content/skills/init.md +82 -0
- package/content/skills/plan.md +34 -0
- package/content/skills/policy.md +31 -0
- package/content/skills/promote.md +41 -0
- package/content/skills/protocol.md +42 -0
- package/content/skills/reconcile.md +49 -0
- package/content/skills/refactor.md +84 -0
- package/content/skills/reflect.md +53 -0
- package/content/skills/remediate.md +39 -0
- package/content/skills/restructure.md +52 -0
- package/content/skills/rule.md +44 -0
- package/content/skills/specify.md +34 -0
- package/content/skills/tasks.md +34 -0
- package/content/skills/update.md +56 -0
- package/content/skills/verify.md +31 -0
- package/content/templates/adr.template.md +51 -0
- package/content/templates/evolution-report.template.md +129 -0
- package/content/templates/feature-plan.template.md +86 -0
- package/content/templates/feature-research.template.md +57 -0
- package/content/templates/feature-spec.template.md +86 -0
- package/content/templates/feature-tasks.induced.template.md +67 -0
- package/content/templates/feature-tasks.template.md +60 -0
- package/content/templates/focus.template.md +30 -0
- package/content/templates/gotcha.template.md +31 -0
- package/content/templates/guardrail-runner.template.md +81 -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 +44 -0
- package/plugin/agy/skills/afm-analyze/SKILL.md +36 -0
- package/plugin/agy/skills/afm-clarify/SKILL.md +36 -0
- package/plugin/agy/skills/afm-deliver/SKILL.md +44 -0
- package/plugin/agy/skills/afm-diagnose/SKILL.md +50 -0
- package/plugin/agy/skills/afm-evolve/SKILL.md +69 -0
- package/plugin/agy/skills/afm-export-template/SKILL.md +42 -0
- package/plugin/agy/skills/afm-generalize/SKILL.md +49 -0
- package/plugin/agy/skills/afm-gotcha/SKILL.md +42 -0
- package/plugin/agy/skills/afm-init/SKILL.md +84 -0
- package/plugin/agy/skills/afm-plan/SKILL.md +36 -0
- package/plugin/agy/skills/afm-policy/SKILL.md +31 -0
- package/plugin/agy/skills/afm-promote/SKILL.md +41 -0
- package/plugin/agy/skills/afm-protocol/SKILL.md +42 -0
- package/plugin/agy/skills/afm-reconcile/SKILL.md +49 -0
- package/plugin/agy/skills/afm-refactor/SKILL.md +86 -0
- package/plugin/agy/skills/afm-reflect/SKILL.md +55 -0
- package/plugin/agy/skills/afm-remediate/SKILL.md +41 -0
- package/plugin/agy/skills/afm-restructure/SKILL.md +54 -0
- package/plugin/agy/skills/afm-rule/SKILL.md +46 -0
- package/plugin/agy/skills/afm-specify/SKILL.md +36 -0
- package/plugin/agy/skills/afm-tasks/SKILL.md +34 -0
- package/plugin/agy/skills/afm-update/SKILL.md +58 -0
- package/plugin/agy/skills/afm-verify/SKILL.md +33 -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 +44 -0
- package/plugin/claude-code/skills/analyze/SKILL.md +36 -0
- package/plugin/claude-code/skills/clarify/SKILL.md +36 -0
- package/plugin/claude-code/skills/deliver/SKILL.md +44 -0
- package/plugin/claude-code/skills/diagnose/SKILL.md +50 -0
- package/plugin/claude-code/skills/evolve/SKILL.md +69 -0
- package/plugin/claude-code/skills/export-template/SKILL.md +42 -0
- package/plugin/claude-code/skills/generalize/SKILL.md +49 -0
- package/plugin/claude-code/skills/gotcha/SKILL.md +42 -0
- package/plugin/claude-code/skills/init/SKILL.md +84 -0
- package/plugin/claude-code/skills/plan/SKILL.md +36 -0
- package/plugin/claude-code/skills/policy/SKILL.md +31 -0
- package/plugin/claude-code/skills/promote/SKILL.md +41 -0
- package/plugin/claude-code/skills/protocol/SKILL.md +42 -0
- package/plugin/claude-code/skills/reconcile/SKILL.md +49 -0
- package/plugin/claude-code/skills/refactor/SKILL.md +86 -0
- package/plugin/claude-code/skills/reflect/SKILL.md +55 -0
- package/plugin/claude-code/skills/remediate/SKILL.md +41 -0
- package/plugin/claude-code/skills/restructure/SKILL.md +54 -0
- package/plugin/claude-code/skills/rule/SKILL.md +46 -0
- package/plugin/claude-code/skills/specify/SKILL.md +36 -0
- package/plugin/claude-code/skills/tasks/SKILL.md +34 -0
- package/plugin/claude-code/skills/update/SKILL.md +58 -0
- package/plugin/claude-code/skills/verify/SKILL.md +33 -0
- package/plugin/opencode/afm.js +173 -0
- package/plugin/opencode/agents/afm-executor.md +31 -0
- package/plugin/opencode/agents/afm-explorer.md +47 -0
- package/plugin/opencode/agents/afm-reviewer.md +48 -0
- package/plugin/opencode/skills/afm-adr/SKILL.md +44 -0
- package/plugin/opencode/skills/afm-analyze/SKILL.md +36 -0
- package/plugin/opencode/skills/afm-clarify/SKILL.md +36 -0
- package/plugin/opencode/skills/afm-deliver/SKILL.md +44 -0
- package/plugin/opencode/skills/afm-diagnose/SKILL.md +50 -0
- package/plugin/opencode/skills/afm-evolve/SKILL.md +69 -0
- package/plugin/opencode/skills/afm-export-template/SKILL.md +42 -0
- package/plugin/opencode/skills/afm-generalize/SKILL.md +49 -0
- package/plugin/opencode/skills/afm-gotcha/SKILL.md +42 -0
- package/plugin/opencode/skills/afm-init/SKILL.md +84 -0
- package/plugin/opencode/skills/afm-plan/SKILL.md +36 -0
- package/plugin/opencode/skills/afm-policy/SKILL.md +31 -0
- package/plugin/opencode/skills/afm-promote/SKILL.md +41 -0
- package/plugin/opencode/skills/afm-protocol/SKILL.md +42 -0
- package/plugin/opencode/skills/afm-reconcile/SKILL.md +49 -0
- package/plugin/opencode/skills/afm-refactor/SKILL.md +86 -0
- package/plugin/opencode/skills/afm-reflect/SKILL.md +55 -0
- package/plugin/opencode/skills/afm-remediate/SKILL.md +41 -0
- package/plugin/opencode/skills/afm-restructure/SKILL.md +54 -0
- package/plugin/opencode/skills/afm-rule/SKILL.md +46 -0
- package/plugin/opencode/skills/afm-specify/SKILL.md +36 -0
- package/plugin/opencode/skills/afm-tasks/SKILL.md +34 -0
- package/plugin/opencode/skills/afm-update/SKILL.md +58 -0
- package/plugin/opencode/skills/afm-verify/SKILL.md +33 -0
- package/README.md +0 -3
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Ratchet — a budget of violations per rule and region, monotonically decreasing.
|
|
3
|
+
#
|
|
4
|
+
# It exists because turning a hard rule on in a repository that already breaks it makes the
|
|
5
|
+
# gate start red, and a gate that is red on day 1 is disabled on day 2. The ratchet lets the
|
|
6
|
+
# pre-existing debt through and fails the GROWTH.
|
|
7
|
+
#
|
|
8
|
+
# Format of the ratchet TSV (header plus rows):
|
|
9
|
+
# rule region count note
|
|
10
|
+
# 21 . 0 no debt — the rule was born clean
|
|
11
|
+
# 34 src/legacy 4 pre-existing when the gate was turned on
|
|
12
|
+
#
|
|
13
|
+
# `region` is a path relative to the repository root; `.` covers the whole repository. A finding
|
|
14
|
+
# counts against the MOST SPECIFIC CONFIGURED region that prefixes its path. That is what lets
|
|
15
|
+
# you turn a rule on only where it is already clean, without a per-file exception list.
|
|
16
|
+
#
|
|
17
|
+
# Sourceable, not executable: `. "${AFM_GATE_LIB}/ratchet.sh"`.
|
|
18
|
+
|
|
19
|
+
# ratchet_budget <rule> <region> -> prints the budget (0 when not registered).
|
|
20
|
+
# Absence means 0 on purpose: a new rule is born with zero tolerance, and debt exists only when
|
|
21
|
+
# somebody measured it and wrote it down. A permissive default would be the door to a phantom gate.
|
|
22
|
+
ratchet_budget() {
|
|
23
|
+
local rule="$1" region="$2" f="${RATCHET:-}"
|
|
24
|
+
[ -n "$f" ] && [ -f "$f" ] || { printf '0'; return 0; }
|
|
25
|
+
# A value bash cannot compare is malformed, not a budget. The TSV is written by hand, and an
|
|
26
|
+
# all-digit number beyond the 64-bit range survives awk (printed back as 100000000000000000000)
|
|
27
|
+
# and then breaks `[ -le ]` itself with "integer expression expected" — which evaluates falsy,
|
|
28
|
+
# so the finding counts as over budget and stderr fills up on every run. Over 9 digits is far
|
|
29
|
+
# beyond any debt anyone measured and far below where the comparison breaks; treat it as 0,
|
|
30
|
+
# the same answer an unregistered rule gets, because a budget nobody can read is a budget
|
|
31
|
+
# nobody measured.
|
|
32
|
+
awk -F'\t' -v r="$rule" -v g="$region" '
|
|
33
|
+
NR>1 && $1==r && $2==g {
|
|
34
|
+
v = $3 + 0
|
|
35
|
+
if (v < 0 || v > 999999999) v = 0
|
|
36
|
+
printf "%d", v; found=1; exit
|
|
37
|
+
}
|
|
38
|
+
END { if (!found) print 0 }
|
|
39
|
+
' "$f"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# ratchet_region <rule> <path> -> prints the most specific configured region that prefixes
|
|
43
|
+
# <path>, or "." when none matches.
|
|
44
|
+
ratchet_region() {
|
|
45
|
+
local rule="$1" path="$2" f="${RATCHET:-}"
|
|
46
|
+
[ -n "$f" ] && [ -f "$f" ] || { printf '.'; return 0; }
|
|
47
|
+
awk -F'\t' -v r="$rule" -v p="$path" '
|
|
48
|
+
# An `exempt` row is a SCOPE statement, not a budget. Letting it act as a region made it
|
|
49
|
+
# absorb every finding under that path into a budget of 0, so the rule failed there instead
|
|
50
|
+
# of skipping it — the exemption turned into its opposite.
|
|
51
|
+
NR>1 && $1==r && $2!="." && tolower($3)!="exempt" {
|
|
52
|
+
# `index()==1` is a literal prefix. No regex, because a path holding "." or "+" would
|
|
53
|
+
# turn into a metacharacter and match the wrong region in silence.
|
|
54
|
+
#
|
|
55
|
+
# A region is a PATH, so the prefix must end at a path boundary. Without that check,
|
|
56
|
+
# `docs/research-extra/x.md` starts with the characters of `docs/research` and spent the
|
|
57
|
+
# the neighbouring budget — a budget that absorbs a directory nobody measured stops meaning
|
|
58
|
+
# anything, and it fails toward green. A trailing slash written by hand is tolerated.
|
|
59
|
+
reg = $2
|
|
60
|
+
sub(/\/+$/, "", reg)
|
|
61
|
+
if (index(p, reg) == 1 \
|
|
62
|
+
&& (length(p) == length(reg) || substr(p, length(reg) + 1, 1) == "/") \
|
|
63
|
+
&& length(reg) > length(best)) best=reg
|
|
64
|
+
}
|
|
65
|
+
END { print (best == "" ? "." : best) }
|
|
66
|
+
' "$f"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ratchet_note <rule> <region> -> the recorded reason for the debt (empty when there is none).
|
|
70
|
+
ratchet_note() {
|
|
71
|
+
local rule="$1" region="$2" f="${RATCHET:-}"
|
|
72
|
+
[ -n "$f" ] && [ -f "$f" ] || return 0
|
|
73
|
+
awk -F'\t' -v r="$rule" -v g="$region" 'NR>1 && $1==r && $2==g { print $4; exit }' "$f"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# ratchet_has_rule <rule> -> exit 0 when the rule already has any row.
|
|
77
|
+
# `--baseline` uses it to refuse to touch a rule a human already decided about.
|
|
78
|
+
ratchet_has_rule() {
|
|
79
|
+
local rule="$1" f="${RATCHET:-}"
|
|
80
|
+
[ -n "$f" ] && [ -f "$f" ] || return 1
|
|
81
|
+
awk -F'\t' -v r="$rule" 'NR>1 && $1==r { found=1; exit } END { exit !found }' "$f"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# ratchet_exempt <rule> <path> -> exit 0 when the path is OUT OF SCOPE for the rule.
|
|
85
|
+
#
|
|
86
|
+
# An exemption is not a budget, and conflating the two was the reason this function exists.
|
|
87
|
+
# A budget says "this many violations are tolerated here, and the number may only fall". An
|
|
88
|
+
# exemption says "this file is not what the rule is about at all" — the detector's own
|
|
89
|
+
# dictionary, a fixture written to be wrong on purpose, an archived changelog.
|
|
90
|
+
#
|
|
91
|
+
# Until 3.20.0 every exemption was a `case ... continue ;;` arm inside the runner, naming a
|
|
92
|
+
# path in ONE repository. That made the runner unshippable: a consumer inherited exclusions for
|
|
93
|
+
# files it does not have, and had nowhere to write its own. Worse, the exclusion was invisible —
|
|
94
|
+
# it lived in code nobody re-reads, while the ratchet is a versioned table a reviewer sees.
|
|
95
|
+
#
|
|
96
|
+
# Written as a row whose count column is the literal `exempt`:
|
|
97
|
+
#
|
|
98
|
+
# rule region count note
|
|
99
|
+
# 38 plugins/afm/framework-template/tools/afm-lang.sh exempt the accented alternation IS the detector
|
|
100
|
+
# 37 docs/legacy exempt vendored, not ours to rewrite
|
|
101
|
+
#
|
|
102
|
+
# `region` is a path prefix that must end at a path boundary — the same rule `ratchet_region`
|
|
103
|
+
# applies, and for the same reason: `docs/research-extra` must not spend `docs/research`'s row.
|
|
104
|
+
# A trailing `/*` is tolerated and means the same as the bare directory.
|
|
105
|
+
ratchet_exempt() {
|
|
106
|
+
local rule="$1" path="$2" f="${RATCHET:-}"
|
|
107
|
+
[ -n "$f" ] && [ -f "$f" ] || return 1
|
|
108
|
+
awk -F'\t' -v r="$rule" -v p="$path" '
|
|
109
|
+
NR>1 && $1==r && tolower($3)=="exempt" {
|
|
110
|
+
reg = $2
|
|
111
|
+
sub(/\/\*+$/, "", reg)
|
|
112
|
+
sub(/\/+$/, "", reg)
|
|
113
|
+
if (reg == "") next
|
|
114
|
+
if (index(p, reg) == 1 \
|
|
115
|
+
&& (length(p) == length(reg) || substr(p, length(reg) + 1, 1) == "/")) { hit=1; exit }
|
|
116
|
+
}
|
|
117
|
+
END { exit !hit }
|
|
118
|
+
' "$f"
|
|
119
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scope of a runner: which files to look at, in each mode.
|
|
3
|
+
#
|
|
4
|
+
# `staged` looks at the INDEX, because the index is what becomes the commit. Failing on a
|
|
5
|
+
# dirty file that is not staged is a false positive, and a gate with false positives is a
|
|
6
|
+
# gate people work around.
|
|
7
|
+
#
|
|
8
|
+
# Declared limit: the CONTENT rules read the file on disk, not the blob in the index. In the
|
|
9
|
+
# normal flow (edit, add, commit) the two are the same. Under a partial `git add -p` the gate
|
|
10
|
+
# can report a line that is not staged. We prefer the rare false positive to the silent false
|
|
11
|
+
# negative, but this is a known imprecision, not an oversight.
|
|
12
|
+
#
|
|
13
|
+
# Sourceable, not executable: `. "${AFM_GATE_LIB}/scope.sh"`.
|
|
14
|
+
|
|
15
|
+
# UNTRACKED files are part of the working tree, so `worktree` must include them. Until
|
|
16
|
+
# 3.9.0-rc.8 it did not: `git ls-files` lists what git tracks and `git diff HEAD` compares
|
|
17
|
+
# against what git tracks, and a file created one minute ago is in neither. A brand new secret
|
|
18
|
+
# (rule 13) or a brand new document in the wrong language (rule 37) therefore passed at exit 0,
|
|
19
|
+
# and exit 0 from a gate reads as "I looked and it is clean", never as "I did not look". The
|
|
20
|
+
# gate failed in the direction that looks like success.
|
|
21
|
+
#
|
|
22
|
+
# `--exclude-standard` keeps .gitignore out: an ignored file is a deliberate stance, not debt.
|
|
23
|
+
# `staged` deliberately does NOT include them — a file nobody added is not entering this commit,
|
|
24
|
+
# and failing on it would be the false positive that makes people work around the gate.
|
|
25
|
+
|
|
26
|
+
# scope_untracked [pathspec...] -> untracked, non-ignored paths
|
|
27
|
+
scope_untracked() { git ls-files --others --exclude-standard -- "$@"; }
|
|
28
|
+
|
|
29
|
+
# scope_files <mode> [pathspec...] -> paths relative to the repository root
|
|
30
|
+
scope_files() {
|
|
31
|
+
local mode="$1"; shift
|
|
32
|
+
case "$mode" in
|
|
33
|
+
worktree) { git ls-files -- "$@"; scope_untracked "$@"; } | sort -u ;;
|
|
34
|
+
*) git diff --cached --name-only --diff-filter=ACMR -- "$@" ;;
|
|
35
|
+
esac
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# scope_diff <mode> [pathspec...] -> the added content only (new lines)
|
|
39
|
+
#
|
|
40
|
+
# For an untracked file there is no blob to diff against, so we synthesize one with
|
|
41
|
+
# `git diff --no-index /dev/null <file>`. That form is read-only. `git add -N` would give the
|
|
42
|
+
# same output by writing to the index, and a gate does not write. `--no-index` exits 1 whenever
|
|
43
|
+
# it finds a difference, which is always here, so its status is discarded on purpose.
|
|
44
|
+
scope_diff() {
|
|
45
|
+
local mode="$1"; shift
|
|
46
|
+
case "$mode" in
|
|
47
|
+
worktree)
|
|
48
|
+
git diff HEAD -- "$@"
|
|
49
|
+
local f
|
|
50
|
+
while IFS= read -r f; do
|
|
51
|
+
[ -f "$f" ] || continue
|
|
52
|
+
git diff --no-index -- /dev/null "$f" || true
|
|
53
|
+
done < <(scope_untracked "$@")
|
|
54
|
+
;;
|
|
55
|
+
*) git diff --cached -- "$@" ;;
|
|
56
|
+
esac
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# scope_all [pathspec...] -> EVERY tracked file.
|
|
60
|
+
#
|
|
61
|
+
# It exists because there are two classes of rule and they cannot share a scope:
|
|
62
|
+
#
|
|
63
|
+
# * a DIFF rule (a plaintext secret, a version bump, a test that must run) speaks about what
|
|
64
|
+
# enters the commit. Its scope is the index. Its budget is normally 0: a new secret is
|
|
65
|
+
# never "inside the quota". A measured exception exists — a rule can hold a small budget in
|
|
66
|
+
# one region when the gate itself found pre-existing debt there.
|
|
67
|
+
#
|
|
68
|
+
# * a COUNT rule (a path convention, the language of an identifier, a single source) speaks
|
|
69
|
+
# about a total of the repository, compared against the ratchet. Its scope is the whole
|
|
70
|
+
# repository, ALWAYS, even under `--staged`.
|
|
71
|
+
#
|
|
72
|
+
# Counting a count rule over the index is a silent FALSE NEGATIVE. With a budget of 7, a commit
|
|
73
|
+
# that adds 1 new violation counts 1 <= 7 and passes clean. The ratchet means "it did not grow"
|
|
74
|
+
# only when it measures the same total it recorded.
|
|
75
|
+
#
|
|
76
|
+
# It takes the mode from `AFM_GATE_MODE`, exported by the aggregator, because its signature has
|
|
77
|
+
# to stay `scope_all [pathspec...]`: every runner in this repository and every runner a consumer
|
|
78
|
+
# already wrote calls it that way. Under `--worktree` it also counts the untracked files, for the
|
|
79
|
+
# same reason `scope_files` does. Default `staged` keeps a runner called directly by hand honest.
|
|
80
|
+
scope_all() {
|
|
81
|
+
case "${AFM_GATE_MODE:-staged}" in
|
|
82
|
+
worktree) { git ls-files -- "$@"; scope_untracked "$@"; } | sort -u ;;
|
|
83
|
+
*) git ls-files -- "$@" ;;
|
|
84
|
+
esac
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rule region count note
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Rule runners — the PROJECT's own
|
|
2
|
+
|
|
3
|
+
This directory is **empty on purpose**, and since 3.20.0 that statement is narrower than it used
|
|
4
|
+
to be. Read [`../core-rules/README.md`](../core-rules/README.md) beside it.
|
|
5
|
+
|
|
6
|
+
There are **two classes of rule**, and conflating them was a real defect (ADR 0021).
|
|
7
|
+
|
|
8
|
+
A **project rule** is about this project's own CODE — a line limit, a layering constraint, a
|
|
9
|
+
naming convention. It belongs here, and the plugin ships none of them. The argument holds
|
|
10
|
+
exactly as it always did: a consumer that inherits a rule nobody chose bypasses it first.
|
|
11
|
+
|
|
12
|
+
An **artifact rule** is about the `docs/` that AFM itself materializes — English prose, no
|
|
13
|
+
absolute path, the OKF frontmatter contract, a versioned runner behind every 0/1 trigger.
|
|
14
|
+
Nobody "chooses" those. They are what an AFM instance IS, and they ship in `../core-rules/`
|
|
15
|
+
under the `PLUGIN-` namespace. Until 3.20.0 they existed only in the plugin's own repository,
|
|
16
|
+
so every consumer received an `afm.md` whose hard rules stopped at 21.
|
|
17
|
+
|
|
18
|
+
## Write one
|
|
19
|
+
|
|
20
|
+
Start from [`guardrail-runner.template.md`](../../../templates/guardrail-runner.template.md)
|
|
21
|
+
and follow [`ops/rule.md` § Install the gate](../../../ops/rule.md).
|
|
22
|
+
|
|
23
|
+
Name it `<NN>-<slug>.sh`, where `NN` is the rule number in `docs/afm.md` § 3. The aggregator
|
|
24
|
+
reads that number from the filename, and `--audit` uses it to cross-check the rule and the
|
|
25
|
+
runner in both directions. A runner whose number matches no rule is reported as an orphan.
|
|
26
|
+
|
|
27
|
+
## Contract
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
argv[1] : "staged" | "worktree"
|
|
31
|
+
env RATCHET : path of the ratchet TSV
|
|
32
|
+
env AFM_GATE_LIB : directory holding scope.sh and ratchet.sh
|
|
33
|
+
stdout : one line per finding — "<NN>\t<path>[:<line>]\t<message>"
|
|
34
|
+
exit 0 compliant · 1 violated · 2 COULD NOT decide
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Resolve the library through `AFM_GATE_LIB`, never through a fixed path:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
. "${AFM_GATE_LIB:?}/scope.sh"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The runner does not know where the gate lives. That is what lets the same runner work in a
|
|
44
|
+
project that keeps the gate under `docs/tools/gate/` and in one that keeps it somewhere else.
|
|
45
|
+
|
|
46
|
+
## Before you turn a rule on
|
|
47
|
+
|
|
48
|
+
Run `guardrails.sh --baseline`. It measures HEAD and records what it finds as declared debt, so
|
|
49
|
+
the gate starts green in a repository that already breaks the rule. A gate that starts red is
|
|
50
|
+
disabled the next day, and it takes every other rule in the same aggregator with it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@tenkara/afm-linux-arm64",
|
|
3
|
+
"version": "4.0.0-alpha.6",
|
|
4
|
+
"description": "The afm binary (CLI and MCP server) for linux-arm64.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"os": [
|
|
10
|
+
"linux"
|
|
11
|
+
],
|
|
12
|
+
"cpu": [
|
|
13
|
+
"arm64"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"plugin",
|
|
18
|
+
"content"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "AFM hooks",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"PreToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "write_to_file|replace_file_content|multi_replace_file_content|run_command",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "afm hook PreToolUse",
|
|
11
|
+
"timeout": 5
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"PostToolUse": [
|
|
17
|
+
{
|
|
18
|
+
"matcher": "write_to_file|replace_file_content|multi_replace_file_content|run_command",
|
|
19
|
+
"hooks": [
|
|
20
|
+
{
|
|
21
|
+
"type": "command",
|
|
22
|
+
"command": "afm hook PostToolUse",
|
|
23
|
+
"timeout": 5
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: afm-adr
|
|
3
|
+
description: |
|
|
4
|
+
Writes an Architecture Decision Record to `<consumer>/docs/adr/NNNN-slug.md`. It records context, decision, alternatives and consequence. In a refactored project, write a retroactive ADR only when it is load-bearing.
|
|
5
|
+
Activates: `afm-adr`, "create ADR", "architectural decision", "record this architecture choice".
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# AFM — ADR
|
|
9
|
+
|
|
10
|
+
> **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
|
|
11
|
+
|
|
12
|
+
Atomic skill of `afm`. The canonical procedure is the op `afm path ops/adr`. The CLI owns the number, the
|
|
13
|
+
template, the frontmatter, the supersede link and the index. This skill keeps only the judgment.
|
|
14
|
+
|
|
15
|
+
## When NOT to activate
|
|
16
|
+
|
|
17
|
+
This is a disambiguation for a skill that already activated. It does not control activation itself. A
|
|
18
|
+
counterintuitive surprise belongs in `afm-gotcha`. A rule with an executable trigger belongs in `afm-rule`.
|
|
19
|
+
|
|
20
|
+
## Pre-check
|
|
21
|
+
|
|
22
|
+
`afm preflight`. Exit 3 means there is no instance: stop and suggest `afm-init` (greenfield) or
|
|
23
|
+
`afm-refactor` (brownfield) first. Exit 4 means the shipped content does not resolve: report it, do not
|
|
24
|
+
fall back silently.
|
|
25
|
+
|
|
26
|
+
## Procedure (judgment only)
|
|
27
|
+
|
|
28
|
+
1. Ask the status: `afm ask adr`. Collect the title, context, decision, alternatives, consequence and refs
|
|
29
|
+
as free text, because these fields carry intent.
|
|
30
|
+
2. Run `afm adr new <slug> --title "<title>" --description "<one sentence>"`, with `--supersedes N` when
|
|
31
|
+
the decision replaces ADR N.
|
|
32
|
+
3. Fill the sections of the printed file with what the operator said, and leave the rest `[TBD]`. Report the
|
|
33
|
+
path (#6).
|
|
34
|
+
|
|
35
|
+
The "Additional step in a refactored project" in the op applies ONLY when `afm-refactor` adopted the
|
|
36
|
+
project (signal: `afm.md` § 3.1 forward-only exists). A greenfield project forbids a retroactive ADR.
|
|
37
|
+
|
|
38
|
+
## Invariant principles
|
|
39
|
+
|
|
40
|
+
Single canonical source: `afm path PRINCIPLES`. Universals §1-8 apply. Additional #9 (a retroactive ADR is
|
|
41
|
+
load-bearing-only) applies ONLY in a refactored project.
|
|
42
|
+
|
|
43
|
+
Passive mode (`afm-reconcile`) is always on and can propose `afm-adr` when it detects a "novel structural
|
|
44
|
+
decision" signal. This skill covers only the active creation.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: afm-analyze
|
|
3
|
+
description: |
|
|
4
|
+
Cross-checks one feature against the instance. It compares `docs/features/NNN-*/{spec,plan,tasks}.md` with prd, ust, ach, afm § 3 and PRINCIPLES. It reports findings by severity in chat and writes no file.
|
|
5
|
+
Activates: `afm-analyze [<feature>]`, "audit feature", "check coverage", "validate consistency before implementation".
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# AFM — Analyze
|
|
9
|
+
|
|
10
|
+
> **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
|
|
11
|
+
|
|
12
|
+
An atomic skill. The procedure lives in the op: read it with `afm path ops/analyze.md`, end to end, and follow it exactly.
|
|
13
|
+
|
|
14
|
+
## When NOT to activate
|
|
15
|
+
|
|
16
|
+
Use `afm-clarify` to resolve markers interactively. Use `afm-deliver` for end-to-end delivery: it runs analyze underneath, in gating mode. Use `afm-diagnose` for the health of the whole doc set.
|
|
17
|
+
|
|
18
|
+
## Pre-check
|
|
19
|
+
|
|
20
|
+
Run `afm preflight` (exit 3: no `docs/.afm-version`, stop and suggest `afm-init` or `afm-refactor`).
|
|
21
|
+
|
|
22
|
+
## Procedure
|
|
23
|
+
|
|
24
|
+
Start from the mechanical findings: `afm feature lint <NNN> --json` covers the passes A, C, D, E and the structure of `tasks.md`. Add the judgment on top (constitution violations, duplication, the semantic side of inconsistency and ambiguity), build the coverage table, assign severities by the matrix, and report in chat with metrics. Close with the `remediation` question (`afm ask analyze`). Gating mode sorts findings into buckets instead of reporting.
|
|
25
|
+
|
|
26
|
+
## Invariant principles
|
|
27
|
+
|
|
28
|
+
Single canonical source: `afm path PRINCIPLES.md`. Universals §1-8 apply, especially #2 (detection is an autonomous scan, not a question), #5 (no finding without evidence) and #6 (analyze is read-only: it reports in chat, writes no file, and remediates only on request).
|
|
29
|
+
|
|
30
|
+
## Continuous doctrine
|
|
31
|
+
|
|
32
|
+
Passive mode is the always-on `reconcile` skill. This skill covers only active auditing.
|
|
33
|
+
|
|
34
|
+
## Version
|
|
35
|
+
|
|
36
|
+
`afm version`. History: `afm path CHANGELOG.md`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: afm-clarify
|
|
3
|
+
description: |
|
|
4
|
+
Scans one feature for ambiguity. It asks the irreducible questions, recommendation first, and writes the answers to `spec.md § 7 Clarifications`.
|
|
5
|
+
Activates: `afm-clarify [<feature>]`, "resolve markers", "clear up ambiguity in feature X", "clarify the spec".
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# AFM — Clarify
|
|
9
|
+
|
|
10
|
+
> **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
|
|
11
|
+
|
|
12
|
+
An atomic skill. The procedure lives in the op: read it with `afm path ops/clarify.md`, end to end, and follow it literally.
|
|
13
|
+
|
|
14
|
+
## When NOT to activate
|
|
15
|
+
|
|
16
|
+
Use `afm-analyze` for a read-only, multi-artifact validation that does not resolve markers. Use `afm-deliver` for end-to-end delivery: it calls clarify underneath when needed.
|
|
17
|
+
|
|
18
|
+
## Pre-check
|
|
19
|
+
|
|
20
|
+
Run `afm preflight` (exit 3: no `docs/.afm-version`, stop and suggest `afm-init` or `afm-refactor`). `afm feature status <NNN> --json` lists the open markers of a feature in `blockers`.
|
|
21
|
+
|
|
22
|
+
## Procedure
|
|
23
|
+
|
|
24
|
+
Judgment only: a 9-category scan of the spec, a prioritized queue (at most 5 questions), auto-resolution from the code and docs before any question, then one recommendation-first question per turn. The questions come from the spec, so they are dynamic; the one fixed decision (`underspecified`) is in `afm ask clarify`. Integrate each answer into `spec.md` at once, remove the marker, and finish with the coverage summary.
|
|
25
|
+
|
|
26
|
+
## Invariant principles
|
|
27
|
+
|
|
28
|
+
Single canonical source: `afm path PRINCIPLES.md`. Universals §1-8 apply. #2 is discover-first: resolve each question yourself through the scan or the docs, then ask. #4 says `[TBD]` beats invention, and a recommendation is a rejectable hypothesis, not a guess. #6 is autonomy-first: write the integration into spec § 7 and report. #8 is recommendation-first questions.
|
|
29
|
+
|
|
30
|
+
## Continuous doctrine
|
|
31
|
+
|
|
32
|
+
Passive mode is the always-on `reconcile` skill. This skill covers only the active resolution of ambiguity.
|
|
33
|
+
|
|
34
|
+
## Version
|
|
35
|
+
|
|
36
|
+
`afm version`. History: `afm path CHANGELOG.md`.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: afm-deliver
|
|
3
|
+
description: |
|
|
4
|
+
Delivers one feature from discovery to reconciliation. It takes one recorded approval (`afm approve`), writes spec, plan and tasks, then runs each task as RED, GREEN, REFACTOR, COMMIT. It resumes from the correct phase when those files exist. Orchestrator: it composes specify, plan, tasks, clarify, analyze and reconcile. External research is not an AFM op; it routes to nasca.
|
|
5
|
+
Activates: `afm-deliver [<feature>]`, "build feature X", "implement X end to end", "the tasks already exist, run them", "continue the delivery of feature N".
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# AFM — Deliver (orchestrated autonomous delivery)
|
|
9
|
+
|
|
10
|
+
> **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
|
|
11
|
+
|
|
12
|
+
The **orchestrator** of the delivery loop. One request takes "build feature X" from discovery to a delivered feature. The procedure lives in the op: read it with `afm path ops/deliver.md`, end to end, and follow it literally. It does not duplicate the sub-steps: it reads and follows `specify`, `plan`, `tasks`, `clarify`, `analyze` and `reconcile` through a suppression contract.
|
|
13
|
+
|
|
14
|
+
## When NOT to activate
|
|
15
|
+
|
|
16
|
+
An isolated step ("just open the spec", "just generate the tasks") is the matching atomic skill. A trivial change goes straight to the `afm.md § 2` loop.
|
|
17
|
+
|
|
18
|
+
## Pre-check
|
|
19
|
+
|
|
20
|
+
Run `afm preflight`. Exit 0 continues. Exit 3 means no `docs/.afm-version`: stop and suggest `afm-init` (greenfield) or `afm-refactor` (brownfield). Exit 4 or 5: report the line it printed. Do not fall back silently.
|
|
21
|
+
|
|
22
|
+
## Procedure
|
|
23
|
+
|
|
24
|
+
The CLI holds the state; the skill keeps only the judgment. Drive the loop with `afm feature status <NNN> --json`, which returns the phase to resume at, the next task and the blockers.
|
|
25
|
+
|
|
26
|
+
- **Discovery:** scan scoped to the feature, external unknowns through the nasca research flow (`nasca-research`, or `nasca-campaign` for a deep study; ask the operator if nasca is missing), unknowns sorted into resolved-by-scan, researchable and irreducible. One batched, recommendation-first round, capped by the protocol (`afm-protocol`).
|
|
27
|
+
- **Draft:** spec, plan and tasks, verified with `afm feature lint <NNN>` plus the judgment passes of `analyze`.
|
|
28
|
+
- **Gate:** the earned questions plus the `outcome` question (`afm ask deliver`), then `afm approve <NNN>`. An explicit "go" skips the questions, never the record.
|
|
29
|
+
- **Execute:** each task is one `core/afm.md` § 2 cycle, checked with `afm gate --staged`, then `afm task done <NNN> <Txxx>`. Halt on a non-`[P]` failure after the capped retry.
|
|
30
|
+
- **Reconcile:** § 8.5 plus a passive sweep.
|
|
31
|
+
|
|
32
|
+
If artifacts already exist, resume from the correct phase, including "tasks already exist, run from the first `[ ]`".
|
|
33
|
+
|
|
34
|
+
## Invariant principles
|
|
35
|
+
|
|
36
|
+
Single canonical source: `afm path PRINCIPLES.md`. Universals §1-8 apply. The backbone is **#2** (discover-first: resolve unknowns by scan and research, ask only the irreducible part, never guess product or criteria) and **#6** (autonomy-first: approval at checkpoint granularity, the consolidated plan IS the proposal).
|
|
37
|
+
|
|
38
|
+
## Continuous doctrine
|
|
39
|
+
|
|
40
|
+
Passive mode is the always-on `reconcile` skill. It runs in parallel with `deliver` and takes the closing turn of Phase 4.
|
|
41
|
+
|
|
42
|
+
## Version
|
|
43
|
+
|
|
44
|
+
`afm version`. History: `afm path CHANGELOG.md`.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: afm-diagnose
|
|
3
|
+
description: |
|
|
4
|
+
Scans the health of the local docs on 7 axes. The axes are dead reference, stale marker, instance-to-plugin drift, broken ADR chain, dead rule trigger, inflation or orphan, and cross-doc contradiction. It reads only `<consumer>/docs/` and returns evidence as `command + file:line`. It proposes no edit.
|
|
5
|
+
Activates: `afm-diagnose`, "diagnose the methodology", "what is broken in the docs?", "run the AFM health check".
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# AFM — Diagnose (mechanical weakness-mining, read-only)
|
|
10
|
+
|
|
11
|
+
This is a mechanism skill of AFM. The canonical procedure is the op file that `afm path ops/diagnose.md` prints.
|
|
12
|
+
|
|
13
|
+
> **Carve-out (does not overlap existing ops):** `analyze` audits **one feature**. `diagnose` audits the **whole instance** of `/docs/`. `reflect` uses **behavioral** evidence, where the agent stumbled. `diagnose` is **static**, based on filesystem and git. `evolve` is the **orchestrator**. It combines `diagnose` (mechanical evidence) with `reflect` (behavioral evidence) into one proposal and one gate. `diagnose` is **read-only**. It never proposes an edit or opens a gate.
|
|
14
|
+
|
|
15
|
+
## When NOT to activate
|
|
16
|
+
|
|
17
|
+
This section disambiguates for a skill that already activated. It does not define activation, so it stays in the body, not in the resident listing.
|
|
18
|
+
|
|
19
|
+
Validation of ONE feature is `afm-analyze`. Behavioral friction, where a doc misguided the agent at apply time, is `afm-reflect`. Proposing or applying a fix is `afm-evolve` (the full cycle, which includes diagnose).
|
|
20
|
+
|
|
21
|
+
## Pre-check
|
|
22
|
+
|
|
23
|
+
1. **Does `<consumer>/docs/.afm-version` exist?** If not, stop. Suggest `afm-init` (greenfield) or `afm-refactor` (brownfield) first.
|
|
24
|
+
2. **Does `afm preflight` exit 0?** A non-zero exit says why (docs not bootstrapped, content unresolved, pin mismatch). Report it to the user. Do not fall back silently.
|
|
25
|
+
|
|
26
|
+
## Procedure
|
|
27
|
+
|
|
28
|
+
Read the op (`afm path ops/diagnose.md`) end to end. Follow it literally.
|
|
29
|
+
|
|
30
|
+
The procedure has two phases. **Phase 0 — Scan** is gap-driven. Run `afm doctor --json`: it checks A1-A6, staleness and dead gotchas mechanically, and every finding carries a command plus `file:line`. You add A7 yourself. All 7 axes are checked, every time.
|
|
31
|
+
|
|
32
|
+
Axis A7, cross-doc contradiction, uses a paired read. That read applies an anti-false-positive layer test. An axis that cannot be covered becomes "not-covered: <reason>".
|
|
33
|
+
|
|
34
|
+
**Phase 1 — Diagnosis** has the ANALYZER role. **It must not propose an edit.** The signature is φ = `(symptom, causal-status-of-behavior, mechanism)`.
|
|
35
|
+
|
|
36
|
+
Phase 1 requires rival mechanisms, for example "agent non-compliant" versus "target legitimately removed". It applies an addressability filter and a self-sufficient severity matrix.
|
|
37
|
+
|
|
38
|
+
**Output** is an evidence bundle ordered by severity. Run à la carte, the skill prints the report to chat. It does not write to disk. It opens no gate. Under `evolve`, it feeds the proposal instead.
|
|
39
|
+
|
|
40
|
+
This scan has no cooldown. It is read-only and cheap. The cycle's cooldown belongs to the orchestrator.
|
|
41
|
+
|
|
42
|
+
## Invariant principles
|
|
43
|
+
|
|
44
|
+
Single canonical source: `PRINCIPLES.md` (`afm path PRINCIPLES.md`). Universals §1-8 apply. Especially relevant: #2 (discover-first — diagnose with evidence before any proposal). Also #3 (mechanize-or-demote — the checks must pass the test they audit). Also #5 (no finding without evidence external to the text).
|
|
45
|
+
|
|
46
|
+
**No new principle here.** `diagnose` is a composed materialization. The doctrine lives in the op file.
|
|
47
|
+
|
|
48
|
+
## Version
|
|
49
|
+
|
|
50
|
+
Content version: `afm version` prints it.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: afm-evolve
|
|
3
|
+
description: |
|
|
4
|
+
Runs the full self-evolution cycle over `<consumer>/docs/`. It combines diagnose and reflect, adds generalize, restructure, update and remediate on demand, and delivers one package through one gate (`afm ask evolve`). It conducts the mechanisms and never touches the plugin.
|
|
5
|
+
Activates: `afm-evolve`, "audit the methodology", "the docs are a mess", "the rules no longer trigger", "clean up the docs", "reorganize the docs".
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# AFM — Evolve (orchestrator for the self-evolution cycle)
|
|
10
|
+
|
|
11
|
+
> **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
|
|
12
|
+
|
|
13
|
+
This is an orchestrator skill of AFM. The canonical procedure is the op file that `afm path ops/evolve.md` prints.
|
|
14
|
+
|
|
15
|
+
> **Conductor, not executor** (same pattern as `deliver`). `evolve` owns 3 things that no mechanism owns alone: (1) the evidence-collection driver for the whole instance (combines `diagnose` + `reflect`), (2) the split into buckets plus the single gate, (3) the apply driver, post-conditions, and the log/lineage record. It reads and follows everything else from the mechanisms, through a suppression contract.
|
|
16
|
+
>
|
|
17
|
+
> **Carve-outs (it does not override existing ops):** `diagnose`/`reflect`/`generalize`/`restructure` are the **mechanisms** (each runs à la carte). `analyze` audits **one feature** (read-only). `reconcile` proposes **one single edit** at the end of a turn. `promote` **records** a finding meant for the plugin (it does not edit the plugin). `evolve` is the **cycle** that chains the mechanisms with one gate and one log — the level no other op covers.
|
|
18
|
+
|
|
19
|
+
## When NOT to activate
|
|
20
|
+
|
|
21
|
+
This section helps someone who already activated the skill decide if it fits. It is not an activation trigger, so it stays in the body, not in the resident listing.
|
|
22
|
+
|
|
23
|
+
Use a single mechanism instead when the need is narrow. A report is `afm-diagnose`. Refining a doc that misled you is `afm-reflect`. Merging gotchas is `afm-generalize`.
|
|
24
|
+
|
|
25
|
+
A structure or staleness check is `afm-restructure`. A version bump is `afm-update`. `reconcile` proposes a one-off additive signal (new gotcha, rule, or ADR) directly.
|
|
26
|
+
|
|
27
|
+
A feature-level check is `afm-analyze` or `afm-deliver`. Recording a finding for the plugin is `afm-promote`.
|
|
28
|
+
|
|
29
|
+
## Pre-check
|
|
30
|
+
|
|
31
|
+
1. **Does `<consumer>/docs/.afm-version` exist?** If not, stop. Suggest `afm-init` (greenfield) or `afm-refactor` (brownfield) first.
|
|
32
|
+
2. **Does `afm preflight` exit 0?** A non-zero exit says why (docs not bootstrapped, content unresolved, pin mismatch). Report it to the user. Do not fall back silently.
|
|
33
|
+
3. **Does `afm evolve gate-check` allow a cycle?** Pass `--symptom "<what the user said>"` when there is one. Exit 0 runs; exit 1 is the cooldown refusing (no new signal inside the window), so say so and stop; 3 and 4 mean not a project or undecided.
|
|
34
|
+
|
|
35
|
+
## Procedure
|
|
36
|
+
|
|
37
|
+
Read the op (`afm path ops/evolve.md`) end to end and follow it exactly. The op has 5 phases.
|
|
38
|
+
|
|
39
|
+
**Phase 1 — Evidence collection.** Combine two sources through the suppression contract. `diagnose` is the mechanical scan of the 7 axes (`afm doctor --json` plus the A7 reading), read-only. `reflect` is behavioral evidence of docs that misled the agent. Run `generalize`/`restructure` on demand, when their own trigger signal fires. An empty diagnosis counts as a valid result.
|
|
40
|
+
|
|
41
|
+
**Phase 2 — Proposal.** This phase plays the IMPROVER role. Apply the op's **shared proposal discipline**: retrieval before create, then a dry run before the gate. Split the proposals into 3 buckets.
|
|
42
|
+
|
|
43
|
+
A non-restorable change always goes through the gate. It never invents its own target. Decide here whether to downgrade, archive, or merge an artifact. Cap bucket (b) at 8 items per cycle (`afm evolve gate-check --critical <n> --high <n> --medium <n> --low <n>` computes what to ask and what to stage). Recurring failures come from `afm failures promotable`.
|
|
44
|
+
|
|
45
|
+
**Phase 3 — Single gate.** Ask item by item with `afm ask evolve --scope <item-slug>`, at most 4 questions per call. This gate is never skippable — there is no "go" shortcut.
|
|
46
|
+
|
|
47
|
+
**Phase 4 — Apply and post-conditions.** Re-run `afm doctor --json`, `afm index --check` and `afm detect --for <file>` over the touched docs, and confirm the content root is clean (`git status`, or a timestamp check with no git).
|
|
48
|
+
|
|
49
|
+
**Phase 5 — Log.** Record the cycle in `docs/evolution/NNN-<date>.md`. Suggest `promote` when a finding generalizes beyond this instance.
|
|
50
|
+
|
|
51
|
+
Consult the rubric `rubrics/when-to-evolve-methodology.md` (`afm path rubrics/when-to-evolve-methodology.md`) to decide between a full cycle, a single mechanism, or a one-off fix.
|
|
52
|
+
|
|
53
|
+
## Invariant principles
|
|
54
|
+
|
|
55
|
+
The single canonical source is `PRINCIPLES.md` (`afm path PRINCIPLES.md`). Universals §1-8 apply. #2 (discover-first) applies: diagnose with evidence before you propose. #3 (mechanize-or-downgrade) applies too: evolve's own signals must pass the same test evolve applies to others.
|
|
56
|
+
|
|
57
|
+
#8 (earned questions, recommendation-first) also applies to this skill.
|
|
58
|
+
|
|
59
|
+
#6 (autonomy-first with self-check) follows the pattern *produce → self-check → report*. The gate is the load-bearing residue. Bright lines for #7 plugin, deletion, and push still apply.
|
|
60
|
+
|
|
61
|
+
**No new principle here** — evolve is a composed materialization. The doctrine lives in the op and the rubric.
|
|
62
|
+
|
|
63
|
+
## Continuous doctrine
|
|
64
|
+
|
|
65
|
+
The `reconcile` skill owns passive mode, and it is always on. Reconcile may **suggest** `afm-evolve` when it detects a Class B signal. That signal is a lived contradiction between docs, or perceived inflation at apply time. Only the user can invoke the full cycle.
|
|
66
|
+
|
|
67
|
+
## Version
|
|
68
|
+
|
|
69
|
+
Content version: `afm version` prints it.
|