@afokapu/atdd-bun 0.10.32 → 0.10.33

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.
@@ -0,0 +1,192 @@
1
+ schema_version: "1.0.0"
2
+ profile: "topology"
3
+ implementations:
4
+ - "atdd_topology"
5
+ conventions:
6
+ - rule_id: "atdd-bun.topology.e2e-location"
7
+ path: "conventions/atdd-bun.topology/atdd-bun.topology.e2e-location.convention.yaml"
8
+ - rule_id: "atdd-bun.topology.feature-source-coverage"
9
+ path: "conventions/atdd-bun.topology/atdd-bun.topology.feature-source-coverage.convention.yaml"
10
+ - rule_id: "atdd-bun.topology.feature-test-coverage"
11
+ path: "conventions/atdd-bun.topology/atdd-bun.topology.feature-test-coverage.convention.yaml"
12
+ - rule_id: "atdd-bun.topology.plan-location"
13
+ path: "conventions/atdd-bun.topology/atdd-bun.topology.plan-location.convention.yaml"
14
+ - rule_id: "atdd-bun.topology.source-location"
15
+ path: "conventions/atdd-bun.topology/atdd-bun.topology.source-location.convention.yaml"
16
+ - rule_id: "atdd-bun.topology.test-location"
17
+ path: "conventions/atdd-bun.topology/atdd-bun.topology.test-location.convention.yaml"
18
+ - rule_id: "planner.feature.wagon-link"
19
+ path: "planner-nodes/nodes/planner.feature.wagon-link.convention.yaml"
20
+ - rule_id: "planner.wagon.features"
21
+ path: "planner-nodes/nodes/planner.wagon.features.convention.yaml"
22
+ relationships:
23
+ - confidence: 1
24
+ constraint: "mandatory"
25
+ control: "internal"
26
+ foundation: "finish_to_start"
27
+ origin: "@afokapu/atdd-bun"
28
+ reason: "journey E2E paths are derived from the journeys the plan declares"
29
+ source_ref: "atdd-bun.topology.e2e-location"
30
+ strength: "critical"
31
+ target_ref: "planner.journey.definition"
32
+ type: "requires"
33
+ - confidence: 1
34
+ constraint: "mandatory"
35
+ control: "internal"
36
+ foundation: "start_to_start"
37
+ origin: "@afokapu/atdd-bun"
38
+ reason: "every route has an end-to-end proof: at its canonical path, and exercising the production runner"
39
+ source_ref: "atdd-bun.topology.e2e-location"
40
+ strength: "important"
41
+ target_ref: "tester.bun.interlocking-route-coverage"
42
+ type: "runs_alongside"
43
+ - confidence: 1
44
+ constraint: "mandatory"
45
+ control: "internal"
46
+ foundation: "start_to_start"
47
+ origin: "@afokapu/atdd-bun"
48
+ reason: "on a frontend journey a browser spec is the route's end-to-end proof"
49
+ source_ref: "atdd-bun.topology.e2e-location"
50
+ strength: "important"
51
+ target_ref: "tester.htmx.train-e2e-coverage"
52
+ type: "runs_alongside"
53
+ - confidence: 1
54
+ constraint: "mandatory"
55
+ control: "internal"
56
+ foundation: "finish_to_start"
57
+ origin: "@afokapu/atdd-bun"
58
+ reason: "coverage counts only source placed beneath its feature"
59
+ source_ref: "atdd-bun.topology.feature-source-coverage"
60
+ strength: "critical"
61
+ target_ref: "atdd-bun.topology.source-location"
62
+ type: "requires"
63
+ - confidence: 1
64
+ constraint: "mandatory"
65
+ control: "internal"
66
+ foundation: "start_to_start"
67
+ origin: "@afokapu/atdd-bun"
68
+ reason: "a feature with WMBTs is implemented and tested"
69
+ source_ref: "atdd-bun.topology.feature-test-coverage"
70
+ strength: "important"
71
+ target_ref: "atdd-bun.topology.feature-source-coverage"
72
+ type: "runs_alongside"
73
+ - confidence: 1
74
+ constraint: "mandatory"
75
+ control: "internal"
76
+ foundation: "finish_to_start"
77
+ origin: "@afokapu/atdd-bun"
78
+ reason: "coverage counts only tests placed beneath their feature and bound to its acceptances"
79
+ source_ref: "atdd-bun.topology.feature-test-coverage"
80
+ strength: "critical"
81
+ target_ref: "atdd-bun.topology.test-location"
82
+ type: "requires"
83
+ - confidence: 1
84
+ constraint: "mandatory"
85
+ control: "internal"
86
+ foundation: "start_to_start"
87
+ origin: "@afokapu/atdd-bun"
88
+ reason: "where a feature lives and which wagon owns it: the same ownership, by path and by link"
89
+ source_ref: "atdd-bun.topology.plan-location"
90
+ strength: "important"
91
+ target_ref: "planner.feature.wagon-link"
92
+ type: "runs_alongside"
93
+ - confidence: 1
94
+ constraint: "mandatory"
95
+ control: "internal"
96
+ foundation: "finish_to_start"
97
+ origin: "@afokapu/atdd-bun"
98
+ reason: "features are placed by path once the wagon's feature decomposition is declared"
99
+ source_ref: "atdd-bun.topology.plan-location"
100
+ strength: "critical"
101
+ target_ref: "planner.wagon.features"
102
+ type: "requires"
103
+ - confidence: 1
104
+ constraint: "mandatory"
105
+ control: "internal"
106
+ foundation: "finish_to_start"
107
+ origin: "@afokapu/atdd-bun"
108
+ reason: "source is placed beneath a feature the plan declares at its canonical path"
109
+ source_ref: "atdd-bun.topology.source-location"
110
+ strength: "critical"
111
+ target_ref: "atdd-bun.topology.plan-location"
112
+ type: "requires"
113
+ - confidence: 1
114
+ constraint: "mandatory"
115
+ control: "internal"
116
+ foundation: "start_to_start"
117
+ origin: "@afokapu/atdd-bun"
118
+ reason: "the same component URN, read for its grammar and for where the file must live"
119
+ source_ref: "atdd-bun.topology.source-location"
120
+ strength: "important"
121
+ target_ref: "coder.bun.green-urn-pattern"
122
+ type: "runs_alongside"
123
+ - confidence: 1
124
+ constraint: "mandatory"
125
+ control: "internal"
126
+ foundation: "finish_to_start"
127
+ origin: "@afokapu/atdd-bun"
128
+ reason: "tests are placed beneath a feature the plan declares"
129
+ source_ref: "atdd-bun.topology.test-location"
130
+ strength: "critical"
131
+ target_ref: "atdd-bun.topology.plan-location"
132
+ type: "requires"
133
+ - confidence: 1
134
+ constraint: "mandatory"
135
+ control: "internal"
136
+ foundation: "start_to_start"
137
+ origin: "@afokapu/atdd-bun"
138
+ reason: "the acceptance a test binds, read for presence and for whether a WMBT declares it"
139
+ source_ref: "atdd-bun.topology.test-location"
140
+ strength: "important"
141
+ target_ref: "tester.bun.acceptance-binding-declared"
142
+ type: "runs_alongside"
143
+ - confidence: 1
144
+ constraint: "mandatory"
145
+ control: "external"
146
+ foundation: "finish_to_start"
147
+ origin: "atdd@36ce055a:src/atdd/coach/graph/relationships.yaml"
148
+ reason: "wagon-feature coverage reads the wagon features manifest"
149
+ source_ref: "planner.coverage.bidirectional-coverage-between-wagons"
150
+ strength: "important"
151
+ target_ref: "planner.wagon.features"
152
+ type: "requires"
153
+ - confidence: 1
154
+ constraint: "mandatory"
155
+ control: "internal"
156
+ foundation: "finish_to_finish"
157
+ origin: "atdd@36ce055a:src/atdd/coach/graph/relationships.yaml"
158
+ reason: "a feature belongs to exactly one wagon"
159
+ source_ref: "planner.feature.definition"
160
+ strength: "critical"
161
+ target_ref: "planner.feature.wagon-link"
162
+ type: "requires"
163
+ - confidence: 1
164
+ constraint: "mandatory"
165
+ control: "external"
166
+ foundation: "finish_to_start"
167
+ origin: "atdd@36ce055a:src/atdd/coach/graph/relationships.yaml"
168
+ reason: "a feature is registered in its wagon _features.yaml"
169
+ source_ref: "planner.feature.wagon-link"
170
+ strength: "important"
171
+ target_ref: "planner.wagon.features"
172
+ type: "requires"
173
+ - confidence: 1
174
+ constraint: "mandatory"
175
+ control: "internal"
176
+ foundation: "finish_to_finish"
177
+ origin: "atdd@36ce055a:src/atdd/coach/graph/relationships.yaml"
178
+ reason: "a wagon groups the features that decompose it"
179
+ source_ref: "planner.wagon.definition"
180
+ strength: "minor"
181
+ target_ref: "planner.wagon.features"
182
+ type: "requires"
183
+ - confidence: 1
184
+ constraint: "mandatory"
185
+ control: "internal"
186
+ foundation: "start_to_start"
187
+ origin: "atdd@36ce055a:src/atdd/coach/graph/relationships.yaml"
188
+ reason: "the features-manifest rule uses the urn-array features format"
189
+ source_ref: "planner.wagon.features-format"
190
+ strength: "important"
191
+ target_ref: "planner.wagon.features"
192
+ type: "runs_alongside"
@@ -0,0 +1,94 @@
1
+ schema_version: "1.0.0"
2
+ profile: "traceability"
3
+ implementations:
4
+ - "atdd_traceability_closure"
5
+ conventions:
6
+ - rule_id: "traceability.plan.executable-acceptance-has-test"
7
+ path: "conventions/traceability/traceability.plan.executable-acceptance-has-test.convention.yaml"
8
+ - rule_id: "traceability.source.tested-by-present"
9
+ path: "conventions/traceability/traceability.source.tested-by-present.convention.yaml"
10
+ - rule_id: "traceability.source.tested-by-resolves"
11
+ path: "conventions/traceability/traceability.source.tested-by-resolves.convention.yaml"
12
+ - rule_id: "traceability.test.binding-resolves"
13
+ path: "conventions/traceability/traceability.test.binding-resolves.convention.yaml"
14
+ relationships:
15
+ - confidence: 1
16
+ constraint: "mandatory"
17
+ control: "internal"
18
+ foundation: "finish_to_start"
19
+ origin: "@afokapu/atdd-bun"
20
+ reason: "a test can bind an acceptance only through the acc: identity the WMBT gives it"
21
+ source_ref: "atdd-bun.planner.acceptance-identity"
22
+ strength: "critical"
23
+ target_ref: "traceability.test.binding-resolves"
24
+ type: "enables"
25
+ - confidence: 1
26
+ constraint: "discretionary"
27
+ control: "internal"
28
+ foundation: "finish_to_start"
29
+ origin: "@afokapu/atdd-bun"
30
+ reason: "traceability joins with the building preset because executable-acceptance-has-test reports every acceptance until tests exist"
31
+ source_ref: "atdd-bun.profiles.presets"
32
+ strength: "minor"
33
+ target_ref: "traceability.plan.executable-acceptance-has-test"
34
+ type: "follows"
35
+ - confidence: 1
36
+ constraint: "mandatory"
37
+ control: "internal"
38
+ foundation: "finish_to_start"
39
+ origin: "@afokapu/atdd-bun"
40
+ reason: "telemetry items follow the acceptance-has-test discipline"
41
+ source_ref: "tester.bun.telemetry-required-item-coverage"
42
+ strength: "important"
43
+ target_ref: "traceability.plan.executable-acceptance-has-test"
44
+ type: "requires"
45
+ - confidence: 1
46
+ constraint: "mandatory"
47
+ control: "internal"
48
+ foundation: "start_to_start"
49
+ origin: "@afokapu/atdd-bun"
50
+ reason: "the two directions of plan-test closure: every acceptance has a test, and every test names an acceptance that exists"
51
+ source_ref: "traceability.plan.executable-acceptance-has-test"
52
+ strength: "critical"
53
+ target_ref: "traceability.test.binding-resolves"
54
+ type: "runs_alongside"
55
+ - confidence: 1
56
+ constraint: "mandatory"
57
+ control: "internal"
58
+ foundation: "start_to_start"
59
+ origin: "@afokapu/atdd-bun"
60
+ reason: "the same Tested-By header, read by the closure check and by the coder header rule"
61
+ source_ref: "traceability.source.tested-by-present"
62
+ strength: "important"
63
+ target_ref: "coder.bun.green-header-tested-by"
64
+ type: "runs_alongside"
65
+ - confidence: 1
66
+ constraint: "mandatory"
67
+ control: "internal"
68
+ foundation: "finish_to_start"
69
+ origin: "@afokapu/atdd-bun"
70
+ reason: "a testing witness can only resolve once one is declared"
71
+ source_ref: "traceability.source.tested-by-resolves"
72
+ strength: "critical"
73
+ target_ref: "traceability.source.tested-by-present"
74
+ type: "requires"
75
+ - confidence: 1
76
+ constraint: "mandatory"
77
+ control: "internal"
78
+ foundation: "start_to_start"
79
+ origin: "@afokapu/atdd-bun"
80
+ reason: "source-to-test and test-to-plan links chain into plan to test to implementation closure"
81
+ source_ref: "traceability.source.tested-by-resolves"
82
+ strength: "important"
83
+ target_ref: "traceability.test.binding-resolves"
84
+ type: "runs_alongside"
85
+ - confidence: 1
86
+ constraint: "mandatory"
87
+ control: "internal"
88
+ foundation: "finish_to_start"
89
+ origin: "@afokapu/atdd-bun"
90
+ reason: "a test's binding must be declared before it can be resolved against the plan"
91
+ source_ref: "traceability.test.binding-resolves"
92
+ strength: "critical"
93
+ target_ref: "tester.bun.acceptance-binding-declared"
94
+ type: "requires"
@@ -13,6 +13,25 @@ terms:
13
13
  planning: [planner]
