@afokapu/atdd-bun 0.6.1 → 0.7.0

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.
Files changed (97) hide show
  1. package/README.md +89 -484
  2. package/conventions/coder.bun/coder.bun.telemetry-forbidden-properties.convention.yaml +51 -0
  3. package/conventions/coder.bun/coder.bun.telemetry-implementation-binding.convention.yaml +45 -0
  4. package/conventions/coder.bun/coder.bun.telemetry-raw-string-emit.convention.yaml +53 -0
  5. package/conventions/coder.bun/coder.bun.telemetry-source-binding.convention.yaml +48 -0
  6. package/conventions/coder.bun/coder.bun.telemetry-vendor-sdk.convention.yaml +51 -0
  7. package/conventions/planner.telemetry/planner.telemetry.acceptance-decision.convention.yaml +58 -0
  8. package/conventions/planner.telemetry/planner.telemetry.logical-ownership.convention.yaml +50 -0
  9. package/conventions/planner.telemetry/planner.telemetry.metric-cardinality.convention.yaml +47 -0
  10. package/conventions/planner.telemetry/planner.telemetry.tracking-plan-schema.convention.yaml +75 -0
  11. package/conventions/tester.bun/tester.bun.telemetry-captured-sink.convention.yaml +47 -0
  12. package/conventions/tester.bun/tester.bun.telemetry-identity-assertion.convention.yaml +44 -0
  13. package/conventions/tester.bun/tester.bun.telemetry-required-item-coverage.convention.yaml +44 -0
  14. package/conventions/tester.bun/tester.bun.telemetry-test-binding.convention.yaml +52 -0
  15. package/conventions/tester.bun/tester.bun.telemetry-timing-semantics.convention.yaml +53 -0
  16. package/detectors/atdd_traceability_closure/detect.mjs +31 -16
  17. package/detectors/bun_telemetry_code/atdd.implementation.yaml +24 -0
  18. package/detectors/bun_telemetry_code/calls.mjs +104 -0
  19. package/detectors/bun_telemetry_code/checks/t_forbidden_properties.mjs +46 -0
  20. package/detectors/bun_telemetry_code/checks/t_implementation_binding.mjs +41 -0
  21. package/detectors/bun_telemetry_code/checks/t_raw_string_emit.mjs +34 -0
  22. package/detectors/bun_telemetry_code/checks/t_source_binding.mjs +48 -0
  23. package/detectors/bun_telemetry_code/checks/t_vendor_sdk.mjs +38 -0
  24. package/detectors/bun_telemetry_code/detect.mjs +50 -0
  25. package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/E001.yaml +10 -0
  26. package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/E002.yaml +7 -0
  27. package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/_commons.yaml +6 -0
  28. package/detectors/bun_telemetry_code/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  29. package/detectors/bun_telemetry_code/fixtures/clean/src/wagons/commons/features/ingress/infrastructure/otel-adapter.ts +8 -0
  30. package/detectors/bun_telemetry_code/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  31. package/detectors/bun_telemetry_code/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  32. package/detectors/bun_telemetry_code/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
  33. package/detectors/bun_telemetry_code/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.test.ts +9 -0
  34. package/detectors/bun_telemetry_code/fixtures/dirty/plan/commons/E001.yaml +8 -0
  35. package/detectors/bun_telemetry_code/fixtures/dirty/plan/commons/_commons.yaml +6 -0
  36. package/detectors/bun_telemetry_code/fixtures/dirty/src/wagons/commons/features/ingress/domain/accept-response.ts +13 -0
  37. package/detectors/bun_telemetry_code/fixtures/dirty/src/wagons/commons/features/ingress/domain/tracing.ts +3 -0
  38. package/detectors/bun_telemetry_code/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  39. package/detectors/bun_telemetry_code/fixtures/dirty/tests/wagons/commons/features/ingress/unit/probe.test.ts +7 -0
  40. package/detectors/bun_telemetry_code/registry.mjs +34 -0
  41. package/detectors/bun_telemetry_test/_shared.mjs +92 -0
  42. package/detectors/bun_telemetry_test/atdd.implementation.yaml +24 -0
  43. package/detectors/bun_telemetry_test/checks/t_captured_sink.mjs +35 -0
  44. package/detectors/bun_telemetry_test/checks/t_identity_assertion.mjs +45 -0
  45. package/detectors/bun_telemetry_test/checks/t_required_item_coverage.mjs +37 -0
  46. package/detectors/bun_telemetry_test/checks/t_test_binding.mjs +56 -0
  47. package/detectors/bun_telemetry_test/checks/t_timing_semantics.mjs +51 -0
  48. package/detectors/bun_telemetry_test/detect.mjs +50 -0
  49. package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/E001.yaml +10 -0
  50. package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/E002.yaml +7 -0
  51. package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/_commons.yaml +6 -0
  52. package/detectors/bun_telemetry_test/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  53. package/detectors/bun_telemetry_test/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  54. package/detectors/bun_telemetry_test/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  55. package/detectors/bun_telemetry_test/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
  56. package/detectors/bun_telemetry_test/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.test.ts +7 -0
  57. package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/E001.yaml +10 -0
  58. package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/E002.yaml +7 -0
  59. package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/_commons.yaml +6 -0
  60. package/detectors/bun_telemetry_test/fixtures/dirty/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  61. package/detectors/bun_telemetry_test/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  62. package/detectors/bun_telemetry_test/fixtures/dirty/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  63. package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/dangling.telemetry.test.ts +9 -0
  64. package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/loose.telemetry.test.ts +11 -0
  65. package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/unbound.telemetry.test.ts +10 -0
  66. package/detectors/planner_telemetry_plan/atdd.implementation.yaml +22 -0
  67. package/detectors/planner_telemetry_plan/detect.mjs +9 -0
  68. package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/E001.yaml +10 -0
  69. package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/E002.yaml +7 -0
  70. package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/_commons.yaml +6 -0
  71. package/detectors/planner_telemetry_plan/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  72. package/detectors/planner_telemetry_plan/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  73. package/detectors/planner_telemetry_plan/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  74. package/detectors/planner_telemetry_plan/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
  75. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E001.yaml +9 -0
  76. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E002.yaml +4 -0
  77. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E003.yaml +7 -0
  78. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/_commons.yaml +6 -0
  79. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/extra.json +1 -0
  80. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/orphan-artifact/event.be.json +14 -0
  81. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  82. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/metric.be.duration.json +22 -0
  83. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/notes.json +1 -0
  84. package/integrity.json +96 -13
  85. package/lib/scan.mjs +37 -0
  86. package/package.json +1 -1
  87. package/planner-schemas/acceptance.schema.json +70 -1
  88. package/planner-schemas/telemetry-plan.schema.json +133 -0
  89. package/relationships.yaml +174 -0
  90. package/src/agent.ts +15 -8
  91. package/src/enforce.ts +28 -3
  92. package/src/hooks.ts +2 -2
  93. package/src/index.ts +2 -0
  94. package/src/integrity.ts +13 -5
  95. package/src/telemetry-plan.ts +223 -0
  96. package/src/topology.ts +3 -1
  97. package/templates/agents/AGENTS.block.md +5 -1
