@afokapu/atdd-bun 0.6.2 → 0.7.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/README.md +13 -3
- package/conventions/coder.bun/coder.bun.telemetry-forbidden-properties.convention.yaml +51 -0
- package/conventions/coder.bun/coder.bun.telemetry-implementation-binding.convention.yaml +45 -0
- package/conventions/coder.bun/coder.bun.telemetry-raw-string-emit.convention.yaml +53 -0
- package/conventions/coder.bun/coder.bun.telemetry-source-binding.convention.yaml +48 -0
- package/conventions/coder.bun/coder.bun.telemetry-vendor-sdk.convention.yaml +51 -0
- package/conventions/planner.telemetry/planner.telemetry.acceptance-decision.convention.yaml +58 -0
- package/conventions/planner.telemetry/planner.telemetry.logical-ownership.convention.yaml +50 -0
- package/conventions/planner.telemetry/planner.telemetry.metric-cardinality.convention.yaml +47 -0
- package/conventions/planner.telemetry/planner.telemetry.tracking-plan-schema.convention.yaml +75 -0
- package/conventions/tester.bun/tester.bun.telemetry-captured-sink.convention.yaml +47 -0
- package/conventions/tester.bun/tester.bun.telemetry-identity-assertion.convention.yaml +44 -0
- package/conventions/tester.bun/tester.bun.telemetry-required-item-coverage.convention.yaml +44 -0
- package/conventions/tester.bun/tester.bun.telemetry-test-binding.convention.yaml +52 -0
- package/conventions/tester.bun/tester.bun.telemetry-timing-semantics.convention.yaml +53 -0
- package/detectors/bun_telemetry_code/atdd.implementation.yaml +24 -0
- package/detectors/bun_telemetry_code/calls.mjs +104 -0
- package/detectors/bun_telemetry_code/checks/t_forbidden_properties.mjs +46 -0
- package/detectors/bun_telemetry_code/checks/t_implementation_binding.mjs +41 -0
- package/detectors/bun_telemetry_code/checks/t_raw_string_emit.mjs +34 -0
- package/detectors/bun_telemetry_code/checks/t_source_binding.mjs +48 -0
- package/detectors/bun_telemetry_code/checks/t_vendor_sdk.mjs +38 -0
- package/detectors/bun_telemetry_code/detect.mjs +50 -0
- package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/E001.yaml +10 -0
- package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/E002.yaml +7 -0
- package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/_commons.yaml +6 -0
- package/detectors/bun_telemetry_code/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
- package/detectors/bun_telemetry_code/fixtures/clean/src/wagons/commons/features/ingress/infrastructure/otel-adapter.ts +8 -0
- package/detectors/bun_telemetry_code/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
- package/detectors/bun_telemetry_code/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
- package/detectors/bun_telemetry_code/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
- package/detectors/bun_telemetry_code/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.test.ts +9 -0
- package/detectors/bun_telemetry_code/fixtures/dirty/plan/commons/E001.yaml +8 -0
- package/detectors/bun_telemetry_code/fixtures/dirty/plan/commons/_commons.yaml +6 -0
- package/detectors/bun_telemetry_code/fixtures/dirty/src/wagons/commons/features/ingress/domain/accept-response.ts +13 -0
- package/detectors/bun_telemetry_code/fixtures/dirty/src/wagons/commons/features/ingress/domain/tracing.ts +3 -0
- package/detectors/bun_telemetry_code/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
- package/detectors/bun_telemetry_code/fixtures/dirty/tests/wagons/commons/features/ingress/unit/probe.test.ts +7 -0
- package/detectors/bun_telemetry_code/registry.mjs +34 -0
- package/detectors/bun_telemetry_test/_shared.mjs +92 -0
- package/detectors/bun_telemetry_test/atdd.implementation.yaml +24 -0
- package/detectors/bun_telemetry_test/checks/t_captured_sink.mjs +35 -0
- package/detectors/bun_telemetry_test/checks/t_identity_assertion.mjs +45 -0
- package/detectors/bun_telemetry_test/checks/t_required_item_coverage.mjs +37 -0
- package/detectors/bun_telemetry_test/checks/t_test_binding.mjs +56 -0
- package/detectors/bun_telemetry_test/checks/t_timing_semantics.mjs +51 -0
- package/detectors/bun_telemetry_test/detect.mjs +50 -0
- package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/E001.yaml +10 -0
- package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/E002.yaml +7 -0
- package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/_commons.yaml +6 -0
- package/detectors/bun_telemetry_test/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
- package/detectors/bun_telemetry_test/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
- package/detectors/bun_telemetry_test/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
- package/detectors/bun_telemetry_test/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
- package/detectors/bun_telemetry_test/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.test.ts +7 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/E001.yaml +10 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/E002.yaml +7 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/_commons.yaml +6 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/dangling.telemetry.test.ts +9 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/loose.telemetry.test.ts +11 -0
- package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/unbound.telemetry.test.ts +10 -0
- package/detectors/planner_telemetry_plan/atdd.implementation.yaml +22 -0
- package/detectors/planner_telemetry_plan/detect.mjs +9 -0
- package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/E001.yaml +10 -0
- package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/E002.yaml +7 -0
- package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/_commons.yaml +6 -0
- package/detectors/planner_telemetry_plan/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
- package/detectors/planner_telemetry_plan/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
- package/detectors/planner_telemetry_plan/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
- package/detectors/planner_telemetry_plan/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E001.yaml +9 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E002.yaml +4 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E003.yaml +7 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/_commons.yaml +6 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/extra.json +1 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/orphan-artifact/event.be.json +14 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/metric.be.duration.json +22 -0
- package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/notes.json +1 -0
- package/integrity.json +94 -11
- package/lib/scan.mjs +37 -0
- package/package.json +1 -1
- package/planner-schemas/acceptance.schema.json +70 -1
- package/planner-schemas/telemetry-plan.schema.json +133 -0
- package/relationships.yaml +174 -0
- package/src/enforce.ts +2 -1
- package/src/index.ts +2 -0
- package/src/integrity.ts +25 -9
- package/src/setup.ts +17 -1
- package/src/telemetry-plan.ts +223 -0
- package/src/topology.ts +3 -1
- package/templates/github/atdd-bun.yml +5 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Telemetry Tracking-Plan Item Schema",
|
|
4
|
+
"description": "Validates one concrete telemetry tracking-plan file under telemetry/<theme>/<artifact>/{kind}.{plane}[.{measure}].json. Local shape only: path mirroring, kind/plane/measure coherence with the file name, wagon ownership resolution and acceptance linkage are cross-artifact validator concerns (src/telemetry-plan.ts).",
|
|
5
|
+
"$comment": "Shipped by the telemetry profile (planner_telemetry_plan). The capability is deliberately inert until a repository adopts it: no telemetry/ root and no acceptance telemetry declaration means no findings. `signal` in acceptance.schema.json keeps the executable bar (signal.metric + signal.threshold); the descriptive arrays it also carries are superseded by the acceptance `telemetry` block and by this registry, and are not renamed here — that migration is a separate, documented step.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["id", "version", "logical_artifact", "kind", "plane", "owner", "purpose", "acceptances", "properties"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"id": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^telemetry:(event|trace|metric|log):(ui|ux|be|nw|db|st|tm|sc|au|fn|if):[a-z][a-z0-9-]*:[a-z][a-z0-9-]*(?::(latency|duration|throughput|error_rate|success_rate|count|size|age|staleness|freshness))?$",
|
|
13
|
+
"description": "Concrete telemetry URN: telemetry:{kind}:{plane}:{theme}:{artifact}[:{measure}]. Must mirror the file's path and name; the validator checks that coherence."
|
|
14
|
+
},
|
|
15
|
+
"version": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"pattern": "^(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)$",
|
|
18
|
+
"description": "SemVer core for this item's contract. Changing properties, required or forbidden_properties is a contract change and must bump it."
|
|
19
|
+
},
|
|
20
|
+
"logical_artifact": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^telemetry:[a-z][a-z0-9-]*:[a-z][a-z0-9-]*$",
|
|
23
|
+
"description": "The stable architectural identity of the observable outcome: telemetry:{theme}:{artifact}. Not an exporter event. Must be owned by exactly one wagon's produce[] telemetry entry."
|
|
24
|
+
},
|
|
25
|
+
"kind": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"enum": ["event", "trace", "metric", "log"]
|
|
28
|
+
},
|
|
29
|
+
"plane": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": ["ui", "ux", "be", "nw", "db", "st", "tm", "sc", "au", "fn", "if"]
|
|
32
|
+
},
|
|
33
|
+
"measure": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"enum": ["latency", "duration", "throughput", "error_rate", "success_rate", "count", "size", "age", "staleness", "freshness"],
|
|
36
|
+
"description": "Required for metric items (and only metric items); must match the measure segment of the file name and of the id."
|
|
37
|
+
},
|
|
38
|
+
"instrument": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"enum": ["counter", "gauge", "histogram", "summary"],
|
|
41
|
+
"description": "Metric instrument type per the telemetry kind vocabulary."
|
|
42
|
+
},
|
|
43
|
+
"unit": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "Unit of measure for metric items (ms, s, bytes, percent, count)."
|
|
46
|
+
},
|
|
47
|
+
"owner": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
50
|
+
"description": "The wagon slug that owns the logical artifact. The validator checks it equals the wagon whose produce[] declares the logical artifact."
|
|
51
|
+
},
|
|
52
|
+
"purpose": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 20,
|
|
55
|
+
"description": "The question this item answers. A tracking plan is read by people deciding what to alert and dashboard on; an item that cannot state its question is not planned."
|
|
56
|
+
},
|
|
57
|
+
"acceptances": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"pattern": "^acc:[A-Za-z0-9._:-]+$",
|
|
62
|
+
"description": "Acceptance URNs that require this item. Each must resolve to a declared acceptance in the plan."
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"properties": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"description": "The fields this item may carry, by name. `required` names must all appear here.",
|
|
68
|
+
"additionalProperties": {
|
|
69
|
+
"$ref": "#/definitions/property"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"required": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": {"type": "string", "minLength": 1},
|
|
75
|
+
"description": "Property names that must be present on every emission."
|
|
76
|
+
},
|
|
77
|
+
"forbidden_properties": {
|
|
78
|
+
"type": "array",
|
|
79
|
+
"items": {"type": "string", "minLength": 1},
|
|
80
|
+
"description": "Property names that must never be attached to this item (raw payloads, secrets, prompts)."
|
|
81
|
+
},
|
|
82
|
+
"dimensions": {
|
|
83
|
+
"type": "array",
|
|
84
|
+
"description": "Metric labels. Every dimension declares cardinality; high cardinality is forbidden for metrics — use an event, log or trace property instead.",
|
|
85
|
+
"items": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"required": ["name", "cardinality"],
|
|
88
|
+
"additionalProperties": false,
|
|
89
|
+
"properties": {
|
|
90
|
+
"name": {"type": "string", "minLength": 1},
|
|
91
|
+
"cardinality": {"type": "string", "enum": ["low", "medium", "high"]}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"timing": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"description": "Critical timing semantics a telemetry test bound to this item must exercise: post-commit (emitted only after the transaction commits), absent-after-rollback (never emitted when the transaction rolls back), correlation-across-async (correlation context continues across an async handoff). Declaring a semantic is what makes it testable; a test that cannot exercise it should not claim the item.",
|
|
98
|
+
"items": {"type": "string", "enum": ["post-commit", "absent-after-rollback", "correlation-across-async"]}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"definitions": {
|
|
102
|
+
"property": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"required": ["type"],
|
|
105
|
+
"additionalProperties": false,
|
|
106
|
+
"properties": {
|
|
107
|
+
"type": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"enum": ["string", "integer", "number", "boolean"]
|
|
110
|
+
},
|
|
111
|
+
"classification": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"enum": ["internal", "external", "pii"],
|
|
114
|
+
"description": "Who may see the value. PII forces a justification nobody has to reconstruct later."
|
|
115
|
+
},
|
|
116
|
+
"cardinality": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"enum": ["low", "medium", "high"],
|
|
119
|
+
"description": "Distinct-value volume. High cardinality is legal in event, log and trace properties where justified; never as a metric label."
|
|
120
|
+
},
|
|
121
|
+
"allowed_values": {
|
|
122
|
+
"type": "array",
|
|
123
|
+
"minItems": 1,
|
|
124
|
+
"items": {"type": ["string", "number", "boolean"]},
|
|
125
|
+
"description": "Closed vocabulary for this property."
|
|
126
|
+
},
|
|
127
|
+
"description": {
|
|
128
|
+
"type": "string"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
package/relationships.yaml
CHANGED
|
@@ -81,6 +81,11 @@ nodes:
|
|
|
81
81
|
- coder.bun.security-sql-injection
|
|
82
82
|
- coder.bun.station-master-interlocking-routing
|
|
83
83
|
- coder.bun.station-master-journey-routing
|
|
84
|
+
- coder.bun.telemetry-forbidden-properties
|
|
85
|
+
- coder.bun.telemetry-implementation-binding
|
|
86
|
+
- coder.bun.telemetry-raw-string-emit
|
|
87
|
+
- coder.bun.telemetry-source-binding
|
|
88
|
+
- coder.bun.telemetry-vendor-sdk
|
|
84
89
|
- coder.bun.wagon-honours-its-contract
|
|
85
90
|
- coder.htmx.fragment-escapes-interpolation
|
|
86
91
|
- coder.htmx.swap-no-inline-handler
|
|
@@ -220,6 +225,10 @@ nodes:
|
|
|
220
225
|
- planner.steps.linguistic-patterns
|
|
221
226
|
- planner.steps.sequence
|
|
222
227
|
- planner.subject.invariants
|
|
228
|
+
- planner.telemetry.acceptance-decision
|
|
229
|
+
- planner.telemetry.logical-ownership
|
|
230
|
+
- planner.telemetry.metric-cardinality
|
|
231
|
+
- planner.telemetry.tracking-plan-schema
|
|
223
232
|
- planner.theme.definition
|
|
224
233
|
- planner.theme.must-be-canonical
|
|
225
234
|
- planner.theme.theme-zero-mandatory
|
|
@@ -310,7 +319,12 @@ nodes:
|
|
|
310
319
|
- tester.bun.security-input
|
|
311
320
|
- tester.bun.smoke-no-collaborator-substitution
|
|
312
321
|
- tester.bun.smoke-observable-assertion
|
|
322
|
+
- tester.bun.telemetry-captured-sink
|
|
313
323
|
- tester.bun.telemetry-emit
|
|
324
|
+
- tester.bun.telemetry-identity-assertion
|
|
325
|
+
- tester.bun.telemetry-required-item-coverage
|
|
326
|
+
- tester.bun.telemetry-test-binding
|
|
327
|
+
- tester.bun.telemetry-timing-semantics
|
|
314
328
|
- tester.bun.test-carries-urn-identity
|
|
315
329
|
- tester.bun.test-imports-bun-test
|
|
316
330
|
- tester.bun.test-isolation-no-live-state
|
|
@@ -5012,3 +5026,163 @@ edges:
|
|
|
5012
5026
|
target_ref: atdd-bun.planner.identity-required
|
|
5013
5027
|
type: enables
|
|
5014
5028
|
origin: "@afokapu/atdd-bun"
|
|
5029
|
+
- confidence: 1
|
|
5030
|
+
constraint: mandatory
|
|
5031
|
+
control: internal
|
|
5032
|
+
foundation: finish_to_start
|
|
5033
|
+
reason: the tracking-plan tree is the versioned realization of the telemetry URN-to-filesystem mapping
|
|
5034
|
+
source_ref: planner.telemetry.tracking-plan-schema
|
|
5035
|
+
strength: important
|
|
5036
|
+
target_ref: planner.wagon.telemetry-filesystem
|
|
5037
|
+
type: requires
|
|
5038
|
+
origin: "@afokapu/atdd-bun"
|
|
5039
|
+
- confidence: 1
|
|
5040
|
+
constraint: mandatory
|
|
5041
|
+
control: internal
|
|
5042
|
+
foundation: finish_to_start
|
|
5043
|
+
reason: "a tracking-plan item's path mirrors its concrete telemetry identity"
|
|
5044
|
+
source_ref: planner.telemetry.tracking-plan-schema
|
|
5045
|
+
strength: important
|
|
5046
|
+
target_ref: planner.artifact-naming.path-mirrors-identity
|
|
5047
|
+
type: follows
|
|
5048
|
+
origin: "@afokapu/atdd-bun"
|
|
5049
|
+
- confidence: 1
|
|
5050
|
+
constraint: mandatory
|
|
5051
|
+
control: internal
|
|
5052
|
+
foundation: finish_to_start
|
|
5053
|
+
reason: "wagon produce[] telemetry entries are the ownership declarations items resolve against"
|
|
5054
|
+
source_ref: planner.telemetry.logical-ownership
|
|
5055
|
+
strength: important
|
|
5056
|
+
target_ref: planner.wagon.produce-consume-artifacts
|
|
5057
|
+
type: requires
|
|
5058
|
+
origin: "@afokapu/atdd-bun"
|
|
5059
|
+
- confidence: 1
|
|
5060
|
+
constraint: mandatory
|
|
5061
|
+
control: internal
|
|
5062
|
+
foundation: finish_to_start
|
|
5063
|
+
reason: the disposition decision governs the telemetry URNs an acceptance may declare
|
|
5064
|
+
source_ref: planner.telemetry.acceptance-decision
|
|
5065
|
+
strength: important
|
|
5066
|
+
target_ref: planner.acceptance.signal-telemetry-naming
|
|
5067
|
+
type: requires
|
|
5068
|
+
origin: "@afokapu/atdd-bun"
|
|
5069
|
+
- confidence: 1
|
|
5070
|
+
constraint: mandatory
|
|
5071
|
+
control: internal
|
|
5072
|
+
foundation: finish_to_start
|
|
5073
|
+
reason: "metric measures and dimensions inherit the naming rule's cardinality discipline"
|
|
5074
|
+
source_ref: planner.telemetry.metric-cardinality
|
|
5075
|
+
strength: important
|
|
5076
|
+
target_ref: planner.acceptance.signal-telemetry-naming
|
|
5077
|
+
type: requires
|
|
5078
|
+
origin: "@afokapu/atdd-bun"
|
|
5079
|
+
- confidence: 1
|
|
5080
|
+
constraint: mandatory
|
|
5081
|
+
control: internal
|
|
5082
|
+
foundation: finish_to_start
|
|
5083
|
+
reason: "a source Telemetry: reference resolves against the tracking-plan registry"
|
|
5084
|
+
source_ref: coder.bun.telemetry-source-binding
|
|
5085
|
+
strength: important
|
|
5086
|
+
target_ref: planner.telemetry.tracking-plan-schema
|
|
5087
|
+
type: requires
|
|
5088
|
+
origin: "@afokapu/atdd-bun"
|
|
5089
|
+
- confidence: 1
|
|
5090
|
+
constraint: mandatory
|
|
5091
|
+
control: internal
|
|
5092
|
+
foundation: finish_to_start
|
|
5093
|
+
reason: only telemetry an acceptance has decided and the plan declares may be emitted by name
|
|
5094
|
+
source_ref: coder.bun.telemetry-raw-string-emit
|
|
5095
|
+
strength: important
|
|
5096
|
+
target_ref: planner.telemetry.acceptance-decision
|
|
5097
|
+
type: requires
|
|
5098
|
+
origin: "@afokapu/atdd-bun"
|
|
5099
|
+
- confidence: 1
|
|
5100
|
+
constraint: mandatory
|
|
5101
|
+
control: internal
|
|
5102
|
+
foundation: start_to_start
|
|
5103
|
+
reason: vendor SDKs stay in infrastructure adapters as framework imports stay out of domain
|
|
5104
|
+
source_ref: coder.bun.telemetry-vendor-sdk
|
|
5105
|
+
strength: important
|
|
5106
|
+
target_ref: coder.bun.commons-domain-no-framework-import
|
|
5107
|
+
type: runs_alongside
|
|
5108
|
+
origin: "@afokapu/atdd-bun"
|
|
5109
|
+
- confidence: 1
|
|
5110
|
+
constraint: mandatory
|
|
5111
|
+
control: internal
|
|
5112
|
+
foundation: finish_to_start
|
|
5113
|
+
reason: the forbidden-property list comes from the tracking-plan item
|
|
5114
|
+
source_ref: coder.bun.telemetry-forbidden-properties
|
|
5115
|
+
strength: important
|
|
5116
|
+
target_ref: planner.telemetry.tracking-plan-schema
|
|
5117
|
+
type: requires
|
|
5118
|
+
origin: "@afokapu/atdd-bun"
|
|
5119
|
+
- confidence: 1
|
|
5120
|
+
constraint: mandatory
|
|
5121
|
+
control: internal
|
|
5122
|
+
foundation: finish_to_start
|
|
5123
|
+
reason: the binding obligation runs on the same header the source rule resolves
|
|
5124
|
+
source_ref: coder.bun.telemetry-implementation-binding
|
|
5125
|
+
strength: important
|
|
5126
|
+
target_ref: coder.bun.telemetry-source-binding
|
|
5127
|
+
type: requires
|
|
5128
|
+
origin: "@afokapu/atdd-bun"
|
|
5129
|
+
- confidence: 1
|
|
5130
|
+
constraint: mandatory
|
|
5131
|
+
control: internal
|
|
5132
|
+
foundation: finish_to_start
|
|
5133
|
+
reason: required items come from acceptance decisions
|
|
5134
|
+
source_ref: coder.bun.telemetry-implementation-binding
|
|
5135
|
+
strength: important
|
|
5136
|
+
target_ref: planner.telemetry.acceptance-decision
|
|
5137
|
+
type: requires
|
|
5138
|
+
origin: "@afokapu/atdd-bun"
|
|
5139
|
+
- confidence: 1
|
|
5140
|
+
constraint: mandatory
|
|
5141
|
+
control: internal
|
|
5142
|
+
foundation: finish_to_start
|
|
5143
|
+
reason: a bound test must point at the acceptance whose decision requires the item
|
|
5144
|
+
source_ref: tester.bun.telemetry-test-binding
|
|
5145
|
+
strength: important
|
|
5146
|
+
target_ref: planner.telemetry.acceptance-decision
|
|
5147
|
+
type: requires
|
|
5148
|
+
origin: "@afokapu/atdd-bun"
|
|
5149
|
+
- confidence: 1
|
|
5150
|
+
constraint: mandatory
|
|
5151
|
+
control: internal
|
|
5152
|
+
foundation: finish_to_start
|
|
5153
|
+
reason: the captured-sink obligation carries the emission assertion into the telemetry profile
|
|
5154
|
+
source_ref: tester.bun.telemetry-captured-sink
|
|
5155
|
+
strength: important
|
|
5156
|
+
target_ref: tester.bun.telemetry-emit
|
|
5157
|
+
type: requires
|
|
5158
|
+
origin: "@afokapu/atdd-bun"
|
|
5159
|
+
- confidence: 1
|
|
5160
|
+
constraint: mandatory
|
|
5161
|
+
control: internal
|
|
5162
|
+
foundation: finish_to_start
|
|
5163
|
+
reason: exact identity extends the emission assertion from any-call to the named item
|
|
5164
|
+
source_ref: tester.bun.telemetry-identity-assertion
|
|
5165
|
+
strength: important
|
|
5166
|
+
target_ref: tester.bun.telemetry-emit
|
|
5167
|
+
type: requires
|
|
5168
|
+
origin: "@afokapu/atdd-bun"
|
|
5169
|
+
- confidence: 1
|
|
5170
|
+
constraint: mandatory
|
|
5171
|
+
control: internal
|
|
5172
|
+
foundation: finish_to_start
|
|
5173
|
+
reason: telemetry items follow the acceptance-has-test discipline
|
|
5174
|
+
source_ref: tester.bun.telemetry-required-item-coverage
|
|
5175
|
+
strength: important
|
|
5176
|
+
target_ref: traceability.plan.executable-acceptance-has-test
|
|
5177
|
+
type: requires
|
|
5178
|
+
origin: "@afokapu/atdd-bun"
|
|
5179
|
+
- confidence: 1
|
|
5180
|
+
constraint: mandatory
|
|
5181
|
+
control: internal
|
|
5182
|
+
foundation: finish_to_start
|
|
5183
|
+
reason: timing semantics are declared on the tracking-plan item
|
|
5184
|
+
source_ref: tester.bun.telemetry-timing-semantics
|
|
5185
|
+
strength: important
|
|
5186
|
+
target_ref: planner.telemetry.tracking-plan-schema
|
|
5187
|
+
type: requires
|
|
5188
|
+
origin: "@afokapu/atdd-bun"
|
package/src/enforce.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { topologyFor } from "./topology";
|
|
6
6
|
|
|
7
|
-
export type Profile = "traceability" | "topology" | "docs" | "planner" | "coder" | "tester" | "security" | "architecture" | "metrics" | "runtime" | "interlocking" | "htmx" | "design" | "all";
|
|
7
|
+
export type Profile = "traceability" | "topology" | "docs" | "planner" | "telemetry" | "coder" | "tester" | "security" | "architecture" | "metrics" | "runtime" | "interlocking" | "htmx" | "design" | "all";
|
|
8
8
|
|
|
9
9
|
export type Violation = {
|
|
10
10
|
rule_id: string;
|
|
@@ -27,6 +27,7 @@ const profiles: Record<Exclude<Profile, "all">, string[]> = {
|
|
|
27
27
|
topology: ["atdd_topology"],
|
|
28
28
|
docs: ["planner_docs_capability"],
|
|
29
29
|
planner: ["planner_plan_integrity", "planner_schema_validation", "planner_static_validators", "atdd_topology"],
|
|
30
|
+
telemetry: ["planner_telemetry_plan", "bun_telemetry_code", "bun_telemetry_test"],
|
|
30
31
|
coder: ["bun_green_traceability_detector", "bun_clean_architecture_detector", "bun_ts_metrics_detector", "bun_fullstack_detector", "bun_design_system_detector", "bun_responsive_detector", "atdd_topology"],
|
|
31
32
|
tester: ["bun_tester_discipline_detector", "htmx_e2e_detector", "atdd_topology"],
|
|
32
33
|
security: ["bun_security_hygiene_detector"],
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type { DocumentationCheck, DocumentationDeclaration, DocumentationRender,
|
|
|
5
5
|
export { loadPlan, traceabilityPlan, validatePlan } from "./planner-kernel";
|
|
6
6
|
export type { PlanArtifact, PlanFinding, PlanGraph, PlanKind } from "./planner-kernel";
|
|
7
7
|
export { validateStaticPlannerConventions } from "./planner-validators";
|
|
8
|
+
export { validateTelemetryPlan, loadTelemetryFiles, telemetryDecisionOf, CONCRETE_URN } from "./telemetry-plan";
|
|
9
|
+
export type { TelemetryPlanItem, TelemetryFile } from "./telemetry-plan";
|
|
8
10
|
export { PLANNER_SCHEMA_RULE_ID, validatePlannerSchemas } from "./planner-schema-validator";
|
|
9
11
|
export { defaultHookPolicy, hookEvents, hooksStatus, installHooks, runHook, uninstallHooks } from "./hooks";
|
|
10
12
|
export type { HookEvent, HookPolicy } from "./hooks";
|
package/src/integrity.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { instructionPaths } from "./agent";
|
|
5
|
-
import { concreteProfiles } from "./enforce";
|
|
6
5
|
import { defaultHookPolicy, type HookPolicy } from "./hooks";
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -106,6 +105,10 @@ async function checkGenerated(root: string, packageRoot: string): Promise<Integr
|
|
|
106
105
|
return findings;
|
|
107
106
|
}
|
|
108
107
|
|
|
108
|
+
/** The operator's explicit profile list, or null when atdd-bun.yaml declares none. Deliberately not enabledProfiles():
|
|
109
|
+
* its absent-means-all default is right for execution and wrong for deciding whether a policy was ever declared. */
|
|
110
|
+
const explicitProfiles = (config: { profiles?: unknown }): string[] | null => Array.isArray(config.profiles) ? config.profiles.map(String) : null;
|
|
111
|
+
|
|
109
112
|
/** Names of the policy fields in `current` that are looser than in `base`. */
|
|
110
113
|
export function loosenedPolicy(base: Partial<HookPolicy> & { profiles?: unknown }, current: Partial<HookPolicy> & { profiles?: unknown }): string[] {
|
|
111
114
|
const b = { ...defaultHookPolicy, ...base, worktrees: { ...defaultHookPolicy.worktrees, ...base.worktrees } }, c = { ...defaultHookPolicy, ...current, worktrees: { ...defaultHookPolicy.worktrees, ...current.worktrees } };
|
|
@@ -116,20 +119,33 @@ export function loosenedPolicy(base: Partial<HookPolicy> & { profiles?: unknown
|
|
|
116
119
|
const removed = b.protected_branches.filter(x => !c.protected_branches.includes(x)), added = c.registry_paths.filter(x => !b.registry_paths.includes(x));
|
|
117
120
|
if (removed.length) out.push(`protected_branches drops ${removed.join(", ")}`);
|
|
118
121
|
if (added.length) out.push(`registry_paths adds ${added.join(", ")}`);
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
// Profiles: an absent list runs every profile (enabledProfiles) but governs none. The first explicit list is the
|
|
123
|
+
// adoption that establishes the governed set, not a drop. From then on, dropping a profile or removing the list
|
|
124
|
+
// (explicit → implicit → narrower would otherwise be a two-step bypass) is loosening a human approves.
|
|
125
|
+
const before = explicitProfiles(base), after = explicitProfiles(current);
|
|
126
|
+
if (before && !after) out.push(`profiles becomes implicit: the explicit list [${before.join(", ")}] was removed`);
|
|
127
|
+
const dropped = before && after ? before.filter(name => !after.includes(name)) : [];
|
|
122
128
|
if (dropped.length) out.push(`profiles drops ${dropped.join(", ")}`);
|
|
123
129
|
return out;
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
/** atdd-bun.yaml is not looser than on the branch being merged into. */
|
|
127
133
|
async function checkPolicy(root: string, base?: string, push = process.env.GITHUB_EVENT_NAME === "push"): Promise<IntegrityFinding[]> {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
134
|
+
// On a push, the generated CI passes the pre-push tip (github.event.before) as ATDD_BASE_REF, so a multi-commit push
|
|
135
|
+
// is judged as a whole: [docs, security] → no list → [docs] in one push cannot read as a first adoption.
|
|
136
|
+
const ref = base || process.env.ATDD_BASE_REF || (process.env.GITHUB_BASE_REF ? `origin/${process.env.GITHUB_BASE_REF}` : "origin/HEAD");
|
|
137
|
+
const newBranch = /^0+$/.test(ref), resolved = newBranch ? "" : (await git(root, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`])).out;
|
|
138
|
+
let against: string;
|
|
139
|
+
// An explicit baseline (the pre-push tip, the merge queue's target) that cannot be resolved fails closed.
|
|
140
|
+
if (!newBranch && !resolved && (base || process.env.ATDD_BASE_REF)) return [{ file: "atdd-bun.yaml", detail: `cannot resolve the policy baseline ${ref.slice(0, 7)} to judge this change against; fetch it (fetch-depth: 0)`, restore: "git fetch origin && re-run the check" }];
|
|
141
|
+
if (push) {
|
|
142
|
+
// A push is judged against the tip it replaced, directly, never a merge base: after a force push the merge base
|
|
143
|
+
// can predate the policy being removed. A new branch has no previous tip and is judged against its parent.
|
|
144
|
+
against = resolved && resolved !== (await git(root, ["rev-parse", "HEAD"])).out ? resolved : (await git(root, ["rev-parse", "--verify", "--quiet", "HEAD~1"])).out;
|
|
145
|
+
} else {
|
|
146
|
+
if (!resolved) return [];
|
|
147
|
+
against = (await git(root, ["merge-base", "HEAD", ref])).out;
|
|
148
|
+
}
|
|
133
149
|
if (!against) return [];
|
|
134
150
|
const read = async (text: string | null) => (text ? Bun.YAML.parse(text) ?? {} : {}) as Partial<HookPolicy>;
|
|
135
151
|
const before = await git(root, ["show", `${against}:atdd-bun.yaml`]), path = join(root, "atdd-bun.yaml");
|
package/src/setup.ts
CHANGED
|
@@ -2,6 +2,21 @@ import { ciInit, ciStatus } from "./ci";
|
|
|
2
2
|
import { hooksStatus, installHooks } from "./hooks";
|
|
3
3
|
import { agentInit, agentStatus } from "./agent";
|
|
4
4
|
import { integrityInit, integrityStatus } from "./integrity";
|
|
5
|
+
import { concreteProfiles } from "./enforce";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { writeFile } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
|
|
10
|
+
/** A new atdd-bun.yaml declares every profile explicitly. An absent list runs every profile but governs none, so
|
|
11
|
+
* without this a greenfield repository's first `profiles:` line could switch most of them off unreported. A
|
|
12
|
+
* brownfield repository trims the list before its first commit; after that, dropping one is a reported loosening.
|
|
13
|
+
* An existing atdd-bun.yaml is never touched. */
|
|
14
|
+
export async function policyInit(root = process.cwd()) {
|
|
15
|
+
const file = join(root, "atdd-bun.yaml");
|
|
16
|
+
if (existsSync(file)) return { ok: true, message: `${file} kept` };
|
|
17
|
+
await writeFile(file, `# Generated by atdd-bun init. The profiles this repository enforces; trim the list before the first commit to\n# adopt gradually. Once committed, removing a profile or the list is reported by the integrity check.\nprofiles: [${concreteProfiles.join(", ")}]\n`);
|
|
18
|
+
return { ok: true, message: file };
|
|
19
|
+
}
|
|
5
20
|
|
|
6
21
|
/** Install the package's opt-in local surfaces without touching unrelated
|
|
7
22
|
* workflows or hook paths. Dependency installation itself never calls this. */
|
|
@@ -21,5 +36,6 @@ export async function initializeRepository(root = process.cwd(), replace = false
|
|
|
21
36
|
const existingIntegrity = await integrityStatus(root);
|
|
22
37
|
const integrity = replace || !existingIntegrity.ok ? await integrityInit(root, replace) : existingIntegrity;
|
|
23
38
|
if (!integrity.ok) return { ok: false, message: `integrity test: ${integrity.message}` };
|
|
24
|
-
|
|
39
|
+
const policy = await policyInit(root);
|
|
40
|
+
return { ok: true, message: `hooks: ${hooks.message}\nCI workflow: ${ci.message}\nagent skill: ${agent.message}\nintegrity test: ${integrity.message}\npolicy: ${policy.message}` };
|
|
25
41
|
}
|