14
14
  building: [coder, tester, traceability, security]
15
15
  instrumenting: [telemetry]
16
+ - term_id: profile_registries
17
+ text: >-
18
+ A deterministic, profile-scoped projection of the detector manifests, convention paths, and
19
+ direct relationship edges. Read the selected profile's registry instead of the complete graph.
20
+ values:
21
+ architecture: conventions/_profiles/architecture.yaml
22
+ coder: conventions/_profiles/coder.yaml
23
+ design: conventions/_profiles/design.yaml
24
+ docs: conventions/_profiles/docs.yaml
25
+ htmx: conventions/_profiles/htmx.yaml
26
+ interlocking: conventions/_profiles/interlocking.yaml
27
+ metrics: conventions/_profiles/metrics.yaml
28
+ planner: conventions/_profiles/planner.yaml
29
+ runtime: conventions/_profiles/runtime.yaml
30
+ security: conventions/_profiles/security.yaml
31
+ telemetry: conventions/_profiles/telemetry.yaml
32
+ tester: conventions/_profiles/tester.yaml
33
+ topology: conventions/_profiles/topology.yaml
34
+ traceability: conventions/_profiles/traceability.yaml
16
35
  content:
17
36
  summary: >-
18
37
  A goal is easier to name than a profile. The presets map what the user is doing to the profiles that judge it, in the order the work creates what they check.