package/README.md CHANGED
@@ -1,537 +1,142 @@
1
1
  # `@afokapu/atdd-bun`
2
2
 
3
- `@afokapu/atdd-bun` is a Bun-native ATDD enforcement package for repositories
4
- that keep their plan, acceptance evidence, tests, and implementation in the
5
- same Git history. It gives developers and coding agents fast local feedback,
6
- then runs the same checks in GitHub Actions.
7
-
8
- It runs entirely on Bun from the repository's own `node_modules`: no global
9
- installation and no network access while enforcing a repository.
10
-
11
- ## What it protects
12
-
13
- The package is an enforcer, not a planner or deployment platform. It checks the
14
- relationships that a plan makes explicit:
3
+ Bun-native ATDD enforcement for repositories that keep their plan, acceptances, tests and
4
+ implementation in one Git history. It checks the links a plan makes explicit:
15
5
 
16
6
  ```text
17
7
  plan / WMBT → acceptance → Bun test → implementation
18
8
  ```
19
9
 
20
- Depending on the selected profile, it also checks the Bun code/test convention
21
- corpus, documentation policy, plan integrity, scoped planner validation, and
22
- interlocking rules.
23
-
24
- It deliberately does **not** claim that static links prove a test is
25
- semantically adequate. Where the package can establish only a structural link,
26
- its report says so. Deployment execution, cloud credentials, and release
27
- publishing remain repository-owned.
28
-
29
- ## How a repository uses it
30
-
31
- A repository normally uses the package in three places:
32
-
33
- 1. A local Bun test or command gives immediate feedback while work is underway.
34
- 2. Git hooks prevent clearly invalid commits and pushes; they are convenience,
35
- not merge authority.
36
- 3. GitHub Actions runs the full policy. Protect the resulting required check in
37
- the repository’s GitHub branch ruleset.
10
+ It runs on Bun from the repository's own `node_modules`, with no global install and no network
11
+ access. The same checks run as local tests, as Git hooks for fast feedback, and in GitHub Actions,
12
+ which is the merge gate. Every finding fails: there is no advisory mode and no ratchet baseline.
38
13
 
39
- The practical result is that an agent cannot quietly add production code without
40
- the plan/test links the repository has chosen to require, and a local hook is
41
- not the only thing standing between an invalid branch and `main`.
42
-
43
- ## Install and first use
44
-
45
- Add the package from npm as a development dependency, then bootstrap the
46
- repository once:
14
+ ## Install
47
15
 
48
16
  ```sh
49
17
  bun add -d @afokapu/atdd-bun
50
18
  bun run atdd-bun init
51
19
  ```
52
20
 
