@afokapu/atdd-bun 0.6.0 → 0.6.1
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/conventions/atdd-bun.planner/atdd-bun.planner.acceptance-identity.convention.yaml +27 -0
- package/conventions/atdd-bun.planner/atdd-bun.planner.identity-required.convention.yaml +28 -0
- package/conventions/atdd-bun.planner/atdd-bun.planner.identity-unique.convention.yaml +27 -0
- package/conventions/atdd-bun.planner/atdd-bun.planner.interlocking-participant-resolves.convention.yaml +28 -0
- package/conventions/atdd-bun.planner/atdd-bun.planner.parse.convention.yaml +27 -0
- package/conventions/atdd-bun.planner/atdd-bun.planner.reference-resolves.convention.yaml +28 -0
- package/conventions/atdd-bun.planner/atdd-bun.planner.schema.convention.yaml +30 -0
- package/conventions/atdd-bun.planner/atdd-bun.planner.train-wagon-resolves.convention.yaml +26 -0
- package/conventions/atdd-bun.topology/atdd-bun.topology.e2e-location.convention.yaml +38 -0
- package/conventions/atdd-bun.topology/atdd-bun.topology.feature-source-coverage.convention.yaml +30 -0
- package/conventions/atdd-bun.topology/atdd-bun.topology.feature-test-coverage.convention.yaml +29 -0
- package/conventions/atdd-bun.topology/atdd-bun.topology.plan-location.convention.yaml +31 -0
- package/conventions/atdd-bun.topology/atdd-bun.topology.source-location.convention.yaml +33 -0
- package/conventions/atdd-bun.topology/atdd-bun.topology.test-location.convention.yaml +34 -0
- package/detectors/atdd_topology/atdd.implementation.yaml +3 -0
- package/detectors/atdd_topology/detect.mjs +14 -3
- package/detectors/atdd_topology/fixtures/clean/e2e/journeys/checkout.journey.test.ts +1 -0
- package/detectors/atdd_topology/fixtures/clean/plan/orders/E001.yaml +2 -0
- package/detectors/planner_docs_capability/atdd.implementation.yaml +7 -0
- package/detectors/planner_plan_integrity/atdd.implementation.yaml +1 -0
- package/detectors/planner_plan_integrity/detect.mjs +4 -1
- package/detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/_interlockings/checkout.yaml +6 -0
- package/detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/_interlockings/unnamed.yaml +2 -0
- package/detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/dup-a.yaml +7 -0
- package/detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/dup-b.yaml +6 -0
- package/detectors/planner_plan_integrity/fixtures/dirty/plan/orders/E001.yaml +5 -0
- package/detectors/planner_schema_validation/fixtures/dirty/plan/_trains/missing-description.yaml +5 -0
- package/integrity.json +33 -13
- package/package.json +1 -1
- package/planner-nodes/ENFORCEMENT_SCOPE.yaml +11 -1
- package/relationships.yaml +224 -0
- package/src/docs-capability.ts +3 -0
- package/src/enforce.ts +22 -0
- package/src/planner-kernel.ts +2 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.acceptance-identity
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Embedded acceptances carry an acceptance identity
|
|
6
|
+
statement: Every acceptance embedded in a WMBT is an object whose `identity.urn` starts with `acc:`. Package rule,
|
|
7
|
+
strict.
|
|
8
|
+
terms:
|
|
9
|
+
- term_id: package_rule
|
|
10
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
11
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
12
|
+
relationship edges.'
|
|
13
|
+
content:
|
|
14
|
+
summary: Embedded acceptances carry an acceptance identity.
|
|
15
|
+
normative_text: Tests bind acceptances by that identity; one without it can never be proven.
|
|
16
|
+
fix_hint: 'Give the acceptance `identity: { urn: acc:<wagon>:<id> }`.'
|
|
17
|
+
exceptions:
|
|
18
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
19
|
+
metadata:
|
|
20
|
+
aliases:
|
|
21
|
+
- ATDD-BUN-PLANNER-ACCEPTANCE-IDENTITY
|
|
22
|
+
severity: 3
|
|
23
|
+
disposition: strict
|
|
24
|
+
introduced_in: 0.1.0
|
|
25
|
+
implementation:
|
|
26
|
+
type: validator
|
|
27
|
+
ref: planner_plan_integrity
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.identity-required
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Plan artifacts carry their identity
|
|
6
|
+
statement: Every plan artifact whose kind the planner recognizes carries its canonical identity (`train_id`, `interlocking_id`,
|
|
7
|
+
`journey_id`, or `urn`). Package rule, strict.
|
|
8
|
+
terms:
|
|
9
|
+
- term_id: package_rule
|
|
10
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
11
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
12
|
+
relationship edges.'
|
|
13
|
+
content:
|
|
14
|
+
summary: Plan artifacts carry their identity.
|
|
15
|
+
normative_text: An artifact without an identity cannot be referenced, bound, or traced; every rule that resolves
|
|
16
|
+
references silently skips it.
|
|
17
|
+
fix_hint: 'Add the identity field for the artifact kind, for example `interlocking_id: interlocking:checkout`.'
|
|
18
|
+
exceptions:
|
|
19
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
20
|
+
metadata:
|
|
21
|
+
aliases:
|
|
22
|
+
- ATDD-BUN-PLANNER-IDENTITY-REQUIRED
|
|
23
|
+
severity: 3
|
|
24
|
+
disposition: strict
|
|
25
|
+
introduced_in: 0.1.0
|
|
26
|
+
implementation:
|
|
27
|
+
type: validator
|
|
28
|
+
ref: planner_plan_integrity
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.identity-unique
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Plan identities are unique
|
|
6
|
+
statement: No two plan artifacts declare the same identity (`train_id`, `interlocking_id`, `journey_id`, `urn`,
|
|
7
|
+
…). Package rule, strict.
|
|
8
|
+
terms:
|
|
9
|
+
- term_id: package_rule
|
|
10
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
11
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
12
|
+
relationship edges.'
|
|
13
|
+
content:
|
|
14
|
+
summary: Plan identities are unique.
|
|
15
|
+
normative_text: A reference to a duplicated identity resolves to whichever file was read first.
|
|
16
|
+
fix_hint: Rename one of the artifacts, or merge them.
|
|
17
|
+
exceptions:
|
|
18
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
19
|
+
metadata:
|
|
20
|
+
aliases:
|
|
21
|
+
- ATDD-BUN-PLANNER-IDENTITY-UNIQUE
|
|
22
|
+
severity: 3
|
|
23
|
+
disposition: strict
|
|
24
|
+
introduced_in: 0.1.0
|
|
25
|
+
implementation:
|
|
26
|
+
type: validator
|
|
27
|
+
ref: planner_plan_integrity
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.interlocking-participant-resolves
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Interlocking participants resolve
|
|
6
|
+
statement: Every participant and lifeline an interlocking lists is either an external actor (`user:…`, `system:…`)
|
|
7
|
+
or a typed reference to an artifact the plan declares. Package rule, strict.
|
|
8
|
+
terms:
|
|
9
|
+
- term_id: package_rule
|
|
10
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
11
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
12
|
+
relationship edges.'
|
|
13
|
+
content:
|
|
14
|
+
summary: Interlocking participants resolve.
|
|
15
|
+
normative_text: A routing decision between participants the plan does not declare cannot be traced to anything
|
|
16
|
+
that implements it.
|
|
17
|
+
fix_hint: Declare the participant, or correct the lifeline.
|
|
18
|
+
exceptions:
|
|
19
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
20
|
+
metadata:
|
|
21
|
+
aliases:
|
|
22
|
+
- ATDD-BUN-PLANNER-INTERLOCKING-PARTICIPANT-RESOLVES
|
|
23
|
+
severity: 3
|
|
24
|
+
disposition: strict
|
|
25
|
+
introduced_in: 0.1.0
|
|
26
|
+
implementation:
|
|
27
|
+
type: validator
|
|
28
|
+
ref: planner_plan_integrity
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.parse
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Plan artifacts parse
|
|
6
|
+
statement: Every `.yaml`, `.yml` or `.json` file under `plan/` parses. Package rule, strict.
|
|
7
|
+
terms:
|
|
8
|
+
- term_id: package_rule
|
|
9
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
10
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
11
|
+
relationship edges.'
|
|
12
|
+
content:
|
|
13
|
+
summary: Plan artifacts parse.
|
|
14
|
+
normative_text: An artifact that does not parse is invisible to every other rule, so its defects hide behind one
|
|
15
|
+
error.
|
|
16
|
+
fix_hint: Fix the syntax the finding reports.
|
|
17
|
+
exceptions:
|
|
18
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
19
|
+
metadata:
|
|
20
|
+
aliases:
|
|
21
|
+
- ATDD-BUN-PLANNER-PARSE
|
|
22
|
+
severity: 3
|
|
23
|
+
disposition: strict
|
|
24
|
+
introduced_in: 0.1.0
|
|
25
|
+
implementation:
|
|
26
|
+
type: validator
|
|
27
|
+
ref: planner_plan_integrity
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.reference-resolves
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Typed plan references resolve
|
|
6
|
+
statement: Every typed reference an artifact makes (`wagon:`, `feature:`, `wmbt:`, `train:`, `interlocking:`, `journey:`)
|
|
7
|
+
names an artifact the plan declares. `contract:` references are resolved by the contract registry rules instead.
|
|
8
|
+
Package rule, strict.
|
|
9
|
+
terms:
|
|
10
|
+
- term_id: package_rule
|
|
11
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
12
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
13
|
+
relationship edges.'
|
|
14
|
+
content:
|
|
15
|
+
summary: Typed plan references resolve.
|
|
16
|
+
normative_text: A reference to nothing is a relationship the plan claims and cannot show.
|
|
17
|
+
fix_hint: Declare the referenced artifact, or correct the reference.
|
|
18
|
+
exceptions:
|
|
19
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
20
|
+
metadata:
|
|
21
|
+
aliases:
|
|
22
|
+
- ATDD-BUN-PLANNER-REFERENCE-RESOLVES
|
|
23
|
+
severity: 3
|
|
24
|
+
disposition: strict
|
|
25
|
+
introduced_in: 0.1.0
|
|
26
|
+
implementation:
|
|
27
|
+
type: validator
|
|
28
|
+
ref: planner_plan_integrity
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.schema
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Plan artifacts satisfy their package-shipped schema
|
|
6
|
+
statement: Every recognized plan artifact (wagon, feature, WMBT and its acceptances, train, train interlocking,
|
|
7
|
+
journey topology, registries) satisfies its JSON Schema in `planner-schemas/`. A violation that belongs to a canonical
|
|
8
|
+
rule is reported under that rule (for example `planner.train.naming`); every other violation is reported here.
|
|
9
|
+
Package rule, strict.
|
|
10
|
+
terms:
|
|
11
|
+
- term_id: package_rule
|
|
12
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
13
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
14
|
+
relationship edges.'
|
|
15
|
+
content:
|
|
16
|
+
summary: Plan artifacts satisfy their package-shipped schema.
|
|
17
|
+
normative_text: The schemas are the shape every other rule assumes. An artifact that breaks its schema breaks
|
|
18
|
+
those assumptions silently.
|
|
19
|
+
fix_hint: Correct the field the finding names, as the schema requires.
|
|
20
|
+
exceptions:
|
|
21
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
22
|
+
metadata:
|
|
23
|
+
aliases:
|
|
24
|
+
- ATDD-BUN-PLANNER-SCHEMA
|
|
25
|
+
severity: 3
|
|
26
|
+
disposition: strict
|
|
27
|
+
introduced_in: 0.1.0
|
|
28
|
+
implementation:
|
|
29
|
+
type: validator
|
|
30
|
+
ref: planner_schema_validation
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.planner.train-wagon-resolves
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Train wagons resolve
|
|
6
|
+
statement: Every wagon a train lists under `wagons:` is declared as `wagon:<slug>`. Package rule, strict.
|
|
7
|
+
terms:
|
|
8
|
+
- term_id: package_rule
|
|
9
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
10
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
11
|
+
relationship edges.'
|
|
12
|
+
content:
|
|
13
|
+
summary: Train wagons resolve.
|
|
14
|
+
normative_text: A train ordering a wagon that does not exist cannot run.
|
|
15
|
+
fix_hint: Declare the wagon, or remove it from the train.
|
|
16
|
+
exceptions:
|
|
17
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
18
|
+
metadata:
|
|
19
|
+
aliases:
|
|
20
|
+
- ATDD-BUN-PLANNER-TRAIN-WAGON-RESOLVES
|
|
21
|
+
severity: 3
|
|
22
|
+
disposition: strict
|
|
23
|
+
introduced_in: 0.1.0
|
|
24
|
+
implementation:
|
|
25
|
+
type: validator
|
|
26
|
+
ref: planner_plan_integrity
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.topology.e2e-location
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Every interlocking route and exposed journey has an end-to-end test at its canonical path
|
|
6
|
+
statement: 'Every interlocking route has `<e2e_root>/interlockings/<interlocking>/<route>.routes.test.ts`, and every
|
|
7
|
+
exposed journey `<e2e_root>/journeys/<journey>.journey.test.ts`, each carrying a `test:{wagon}:{feature}:{acceptance}`
|
|
8
|
+
URN for a declared feature and binding `Acceptance: acc:{wagon}:{acceptance}` declared by a WMBT. On an exposed
|
|
9
|
+
journey whose surfaces include `frontend`, a Playwright spec (`*.e2e.ts`) substitutes for the Bun test when it
|
|
10
|
+
is bound to the route''s train (`// Train:`) or the journey (`// Journey:`) and carries a valid E2E or SMOKE test
|
|
11
|
+
URN for that same subject (`test:train:<id>:E2E-NNN-…`, `test:journey:<id>:SMOKE-NNN-…`). A browser spec carries
|
|
12
|
+
its identity as that URN instead of an `Acceptance:` line, which `tester.htmx.journey-no-acceptance-marker` forbids
|
|
13
|
+
on journey specs. Package rule, strict.'
|
|
14
|
+
terms:
|
|
15
|
+
- term_id: package_rule
|
|
16
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
17
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
18
|
+
relationship edges.'
|
|
19
|
+
- term_id: topology_settings
|
|
20
|
+
text: the `topology:` keys `plan_root`, `source_root`, `test_root`, `e2e_root` in `atdd-bun.yaml` (defaults `plan`,
|
|
21
|
+
`src/wagons`, `tests/wagons`, `e2e`).
|
|
22
|
+
content:
|
|
23
|
+
summary: Every interlocking route and exposed journey has an end-to-end test at its canonical path.
|
|
24
|
+
normative_text: 'One behaviour, one end-to-end proof at a path derived from the plan: a missing proof is found
|
|
25
|
+
by path, not by searching.'
|
|
26
|
+
fix_hint: Add the test at the path the finding names, or, on a frontend journey, a Playwright spec bound with
|
|
27
|
+
`// Train:` or `// Journey:`.
|
|
28
|
+
exceptions:
|
|
29
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
30
|
+
metadata:
|
|
31
|
+
aliases:
|
|
32
|
+
- ATDD-BUN-TOPOLOGY-E2E-LOCATION
|
|
33
|
+
severity: 3
|
|
34
|
+
disposition: strict
|
|
35
|
+
introduced_in: 0.6.0
|
|
36
|
+
implementation:
|
|
37
|
+
type: validator
|
|
38
|
+
ref: atdd_topology
|
package/conventions/atdd-bun.topology/atdd-bun.topology.feature-source-coverage.convention.yaml
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.topology.feature-source-coverage
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: A feature with WMBTs has source
|
|
6
|
+
statement: A feature that owns WMBTs has at least one source component beneath `<source_root>/<wagon>/features/<feature>/`.
|
|
7
|
+
Package rule, strict.
|
|
8
|
+
terms:
|
|
9
|
+
- term_id: package_rule
|
|
10
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
11
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
12
|
+
relationship edges.'
|
|
13
|
+
- term_id: topology_settings
|
|
14
|
+
text: the `topology:` keys `plan_root`, `source_root`, `test_root`, `e2e_root` in `atdd-bun.yaml` (defaults `plan`,
|
|
15
|
+
`src/wagons`, `tests/wagons`, `e2e`).
|
|
16
|
+
content:
|
|
17
|
+
summary: A feature with WMBTs has source.
|
|
18
|
+
normative_text: A feature with behaviour to deliver and no implementation is a gap the plan already names.
|
|
19
|
+
fix_hint: Implement the feature beneath its directory, or move the WMBTs to the feature that delivers them.
|
|
20
|
+
exceptions:
|
|
21
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
22
|
+
metadata:
|
|
23
|
+
aliases:
|
|
24
|
+
- ATDD-BUN-TOPOLOGY-FEATURE-SOURCE-COVERAGE
|
|
25
|
+
severity: 3
|
|
26
|
+
disposition: strict
|
|
27
|
+
introduced_in: 0.6.0
|
|
28
|
+
implementation:
|
|
29
|
+
type: validator
|
|
30
|
+
ref: atdd_topology
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.topology.feature-test-coverage
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: A feature with WMBTs has a test bound to one of its acceptances
|
|
6
|
+
statement: A feature that owns WMBTs has at least one test bound to one of its acceptances. Package rule, strict.
|
|
7
|
+
terms:
|
|
8
|
+
- term_id: package_rule
|
|
9
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
10
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
11
|
+
relationship edges.'
|
|
12
|
+
- term_id: topology_settings
|
|
13
|
+
text: the `topology:` keys `plan_root`, `source_root`, `test_root`, `e2e_root` in `atdd-bun.yaml` (defaults `plan`,
|
|
14
|
+
`src/wagons`, `tests/wagons`, `e2e`).
|
|
15
|
+
content:
|
|
16
|
+
summary: A feature with WMBTs has a test bound to one of its acceptances.
|
|
17
|
+
normative_text: Behaviour declared and never tested is a silent gap.
|
|
18
|
+
fix_hint: Add a test beneath the feature bound to one of its acceptances.
|
|
19
|
+
exceptions:
|
|
20
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
21
|
+
metadata:
|
|
22
|
+
aliases:
|
|
23
|
+
- ATDD-BUN-TOPOLOGY-FEATURE-TEST-COVERAGE
|
|
24
|
+
severity: 3
|
|
25
|
+
disposition: strict
|
|
26
|
+
introduced_in: 0.6.0
|
|
27
|
+
implementation:
|
|
28
|
+
type: validator
|
|
29
|
+
ref: atdd_topology
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.topology.plan-location
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Wagons, features and WMBTs live at their canonical plan paths
|
|
6
|
+
statement: Every wagon is declared at `<plan_root>/<wagon>/_<wagon>.yaml`, every feature at `<plan_root>/<wagon>/<feature>.yaml`,
|
|
7
|
+
and every WMBT at `<plan_root>/<wagon>/<WMBT>.yaml`. Package rule, strict.
|
|
8
|
+
terms:
|
|
9
|
+
- term_id: package_rule
|
|
10
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
11
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
12
|
+
relationship edges.'
|
|
13
|
+
- term_id: topology_settings
|
|
14
|
+
text: the `topology:` keys `plan_root`, `source_root`, `test_root`, `e2e_root` in `atdd-bun.yaml` (defaults `plan`,
|
|
15
|
+
`src/wagons`, `tests/wagons`, `e2e`).
|
|
16
|
+
content:
|
|
17
|
+
summary: Wagons, features and WMBTs live at their canonical plan paths.
|
|
18
|
+
normative_text: The plan is navigated by path as much as by identity. A document at a non-canonical path is found
|
|
19
|
+
by nothing that walks the layout, and two layouts in one repository make every tool guess.
|
|
20
|
+
fix_hint: Move the document to the path the finding names.
|
|
21
|
+
exceptions:
|
|
22
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
23
|
+
metadata:
|
|
24
|
+
aliases:
|
|
25
|
+
- ATDD-BUN-TOPOLOGY-PLAN-LOCATION
|
|
26
|
+
severity: 3
|
|
27
|
+
disposition: strict
|
|
28
|
+
introduced_in: 0.6.0
|
|
29
|
+
implementation:
|
|
30
|
+
type: validator
|
|
31
|
+
ref: atdd_topology
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.topology.source-location
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Feature source lives beneath its feature and carries a component URN
|
|
6
|
+
statement: 'When the plan models at least one wagon, feature or WMBT, every source file beneath `<source_root>`
|
|
7
|
+
carries `URN: component:{wagon}:{feature}:{name}:{frontend|backend}:{domain|application|integration|presentation}`
|
|
8
|
+
and lives beneath `<source_root>/<wagon>/features/<feature>/`, for a feature the plan declares. Package rule,
|
|
9
|
+
strict.'
|
|
10
|
+
terms:
|
|
11
|
+
- term_id: package_rule
|
|
12
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
13
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
14
|
+
relationship edges.'
|
|
15
|
+
- term_id: topology_settings
|
|
16
|
+
text: the `topology:` keys `plan_root`, `source_root`, `test_root`, `e2e_root` in `atdd-bun.yaml` (defaults `plan`,
|
|
17
|
+
`src/wagons`, `tests/wagons`, `e2e`).
|
|
18
|
+
content:
|
|
19
|
+
summary: Feature source lives beneath its feature and carries a component URN.
|
|
20
|
+
normative_text: Source is traced to the feature that owns it by its URN and found by its path. Either missing,
|
|
21
|
+
the feature cannot be shown to have an implementation.
|
|
22
|
+
fix_hint: Add the component URN header and move the file beneath its feature's directory.
|
|
23
|
+
exceptions:
|
|
24
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
25
|
+
metadata:
|
|
26
|
+
aliases:
|
|
27
|
+
- ATDD-BUN-TOPOLOGY-SOURCE-LOCATION
|
|
28
|
+
severity: 3
|
|
29
|
+
disposition: strict
|
|
30
|
+
introduced_in: 0.6.0
|
|
31
|
+
implementation:
|
|
32
|
+
type: validator
|
|
33
|
+
ref: atdd_topology
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
schema_version: 1.1.0
|
|
2
|
+
rule_id: atdd-bun.topology.test-location
|
|
3
|
+
kind: rule
|
|
4
|
+
status: active
|
|
5
|
+
name: Feature tests live beneath their feature and bind a declared acceptance
|
|
6
|
+
statement: 'When the plan models at least one wagon, feature or WMBT, every test beneath `<test_root>` carries `URN:
|
|
7
|
+
test:{wagon}:{feature}:{acceptance}`, lives beneath `<test_root>/<wagon>/features/<feature>/{unit,contract,integration}/`,
|
|
8
|
+
and binds `Acceptance: acc:{wagon}:{acceptance}` declared by a WMBT that the test''s own feature lists. Package
|
|
9
|
+
rule, strict.'
|
|
10
|
+
terms:
|
|
11
|
+
- term_id: package_rule
|
|
12
|
+
text: 'a rule of this package''s own gates (`atdd-bun.*`), realized by a package guard rather than a canonical
|
|
13
|
+
planner node. It is held to the same per-rule checks as every convention: a convention, strict, a failing fixture,
|
|
14
|
+
relationship edges.'
|
|
15
|
+
- term_id: topology_settings
|
|
16
|
+
text: the `topology:` keys `plan_root`, `source_root`, `test_root`, `e2e_root` in `atdd-bun.yaml` (defaults `plan`,
|
|
17
|
+
`src/wagons`, `tests/wagons`, `e2e`).
|
|
18
|
+
content:
|
|
19
|
+
summary: Feature tests live beneath their feature and bind a declared acceptance.
|
|
20
|
+
normative_text: A test proves an acceptance only when it names one that exists; its path says which feature and
|
|
21
|
+
which kind of test it is.
|
|
22
|
+
fix_hint: Add the test URN and Acceptance header, and move the test beneath its feature under unit/, contract/
|
|
23
|
+
or integration/.
|
|
24
|
+
exceptions:
|
|
25
|
+
- Vendored and generated trees (`node_modules`, `dist`, `build`, `_generated`) are never scanned.
|
|
26
|
+
metadata:
|
|
27
|
+
aliases:
|
|
28
|
+
- ATDD-BUN-TOPOLOGY-TEST-LOCATION
|
|
29
|
+
severity: 3
|
|
30
|
+
disposition: strict
|
|
31
|
+
introduced_in: 0.6.0
|
|
32
|
+
implementation:
|
|
33
|
+
type: validator
|
|
34
|
+
ref: atdd_topology
|
|
@@ -21,7 +21,7 @@ function add(rule_id, root, path, evidence, line = 1, source_line = "") { violat
|
|
|
21
21
|
function walk(dir, predicate, found = []) {
|
|
22
22
|
let entries; try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return found; }
|
|
23
23
|
for (const entry of entries) {
|
|
24
|
-
if (["node_modules", ".git", ".atdd", "dist", "build", ".next"].includes(entry.name)) continue;
|
|
24
|
+
if (["node_modules", ".git", ".atdd", "dist", "build", ".next", "_generated"].includes(entry.name)) continue;
|
|
25
25
|
const path = join(dir, entry.name);
|
|
26
26
|
if (entry.isDirectory()) walk(path, predicate, found); else if (predicate(path)) found.push(path);
|
|
27
27
|
}
|
|
@@ -47,6 +47,10 @@ for (const root of roots) {
|
|
|
47
47
|
const wmbts = docs.filter(doc => text(doc.data, "urn").startsWith("wmbt:")).map(doc => { const [, wagon, code] = text(doc.data, "urn").split(":"); return { ...doc, wagon, code, urn: text(doc.data, "urn") }; });
|
|
48
48
|
const featureByUrn = new Map(features.map(feature => [feature.urn, feature])), memberships = new Map(), wmbtOwners = new Map(), sourceByFeature = new Map(), testByFeature = new Map();
|
|
49
49
|
const acceptances = new Set(wmbts.flatMap(wmbt => list(wmbt.data, "acceptances").map(acceptance => text(acceptance?.identity, "urn")).filter(Boolean)));
|
|
50
|
+
// The feature(s) that own each acceptance, through the WMBTs they list: a test is credited only to its owner.
|
|
51
|
+
const acceptanceOwners = new Map();
|
|
52
|
+
for (const feature of features) for (const wmbtUrn of list(feature.data, "wmbts").map(String)) for (const wmbt of wmbts.filter(w => w.urn === wmbtUrn))
|
|
53
|
+
for (const acceptance of list(wmbt.data, "acceptances").map(a => text(a?.identity, "urn")).filter(Boolean)) acceptanceOwners.set(acceptance, [...(acceptanceOwners.get(acceptance) || []), `${feature.wagon}:${feature.slug}`]);
|
|
50
54
|
const trains = new Set(docs.map(doc => text(doc.data, "train_id")).filter(Boolean));
|
|
51
55
|
|
|
52
56
|
for (const wagon of wagons) {
|
|
@@ -95,6 +99,7 @@ for (const root of roots) {
|
|
|
95
99
|
const binding = `acc:${wagon}:${acceptance}`;
|
|
96
100
|
if (h.acceptance.value !== binding) add("atdd-bun.topology.test-location", root, path, `${h.urn.value} must bind Acceptance: ${binding}`, h.acceptance.line, h.acceptance.raw);
|
|
97
101
|
else if (!acceptances.has(binding)) add("atdd-bun.topology.test-location", root, path, `${binding} is not declared by a WMBT in ${cfg.plan_root}/`, h.acceptance.line, h.acceptance.raw);
|
|
102
|
+
else if (acceptanceOwners.has(binding) && !acceptanceOwners.get(binding).includes(`${wagon}:${feature}`)) add("atdd-bun.topology.test-location", root, path, `${binding} belongs to feature:${acceptanceOwners.get(binding).join(", feature:")}, not feature:${wagon}:${feature}; a test lives beneath the feature whose acceptance it proves`, h.acceptance.line, h.acceptance.raw);
|
|
98
103
|
else testByFeature.set(`${wagon}:${feature}`, [...(testByFeature.get(`${wagon}:${feature}`) || []), path]);
|
|
99
104
|
}
|
|
100
105
|
for (const feature of features) if (list(feature.data, "wmbts").length) {
|
|
@@ -111,8 +116,13 @@ for (const root of roots) {
|
|
|
111
116
|
for (const path of walk(e2eRoot, path => /\.e2e\.[cm]?[jt]sx?$/.test(path))) {
|
|
112
117
|
const content = read(path), h = header(path);
|
|
113
118
|
if (!/from\s+["']@playwright\/test["']/.test(content) || !/(^|[^.\w])test(\.(describe|only|skip|fixme|fail|slow|step))?\s*\(/m.test(content)) continue;
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
// A browser spec carries its journey identity as a test URN, not an Acceptance (tester.htmx forbids
|
|
120
|
+
// Acceptance on journey specs). It substitutes only when that URN is a valid E2E or SMOKE proof for the
|
|
121
|
+
// very train or journey it is bound to.
|
|
122
|
+
// The proof is the `// URN:` header, whole: a URN-shaped string elsewhere in the file (a decoy constant) proves nothing.
|
|
123
|
+
const proves = (subject) => h.urn.value.startsWith(`test:${subject}:`) && /^(E2E|SMOKE)-\d{3}-[a-z0-9][a-z0-9-]*$/.test(h.urn.value.slice(`test:${subject}:`.length));
|
|
124
|
+
if (h.train.value && proves(h.train.value)) browserTrains.add(h.train.value);
|
|
125
|
+
if (h.journey.value && proves(h.journey.value)) browserJourneys.add(h.journey.value);
|
|
116
126
|
}
|
|
117
127
|
const interlockingRoutes = new Set();
|
|
118
128
|
const interlockings = new Map(docs.filter(doc => text(doc.data, "interlocking_id").startsWith("interlocking:")).map(doc => [text(doc.data, "interlocking_id"), doc]));
|
|
@@ -153,6 +163,7 @@ for (const root of roots) {
|
|
|
153
163
|
else if (entry && typeof entry === "object" && entry.exposed === true && existsSync(path)) {
|
|
154
164
|
const h = header(path), match = testUrn.exec(h.urn.value);
|
|
155
165
|
if (!match || !declaredFeatures.has(`${match[1]}:${match[2]}`)) add("atdd-bun.topology.e2e-location", root, path, "journey E2E test requires a test:{wagon}:{feature}:{acceptance} URN naming a declared feature", h.urn.line, h.urn.raw);
|
|
166
|
+
else if (h.acceptance.value !== `acc:${match[1]}:${match[3]}` || !acceptances.has(h.acceptance.value)) add("atdd-bun.topology.e2e-location", root, path, `journey E2E test requires Acceptance: acc:${match[1]}:${match[3]}, declared by a WMBT in ${cfg.plan_root}/`, h.acceptance.line, h.acceptance.raw);
|
|
156
167
|
if (!trains.has(h.train.value) || !journeyTrains.has(h.train.value)) add("atdd-bun.topology.e2e-location", root, path, `journey E2E Train: must resolve to a reachable selected train; found ${h.train.value || "<missing>"}`, h.train.line, h.train.raw);
|
|
157
168
|
}
|
|
158
169
|
}
|
|
@@ -34,6 +34,13 @@ emits_rule_ids:
|
|
|
34
34
|
- planner.docs.adr-registry-derived # disposition: advisory (registry drifted from :decides:)
|
|
35
35
|
- planner.docs.journey-view-current # disposition: strict (generated journey view differs from plan/)
|
|
36
36
|
|
|
37
|
+
# Rules the capability API (checkDocumentation) emits and the filesystem report cannot: they need a
|
|
38
|
+
# declaration, a change set, or a render. tests/docs-capability.test.ts triggers each one.
|
|
39
|
+
api_emits_rule_ids:
|
|
40
|
+
- planner.docs.artifact-path-shape
|
|
41
|
+
- planner.docs.undeclared-change
|
|
42
|
+
- planner.docs.reference-integrity
|
|
43
|
+
|
|
37
44
|
# TWO SURFACES, ONE DETECTOR.
|
|
38
45
|
#
|
|
39
46
|
# entrypoint the CAPABILITY seam. Core calls
|
|
@@ -9,6 +9,7 @@ contract_version: "1.0.0"
|
|
|
9
9
|
# fail closed before convention validators can answer.
|
|
10
10
|
emits_rule_ids:
|
|
11
11
|
- atdd-bun.planner.parse
|
|
12
|
+
- atdd-bun.planner.identity-required
|
|
12
13
|
- atdd-bun.planner.identity-unique
|
|
13
14
|
- atdd-bun.planner.reference-resolves
|
|
14
15
|
- atdd-bun.planner.train-wagon-resolves
|
|
@@ -6,6 +6,7 @@ const roots = JSON.parse(process.env.ATDD_SCAN_ROOTS ?? "[]"), report = process.
|
|
|
6
6
|
if (!report) throw new Error("ATDD_VIOLATIONS_REPORT is required");
|
|
7
7
|
const ids = {
|
|
8
8
|
"planner.kernel.parse": "atdd-bun.planner.parse",
|
|
9
|
+
"planner.kernel.identity-required": "atdd-bun.planner.identity-required",
|
|
9
10
|
"planner.kernel.identity-unique": "atdd-bun.planner.identity-unique",
|
|
10
11
|
"planner.kernel.reference-resolves": "atdd-bun.planner.reference-resolves",
|
|
11
12
|
"planner.kernel.train-wagon-resolves": "atdd-bun.planner.train-wagon-resolves",
|
|
@@ -14,7 +15,9 @@ const ids = {
|
|
|
14
15
|
};
|
|
15
16
|
const violations = (await Promise.all(roots.map(validatePlan))).flatMap(graph => graph.findings).map(item => ({
|
|
16
17
|
...item,
|
|
17
|
-
|
|
18
|
+
// Every kernel finding reports under a declared package rule. An unmapped one is a new emission path:
|
|
19
|
+
// fail loudly rather than publish a rule id nothing declares (tests/rule-coverage.test.ts maps them all).
|
|
20
|
+
rule_id: ids[item.rule_id] ?? (() => { throw new Error(`planner_plan_integrity: kernel rule ${item.rule_id} has no declared package rule; map it in detect.mjs`); })(),
|
|
18
21
|
line: 1,
|
|
19
22
|
col: 1,
|
|
20
23
|
source_line: "",
|
package/integrity.json
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.6.
|
|
2
|
+
"version": "0.6.1",
|
|
3
3
|
"files": {
|
|
4
4
|
"HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
|
|
5
5
|
"PLANNER_PORT.md": "fb5935bac8b7ac18994de21e43ace3a5ef8cd55f85b0e3349fca261280054f11",
|
|
6
6
|
"README.md": "622e7768f567099b0e30ac82f2053c165b8023e571966fc327f77f985ae1be0d",
|
|
7
7
|
"bunfig.toml": "b9fc65eca9014c5179380259d70e76385a6a80788fa9a2df5fb3eaa5554fd2fe",
|
|
8
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.acceptance-identity.convention.yaml": "81c5c773d5ee15e8d99a2c237aa9846b110b2533df45ebf407d0e3ec6ed3dd03",
|
|
9
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.identity-required.convention.yaml": "e96d7c1455d0c221072d82e7f2b55da1c4f9ceb17718eb6ed96affdeef37675b",
|
|
10
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.identity-unique.convention.yaml": "409ba1d035ed46bc45f3627b62fd3a6a5967d984cc4c071c41be3f3fa887c6d6",
|
|
11
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.interlocking-participant-resolves.convention.yaml": "fef62f58ad2cd6b036556cfbe17d32fedcd13656eb04c0747e955af7f954ff02",
|
|
12
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.parse.convention.yaml": "953ccd7de6d129f1bd17ce680a1a4819bff6d57b379aaa03323bb22cf255ae08",
|
|
13
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.reference-resolves.convention.yaml": "b96927d269692c29c02fee38ecbeb999480791f8dc7486c4072a9ff30183d7d6",
|
|
14
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.schema.convention.yaml": "16d0a26451758b759e6ca1f5b4f38ae1feff129688306ffcc2f70f6d3974b591",
|
|
15
|
+
"conventions/atdd-bun.planner/atdd-bun.planner.train-wagon-resolves.convention.yaml": "6410e248351e701dcba38c671c07c4d8f6d67927984ed4d18e567abd1eb1f3f0",
|
|
16
|
+
"conventions/atdd-bun.topology/atdd-bun.topology.e2e-location.convention.yaml": "3a3b991bc851b86d597636200f5cb10a0defda3920348860cdca28955ab91f4c",
|
|
17
|
+
"conventions/atdd-bun.topology/atdd-bun.topology.feature-source-coverage.convention.yaml": "2b64093f1e7f1fe0e2edcadf12e560fa80cecec19536abaa5bf09af362c6694e",
|
|
18
|
+
"conventions/atdd-bun.topology/atdd-bun.topology.feature-test-coverage.convention.yaml": "279be57399c9b79a1bd758414076e3053d67b7d4a7fcbbb950ea12d1201effeb",
|
|
19
|
+
"conventions/atdd-bun.topology/atdd-bun.topology.plan-location.convention.yaml": "82149598eb329a0f5db0feb345482a99fef65bef90d3fe34768db66406c30e24",
|
|
20
|
+
"conventions/atdd-bun.topology/atdd-bun.topology.source-location.convention.yaml": "7e7bb788eb1ff9cbe6f67dfddc78a20b3800e733ab1036b70adb67e404b77113",
|
|
21
|
+
"conventions/atdd-bun.topology/atdd-bun.topology.test-location.convention.yaml": "a1f7f772a889607d4b6c0f20e9924b88838199a3e763b6328dbe09b8f29f2e50",
|
|
8
22
|
"conventions/coder.bun/coder.bun.boundaries-http-client.convention.yaml": "b846bb6ced2c4c967466520d52b00ff1ca802dafcdbb2ec2adbd0ce19e752a28",
|
|
9
23
|
"conventions/coder.bun/coder.bun.commons-application-no-integration.convention.yaml": "f63e7f6442bcdf5cf79f58a091547a1a990d0ee7c006b797fd5187d6f8bf8712",
|
|
10
24
|
"conventions/coder.bun/coder.bun.commons-cross-feature-imports-in.convention.yaml": "2d66bd3c04186fcef67a89c2b6d81a697f8f43f978ff27284a3b8a3848097182",
|
|
@@ -124,16 +138,16 @@
|
|
|
124
138
|
"conventions/traceability/traceability.source.tested-by-present.convention.yaml": "27a2340a9dae9eee67a3fbdcea0d2f8f35f222f22dbce7f84b2a0c6ab6321018",
|
|
125
139
|
"conventions/traceability/traceability.source.tested-by-resolves.convention.yaml": "ad4516670934dc82f1ebf9dc057110313059110302f88b4747dde89dd060870b",
|
|
126
140
|
"conventions/traceability/traceability.test.binding-resolves.convention.yaml": "03c118f498b1e824d457d25c060c8f9689ccbcca49c58d8bcd158e3cd3144ee9",
|
|
127
|
-
"detectors/atdd_topology/atdd.implementation.yaml": "
|
|
128
|
-
"detectors/atdd_topology/detect.mjs": "
|
|
141
|
+
"detectors/atdd_topology/atdd.implementation.yaml": "7879f441a62691bf041b0fd8220feb12996d4a036d74d6c79d0985ee20992efb",
|
|
142
|
+
"detectors/atdd_topology/detect.mjs": "33cb5584ce334352324e4a8331f75697cc9c09f7ebc07e59c5f917ad9879f447",
|
|
129
143
|
"detectors/atdd_topology/fixtures/clean/atdd-bun.yaml": "85049c05fb624037e1e2200a117b07d6c8d089d4e81fd784ade15a860a444f59",
|
|
130
144
|
"detectors/atdd_topology/fixtures/clean/e2e/interlockings/checkout/nominal.routes.test.ts": "be769e192e8165d4d36be743e8df578c1790b900c6b89acc7d2046ba40105135",
|
|
131
|
-
"detectors/atdd_topology/fixtures/clean/e2e/journeys/checkout.journey.test.ts": "
|
|
145
|
+
"detectors/atdd_topology/fixtures/clean/e2e/journeys/checkout.journey.test.ts": "345c391888e3e8c93d0437428dc1a3358968c7871ef54d313d6e083717254bbe",
|
|
132
146
|
"detectors/atdd_topology/fixtures/clean/implementation/orders/features/place-order/domain/order.ts": "b81c0630a2c518749ed6d81741ba4e1503ffdbc64f232747518a17e8e8a46927",
|
|
133
147
|
"detectors/atdd_topology/fixtures/clean/plan/_journeys/checkout.yaml": "c61021d0bf2f7f3bfe306b18c4b34563292de02acce47326db71ac4f8d67bc8d",
|
|
134
148
|
"detectors/atdd_topology/fixtures/clean/plan/_trains/_interlockings/checkout.yaml": "99ee3ef93e3098943878c947a584d93fec1407f0a5bd85c5383129229a98d0bc",
|
|
135
149
|
"detectors/atdd_topology/fixtures/clean/plan/_trains/checkout.yaml": "d022cd946cced5961d16535f93cc875b76e2cad872625e5e040242810f8cfad1",
|
|
136
|
-
"detectors/atdd_topology/fixtures/clean/plan/orders/E001.yaml": "
|
|
150
|
+
"detectors/atdd_topology/fixtures/clean/plan/orders/E001.yaml": "97cec4c393561e40252c0f72bed3a471831006e4cf231653506c601df0ed9172",
|
|
137
151
|
"detectors/atdd_topology/fixtures/clean/plan/orders/_orders.yaml": "33f0f30b31e3fb1bedf1ba8d2071dc7cea02749b48d18196038c9d2321aaf628",
|
|
138
152
|
"detectors/atdd_topology/fixtures/clean/plan/orders/place-order.yaml": "cccbad80a40da7bbb52c475fe73f5cd6e523994b428dc133aefc37225b04a80c",
|
|
139
153
|
"detectors/atdd_topology/fixtures/clean/tests/wagons/orders/features/place-order/unit/order.test.ts": "01a9a560d3d977346e39b88a88e7112d9f47f02e8dc8d995ba5fb45f77842153",
|
|
@@ -683,7 +697,7 @@
|
|
|
683
697
|
"detectors/htmx_tester_detector/fixtures/dirty/statusonly.test.ts": "a2daf2874bf68c6221374ac05619642a42d56b5d1f76e30fa3d2a1826adaf51b",
|
|
684
698
|
"detectors/htmx_tester_detector/test_header.mjs": "a22221fe9ef6d10297ec777c5d0c6e7b84010ccf41e90db30c036206790ea198",
|
|
685
699
|
"detectors/planner_docs_capability/README.md": "1e1f8ee258242c05293447fcde124543447ba375306e8a7e5f665881b038bcd1",
|
|
686
|
-
"detectors/planner_docs_capability/atdd.implementation.yaml": "
|
|
700
|
+
"detectors/planner_docs_capability/atdd.implementation.yaml": "0bc4c27c61faa15147ad8ed55a866d7830875efb6222aa463d49f1163a8b6121",
|
|
687
701
|
"detectors/planner_docs_capability/detect.mjs": "d10773fa5b2d8f65ce351804694171a6eb75e05eb4c80fa1edce4a5d56b72c5d",
|
|
688
702
|
"detectors/planner_docs_capability/fixtures/clean/docs/architecture/decisions/adr-20260906-001-shared-control-root.adoc": "6d30e5545e7b051c80605c3dbee50286f71e344ab2e71a987db945941de42523",
|
|
689
703
|
"detectors/planner_docs_capability/fixtures/clean/docs/architecture/decisions/index.adoc": "4a6cb87e249d587c85bb6389ff5fecae7a96194af8677b588eed0c791099123b",
|
|
@@ -810,10 +824,15 @@
|
|
|
810
824
|
"detectors/planner_docs_capability/fixtures/dirty_unresolved_edge/docs/index.adoc": "8aabcd7a4ecfc6ef565a604c1172364f761bef7d3fc4f2c96c07dce4e0ebe19b",
|
|
811
825
|
"detectors/planner_docs_capability/fixtures/dirty_unresolved_edge/docs/purpose/index.adoc": "e10193ea48f3c77b67ca8ed7221c53f23ce699e63f651cc340cea9ef6b44cffb",
|
|
812
826
|
"detectors/planner_docs_capability/fixtures/dirty_unresolved_edge/docs/purpose/worktrees.adoc": "de957bd8aec411c930fa9785d793c3345c82f5d7cb7f28f40f73f1c53a436440",
|
|
813
|
-
"detectors/planner_plan_integrity/atdd.implementation.yaml": "
|
|
814
|
-
"detectors/planner_plan_integrity/detect.mjs": "
|
|
827
|
+
"detectors/planner_plan_integrity/atdd.implementation.yaml": "11e6c1a12c9abe5d4c169fd3ba181e13021e4c58b951757dddd4794417db4367",
|
|
828
|
+
"detectors/planner_plan_integrity/detect.mjs": "ee21ec706c48182250dbcb6045521185b2c91cc8dbcdbbad6e417f2dbfe641b8",
|
|
815
829
|
"detectors/planner_plan_integrity/fixtures/clean/README.md": "d64b0a6ee983757a62b634bb5f64e19f4e5c1eb2e6b23dfd33cf8cb8f3ec0b4a",
|
|
830
|
+
"detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/_interlockings/checkout.yaml": "4d994aae6517b6d8fc2398ca3c37e4a87d999fc559027ae9f2bab0b036e09870",
|
|
831
|
+
"detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/_interlockings/unnamed.yaml": "1e4844007b6679c5d0555e56e088eab3a6d35d0b929261dda3033897e39eace8",
|
|
832
|
+
"detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/dup-a.yaml": "1879b918b34af2a6bb929ae5010619002ece1698bf71ecbcc0414617f88da481",
|
|
833
|
+
"detectors/planner_plan_integrity/fixtures/dirty/plan/_trains/dup-b.yaml": "36bf938cde33a57a0316ffde20fe632b185d9f43a9429df2aa55bdaef987ebf9",
|
|
816
834
|
"detectors/planner_plan_integrity/fixtures/dirty/plan/broken.yaml": "81865b7554ea4a1eb4598de81265abe0760d9ea722945f5bfff5415bc272e95a",
|
|
835
|
+
"detectors/planner_plan_integrity/fixtures/dirty/plan/orders/E001.yaml": "cc800a145aa136328d0e28dfc46d3911e841b47cf69e2f785cacd1f54c2ed3f6",
|
|
817
836
|
"detectors/planner_schema_validation/atdd.implementation.yaml": "2daeff9f85558ad6d246ecdd3d7eb7a172dd1bde646b3cf3b64f21a162da721f",
|
|
818
837
|
"detectors/planner_schema_validation/detect.mjs": "b208e1483e9bae74d3dcff12a824e6dfd0dcb8009affa3c3f58b2a55fe7ae8bf",
|
|
819
838
|
"detectors/planner_schema_validation/fixtures/clean/contracts/_contracts.yaml": "967f4f0aac0be000cb6107dc735ca21b73a3a21870e9ada1396df445584cad41",
|
|
@@ -823,6 +842,7 @@
|
|
|
823
842
|
"detectors/planner_schema_validation/fixtures/clean/plan/orders/_orders.yaml": "77d92ee0c9abae5ef7a407d5e0ed5baabf38391d75b91d47b598777ecc6b87ec",
|
|
824
843
|
"detectors/planner_schema_validation/fixtures/dirty/contracts/_contracts.yaml": "6460d5b8c73bb5f91ae356b29935c0a377cfef2d79ad6180e8cbb8a31fe2e882",
|
|
825
844
|
"detectors/planner_schema_validation/fixtures/dirty/plan/_themes.yaml": "c3e8fb1939d83fdde56405ddfee68e266a16c4a7ed8439b6f7906d47372df473",
|
|
845
|
+
"detectors/planner_schema_validation/fixtures/dirty/plan/_trains/missing-description.yaml": "34052c4d46487751814cdb65e90beb4964493a3a64331b27e1927a5efb8949c5",
|
|
826
846
|
"detectors/planner_schema_validation/fixtures/dirty/plan/_trains/orders.yaml": "ae2fdb750fd5a78c0baddd54fada48522671ae2a9a6538c50ab41ca32a90f682",
|
|
827
847
|
"detectors/planner_static_validators/atdd.implementation.yaml": "030d9999473bd488590a608b6f60f97f2b2c484a183574581edaa262a2e24a10",
|
|
828
848
|
"detectors/planner_static_validators/detect.mjs": "efbff2e8cd3f1b7d8e3240f132e90f09cc081b9352511f3ade67ec6fe06d124b",
|
|
@@ -847,7 +867,7 @@
|
|
|
847
867
|
"lib/frontend.mjs": "d6d9b29c3d7f2d162cda03bb34b20a3224a82887b6be3db7cab375510913c9a3",
|
|
848
868
|
"lib/imports.mjs": "3db4979269488fa26f6e7aefded6ece9c646be6587a10e249821a5a208109962",
|
|
849
869
|
"lib/scan.mjs": "f232a6178f9b6d3d63ba4056709664b1700d4292c49bad1dec18e9560a7c0461",
|
|
850
|
-
"planner-nodes/ENFORCEMENT_SCOPE.yaml": "
|
|
870
|
+
"planner-nodes/ENFORCEMENT_SCOPE.yaml": "d8ec7039c219857f3d77af5d4d84612b533b69c1a5fcef935fbf46470adafed4",
|
|
851
871
|
"planner-nodes/nodes/planner.acceptance.abstract-fields-required.convention.yaml": "bd565693f0ae861f0561ad2f7a91a8c55f9a209c3acd59c5750cfcffd96a85d8",
|
|
852
872
|
"planner-nodes/nodes/planner.acceptance.authoring-guidelines.convention.yaml": "16348dceed5868041b832bde766e9c8d4a950d4032f1f3066b357644081a3b17",
|
|
853
873
|
"planner-nodes/nodes/planner.acceptance.boundary-kind-vocabulary.convention.yaml": "c5645225399568c2d4fe632442f9c597b2c59e4f4239880df7db2cbdc0353c9c",
|
|
@@ -1065,17 +1085,17 @@
|
|
|
1065
1085
|
"planner-schemas/train.schema.json": "d6fbac9067c1bffeb84eb79a71228441055af6e6e7ba141dd4e0bcb079345ec0",
|
|
1066
1086
|
"planner-schemas/wagon.schema.json": "57b98f1ade998d745538eb3d72fb3014a50042006870f90bce47028eea336eb4",
|
|
1067
1087
|
"planner-schemas/wmbt.schema.json": "7a3a5693bfcf01340667c2be4a3c48e74f34b1af7e8dc13372c0de06b7f4fe12",
|
|
1068
|
-
"relationships.yaml": "
|
|
1088
|
+
"relationships.yaml": "3a3b4987dacbc6ae566e607183f60503c934bf8c60925943566e6887726d8bbf",
|
|
1069
1089
|
"src/agent.ts": "38a14f0f2ccce357efb1b2927e59bf55c20ff0050374367f15da36db20c46e77",
|
|
1070
1090
|
"src/ci.ts": "899125ec76d14f7d0126a5db2249913e373e3a325cbc98d7f053e1351bdf47af",
|
|
1071
1091
|
"src/cli.ts": "bb2047cee60f48bfe3eaee4c1b3dcd3538a0e1edc170cf58e87686117bea07a7",
|
|
1072
|
-
"src/docs-capability.ts": "
|
|
1073
|
-
"src/enforce.ts": "
|
|
1092
|
+
"src/docs-capability.ts": "0feae94b7697dd53d8a457b24765e81fcf804c774f0cc4b6035cb3da822e486a",
|
|
1093
|
+
"src/enforce.ts": "d8a2fc2e458fde464365152df376859939bd5c5a4f7d69f88f53b1601bb1f857",
|
|
1074
1094
|
"src/hooks.ts": "54d70d7d6923f1e39b259e3b9f377c43609aecdc64ff4d1232da595950d7b1aa",
|
|
1075
1095
|
"src/index.ts": "2743b7969526d0aaf21179048e9896141e97d89c45c4a503b795f41fed57f6cc",
|
|
1076
1096
|
"src/integrity.ts": "48d7adce486ece1885a95aac6867d286bb972d7a97bdbc32222fd6d75c2e2b49",
|
|
1077
1097
|
"src/journey-docs.ts": "994d229376244c84212cff951032823068a76565dd2fd427b9d44e341bd14853",
|
|
1078
|
-
"src/planner-kernel.ts": "
|
|
1098
|
+
"src/planner-kernel.ts": "5d3f5305fb59becb2cc97d8a03aca6b104f385805d91cc0410856e289dd6fbb1",
|
|
1079
1099
|
"src/planner-schema-validator.ts": "ce529c0936171075e93c7539b3655fe895a98dd2973ebda5775958e6ebc316d9",
|
|
1080
1100
|
"src/planner-validators.ts": "99adeb65f579af923c11f21a8f55cfb9993b007fd8df59c5f764696b55379cfc",
|
|
1081
1101
|
"src/register.ts": "6a2f1c6b8df0dc9c8f45df8ec9fac2be29044e048b08c8ffe423410e94dadedd",
|
package/package.json
CHANGED
|
@@ -117,8 +117,18 @@ canonical_bun_enforcement:
|
|
|
117
117
|
coverage: partial
|
|
118
118
|
predicates:
|
|
119
119
|
- duplicate feature references are reported
|
|
120
|
+
- a wagon with WMBTs declares a feature decomposition (atdd_topology)
|
|
121
|
+
- every feature a wagon lists is declared by a feature document (atdd_topology)
|
|
122
|
+
- every WMBT belongs to exactly one feature (atdd_topology)
|
|
120
123
|
unported:
|
|
121
124
|
- complete feature-manifest coverage
|
|
125
|
+
- rule_id: planner.feature.wagon-link
|
|
126
|
+
disposition: strict
|
|
127
|
+
origin: bun-custom-validator
|
|
128
|
+
coverage: partial
|
|
129
|
+
predicates:
|
|
130
|
+
- every feature is listed by exactly one wagon and belongs to it
|
|
131
|
+
- source and test URNs name a declared feature
|
|
122
132
|
- rule_id: planner.wagon.telemetry-filesystem
|
|
123
133
|
disposition: strict
|
|
124
134
|
origin: bun-custom-validator
|
|
@@ -130,7 +140,7 @@ canonical_bun_enforcement:
|
|
|
130
140
|
|
|
131
141
|
reference_only:
|
|
132
142
|
selection: "every canonical planner node not named in canonical_bun_enforcement"
|
|
133
|
-
count:
|
|
143
|
+
count: 180
|
|
134
144
|
|
|
135
145
|
package_integrity_guard:
|
|
136
146
|
implementation_id: planner.plan-integrity.bun
|
package/relationships.yaml
CHANGED
|
@@ -8,6 +8,20 @@
|
|
|
8
8
|
schema_version: 1.0.0
|
|
9
9
|
graph_id: "@afokapu/atdd-bun"
|
|
10
10
|
nodes:
|
|
11
|
+
- atdd-bun.planner.acceptance-identity
|
|
12
|
+
- atdd-bun.planner.identity-required
|
|
13
|
+
- atdd-bun.planner.identity-unique
|
|
14
|
+
- atdd-bun.planner.interlocking-participant-resolves
|
|
15
|
+
- atdd-bun.planner.parse
|
|
16
|
+
- atdd-bun.planner.reference-resolves
|
|
17
|
+
- atdd-bun.planner.schema
|
|
18
|
+
- atdd-bun.planner.train-wagon-resolves
|
|
19
|
+
- atdd-bun.topology.e2e-location
|
|
20
|
+
- atdd-bun.topology.feature-source-coverage
|
|
21
|
+
- atdd-bun.topology.feature-test-coverage
|
|
22
|
+
- atdd-bun.topology.plan-location
|
|
23
|
+
- atdd-bun.topology.source-location
|
|
24
|
+
- atdd-bun.topology.test-location
|
|
11
25
|
- coder.bun.boundaries-http-client
|
|
12
26
|
- coder.bun.commons-application-no-integration
|
|
13
27
|
- coder.bun.commons-cross-feature-imports-in
|
|
@@ -4788,3 +4802,213 @@ edges:
|
|
|
4788
4802
|
target_ref: tester.htmx.a11y-harness
|
|
4789
4803
|
type: runs_alongside
|
|
4790
4804
|
origin: "@afokapu/atdd-bun"
|
|
4805
|
+
- confidence: 1
|
|
4806
|
+
constraint: mandatory
|
|
4807
|
+
control: internal
|
|
4808
|
+
foundation: finish_to_start
|
|
4809
|
+
reason: an artifact is only identified once it parses
|
|
4810
|
+
source_ref: atdd-bun.planner.parse
|
|
4811
|
+
strength: critical
|
|
4812
|
+
target_ref: atdd-bun.planner.identity-unique
|
|
4813
|
+
type: enables
|
|
4814
|
+
origin: "@afokapu/atdd-bun"
|
|
4815
|
+
- confidence: 1
|
|
4816
|
+
constraint: mandatory
|
|
4817
|
+
control: internal
|
|
4818
|
+
foundation: finish_to_start
|
|
4819
|
+
reason: a reference resolves to exactly one target only when identities are unique
|
|
4820
|
+
source_ref: atdd-bun.planner.identity-unique
|
|
4821
|
+
strength: critical
|
|
4822
|
+
target_ref: atdd-bun.planner.reference-resolves
|
|
4823
|
+
type: enables
|
|
4824
|
+
origin: "@afokapu/atdd-bun"
|
|
4825
|
+
- confidence: 1
|
|
4826
|
+
constraint: mandatory
|
|
4827
|
+
control: internal
|
|
4828
|
+
foundation: start_to_start
|
|
4829
|
+
reason: wagon membership of a train is the train-specific case of reference resolution
|
|
4830
|
+
source_ref: atdd-bun.planner.train-wagon-resolves
|
|
4831
|
+
strength: important
|
|
4832
|
+
target_ref: atdd-bun.planner.reference-resolves
|
|
4833
|
+
type: runs_alongside
|
|
4834
|
+
origin: "@afokapu/atdd-bun"
|
|
4835
|
+
- confidence: 1
|
|
4836
|
+
constraint: mandatory
|
|
4837
|
+
control: internal
|
|
4838
|
+
foundation: start_to_start
|
|
4839
|
+
reason: interlocking participants are the interlocking-specific case of reference resolution
|
|
4840
|
+
source_ref: atdd-bun.planner.interlocking-participant-resolves
|
|
4841
|
+
strength: important
|
|
4842
|
+
target_ref: atdd-bun.planner.reference-resolves
|
|
4843
|
+
type: runs_alongside
|
|
4844
|
+
origin: "@afokapu/atdd-bun"
|
|
4845
|
+
- confidence: 1
|
|
4846
|
+
constraint: mandatory
|
|
4847
|
+
control: internal
|
|
4848
|
+
foundation: finish_to_start
|
|
4849
|
+
reason: "a test can bind an acceptance only through the acc: identity the WMBT gives it"
|
|
4850
|
+
source_ref: atdd-bun.planner.acceptance-identity
|
|
4851
|
+
strength: critical
|
|
4852
|
+
target_ref: traceability.test.binding-resolves
|
|
4853
|
+
type: enables
|
|
4854
|
+
origin: "@afokapu/atdd-bun"
|
|
4855
|
+
- confidence: 1
|
|
4856
|
+
constraint: mandatory
|
|
4857
|
+
control: internal
|
|
4858
|
+
foundation: finish_to_start
|
|
4859
|
+
reason: a document is validated against its schema only after it parses
|
|
4860
|
+
source_ref: atdd-bun.planner.schema
|
|
4861
|
+
strength: critical
|
|
4862
|
+
target_ref: atdd-bun.planner.parse
|
|
4863
|
+
type: requires
|
|
4864
|
+
origin: "@afokapu/atdd-bun"
|
|
4865
|
+
- confidence: 1
|
|
4866
|
+
constraint: mandatory
|
|
4867
|
+
control: internal
|
|
4868
|
+
foundation: start_to_start
|
|
4869
|
+
reason: the schema catch-all beside the canonical rules its violations are routed to
|
|
4870
|
+
source_ref: atdd-bun.planner.schema
|
|
4871
|
+
strength: important
|
|
4872
|
+
target_ref: planner.train.naming
|
|
4873
|
+
type: runs_alongside
|
|
4874
|
+
origin: "@afokapu/atdd-bun"
|
|
4875
|
+
- confidence: 1
|
|
4876
|
+
constraint: mandatory
|
|
4877
|
+
control: internal
|
|
4878
|
+
foundation: finish_to_start
|
|
4879
|
+
reason: "features are placed by path once the wagon's feature decomposition is declared"
|
|
4880
|
+
source_ref: atdd-bun.topology.plan-location
|
|
4881
|
+
strength: critical
|
|
4882
|
+
target_ref: planner.wagon.features
|
|
4883
|
+
type: requires
|
|
4884
|
+
origin: "@afokapu/atdd-bun"
|
|
4885
|
+
- confidence: 1
|
|
4886
|
+
constraint: mandatory
|
|
4887
|
+
control: internal
|
|
4888
|
+
foundation: start_to_start
|
|
4889
|
+
reason: "where a feature lives and which wagon owns it: the same ownership, by path and by link"
|
|
4890
|
+
source_ref: atdd-bun.topology.plan-location
|
|
4891
|
+
strength: important
|
|
4892
|
+
target_ref: planner.feature.wagon-link
|
|
4893
|
+
type: runs_alongside
|
|
4894
|
+
origin: "@afokapu/atdd-bun"
|
|
4895
|
+
- confidence: 1
|
|
4896
|
+
constraint: mandatory
|
|
4897
|
+
control: internal
|
|
4898
|
+
foundation: finish_to_start
|
|
4899
|
+
reason: source is placed beneath a feature the plan declares at its canonical path
|
|
4900
|
+
source_ref: atdd-bun.topology.source-location
|
|
4901
|
+
strength: critical
|
|
4902
|
+
target_ref: atdd-bun.topology.plan-location
|
|
4903
|
+
type: requires
|
|
4904
|
+
origin: "@afokapu/atdd-bun"
|
|
4905
|
+
- confidence: 1
|
|
4906
|
+
constraint: mandatory
|
|
4907
|
+
control: internal
|
|
4908
|
+
foundation: start_to_start
|
|
4909
|
+
reason: "the same component URN, read for its grammar and for where the file must live"
|
|
4910
|
+
source_ref: atdd-bun.topology.source-location
|
|
4911
|
+
strength: important
|
|
4912
|
+
target_ref: coder.bun.green-urn-pattern
|
|
4913
|
+
type: runs_alongside
|
|
4914
|
+
origin: "@afokapu/atdd-bun"
|
|
4915
|
+
- confidence: 1
|
|
4916
|
+
constraint: mandatory
|
|
4917
|
+
control: internal
|
|
4918
|
+
foundation: finish_to_start
|
|
4919
|
+
reason: tests are placed beneath a feature the plan declares
|
|
4920
|
+
source_ref: atdd-bun.topology.test-location
|
|
4921
|
+
strength: critical
|
|
4922
|
+
target_ref: atdd-bun.topology.plan-location
|
|
4923
|
+
type: requires
|
|
4924
|
+
origin: "@afokapu/atdd-bun"
|
|
4925
|
+
- confidence: 1
|
|
4926
|
+
constraint: mandatory
|
|
4927
|
+
control: internal
|
|
4928
|
+
foundation: start_to_start
|
|
4929
|
+
reason: "the acceptance a test binds, read for presence and for whether a WMBT declares it"
|
|
4930
|
+
source_ref: atdd-bun.topology.test-location
|
|
4931
|
+
strength: important
|
|
4932
|
+
target_ref: tester.bun.acceptance-binding-declared
|
|
4933
|
+
type: runs_alongside
|
|
4934
|
+
origin: "@afokapu/atdd-bun"
|
|
4935
|
+
- confidence: 1
|
|
4936
|
+
constraint: mandatory
|
|
4937
|
+
control: internal
|
|
4938
|
+
foundation: finish_to_start
|
|
4939
|
+
reason: journey E2E paths are derived from the journeys the plan declares
|
|
4940
|
+
source_ref: atdd-bun.topology.e2e-location
|
|
4941
|
+
strength: critical
|
|
4942
|
+
target_ref: planner.journey.definition
|
|
4943
|
+
type: requires
|
|
4944
|
+
origin: "@afokapu/atdd-bun"
|
|
4945
|
+
- confidence: 1
|
|
4946
|
+
constraint: mandatory
|
|
4947
|
+
control: internal
|
|
4948
|
+
foundation: start_to_start
|
|
4949
|
+
reason: "every route has an end-to-end proof: at its canonical path, and exercising the production runner"
|
|
4950
|
+
source_ref: atdd-bun.topology.e2e-location
|
|
4951
|
+
strength: important
|
|
4952
|
+
target_ref: tester.bun.interlocking-route-coverage
|
|
4953
|
+
type: runs_alongside
|
|
4954
|
+
origin: "@afokapu/atdd-bun"
|
|
4955
|
+
- confidence: 1
|
|
4956
|
+
constraint: mandatory
|
|
4957
|
+
control: internal
|
|
4958
|
+
foundation: start_to_start
|
|
4959
|
+
reason: "on a frontend journey a browser spec is the route's end-to-end proof"
|
|
4960
|
+
source_ref: atdd-bun.topology.e2e-location
|
|
4961
|
+
strength: important
|
|
4962
|
+
target_ref: tester.htmx.train-e2e-coverage
|
|
4963
|
+
type: runs_alongside
|
|
4964
|
+
origin: "@afokapu/atdd-bun"
|
|
4965
|
+
- confidence: 1
|
|
4966
|
+
constraint: mandatory
|
|
4967
|
+
control: internal
|
|
4968
|
+
foundation: finish_to_start
|
|
4969
|
+
reason: coverage counts only source placed beneath its feature
|
|
4970
|
+
source_ref: atdd-bun.topology.feature-source-coverage
|
|
4971
|
+
strength: critical
|
|
4972
|
+
target_ref: atdd-bun.topology.source-location
|
|
4973
|
+
type: requires
|
|
4974
|
+
origin: "@afokapu/atdd-bun"
|
|
4975
|
+
- confidence: 1
|
|
4976
|
+
constraint: mandatory
|
|
4977
|
+
control: internal
|
|
4978
|
+
foundation: finish_to_start
|
|
4979
|
+
reason: coverage counts only tests placed beneath their feature and bound to its acceptances
|
|
4980
|
+
source_ref: atdd-bun.topology.feature-test-coverage
|
|
4981
|
+
strength: critical
|
|
4982
|
+
target_ref: atdd-bun.topology.test-location
|
|
4983
|
+
type: requires
|
|
4984
|
+
origin: "@afokapu/atdd-bun"
|
|
4985
|
+
- confidence: 1
|
|
4986
|
+
constraint: mandatory
|
|
4987
|
+
control: internal
|
|
4988
|
+
foundation: start_to_start
|
|
4989
|
+
reason: a feature with WMBTs is implemented and tested
|
|
4990
|
+
source_ref: atdd-bun.topology.feature-test-coverage
|
|
4991
|
+
strength: important
|
|
4992
|
+
target_ref: atdd-bun.topology.feature-source-coverage
|
|
4993
|
+
type: runs_alongside
|
|
4994
|
+
origin: "@afokapu/atdd-bun"
|
|
4995
|
+
- confidence: 1
|
|
4996
|
+
constraint: mandatory
|
|
4997
|
+
control: internal
|
|
4998
|
+
foundation: finish_to_start
|
|
4999
|
+
reason: identities can only be compared once every artifact carries one
|
|
5000
|
+
source_ref: atdd-bun.planner.identity-required
|
|
5001
|
+
strength: critical
|
|
5002
|
+
target_ref: atdd-bun.planner.identity-unique
|
|
5003
|
+
type: enables
|
|
5004
|
+
origin: "@afokapu/atdd-bun"
|
|
5005
|
+
- confidence: 1
|
|
5006
|
+
constraint: mandatory
|
|
5007
|
+
control: internal
|
|
5008
|
+
foundation: finish_to_start
|
|
5009
|
+
reason: an artifact is read for its identity only once it parses
|
|
5010
|
+
source_ref: atdd-bun.planner.parse
|
|
5011
|
+
strength: critical
|
|
5012
|
+
target_ref: atdd-bun.planner.identity-required
|
|
5013
|
+
type: enables
|
|
5014
|
+
origin: "@afokapu/atdd-bun"
|
package/src/docs-capability.ts
CHANGED
|
@@ -155,6 +155,9 @@ async function checkDocumentationInner(input: { root: string; declaration: Docum
|
|
|
155
155
|
if (input.declaration) { const declarationFindings = declarationViolations(input.declaration, input.changeSet ?? undefined); findings.push(...declarationFindings); definite ||= declarationFindings.length > 0; checked.push("<declaration>"); if (input.declaration.impact === "change") for (const artifact of input.declaration.artifacts ?? []) if (["create", "modify"].includes(artifact.action ?? "") && artifact.path && !existsSync(join(input.root, artifact.path))) { findings.push(violation("planner.docs.artifact-path-shape", artifact.path, 1, `declared artifact ${JSON.stringify(artifact.path)} (action: ${artifact.action}) is not in the tree, so it was never examined; a declared document that was never written has not discharged the obligation.`)); definite = true; } } else findings.push({ rule_id: null, where: "<declaration>", message: "core supplied no documentation declaration, so no declaration-dependent rule could be evaluated. This is COULD_NOT_CHECK and it BLOCKS." });
|
|
156
156
|
if (input.declaration && input.changeSet === null) findings.push({ rule_id: null, where: "<change_set>", message: "core supplied no change set, so whether this diff touches docs/ without declaring it could not be established. This is COULD_NOT_CHECK and it BLOCKS." });
|
|
157
157
|
const rendered = await (input.render ?? (() => renderDocumentation(input.root)))(); findings.push(...rendered.findings); definite ||= rendered.findings.length > 0; if (rendered.couldNotCheck) findings.push({ rule_id: "planner.docs.reference-integrity", file: "docs/", line: 1, col: 1, evidence: rendered.couldNotCheck, source_line: "" }); else checked.push("<render:asciidoctor>");
|
|
158
|
+
// Fail closed on any rule id this capability does not declare, however it was built (a renderer can return anything).
|
|
159
|
+
const undeclared = [...new Set(findings.map(f => f.rule_id).filter((id): id is string => id !== null && !(DOC_RULE_IDS as readonly string[]).includes(id)))];
|
|
160
|
+
if (undeclared.length) throw new Error(`documentation capability produced undeclared rule id(s): ${undeclared.join(", ")}. Declare them in DOC_RULE_IDS and the manifest, each with a convention.`);
|
|
158
161
|
return { verdict: definite ? "FAIL" : input.declaration === null || input.changeSet === null || Boolean(rendered.couldNotCheck) ? "COULD_NOT_CHECK" : "PASS", findings, checked };
|
|
159
162
|
}
|
|
160
163
|
|
package/src/enforce.ts
CHANGED
|
@@ -57,6 +57,26 @@ export function implementationsFor(requested: Profile[] = ["all"]): string[] {
|
|
|
57
57
|
return [...selected].sort();
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/** The rule ids a detector's manifest declares it emits (emits_rule_ids and api_emits_rule_ids). */
|
|
61
|
+
export async function declaredRuleIds(implementation: string): Promise<Set<string>> {
|
|
62
|
+
const ids = new Set<string>();
|
|
63
|
+
let inList = false;
|
|
64
|
+
for (const line of (await readFile(join(detectorRoot, implementation, "atdd.implementation.yaml"), "utf8")).split("\n")) {
|
|
65
|
+
if (line === "emits_rule_ids:" || line === "api_emits_rule_ids:") { inList = true; continue; }
|
|
66
|
+
if (/^[A-Za-z_][\w-]*:/.test(line)) { inList = false; continue; }
|
|
67
|
+
const match = inList && line.match(/^\s*-\s+([^#\s]+)/);
|
|
68
|
+
if (match) ids.add(match[1]);
|
|
69
|
+
}
|
|
70
|
+
return ids;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Rule ids a detector actually emitted without declaring them. Checked at run time, so an id built at run
|
|
74
|
+
* time (a template string, a concatenation, a value read from YAML) cannot escape the per-rule checks. */
|
|
75
|
+
export async function undeclaredEmissions(implementation: string, violations: Pick<Violation, "rule_id">[]): Promise<string[]> {
|
|
76
|
+
const declared = await declaredRuleIds(implementation);
|
|
77
|
+
return [...new Set(violations.map(v => v.rule_id).filter(id => !declared.has(id)))].sort();
|
|
78
|
+
}
|
|
79
|
+
|
|
60
80
|
export async function runImplementation(
|
|
61
81
|
implementation: string,
|
|
62
82
|
config: Required<Pick<EnforcementConfig, "scanRoots" | "excludes">>,
|
|
@@ -88,6 +108,8 @@ export async function runImplementation(
|
|
|
88
108
|
if (!raw || !Array.isArray(raw.violations)) {
|
|
89
109
|
throw new Error(`${implementation} emitted no structured violation report`);
|
|
90
110
|
}
|
|
111
|
+
const undeclared = await undeclaredEmissions(implementation, raw.violations as Violation[]);
|
|
112
|
+
if (undeclared.length) throw new Error(`${implementation} emitted rule id(s) its manifest does not declare: ${undeclared.join(", ")}. Declare them in emits_rule_ids, each with a convention.`);
|
|
91
113
|
return raw.violations as Violation[];
|
|
92
114
|
} finally {
|
|
93
115
|
await rm(scratch, { recursive: true, force: true });
|
package/src/planner-kernel.ts
CHANGED
|
@@ -17,6 +17,7 @@ async function walk(root: string): Promise<string[]> {
|
|
|
17
17
|
const entries = await readdir(root, { withFileTypes: true });
|
|
18
18
|
return (await Promise.all(entries.map(async entry => {
|
|
19
19
|
const path = join(root, entry.name);
|
|
20
|
+
if (entry.isDirectory() && entry.name === "_generated") return [];
|
|
20
21
|
return entry.isDirectory() ? walk(path) : entry.isFile() && /\.(?:yaml|yml|json)$/.test(entry.name) ? [path] : [];
|
|
21
22
|
}))).flat().sort();
|
|
22
23
|
}
|
|
@@ -97,7 +98,7 @@ export async function validatePlan(root = process.cwd()): Promise<PlanGraph> {
|
|
|
97
98
|
const references = structuralRefs(artifact);
|
|
98
99
|
for (const reference of new Set(references)) if (!known.has(reference) && !reference.startsWith("contract:")) graph.findings.push(finding("planner.kernel.reference-resolves", artifact.file, `${artifact.id} references undeclared ${reference}`));
|
|
99
100
|
if (artifact.kind === "train") for (const wagon of values(artifact.data.wagons)) { const reference = `wagon:${id(wagon)}`; if (id(wagon) && !known.has(reference)) graph.findings.push(finding("planner.kernel.train-wagon-resolves", artifact.file, `${artifact.id} lists undeclared ${reference}`)); }
|
|
100
|
-
if (artifact.kind === "interlocking") for (const participant of [...values(artifact.data.participants), ...values(artifact.data.lifelines).map(value => value && typeof value === "object" ? (value as Record<string, unknown>).ref : value)]) { const reference = id(participant); if (reference
|
|
101
|
+
if (artifact.kind === "interlocking") for (const participant of [...values(artifact.data.participants), ...values(artifact.data.lifelines).map(value => value && typeof value === "object" ? (value as Record<string, unknown>).ref : value)]) { const reference = id(participant); if (reference && !/^(user|system):/.test(reference) && !known.has(reference)) graph.findings.push(finding("planner.kernel.interlocking-participant-resolves", artifact.file, `${artifact.id} lists undeclared ${reference}`)); }
|
|
101
102
|
}
|
|
102
103
|
return graph;
|
|
103
104
|
}
|