package/integrity.json CHANGED
@@ -1,10 +1,24 @@
1
1
  {
2
- "version": "0.10.32",
2
+ "version": "0.10.33",
3
3
  "files": {
4
4
  "HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
5
5
  "PLANNER_PORT.md": "fb5935bac8b7ac18994de21e43ace3a5ef8cd55f85b0e3349fca261280054f11",
6
- "README.md": "78a1acc1f18f84a567448051bec90f676e3cab7956d971abc97a6201c96fc409",
6
+ "README.md": "edcd36ebed5853c412c89ed998cd198f7a58237a68dcf37c14be5f7f5c0e9ff4",
7
7
  "bunfig.toml": "b9fc65eca9014c5179380259d70e76385a6a80788fa9a2df5fb3eaa5554fd2fe",
8
+ "conventions/_profiles/architecture.yaml": "f1808768b720a4e476047e4a0912002ca673946562cea9aeef5d0c214828d93a",
9
+ "conventions/_profiles/coder.yaml": "c38e46405ddd7e82fd7679246d027821db19db86c307a23ce5d14f18165d0699",
10
+ "conventions/_profiles/design.yaml": "25a02a67152d49728ad3517f6510da7285116fa69c56cbda55613a7cfcfd3b89",
11
+ "conventions/_profiles/docs.yaml": "05485c3b32f1757e77c42e331eec1db651cb52ef57fc4e8607bb2f73b056cbb3",
12
+ "conventions/_profiles/htmx.yaml": "38fc8b3e1a53429e16977ed63e83e751b203deffe9b338ea171050fb64641559",
13
+ "conventions/_profiles/interlocking.yaml": "46a2a4fb2eb537941c991ef4178d009a30d14f84279e525ca0d0887d3239047d",
14
+ "conventions/_profiles/metrics.yaml": "6028a227ccdef6d97ff10602f1a146eb73fd02b0a03ebce6726540fa30540f3c",
15
+ "conventions/_profiles/planner.yaml": "b3938783793c34f832968b7293b7c301f3e1deff5f06536a078168c911d0d089",
16
+ "conventions/_profiles/runtime.yaml": "48fae0d31fb9228b8819243c264721037720acb1763bf27f74325c14ffef46d4",
17
+ "conventions/_profiles/security.yaml": "38eb6b17e35e1439fe4f19c8ccf3ba1b2f18759a984dd1d33171ec3a29128876",
18
+ "conventions/_profiles/telemetry.yaml": "4c07c693357fad420df0df82a3ad196cd159df017fec6b98244841530516dfd9",
19
+ "conventions/_profiles/tester.yaml": "348183eee5298cbb843031f6c41c3f9d0a725b11b3c66e154b47c9b340a72666",
20
+ "conventions/_profiles/topology.yaml": "08df698c2e5419e8f7faa83f5f15c3ba9c085cc2ce72579624eb24962a6b8612",
21
+ "conventions/_profiles/traceability.yaml": "adce93fb8392307685a01bec85e60534c59a9f4f0e4f8ea4767d79d22cf80878",
8
22
  "conventions/atdd-bun.planner/atdd-bun.planner.acceptance-identity.convention.yaml": "81c5c773d5ee15e8d99a2c237aa9846b110b2533df45ebf407d0e3ec6ed3dd03",
9
23
  "conventions/atdd-bun.planner/atdd-bun.planner.identity-required.convention.yaml": "e96d7c1455d0c221072d82e7f2b55da1c4f9ceb17718eb6ed96affdeef37675b",
10
24
  "conventions/atdd-bun.planner/atdd-bun.planner.identity-unique.convention.yaml": "409ba1d035ed46bc45f3627b62fd3a6a5967d984cc4c071c41be3f3fa887c6d6",
@@ -13,7 +27,7 @@
13
27
  "conventions/atdd-bun.planner/atdd-bun.planner.reference-resolves.convention.yaml": "b96927d269692c29c02fee38ecbeb999480791f8dc7486c4072a9ff30183d7d6",
14
28
  "conventions/atdd-bun.planner/atdd-bun.planner.schema.convention.yaml": "16d0a26451758b759e6ca1f5b4f38ae1feff129688306ffcc2f70f6d3974b591",
15
29
  "conventions/atdd-bun.planner/atdd-bun.planner.train-wagon-resolves.convention.yaml": "6410e248351e701dcba38c671c07c4d8f6d67927984ed4d18e567abd1eb1f3f0",
16
- "conventions/atdd-bun.profiles/atdd-bun.profiles.presets.convention.yaml": "a5885bfa387d0abbc4694ea2d0cf1f3639d5b45588707d4f70f2f2be4e12e847",
30
+ "conventions/atdd-bun.profiles/atdd-bun.profiles.presets.convention.yaml": "4f6f6367d6baa1c5f2eac8c686a7a0e309b3e6335fdaa5e6641f3cc43dbb97d0",
17
31
  "conventions/atdd-bun.topology/atdd-bun.topology.e2e-location.convention.yaml": "3a3b991bc851b86d597636200f5cb10a0defda3920348860cdca28955ab91f4c",
18
32
  "conventions/atdd-bun.topology/atdd-bun.topology.feature-source-coverage.convention.yaml": "2b64093f1e7f1fe0e2edcadf12e560fa80cecec19536abaa5bf09af362c6694e",
19
33
  "conventions/atdd-bun.topology/atdd-bun.topology.feature-test-coverage.convention.yaml": "279be57399c9b79a1bd758414076e3053d67b7d4a7fcbbb950ea12d1201effeb",
@@ -1176,26 +1190,26 @@
1176
1190
  "planner-schemas/wagon.schema.json": "57b98f1ade998d745538eb3d72fb3014a50042006870f90bce47028eea336eb4",
1177
1191
  "planner-schemas/wmbt.schema.json": "7a3a5693bfcf01340667c2be4a3c48e74f34b1af7e8dc13372c0de06b7f4fe12",
1178
1192
  "relationships.yaml": "26dd50489d7cbd45400937a8949d6ff77cdd4400bfa828d6b7a17e90c64c879b",
1179
- "src/agent.ts": "009d3170752ded12e9e3d4f7f07fd68d4bab821046714ad68cea428581343c67",
1193
+ "src/agent.ts": "39ca8b3183063c60e00eb96e5629d0397aeb83cc82d4c4bc32c01715c906649b",
1180
1194
  "src/ci.ts": "67e54d2cbfc44a9e42837d5e750af526d6255c539d3c452a4d5fca3226c0d9ac",
1181
- "src/cli.ts": "09e6b4bdebef32ff482a12182e2efbc2e42424ff854e813a10efd3764e45d4fc",
1195
+ "src/cli.ts": "cbc50f1e7fc7d0935f78230b7da6e232ed7ee6f04828e015c599fb6f6edcbfa2",
1182
1196
  "src/docs-capability.ts": "bdeeb414649049b1cd4fa8008cada3a576318be51c43bec91bfa46a8e250c270",
1183
- "src/enforce.ts": "45c473ffec9f565fba24df2a7db6564d76ee885cd4b0ab8b34bc14b7f2bcc7b2",
1184
- "src/hooks.ts": "9779eb48f9d0cb6234c33f6616f98dde6583d2ee03ac3c19f3ace2d156af26d0",
1185
- "src/index.ts": "b3cc97f9867bd605aefa95650a6b8b773e32e6e52e58792d0dbf6736dd3406be",
1186
- "src/integrity.ts": "3a74cc03bbdebf83d607073a4a13ab4afd4a163c2307062ed137d7d21e38b988",
1197
+ "src/enforce.ts": "736da6a864785137f3c2ef5b4f25060d2f4a1eba5d2872b7f4e7cd7395eb46de",
1198
+ "src/hooks.ts": "0b4ca8c857e637bbd144e737e78eda924e3cba7b038c0abe5e6916d200d73068",
1199
+ "src/index.ts": "4081f8b08d1d3ee9e15a9964143e53d9cfb94a5616ab3e58a180d24cc16dc381",
1200
+ "src/integrity.ts": "44ebe101933e8b1a42c563acdffcaf7a24baa4861eb8731a914bc51500d0a93d",
1187
1201
  "src/journey-docs.ts": "994d229376244c84212cff951032823068a76565dd2fd427b9d44e341bd14853",
1188
1202
  "src/planner-kernel.ts": "5d3f5305fb59becb2cc97d8a03aca6b104f385805d91cc0410856e289dd6fbb1",
1189
1203
  "src/planner-schema-validator.ts": "ce529c0936171075e93c7539b3655fe895a98dd2973ebda5775958e6ebc316d9",
1190
1204
  "src/planner-validators.ts": "99adeb65f579af923c11f21a8f55cfb9993b007fd8df59c5f764696b55379cfc",
1205
+ "src/profile-registry.ts": "1c0d64172d2006a8d865bffbb47506684eb5f42b75df6308d28564dbb942088c",
1191
1206
  "src/register.ts": "6a2f1c6b8df0dc9c8f45df8ec9fac2be29044e048b08c8ffe423410e94dadedd",
1192
1207
  "src/release.ts": "7b0fe444ee81f476f53b9c21b6c347ef588ee616c60211419cf984132be768ba",
1193
- "src/setup.ts": "2dbdddec898e9a3779889f3491eaa1423c4427c9ec63167663245b3ae4db1e6f",
1208
+ "src/setup.ts": "19629b3c03e6d35b3e0e8b8a697a3c796d0e8cc4f9bac65875a26421573f3447",
1194
1209
  "src/telemetry-plan.ts": "cdb1e8bfff414f3ea81977388973da2cf6d89567d9e060dc9d5989a33600f153",
1195
1210
  "src/topology.ts": "9779ee48e1c453abb76363163d4ab363463fa14e359571139f6d1f1fcc51ae2c",
1196
- "templates/agents/AGENTS.block.md": "d7f6013ef7b1793f236b610f78f2898f6c10c60751f671ff2c4b72d49b4f6011",
1211
+ "templates/agents/AGENTS.block.md": "978ea89e1469f0a89a6ef6e4ac40bdc11ae856bb2754069743758ba204c395a1",
1197
1212
  "templates/agents/atdd-bun.integrity.test.ts": "dec6f6e5f65a08d9512fc703b367c163f8c9da1f6aa7fd466835c1ec30348a41",
1198
- "templates/agents/atdd/SKILL.md": "8f36d7d571aa17beb03df5e620ff837be73eb21a4e73dabd2c12188d2b378022",
1199
1213
  "templates/docs/site.css": "482b3427e3a3cb02a85dc85cfbeac80592dfd53abf42fc92d6ea7071f22afc95",
1200
1214
  "templates/github/atdd-bun-release.yml": "d9547e9e6ef3ae010d53d55314f6a54567f88bd900f7e50d994cd308601d2684",
1201
1215
  "templates/github/atdd-bun.yml": "404dc06b6ff4faf5eab909f9f77bac0ae58163d810c3928c4353b1f4b3529048"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afokapu/atdd-bun",
3
- "version": "0.10.32",
3
+ "version": "0.10.33",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/afokapu/atdd-bun.git"
package/src/agent.ts CHANGED
@@ -1,26 +1,26 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
1
+ import { readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { existsSync } from "node:fs";
3
- import { dirname, join, resolve } from "node:path";
3
+ import { join, resolve } from "node:path";
4
4
 
5
5
  const root = resolve(import.meta.dir, "..");
6
6
  const version = (await Bun.file(join(root, "package.json")).json() as { version: string }).version;
7
7
  const render = async (path: string) => (await readFile(join(root, "templates/agents", path), "utf8")).replace("{{VERSION}}", version);
8
- // .agents/skills is the vendor-neutral Agent Skills path (Codex, Copilot, Cursor, Gemini CLI, …); .claude/skills is Claude Code's.
9
- const skillPaths = [".agents/skills/atdd/SKILL.md", ".claude/skills/atdd/SKILL.md"];
8
+ const legacySkillPaths = [".agents/skills/atdd/SKILL.md", ".claude/skills/atdd/SKILL.md"];
10
9
  // AGENTS.md is read by Codex, Cursor and most agents; CLAUDE.md by Claude Code. Both carry the same block.
11
10
  export const instructionPaths = ["AGENTS.md", "CLAUDE.md"];
12
11
  const block = /<!-- atdd-bun:start[\s\S]*?<!-- atdd-bun:end -->\n?/;
12
+ const generatedSkill = /<!-- Generated by @afokapu\/atdd-bun /;
13
13
  /** `current` with the managed block replaced, or appended after at least one blank line; `current` itself is kept. */
14
14
  const withBlock = (current: string, managed: string) => block.test(current) ? current.replace(block, managed) : `${current}${current ? "\n\n".slice(current.match(/\n{0,2}$/)![0].length) : ""}${managed}`;
15
15
 
16
- /** Write the ATDD skill for every agent and a managed pointer block in AGENTS.md and CLAUDE.md. Existing files
17
- * and an existing block are kept unless `replace`; the rest of each instruction file is never touched. */
16
+ /** Write the managed ATDD block in AGENTS.md and CLAUDE.md. Existing files and an existing block are kept unless
17
+ * `replace`; the rest of each instruction file is never touched. Generated legacy skills are retired automatically. */
18
18
  export async function agentInit(repo = process.cwd(), replace = false) {
19
- const written: string[] = [], kept: string[] = [];
20
- for (const path of skillPaths) {
19
+ const written: string[] = [], kept: string[] = [], retired: string[] = [];
20
+ for (const path of legacySkillPaths) {
21
21
  const output = join(repo, path);
22
- if (existsSync(output) && !replace) { kept.push(output); continue; }
23
- await mkdir(dirname(output), { recursive: true }); await writeFile(output, await render("atdd/SKILL.md")); written.push(output);
22
+ if (!existsSync(output) || (!replace && !generatedSkill.test(await readFile(output, "utf8")))) continue;
23
+ await rm(output); retired.push(output);
24
24
  }
25
25
  const managed = await render("AGENTS.block.md");
26
26
  for (const path of instructionPaths) {
@@ -28,12 +28,12 @@ export async function agentInit(repo = process.cwd(), replace = false) {
28
28
  if (block.test(current) && !replace) { kept.push(file); continue; }
29
29
  await writeFile(file, withBlock(current, managed)); written.push(file);
30
30
  }
31
- if (!written.length) return { ok: false, message: `${kept.join(", ")} exist; use --replace` };
32
- return { ok: true, message: written.join("\n") };
31
+ if (!written.length && !retired.length) return { ok: false, message: `${kept.join(", ")} exist; use --replace` };
32
+ return { ok: true, message: [...written, ...retired.map(path => `${path} removed`)].join("\n") };
33
33
  }
34
34
  export async function agentStatus(repo = process.cwd()) {
35
- const expected = skillPaths;
36
- const missing = expected.map(path => join(repo, path)).filter(path => !existsSync(path));
35
+ const missing: string[] = [];
37
36
  for (const path of instructionPaths) { const agents = join(repo, path); if (!existsSync(agents) || !block.test(await readFile(agents, "utf8"))) missing.push(`${agents} (atdd-bun block)`); }
38
- return { ok: !missing.length, message: missing.length ? `missing: ${missing.join(", ")}` : [...expected, ...instructionPaths].map(path => join(repo, path)).join("\n") };
37
+ for (const path of legacySkillPaths) { const skill = join(repo, path); if (existsSync(skill) && generatedSkill.test(await readFile(skill, "utf8"))) missing.push(`${skill} (retired generated skill)`); }
38
+ return { ok: !missing.length, message: missing.length ? `missing: ${missing.join(", ")}` : instructionPaths.map(path => join(repo, path)).join("\n") };
39
39
  }
package/src/cli.ts CHANGED
@@ -8,6 +8,7 @@ import { journeyDocs } from "./journey-docs";
8
8
  import { adrRegister } from "./docs-capability";
9
9
  import { releaseCheck } from "./release";
10
10
  import { initializeRepository } from "./setup";
11
+ import { profileRegistries } from "./profile-registry";
11
12
 
12
13
  const args = process.argv.slice(2);
13
14
  const usage = {
@@ -19,6 +20,7 @@ const usage = {
19
20
  "atdd-bun worktree <start|finish|status>",
20
21
  "atdd-bun ci <init|status> [--replace]",
21
22
  "atdd-bun agent <init|status> [--replace]",
23
+ "atdd-bun profiles registry [--check]",
22
24
  "atdd-bun integrity [init|status] [--replace]",
23
25
  "atdd-bun docs journeys [--out <dir>] [--check] [--force]",
24
26
  "atdd-bun docs adr-register [--check]",
@@ -94,6 +96,11 @@ if (args[0] === "agent") {
94
96
  const result = args[1] === "init" ? await agentInit(process.cwd(), args.includes("--replace")) : args[1] === "status" ? await agentStatus() : fail("agent requires init or status");
95
97
  console[result.ok ? "log" : "error"](result.message); process.exit(result.ok ? 0 : 1);
96
98
  }
99
+ if (args[0] === "profiles") {
100
+ if (args[1] !== "registry") fail("profiles requires registry");
101
+ const result = await profileRegistries({ check: args.includes("--check") });
102
+ console[result.ok ? "log" : "error"](result.message); process.exit(result.ok ? 0 : 1);
103
+ }
97
104
  if (args[0] === "release") {
98
105
  if (args[1] !== "check") fail("release requires check");
99
106
  const result = await releaseCheck(); console[result.ok ? "log" : "error"](result.message); process.exit(result.ok ? 0 : 1);
package/src/enforce.ts CHANGED
@@ -5,6 +5,7 @@ import { join, resolve } from "node:path";
5
5
  import { topologyFor } from "./topology";
6
6
 
7
7
  export type Profile = "traceability" | "topology" | "docs" | "planner" | "telemetry" | "coder" | "tester" | "security" | "architecture" | "metrics" | "runtime" | "interlocking" | "htmx" | "design" | "all";
8
+ export type ConcreteProfile = Exclude<Profile, "all">;
8
9
 
9
10
  export type Violation = {
10
11
  rule_id: string;
@@ -22,7 +23,7 @@ export type EnforcementConfig = {
22
23
  profiles?: Profile[];
23
24
  };
24
25
 
25
- const profiles: Record<Exclude<Profile, "all">, string[]> = {
26
+ export const profileImplementations: Record<ConcreteProfile, string[]> = {
26
27
  traceability: ["atdd_traceability_closure"],
27
28
  topology: ["atdd_topology"],
28
29
  docs: ["planner_docs_capability"],
@@ -39,9 +40,8 @@ const profiles: Record<Exclude<Profile, "all">, string[]> = {
39
40
  design: ["bun_design_system_detector", "bun_responsive_detector"],
40
41
  };
41
42
 
42
- type ConcreteProfile = Exclude<Profile, "all">;
43
43
  /** Every profile but `all`. */
44
- export const concreteProfiles = Object.keys(profiles) as ConcreteProfile[];
44
+ export const concreteProfiles = Object.keys(profileImplementations) as ConcreteProfile[];
45
45
  /** Profile names accepted by the CLI and public integrations. */
46
46
  export const profileNames = [...concreteProfiles, "all"] as Profile[];
47
47
 
@@ -68,10 +68,10 @@ export function implementationsFor(requested: Profile[] = ["all"]): string[] {
68
68
  const selected = new Set<string>();
69
69
  for (const profile of requested) {
70
70
  if (profile === "all") {
71
- for (const ids of Object.values(profiles)) ids.forEach((id) => selected.add(id));
71
+ for (const ids of Object.values(profileImplementations)) ids.forEach((id) => selected.add(id));
72
72
  continue;
73
73
  }
74
- const ids = profiles[profile];
74
+ const ids = profileImplementations[profile];
75
75
  if (!ids) throw new Error(`unknown enforcement profile: ${profile}`);
76
76
  ids.forEach((id) => selected.add(id));
77
77
  }
@@ -79,10 +79,10 @@ export function implementationsFor(requested: Profile[] = ["all"]): string[] {
79
79
  }
80
80
 
81
81
  /** The rule ids a detector's manifest declares it emits (emits_rule_ids and api_emits_rule_ids). */
82
- export async function declaredRuleIds(implementation: string): Promise<Set<string>> {
82
+ export async function declaredRuleIds(implementation: string, detectors = detectorRoot): Promise<Set<string>> {
83
83
  const ids = new Set<string>();
84
84
  let inList = false;
85
- for (const line of (await readFile(join(detectorRoot, implementation, "atdd.implementation.yaml"), "utf8")).split("\n")) {
85
+ for (const line of (await readFile(join(detectors, implementation, "atdd.implementation.yaml"), "utf8")).split("\n")) {
86
86
  if (line === "emits_rule_ids:" || line === "api_emits_rule_ids:") { inList = true; continue; }
87
87
  if (/^[A-Za-z_][\w-]*:/.test(line)) { inList = false; continue; }
88
88
  const match = inList && line.match(/^\s*-\s+([^#\s]+)/);
package/src/hooks.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { chmod, mkdir, readdir, readFile, realpath, rm, writeFile } from "node:fs/promises";
2
2
  import { existsSync } from "node:fs";
3
- import { dirname, join, relative, resolve } from "node:path";
3
+ import { basename, dirname, join, relative, resolve } from "node:path";
4
4
  import { enabledProfiles, enforce } from "./enforce";
5
5
  import { topologyFor } from "./topology";
6
6
  import { JOURNEY_DOCS_DIR } from "./journey-docs";
7
7
 
8
8
  export type WorktreePolicy = { enabled: boolean; root: string; primary_directory: string; primary_branch: string; require_linked_worktree: boolean };
9
9
  export type HookPolicy = { max_staged_files: number; max_staged_changed_lines: number; max_commits_per_push: number; max_registry_removed_lines: number; registry_paths: string[]; protected_branches: string[]; require_plan_reference: boolean; require_traceability: boolean; worktrees: WorktreePolicy };
10
- export const defaultWorktreePolicy: WorktreePolicy = { enabled: false, root: "../worktrees", primary_directory: "main", primary_branch: "main", require_linked_worktree: true };
10
+ export const defaultWorktreePolicy: WorktreePolicy = { enabled: false, root: "../worktrees/{repo}", primary_directory: ".", primary_branch: "main", require_linked_worktree: true };
11
11
  export const defaultHookPolicy: HookPolicy = { max_staged_files: 20, max_staged_changed_lines: 350, max_commits_per_push: 10, max_registry_removed_lines: 350, registry_paths: ["plan/_*.yaml", "plan/_*.yml", "contracts/_*.yaml", "contracts/_*.yml"], protected_branches: ["main", "master"], require_plan_reference: true, require_traceability: true, worktrees: defaultWorktreePolicy };
12
12
  export const hookEvents = ["pre-commit", "commit-msg", "pre-push", "pre-merge-commit", "post-commit", "post-merge"] as const;
13
13
  export type HookEvent = typeof hookEvents[number];
@@ -33,7 +33,7 @@ async function validation(root: string, changed: string[], full: boolean, regist
33
33
  const inside = (parent: string, child: string) => { const path = relative(parent, child); return path === "" || (path !== ".." && !path.startsWith("../") && !path.startsWith("..\\")); };
34
34
  async function primaryRoot(root: string): Promise<string | null> { const result = await git(root, ["rev-parse", "--git-common-dir"]); if (result.code) return null; return dirname(resolve(root, result.out)); }
35
35
  export type WorktreeLayout = { primary: string; worktreeRoot: string; current: string; branch: string };
36
- async function layout(root: string, cfg: WorktreePolicy): Promise<WorktreeLayout | { error: string }> { const current = await realpath(resolve(root)), commonRoot = await primaryRoot(current); if (!commonRoot) return { error: "not inside a Git worktree" }; const primary = await realpath(commonRoot), expectedPrimary = join(dirname(primary), cfg.primary_directory); if (primary !== expectedPrimary) return { error: `primary checkout must be ${expectedPrimary}, not ${primary}` }; const branch = (await git(primary, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out; if (branch !== cfg.primary_branch) return { error: `primary checkout ${primary} must be on ${cfg.primary_branch}, not ${branch || "a detached HEAD"}` }; return { primary, worktreeRoot: resolve(primary, cfg.root), current, branch: (await git(current, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out }; }
36
+ async function layout(root: string, cfg: WorktreePolicy): Promise<WorktreeLayout | { error: string }> { const current = await realpath(resolve(root)), commonRoot = await primaryRoot(current); if (!commonRoot) return { error: "not inside a Git worktree" }; const primary = await realpath(commonRoot), expectedPrimary = cfg.primary_directory === "." ? primary : join(dirname(primary), cfg.primary_directory); if (primary !== expectedPrimary) return { error: `primary checkout must be ${expectedPrimary}, not ${primary}` }; const branch = (await git(primary, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out; if (branch !== cfg.primary_branch) return { error: `primary checkout ${primary} must be on ${cfg.primary_branch}, not ${branch || "a detached HEAD"}` }; return { primary, worktreeRoot: resolve(primary, cfg.root.replaceAll("{repo}", basename(primary))), current, branch: (await git(current, ["symbolic-ref", "--quiet", "--short", "HEAD"])).out }; }
37
37
  async function worktreeCommitPolicy(root: string, cfg: WorktreePolicy): Promise<string | null> { if (!cfg.enabled || !cfg.require_linked_worktree) return null; const state = await layout(root, cfg); if ("error" in state) return state.error; if (state.current === state.primary) return `commits must use a linked worktree beneath ${state.worktreeRoot}; run: atdd-bun worktree start <branch>`; if (!inside(state.worktreeRoot, state.current)) return `linked worktree ${state.current} is outside required root ${state.worktreeRoot}`; if (!state.branch) return "commits require a named branch, not a detached HEAD"; return null; }
38
38
 
39
39
  export async function runHook(event: HookEvent, root = process.cwd(), args: string[] = [], stdin = "") {
package/src/index.ts CHANGED
@@ -17,5 +17,6 @@ export type { Gap, Journey, JourneyModel, JourneyPath } from "./journey-docs";
17
17
  export { checkIntegrity, checkInstalledPackage, formatIntegrity, integrityInit, loosenedPolicy, writeManifest } from "./integrity";
18
18
  export type { IntegrityFinding, IntegrityOptions } from "./integrity";
19
19
  export { releaseCheck } from "./release";
20
+ export { profileRegistries, renderProfileRegistries, PROFILE_REGISTRY_DIR } from "./profile-registry";
20
21
  export { defaultTopology, topologyFor } from "./topology";
21
22
  export type { Topology } from "./topology";
package/src/integrity.ts CHANGED
@@ -18,7 +18,6 @@ const PACKAGE = "@afokapu/atdd-bun";
18
18
  const ownRoot = resolve(import.meta.dir, "..");
19
19
  export const MANIFEST = "integrity.json";
20
20
  export const TEST_FILE = "atdd-bun.integrity.test.ts";
21
- const SKILLS = [".agents/skills/atdd/SKILL.md", ".claude/skills/atdd/SKILL.md"];
22
21
  const WORKFLOW = ".github/workflows/atdd-bun.yml";
23
22
  const BLOCK = /<!-- atdd-bun:start[\s\S]*?<!-- atdd-bun:end -->/;
24
23
  // Generated files carry the version that wrote them; an upgrade must not read as tampering.
@@ -99,7 +98,6 @@ async function checkGenerated(root: string, packageRoot: string, skipWorkflow =
99
98
  if (skipWorkflow) { /* rendered from atdd-bun.yaml, which does not parse: reported by the caller */ }
100
99
  else if (!existsSync(workflow)) findings.push({ file: WORKFLOW, detail: "is missing", restore: "bun run atdd-bun ci init --replace" });
101
100
  else if (unstamp(await readFile(workflow, "utf8")) !== unstamp(await renderWorkflow(root))) findings.push({ file: WORKFLOW, detail: "was edited; it must match what the package generates (protected_branches decides its push branches)", restore: "bun run atdd-bun ci init --replace" });
102
- for (const skill of SKILLS) await same(skill, "templates/agents/atdd/SKILL.md", "bun run atdd-bun agent init --replace");
103
101
  await same(relative(root, await testFilePath(root)), "templates/agents/atdd-bun.integrity.test.ts", "bun run atdd-bun integrity init --replace");
104
102
  const canonical = (await readFile(join(packageRoot, "templates/agents/AGENTS.block.md"), "utf8")).match(BLOCK)![0];
105
103
  for (const file of instructionPaths) {