53
- `init` installs the local surfaces described below: Git
54
- [hooks](#hooks-fast-feedback-not-merge-authority), the
55
- [CI workflow](#ci-the-merge-gate), the
56
- [agent skill](#agent-skill-the-lifecycle-in-the-agents-context), and the
57
- [integrity test](#integrity-files-agents-must-not-change). Commit what it
58
- generates (`.githooks/`, `.github/workflows/atdd-bun.yml`, `.agents/`, `.claude/`,
59
- `AGENTS.md`, `atdd-bun.integrity.test.ts`) so every clone and every agent session
60
- gets them. To keep the
61
- package and the skill current automatically, see
62
- [Staying up to date](#staying-up-to-date).
21
+ `init` installs the Git hooks (`.githooks/`), the CI workflow (`.github/workflows/atdd-bun.yml`),
22
+ the agent skill (`.agents/skills/atdd/`, `.claude/skills/atdd/`), a managed block in `AGENTS.md`
23
+ and `CLAUDE.md`, and `atdd-bun.integrity.test.ts`. Commit all of them. Nothing is overwritten
24
+ without `--replace`, and adding the dependency changes nothing until you run `init`.
63
25
 
64
- Run the complete installed policy from the repository root:
26
+ Then require the workflow's job in the GitHub branch ruleset, so it gates merges.
65
27
 
66
- ```sh
67
- bun run atdd-bun all
68
- ```
28
+ ## Commands
69
29
 
70
- `all` runs every enabled packaged detector. It is the command that CI uses for
71
- the local enforcement portion of its check. Run a focused profile directly—for
72
- example, `bun run atdd-bun planner` or `bun run atdd-bun traceability`—without
73
- an extra `profile` verb. `--profile <name>` remains available for compatibility.
74
-
75
- For a focused local test, register only the policy that matters to that test:
30
+ | Command | What it does |
31
+ |---|---|
32
+ | `atdd-bun [profile ...] [--root <path>]` | Enforce the named profiles; `all` (the default) runs every activated one |
33
+ | `atdd-bun init [--replace]` | Install hooks, CI, agent files and the integrity test |
34
+ | `atdd-bun hooks <install\|uninstall\|status>` | Manage only the Git hooks |
35
+ | `atdd-bun ci <init\|status>` | Manage only the CI workflow |
36
+ | `atdd-bun agent <init\|status>` | Manage only the skills and the `AGENTS.md`/`CLAUDE.md` block |
37
+ | `atdd-bun integrity [init\|status]` | Check that the toolkit and its generated files are unmodified |
38
+ | `atdd-bun docs journeys [--check]` | Generate (or verify) the journey, interlocking and train views |
39
+ | `atdd-bun worktree <start\|finish\|status>` | Optional linked-worktree policy for agent work |
40
+ | `atdd-bun release check` | Read-only SemVer and release-intent check |
41
+ | `atdd-bun help [--json]` | Every command and profile, human- or agent-readable |
42
+
43
+ Run them with `bun run atdd-bun …`. To make a single Bun test fail on broken closure:
76
44
 
77
45
  ```ts
78
46
  import { registerEnforcementTest } from "@afokapu/atdd-bun/register";
79
-
80
- registerEnforcementTest({
81
- root: import.meta.dir + "/..",
82
- profiles: ["traceability"],
83
- });
47
+ registerEnforcementTest({ root: import.meta.dir + "/..", profiles: ["traceability"] });
84
48
  ```
85
49
 
86
- This makes a Bun test fail when plan → acceptance → test → implementation
87
- closure is broken, without enabling unrelated code-quality profiles.
50
+ ## Profiles
88
51
 
89
- ## Choose the enforcement scope
52
+ | Profile | Checks |
53
+ |---|---|
54
+ | `traceability` | acceptance → Bun test → source closure: every acceptance tested, every binding and `Tested-By` resolving |
55
+ | `topology` | feature decomposition and the plan, source, test and E2E locations |
56
+ | `planner` | schemas for every plan artifact, graph integrity, the scoped planner rules |
57
+ | `telemetry` | the telemetry tracking plan: item shape, path-mirrored identity and versioning under `telemetry/`, wagon ownership of logical artifacts, the per-acceptance telemetry decision, metric label cardinality, source `Telemetry:` references, raw-string and forbidden-property emission, the vendor-SDK boundary around the TelemetryPort, and telemetry tests that bind the acceptance and item, assert the exact identity on a captured sink, cover every required item, and exercise declared timing semantics |
58
+ | `docs` | the documentation capability, including the generated journey view |
59
+ | `coder`, `tester`, `security`, `architecture`, `metrics`, `runtime` | Bun source and test conventions |
60
+ | `interlocking` | train/interlocking binding, infrastructure and route coverage |
61
+ | `htmx` | htmx source/test conventions and Playwright browser specs (`*.e2e.ts`) |
62
+ | `design` | design-system layering, token-only styling and responsiveness |
63
+ | `all` | every activated profile; what the hooks and CI run |
90
64
 
91
- Run `bun run atdd-bun help` to see the complete command list, or
92
- `bun run atdd-bun help --json` for an agent-readable command/profile inventory.
65
+ `planner-nodes/ENFORCEMENT_SCOPE.yaml` says which canonical planner rules have a Bun realization.
93
66
 
94
- Profiles describe *what is being checked*, rather than a technical detector
95
- name.
67
+ ## Greenfield and brownfield
96
68
 
97
- | Profile | Use it when you need to check |
98
- |---|---|
99
- | `traceability` | plan acceptance, Bun test, and implementation closure |
100
- | `topology` | feature decomposition and canonical plan, source, test, and E2E locations |
101
- | `planner` | plan parsing/graph integrity plus the explicitly scoped planner rules |
102
- | `docs` | the optional documentation capability and its declared artifacts, including the generated journey view |
103
- | `coder`, `tester`, `security`, `architecture`, `metrics`, `runtime` | Bun source and test conventions for that concern |
104
- | `interlocking` | declared train/interlocking binding, infrastructure, and coverage |
105
- | `htmx` | htmx-specific source and test conventions, including browser specs |
106
- | `design` | the design system (tokens ← primitives ← components ← templates, token-only colors, spacing, radii, motion) and responsiveness (also part of `coder`) |
107
- | `all` | the complete package policy, normally used by CI |
108
-
109
- The package ships the canonical planner-node corpus as planning reference, but
110
- does not pretend that every node is executable. The
111
- [planner enforcement scope](planner-nodes/ENFORCEMENT_SCOPE.yaml) identifies
112
- which rules have a Bun realization, which predicates are only partial, and which
113
- nodes are reference-only.
114
-
115
- The planner profile first validates recognized plan artifacts against the
116
- package-shipped JSON Schemas—wagon, feature, WMBT (including embedded
117
- acceptances), train, train interlocking, and journey topology. It then runs
118
- cross-artifact validators such as registry coherence, traceability, and
119
- cross-interlocking continuation closure. A journey starts at one interlocking;
120
- each reachable route must either terminate explicitly or continue, through an
121
- artifact produced by that route's selected train, to another interlocking. Exposed journeys also
122
- carry Station Master actions; the Bun interlocking family checks those actions resolve through
123
- `JOURNEY_MAP` to `JourneyRunner`, while internal journeys carry no public reachability obligation.
124
- Hooks, direct CLI use, and CI invoke this same profile and therefore share the
125
- same schema source.
126
-
127
- ### Repository topology
128
-
129
- The topology gate makes a feature a required plan artifact instead of an
130
- optional label on a WMBT. Its default layout is:
69
+ A greenfield repository enforces every profile from the start: that is the default. A brownfield
70
+ (legacy) repository can adopt enforcement gradually. The operator lists the profiles it is ready
71
+ for, and adds more as the code catches up:
131
72
 
132
- ```text
133
- plan/<wagon>/{_<wagon>,<feature>,<WMBT>}.yaml
134
- src/wagons/<wagon>/features/<feature>/{domain,application,infrastructure,presentation}/
135
- tests/wagons/<wagon>/features/<feature>/{unit,contract,integration}/
136
- e2e/interlockings/<interlocking>/<route>.routes.test.ts
137
- e2e/journeys/<journey>.journey.test.ts
73
+ ```yaml
74
+ # atdd-bun.yaml
75
+ profiles: [traceability, planner]
138
76
  ```
139
77
 
140
- Each feature must be declared by exactly one wagon and each WMBT by exactly one
141
- feature. Source and test headers must name the feature in their path; tests bind
142
- with `Acceptance:`, while exposed journey E2E tests bind with `Train:`. The
143
- component-URN `integration` layer maps to the `infrastructure/` directory.
78
+ `all`, the hooks and the generated CI then run only those profiles. Any profile can still be run
79
+ by name (`bun run atdd-bun coder`) to see what remains. An unknown name or an empty list is an
80
+ error, never a silent run of nothing. Removing a profile loosens `atdd-bun.yaml`, so the integrity
81
+ check reports it against the base branch until a human approves the change.
82
+
83
+ ## Configuration
144
84
 
145
- Change the four roots for an existing repository in `atdd-bun.yaml`; the same
146
- validation then follows the configured locations:
85
+ Everything is set in `atdd-bun.yaml`. The main keys:
147
86
 
148
87
  ```yaml
149
- topology:
88
+ profiles: [traceability, planner, topology] # default: every profile
89
+ topology: # default layout; point it at existing roots
150
90
  plan_root: plan
151
91
  source_root: src/wagons
152
92
  test_root: tests/wagons
153
93
  e2e_root: e2e
154
- ```
155
-
156
- ### Design system
157
-
158
- The `design` profile (also part of `coder`) is the Bun realization of the core
159
- `coder.design.*` obligations. It recognizes a design system by its directory:
160
- a folder named `design`, `design_system`, or `design-system`, whose first
161
- subfolder names the layer:
162
-
163
- ```text
164
- design/
165
- tokens/ or foundations/ values only: palette, spacing, radii, motion
166
- primitives/ Button, Text, Stack … built from tokens
167
- components/ composed from primitives
168
- templates/ page structure composed from components
169
- ```
170
-
171
- Imports flow downward only, and the design system never imports app code.
172
- Outside the tokens layer, `.tsx`, `.html`, and `.css` files take colors,
173
- spacing, radii, and durations from tokens (`var(--…)`); app components render
174
- controls through primitives and import at least one design-system element; and
175
- every exported component has a consumer. Defining a custom property
176
- (`--accent: #0ea5e9`) is defining a token and is allowed anywhere. A repository
177
- without a design directory is not judged by these rules.
178
-
179
- ### Journey documentation
180
-
181
- A plan describes behaviour on three levels. A journey (`plan/_journeys/`) enters
182
- at one interlocking and continues, through an artifact the selected train
183
- produces, into the next one. An interlocking chooses one route by its guards.
184
- A route runs one train, a linear sequence of handovers. Generate the view of all
185
- three from `plan/`:
186
-
187
- ```sh
188
- bun run atdd-bun docs journeys # writes docs/purpose/journeys/
189
- bun run atdd-bun docs journeys --check # fails when the committed view differs from plan/
190
- ```
191
-
192
- It writes `docs/purpose/journeys/index.adoc` and one SVG for each of:
193
-
194
- - **journey map:** the entry action, each interlocking, the routes it chooses
195
- and their guards, the trains they run, continuations (labelled with the
196
- artifact that carries control) and terminal outcomes;
197
- - **nominal path, end to end:** the journey's trains across all its
198
- interlockings, joined into one sequence diagram;
199
- - **train:** each routed train as its own sequence diagram, under its
200
- interlocking's route table.
201
-
202
- The page also tables every path with how it ends, and every **gap**: unreached
203
- interlockings, routes with no continuation or outcome, unrouted trains,
204
- unresolved guards. Participants are coloured by what they are (wagon, person,
205
- outside system) and arrows by the boundary they cross. Diagrams are inlined
206
- (`opts=inline`) and coloured with `var(--atdd-*, fallback)`, so a doc site can
207
- theme them by defining `--atdd-ink`, `--atdd-paper`, `--atdd-wagon`,
208
- `--atdd-person`, `--atdd-system`, `--atdd-nominal`, `--atdd-alternate`,
209
- `--atdd-error`, `--atdd-exception`, and the like. The output carries no
210
- timestamp and no package version, so it changes only when the plan does.
211
-
212
- The planner requires the journey level itself: `planner.journey.interlocking-composed`
213
- fails when a plan declares interlockings and no journey, or when an interlocking
214
- is neither a journey's entrypoint nor reached by one of its continuations.
215
- `planner.journey.continuation-closure` then checks each declared journey closes.
216
-
217
- **Required.** In a repository with `docs/` whose plan has journeys or
218
- interlockings, the `docs` profile's `planner.docs.journey-view-current` rule
219
- fails when any generated file is missing, stale, hand-edited, or extra. The
220
- generator refuses to overwrite a hand-written `index.adoc` unless you pass
221
- `--force`; use `--out <dir>` to preview elsewhere.
222
-
223
- ### Every enforced rule is strict
224
-
225
- atdd-bun fails on every finding: it has no advisory mode and no ratchet
226
- baseline. Every convention with a validator is therefore `strict` (or `block`),
227
- and `tests/dispositions.test.ts` fails on any that promises otherwise. For the
228
- canonical planner nodes, which ship verbatim, the package states its own
229
- `disposition: strict` in `planner-nodes/ENFORCEMENT_SCOPE.yaml`.
230
-
231
- ### Browser specs (Playwright)
232
-
233
- The `tester` and `htmx` profiles judge browser specs against the plan. A browser
234
- spec is a `*.e2e.ts` file run by Playwright, never `*.spec.ts`, which `bun test`
235
- would try to run itself. It binds to one plan subject and declares its layer
236
- and test URN:
237
-
238
- ```ts
239
- import { expect, test } from "@playwright/test";
240
-
241
- // Train: train:orders:place-order (or // Journey: journey:buy)
242
- // Layer: assembly
243
- // URN: test:train:orders:place-order:E2E-001-places-an-order
244
- ```
245
-
246
- The harness code in the URN (`E2E`, `SMOKE`, `A11Y`, `VIS`, `RESP`) says what
247
- the spec must do: an `A11Y` spec runs `@axe-core/playwright` and asserts on its
248
- violations, a `VIS` spec compares a screenshot, a `RESP` spec renders at every
249
- declared viewport and asserts on `scrollWidth`. Coverage is read from `plan/`:
250
- every train has a spec, every covered train is routed by an interlocking, every
251
- exposed journey has an E2E or SMOKE spec and a RESP spec, and every presentation
252
- component has a SMOKE spec naming its wagon. Route and Station Master coverage
253
- remain `tester.bun` rules; browser specs count toward them.
254
-
255
- ### Responsiveness
256
-
257
- The `coder` and `design` profiles check the structural causes of screens that
258
- break on small devices: every HTML document declares
259
- `<meta name="viewport" content="width=device-width, initial-scale=1">` and
260
- never blocks zoom; no `width`/`min-width` is wider than the smallest viewport;
261
- and every `@media` width is a declared breakpoint (media queries cannot read
262
- CSS variables, so breakpoints are declared once). The browser proof is the
263
- `RESP` spec above. Both read the same settings:
264
-
265
- ```yaml
266
- # atdd-bun.yaml
94
+ telemetry_root: telemetry # tracking-plan registry; inert until the tree or a decision exists
267
95
  frontend:
268
- viewports: [375, 768, 1280] # default; RESP specs render at each, the smallest bounds fixed widths
269
- breakpoints: [480, 768, 1024, 1280] # default; the only widths @media may use
270
- ```
271
-
272
- ### Theme and contract registry
273
-
274
- Theme vocabulary belongs to the repository, not the package. When a plan uses
275
- themes, declare them in `plan/_themes.yaml`; only index `0: commons` is
276
- reserved. Every other index and kebab-case name is repository-defined.
277
-
278
- ```yaml
279
- themes:
280
- "0": commons
281
- "1": orders
282
- "2": inventory
96
+ viewports: [375, 768, 1280]
97
+ breakpoints: [480, 768, 1024, 1280]
98
+ registry_paths: ["plan/_*.yaml", "contracts/_*.yaml"] # exempt from micro-commit size caps only
99
+ max_registry_removed_lines: 350 # larger removals need [mass-delete-approved]
100
+ worktrees: { enabled: false }
101
+ release: { enabled: false }
283
102
  ```
284
103
 
285
- Every contract is recorded in `contracts/_contracts.yaml` with its identity,
286
- path, theme, producers, and consumers. The planner profile checks that contract
287
- references resolve, registry paths exist, a contract identity begins with its
288
- declared theme, and cross-wagon artifacts have contract evidence.
289
-
290
- ## Hooks: fast feedback, not merge authority
291
-
292
- Run the explicit repository bootstrap once in each worktree where you work:
293
-
294
- ```sh
295
- bun run atdd-bun init
296
- ```
104
+ The hooks enforce protected-branch blocking, micro-commit limits, mass-delete approval and
105
+ validation of the affected area. Git can bypass them, so CI is the authority.
297
106
 
298
- It creates `.githooks/` dispatchers, sets a worktree-local `core.hooksPath`,
299
- generates `.github/workflows/atdd-bun.yml`, and installs the coding-agent skill
300
- when they are absent. It never overwrites another hook path, an existing
301
- generated workflow, or an existing skill unless you explicitly pass `--replace`.
302
- The package itself has no install script: adding the dependency never changes
303
- the repository until you run `init`.
107
+ ## Agents and integrity
304
108
 
305
- Use `hooks install`, `ci init`, `agent init`, or `integrity init` when only one surface is wanted:
109
+ The skill gives every coding agent the lifecycle PLAN → RED → GREEN → SMOKE → REFACTOR → TRACE and
110
+ the profile that gates each stage. The block in `AGENTS.md` and `CLAUDE.md` adds the rules: never
111
+ modify the toolkit itself, only the configuration it offers, and enable capabilities through
112
+ `profiles`.
306
113
 
307
- ```sh
308
- bun run atdd-bun hooks install
309
- bun run atdd-bun ci init
310
- bun run atdd-bun agent init
311
- bun run atdd-bun integrity init
312
- ```
114
+ `atdd-bun integrity`, run by the generated test and first in CI on a clean install, fails when:
313
115
 
314
- The hooks enforce protected-branch blocking, micro-commit limits, mass-delete
315
- approval, affected-area validation, and configured traceability. To inspect or
316
- remove that installation, use:
116
+ - the installed package differs from its published hashes;
117
+ - the dependency is not an npm registry version;
118
+ - a generated file (workflow, skills, instruction block, integrity test) was edited;
119
+ - `atdd-bun.yaml` is looser than on the base branch.
317
120
 
318
- ```sh
319
- bun run atdd-bun hooks status
320
- bun run atdd-bun hooks uninstall
321
- ```
322
-
323
- Hooks can be bypassed by Git and therefore are never the merge gate. The CI
324
- workflow and GitHub branch ruleset are the authority for merging.
325
-
326
- ### Declarative registries
327
-
328
- Micro-commit limits (`max_staged_files`, `max_staged_changed_lines`, default
329
- 350) exist to keep imperative code changes small. They do not apply to
330
- declarative registries, which can legitimately be hundreds or thousands of lines
331
- and must not be split into invalid intermediate states. Registries are matched by
332
- `registry_paths` (default `plan/_*.yaml`, `plan/_*.yml`, `contracts/_*.yaml`,
333
- `contracts/_*.yml`).
334
-
335
- A staged registry is exempt from the size caps only. It is still:
336
-
337
- - validated by the `planner` and `traceability` profiles on every commit that
338
- touches it, even when `require_traceability` is `false`;
339
- - subject to removal approval: a net removal above `max_registry_removed_lines`
340
- (default 350) needs `[mass-delete-approved]` in the commit message. Rewriting
341
- or reordering entries in place is not a removal.
342
-
343
- ```yaml
344
- # atdd-bun.yaml
345
- registry_paths: ["plan/_*.yaml", "contracts/_*.yaml", "telemetry/_*.yaml"]
346
- max_registry_removed_lines: 200
347
- ```
348
-
349
- Duplicate, stale, and ownership checks come from the planner rules, and
350
- independent review comes from the CI workflow and branch ruleset.
351
-
352
- ## Agent skill: the lifecycle in the agent's context
353
-
354
- `agent init` gives every coding agent the same short ATDD skill:
355
-
356
- - `.agents/skills/atdd/SKILL.md`: the vendor-neutral Agent Skills path (Codex,
357
- GitHub Copilot, Cursor, Gemini CLI, and others);
358
- - `.claude/skills/atdd/SKILL.md`: Claude Code;
359
- - a managed `<!-- atdd-bun:start -->` block in `AGENTS.md` pointing at the skill,
360
- for agents that read `AGENTS.md` but not skills. The rest of `AGENTS.md` is
361
- never touched.
362
-
363
- The skill names the lifecycle PLAN → RED → GREEN → SMOKE → REFACTOR → TRACE, the
364
- conventions each stage follows, and the `atdd-bun` profile that gates it. It
365
- points at the conventions shipped in this package instead of restating them, so
366
- it stays correct as they change; after upgrading, refresh it with
367
- `bun run atdd-bun agent init --replace`, or let the repository's own
368
- `postinstall` do it (see [Staying up to date](#staying-up-to-date)). The skill
369
- steers the agent; the profiles, hooks, and CI remain the enforcement.
370
-
371
- ## Integrity: files agents must not change
372
-
373
- Coding agents can edit anything on the machine they run on, including
374
- `node_modules/@afokapu/atdd-bun`, the files this package generates, and
375
- `atdd-bun.yaml`. `init` therefore writes `atdd-bun.integrity.test.ts` (into the
376
- `[test] root` from `bunfig.toml`, if one is set), and the generated CI workflow
377
- runs `bun run atdd-bun integrity` before enforcement. Both run the same check:
378
-
379
- | Checked | Canonical source | Restore |
380
- |---|---|---|
381
- | Every file of the installed package | `integrity.json`, the hashes published with the package | `bun install --force` |
382
- | The dependency is an npm version range, locked to the registry with an integrity hash | npm | `bun add -d @afokapu/atdd-bun` |
383
- | The CI workflow, both skills, the `AGENTS.md` block, and the integrity test | what the installed version generates (its version stamp is ignored) | `bun run atdd-bun init --replace` |
384
- | `atdd-bun.yaml` is not looser than on the branch being merged into | the merge base; for a push to the base branch, the previous commit | `git checkout <base> -- atdd-bun.yaml` |
385
-
386
- A failure is addressed to the agent: it lists every changed file with its
387
- restore command and tells it to stop and ask a human instead of working around
388
- the check. Locally, this is a reminder an agent can still ignore, because
389
- anything on its machine can be edited. In CI the check runs on a clean install,
390
- so its verdict cannot be faked. Loosening the policy remains possible, as a
391
- separate change a human approves.
392
-
393
- After upgrading to a version that changes generated files, run
394
- `bun run atdd-bun init --replace` and commit the result.
395
-
396
- ## CI: the merge gate
397
-
398
- Generate the repository-owned workflow with:
399
-
400
- ```sh
401
- bun run atdd-bun ci init
402
- ```
403
-
404
- This writes `.github/workflows/atdd-bun.yml`. It refuses to overwrite an
405
- existing workflow unless `--replace` is supplied. The generated workflow runs
406
- on pull requests, merge-queue merge groups, and pushes to `main`/`master`; it
407
- installs with `bun install --frozen-lockfile`, runs the locally installed package
408
- without `bunx`, runs `bun test`, and uploads reports when present.
409
-
410
- After generating it, configure the GitHub branch ruleset to require the workflow
411
- job before merging. `merge_group` is included so the same protection works with
412
- GitHub Merge Queue.
121
+ Each finding names its restore command.
413
122
 
414
123
  ## Staying up to date
415
124
 
416
- Every change merged into this package's `main` is published to npm
417
- automatically as the next patch version, with provenance, and tagged `vX.Y.Z`.
418
-
419
- The hooks and the CI workflow run the package installed in `node_modules`, so
420
- conventions, validators, and hook policy change as soon as a repository
421
- upgrades the dependency; nothing needs reinstalling. The generated files are
422
- copies: when an upgrade changes them, the integrity check reports them until you
423
- run `bun run atdd-bun init --replace` and commit the result. The agent skill is
424
- the one refreshed automatically. To refresh it on every install, add a script to the
425
- repository's own `package.json` (Bun runs a project's own lifecycle scripts, not
426
- a dependency's):
125
+ Every merge to this package's `main` is published to npm with provenance as the next patch and
126
+ tagged `vX.Y.Z`. Rules and hooks change as soon as a repository upgrades the dependency. To refresh
127
+ the skills and instruction blocks on every install, add this to the repository's own
128
+ `package.json`:
427
129
 
428
130
  ```json
429
- "scripts": {
430
- "postinstall": "atdd-bun agent init --replace"
431
- }
131
+ "scripts": { "postinstall": "atdd-bun agent init --replace" }
432
132
  ```
433
133
 
434
- To receive each release as a pull request, add `.github/dependabot.yml` on the
435
- default branch:
134
+ Upgrade with `bun update @afokapu/atdd-bun`, or let Dependabot (`package-ecosystem: "bun"`) open a
135
+ pull request per release. For a new `0.x` minor, run `bun add -d @afokapu/atdd-bun@latest`. If an
136
+ upgrade changes the workflow or integrity test, run `bun run atdd-bun init --replace` and commit.
436
137
 
437
- ```yaml
438
- version: 2
439
- updates:
440
- - package-ecosystem: "bun"
441
- directory: "/"
442
- schedule:
443
- interval: "daily"
444
- allow:
445
- - dependency-name: "@afokapu/atdd-bun"
446
- ```
447
-
448
- Merging that pull request installs the new version and, through `postinstall`,
449
- rewrites the skill. Without Dependabot, upgrade with
450
- `bun update @afokapu/atdd-bun`. The lockfile pins the installed version, so
451
- nothing changes until one of these runs. While the package is `0.x`, a `^0.1.x`
452
- range accepts only `0.1.*`; move to a new minor with
453
- `bun add -d @afokapu/atdd-bun@latest`.
454
-
455
- ## Linked worktrees for agent work
456
-
457
- An optional policy reserves one primary checkout for `main` and requires feature
458
- commits to happen in sibling linked worktrees:
459
-
460
- ```text
461
- my-repo/
462
- main/ # primary checkout, on branch main
463
- worktrees/feature-x/ # linked checkout, on branch feature/x
464
- ```
465
-
466
- Enable the policy in `main/atdd-bun.yaml`:
467
-
468
- ```yaml
469
- worktrees:
470
- enabled: true
471
- root: ../worktrees
472
- primary_directory: main
473
- primary_branch: main
474
- require_linked_worktree: true
475
- ```
476
-
477
- An agent starts one worktree per work item—not per commit:
478
-
479
- ```sh
480
- # Run from my-repo/main/ while it is on main.
481
- bun run atdd-bun worktree start feature/x
482
- ```
483
-
484
- That command creates `worktrees/feature-x`, creates the `feature/x` branch, and
485
- installs the package hooks there. Subsequent commits happen normally from that
486
- linked checkout. The hook rejects commits from the primary checkout, protected
487
- branches, detached heads, and linked checkouts outside the configured root.
488
-
489
- When the branch is merged into local `main`, inspect or safely retire it with:
490
-
491
- ```sh
492
- bun run atdd-bun worktree status
493
- bun run atdd-bun worktree finish --delete-branch
494
- ```
495
-
496
- `finish` refuses a dirty or unmerged worktree. It never removes a worktree just
497
- because a hook ran.
498
-
499
- Git stores linked-worktree metadata in `main/.git/worktrees/`; the linked
500
- checkouts themselves belong beside `main`, not inside `.git` or inside the
501
- primary repository working tree.
502
-
503
- ## Release policy
504
-
505
- If a repository enables `release` in `atdd-bun.yaml`, this read-only command
506
- checks SemVer, a single release decision, release intent, and that the proposed
507
- version is greater than the latest reachable matching local Git tag:
508
-
509
- ```sh
510
- bun run atdd-bun release check
511
- ```
138
+ ## Developing this package
512
139
 
513
- It creates no tag, makes no network request, and does not publish anything.
514
- The separate optional release workflow is where a repository may create a tag or
515
- publish using its own credentials and registry configuration.
516
-
517
- ## Convention relationships
518
-
519
- `relationships.yaml` relates every convention the package ships (`planner-nodes/`
520
- and `conventions/`) to at least one other, as
521
- `planner.relationship.no-orphan-nodes` requires. Edges that touch a shipped
522
- convention are imported from the upstream ATDD graphs with their `origin`; the
523
- package adds its own for the conventions it introduces. A test fails when any
524
- shipped convention has no edge, when the node list drifts from the shipped
525
- conventions, or when an edge breaks `relationship.schema.json`, so a new
526
- convention cannot land without its relationships.
527
-
528
- ## Verification of this package
529
-
530
- `bun test` runs the package’s real-Git fixtures and detector clean/dirty corpora.
531
- The suite proves that every declared convention output has a matching convention
532
- and a deliberate failing case, and runs the frontend chain end to end: a Bun app
533
- built from a plan, its browser specs accepted by the detectors and passing in
534
- Chromium, and broken variants of the page failing both the static rules and the
535
- browser (Chromium must be installed: `bunx playwright install chromium`); it also covers hook isolation, the declarative
536
- registry policy, CI generation, agent-skill installation, planner scope, release
537
- validation, and linked-worktree policy.
140
+ `bun test` runs the detectors' clean and dirty corpora, real-Git hook fixtures, and the frontend
141
+ chain in Chromium (`bunx playwright install chromium`). Guard tests require every emitted rule to
142
+ have a strict convention, a failing fixture and an edge in `relationships.yaml`.