@afokapu/atdd-bun 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +2 -0
  2. package/conventions/coder.bun/coder.bun.telemetry-forbidden-properties.convention.yaml +51 -0
  3. package/conventions/coder.bun/coder.bun.telemetry-implementation-binding.convention.yaml +45 -0
  4. package/conventions/coder.bun/coder.bun.telemetry-raw-string-emit.convention.yaml +53 -0
  5. package/conventions/coder.bun/coder.bun.telemetry-source-binding.convention.yaml +48 -0
  6. package/conventions/coder.bun/coder.bun.telemetry-vendor-sdk.convention.yaml +51 -0
  7. package/conventions/planner.telemetry/planner.telemetry.acceptance-decision.convention.yaml +58 -0
  8. package/conventions/planner.telemetry/planner.telemetry.logical-ownership.convention.yaml +50 -0
  9. package/conventions/planner.telemetry/planner.telemetry.metric-cardinality.convention.yaml +47 -0
  10. package/conventions/planner.telemetry/planner.telemetry.tracking-plan-schema.convention.yaml +75 -0
  11. package/conventions/tester.bun/tester.bun.telemetry-captured-sink.convention.yaml +47 -0
  12. package/conventions/tester.bun/tester.bun.telemetry-identity-assertion.convention.yaml +44 -0
  13. package/conventions/tester.bun/tester.bun.telemetry-required-item-coverage.convention.yaml +44 -0
  14. package/conventions/tester.bun/tester.bun.telemetry-test-binding.convention.yaml +52 -0
  15. package/conventions/tester.bun/tester.bun.telemetry-timing-semantics.convention.yaml +53 -0
  16. package/detectors/bun_telemetry_code/atdd.implementation.yaml +24 -0
  17. package/detectors/bun_telemetry_code/calls.mjs +104 -0
  18. package/detectors/bun_telemetry_code/checks/t_forbidden_properties.mjs +46 -0
  19. package/detectors/bun_telemetry_code/checks/t_implementation_binding.mjs +41 -0
  20. package/detectors/bun_telemetry_code/checks/t_raw_string_emit.mjs +34 -0
  21. package/detectors/bun_telemetry_code/checks/t_source_binding.mjs +48 -0
  22. package/detectors/bun_telemetry_code/checks/t_vendor_sdk.mjs +38 -0
  23. package/detectors/bun_telemetry_code/detect.mjs +50 -0
  24. package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/E001.yaml +10 -0
  25. package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/E002.yaml +7 -0
  26. package/detectors/bun_telemetry_code/fixtures/clean/plan/commons/_commons.yaml +6 -0
  27. package/detectors/bun_telemetry_code/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  28. package/detectors/bun_telemetry_code/fixtures/clean/src/wagons/commons/features/ingress/infrastructure/otel-adapter.ts +8 -0
  29. package/detectors/bun_telemetry_code/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  30. package/detectors/bun_telemetry_code/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  31. package/detectors/bun_telemetry_code/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
  32. package/detectors/bun_telemetry_code/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.test.ts +9 -0
  33. package/detectors/bun_telemetry_code/fixtures/dirty/plan/commons/E001.yaml +8 -0
  34. package/detectors/bun_telemetry_code/fixtures/dirty/plan/commons/_commons.yaml +6 -0
  35. package/detectors/bun_telemetry_code/fixtures/dirty/src/wagons/commons/features/ingress/domain/accept-response.ts +13 -0
  36. package/detectors/bun_telemetry_code/fixtures/dirty/src/wagons/commons/features/ingress/domain/tracing.ts +3 -0
  37. package/detectors/bun_telemetry_code/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  38. package/detectors/bun_telemetry_code/fixtures/dirty/tests/wagons/commons/features/ingress/unit/probe.test.ts +7 -0
  39. package/detectors/bun_telemetry_code/registry.mjs +34 -0
  40. package/detectors/bun_telemetry_test/_shared.mjs +92 -0
  41. package/detectors/bun_telemetry_test/atdd.implementation.yaml +24 -0
  42. package/detectors/bun_telemetry_test/checks/t_captured_sink.mjs +35 -0
  43. package/detectors/bun_telemetry_test/checks/t_identity_assertion.mjs +45 -0
  44. package/detectors/bun_telemetry_test/checks/t_required_item_coverage.mjs +37 -0
  45. package/detectors/bun_telemetry_test/checks/t_test_binding.mjs +56 -0
  46. package/detectors/bun_telemetry_test/checks/t_timing_semantics.mjs +51 -0
  47. package/detectors/bun_telemetry_test/detect.mjs +50 -0
  48. package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/E001.yaml +10 -0
  49. package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/E002.yaml +7 -0
  50. package/detectors/bun_telemetry_test/fixtures/clean/plan/commons/_commons.yaml +6 -0
  51. package/detectors/bun_telemetry_test/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  52. package/detectors/bun_telemetry_test/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  53. package/detectors/bun_telemetry_test/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  54. package/detectors/bun_telemetry_test/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
  55. package/detectors/bun_telemetry_test/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.test.ts +7 -0
  56. package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/E001.yaml +10 -0
  57. package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/E002.yaml +7 -0
  58. package/detectors/bun_telemetry_test/fixtures/dirty/plan/commons/_commons.yaml +6 -0
  59. package/detectors/bun_telemetry_test/fixtures/dirty/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  60. package/detectors/bun_telemetry_test/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  61. package/detectors/bun_telemetry_test/fixtures/dirty/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  62. package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/dangling.telemetry.test.ts +9 -0
  63. package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/loose.telemetry.test.ts +11 -0
  64. package/detectors/bun_telemetry_test/fixtures/dirty/tests/wagons/commons/features/ingress/unit/unbound.telemetry.test.ts +10 -0
  65. package/detectors/planner_telemetry_plan/atdd.implementation.yaml +22 -0
  66. package/detectors/planner_telemetry_plan/detect.mjs +9 -0
  67. package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/E001.yaml +10 -0
  68. package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/E002.yaml +7 -0
  69. package/detectors/planner_telemetry_plan/fixtures/clean/plan/commons/_commons.yaml +6 -0
  70. package/detectors/planner_telemetry_plan/fixtures/clean/src/wagons/commons/features/ingress/domain/accept-response.ts +8 -0
  71. package/detectors/planner_telemetry_plan/fixtures/clean/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  72. package/detectors/planner_telemetry_plan/fixtures/clean/telemetry/commons/response-invocation-accepted/metric.be.duration.json +19 -0
  73. package/detectors/planner_telemetry_plan/fixtures/clean/tests/wagons/commons/features/ingress/unit/accept-response.telemetry.test.ts +13 -0
  74. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E001.yaml +9 -0
  75. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E002.yaml +4 -0
  76. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/E003.yaml +7 -0
  77. package/detectors/planner_telemetry_plan/fixtures/dirty/plan/commons/_commons.yaml +6 -0
  78. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/extra.json +1 -0
  79. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/orphan-artifact/event.be.json +14 -0
  80. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/event.be.json +16 -0
  81. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/metric.be.duration.json +22 -0
  82. package/detectors/planner_telemetry_plan/fixtures/dirty/telemetry/commons/response-invocation-accepted/notes.json +1 -0
  83. package/integrity.json +91 -8
  84. package/lib/scan.mjs +37 -0
  85. package/package.json +1 -1
  86. package/planner-schemas/acceptance.schema.json +70 -1
  87. package/planner-schemas/telemetry-plan.schema.json +133 -0
  88. package/relationships.yaml +174 -0
  89. package/src/enforce.ts +2 -1
  90. package/src/index.ts +2 -0
  91. package/src/telemetry-plan.ts +223 -0
  92. package/src/topology.ts +3 -1
package/README.md CHANGED
@@ -54,6 +54,7 @@ registerEnforcementTest({ root: import.meta.dir + "/..", profiles: ["traceabilit
54
54
  | `traceability` | acceptance → Bun test → source closure: every acceptance tested, every binding and `Tested-By` resolving |
55
55
  | `topology` | feature decomposition and the plan, source, test and E2E locations |
56
56
  | `planner` | schemas for every plan artifact, graph integrity, the scoped planner rules |
57
+ | `telemetry` | the telemetry tracking plan: item shape, path-mirrored identity and versioning under `telemetry/`, wagon ownership of logical artifacts, the per-acceptance telemetry decision, metric label cardinality, source `Telemetry:` references, raw-string and forbidden-property emission, the vendor-SDK boundary around the TelemetryPort, and telemetry tests that bind the acceptance and item, assert the exact identity on a captured sink, cover every required item, and exercise declared timing semantics |
57
58
  | `docs` | the documentation capability, including the generated journey view |
58
59
  | `coder`, `tester`, `security`, `architecture`, `metrics`, `runtime` | Bun source and test conventions |
59
60
  | `interlocking` | train/interlocking binding, infrastructure and route coverage |
@@ -90,6 +91,7 @@ topology: # default layout; point it at exis
90
91
  source_root: src/wagons
91
92
  test_root: tests/wagons
92
93
  e2e_root: e2e
94
+ telemetry_root: telemetry # tracking-plan registry; inert until the tree or a decision exists
93
95
  frontend:
94
96
  viewports: [375, 768, 1280]
95
97
  breakpoints: [480, 768, 1024, 1280]
@@ -0,0 +1,51 @@
1
+ schema_version: 1.1.0
2
+ rule_id: coder.bun.telemetry-forbidden-properties
3
+ kind: rule
4
+ status: active
5
+ name: Forbidden properties never reach an emit call
6
+ statement: >-
7
+ The object literal passed to an emit-like call naming a declared tracking-plan item MUST NOT
8
+ carry a property the plan forbids on that item (REQUIRED).
9
+ terms:
10
+ - term_id: forbidden_property
11
+ text: >-
12
+ a name in the item's forbidden_properties list (raw payloads, secrets, prompts): what must
13
+ never leave the process on that item. The plan writes snake_case; code may write camelCase;
14
+ comparison is normalized.
15
+ - term_id: properties_object
16
+ text: >-
17
+ the object literal following the first argument of the emit-like call, on the same statement.
18
+ content:
19
+ summary: >-
20
+ The forbidden list is a privacy and cost promise. It is enforced where the data leaves: the
21
+ emit call itself, against the plan entry the call names.
22
+ normative_text: |
23
+ When an emit-like call's first argument is a declared concrete id, the properties object is
24
+ scanned and every top-level key normalized (separators stripped, case folded) against the
25
+ item's forbidden list. Shorthand properties carry no name of their own and are invisible to
26
+ this scan; nested objects are judged at their own level. Properties the plan merely does not
27
+ declare are not forbidden — undeclared-key drift is the generated contract's concern, not this
28
+ rule's.
29
+ fix_hint: |
30
+ Pass the identifier, not the thing it identifies:
31
+
32
+ telemetry.emit("telemetry:event:be:commons:response-invocation-accepted", {
33
+ response_id: response.id, // the identifier: allowed, planned, high-cardinality-declared
34
+ // raw_payload: response.raw, // forbidden by the plan: never attach it
35
+ });
36
+ exceptions:
37
+ - >-
38
+ Only calls naming a declared item are checked: an id the registry does not hold is the
39
+ raw-string rule's finding, and double-reporting it here would be noise.
40
+ - >-
41
+ Inert until adoption, like every rule in this family.
42
+ metadata:
43
+ aliases:
44
+ - CODER-TELEMETRY-FORBIDDEN-PROPERTIES-001
45
+ severity: 3
46
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
47
+ disposition: strict
48
+ introduced_in: 0.7.0
49
+ implementation:
50
+ type: validator
51
+ ref: bun_telemetry_code
@@ -0,0 +1,45 @@
1
+ schema_version: 1.1.0
2
+ rule_id: coder.bun.telemetry-implementation-binding
3
+ kind: rule
4
+ status: active
5
+ name: Required telemetry is bound to its implementation
6
+ statement: >-
7
+ Every concrete telemetry item an acceptance requires MUST be bound to at least one non-test
8
+ implementation source file by a `// Telemetry:` header (REQUIRED).
9
+ terms:
10
+ - term_id: required_item
11
+ text: >-
12
+ a concrete telemetry URN listed by an acceptance whose telemetry disposition is required.
13
+ - term_id: implementation_binding
14
+ text: >-
15
+ a `// Telemetry:` header naming the item in non-test source — the source-to-telemetry edge
16
+ coder.bun.telemetry-source-binding resolves.
17
+ content:
18
+ summary: >-
19
+ The third leg of the traceability tripod: planned, implemented, tested. This rule closes the
20
+ second leg — a required item no source claims is telemetry the plan believes in and the code
21
+ has never heard of.
22
+ normative_text: |
23
+ Required items are collected from acceptance decisions; implementation bindings from every
24
+ resolved Telemetry: header in non-test source. Their difference is reported at the tracking-plan
25
+ item's own file, because that is where the remedy starts: either bind the emitting source, or
26
+ stop requiring what nothing emits. An unresolvable required URN is the acceptance-decision
27
+ rule's finding and is not double-reported here.
28
+ fix_hint: |
29
+ Bind the emitting source, or drop the requirement:
30
+
31
+ // src/.../accept-response.ts
32
+ // Telemetry: telemetry:metric:be:commons:response-invocation-accepted:duration
33
+ exceptions:
34
+ - >-
35
+ Inert until adoption, like every rule in this family.
36
+ metadata:
37
+ aliases:
38
+ - CODER-TELEMETRY-IMPLEMENTATION-BINDING-001
39
+ severity: 2
40
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
41
+ disposition: strict
42
+ introduced_in: 0.7.0
43
+ implementation:
44
+ type: validator
45
+ ref: bun_telemetry_code
@@ -0,0 +1,53 @@
1
+ schema_version: 1.1.0
2
+ rule_id: coder.bun.telemetry-raw-string-emit
3
+ kind: rule
4
+ status: active
5
+ name: No undeclared raw-string telemetry names
6
+ statement: >-
7
+ An emit-like call (emit, emitted, track, capture, record, logEvent, emitEvent, sendEvent) whose
8
+ first argument is a string literal naming an event MUST name an item declared in the tracking
9
+ plan (REQUIRED).
10
+ terms:
11
+ - term_id: emit_like_call
12
+ text: >-
13
+ a call to the emit vocabulary, as function or method, with a string literal first argument.
14
+ Calls with computed first arguments (variables, template literals, contract constants) are
15
+ invisible to this check by design.
16
+ - term_id: event_shaped_string
17
+ text: >-
18
+ a string that looks like a telemetry event name: a telemetry: URN, snake_case, camelCase with
19
+ an internal case shift, or a Title Case analytics name. Single lowercase words (log levels,
20
+ keys) are not event names.
21
+ content:
22
+ summary: >-
23
+ Undeclared event strings are how tracking plans rot: the string compiles, the dashboard never
24
+ fills, and nobody owns the difference. The registry is the list of names that may be emitted.
25
+ normative_text: |
26
+ When an emit-like call passes an event-shaped string, that string must be a declared concrete
27
+ telemetry id. A declared id passed as a raw string passes today; once generated contracts ship,
28
+ emitting even a declared id by string is the drift the contract exists to prevent, and this
29
+ rule tightens with it. This check does not attempt to prove transaction ordering or any other
30
+ runtime property — it is a name-resolution check.
31
+ fix_hint: |
32
+ Declare the item, then emit through the plan:
33
+
34
+ telemetry/commons/response-invocation-accepted/event.be.json # declares the id
35
+ telemetry.emit("telemetry:event:be:commons:response-invocation-accepted", { ... });
36
+ exceptions:
37
+ - >-
38
+ Test files are excluded: assertion code legitimately names events it is proving.
39
+ - >-
40
+ Strings that are not event-shaped (log levels like "info", single-word keys) never trigger the
41
+ check; the emit vocabulary with a non-event string is some other concern.
42
+ - >-
43
+ Inert until adoption, like every rule in this family.
44
+ metadata:
45
+ aliases:
46
+ - CODER-TELEMETRY-RAW-STRING-EMIT-001
47
+ severity: 3
48
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
49
+ disposition: strict
50
+ introduced_in: 0.7.0
51
+ implementation:
52
+ type: validator
53
+ ref: bun_telemetry_code
@@ -0,0 +1,48 @@
1
+ schema_version: 1.1.0
2
+ rule_id: coder.bun.telemetry-source-binding
3
+ kind: rule
4
+ status: active
5
+ name: Source Telemetry references resolve into the tracking plan
6
+ statement: >-
7
+ A `// Telemetry:` reference in implementation source names a concrete telemetry URN that resolves
8
+ to a tracking-plan item under the telemetry root (REQUIRED).
9
+ terms:
10
+ - term_id: telemetry_reference
11
+ text: >-
12
+ a line comment of the form `// Telemetry: telemetry:{kind}:{plane}:{theme}:{artifact}[:{measure}]`
13
+ in a non-test source file, binding the file to the concrete item it emits.
14
+ - term_id: tracking_plan_item
15
+ text: >-
16
+ a JSON registry entry under telemetry/<theme>/<artifact>/ whose id is the referenced URN.
17
+ content:
18
+ summary: >-
19
+ The Telemetry: header is the source-to-telemetry traceability edge. Like Tested-By, it is a
20
+ promise the package can check: the URN must be well-formed and the registry must hold it.
21
+ normative_text: |
22
+ Implementation source that emits telemetry says so, by name, in a header comment. Two failures
23
+ are reported: a reference that is not a concrete telemetry URN (grammar), and a reference that
24
+ is grammatical but names no tracking-plan item (resolution). A header inside a string literal
25
+ is not a header. Test files are implementation's mirror, not implementation: they are excluded,
26
+ because a telemetry test legitimately names items while asserting them.
27
+ fix_hint: |
28
+ Bind the file to the item it emits, and keep the binding current when the plan renames:
29
+
30
+ // Telemetry: telemetry:event:be:commons:response-invocation-accepted
31
+ export function acceptResponse(response, telemetry) { ... }
32
+ exceptions:
33
+ - >-
34
+ The capability is inert until adopted (no telemetry root and no acceptance telemetry declaration
35
+ anywhere): an unadopting repository emits nothing, whichever profiles it runs.
36
+ - >-
37
+ The header is a line comment in source syntax (`//`). Block comments and markup attributes are
38
+ not parsed in this realization.
39
+ metadata:
40
+ aliases:
41
+ - CODER-TELEMETRY-SOURCE-BINDING-001
42
+ severity: 2
43
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
44
+ disposition: strict
45
+ introduced_in: 0.7.0
46
+ implementation:
47
+ type: validator
48
+ ref: bun_telemetry_code
@@ -0,0 +1,51 @@
1
+ schema_version: 1.1.0
2
+ rule_id: coder.bun.telemetry-vendor-sdk
3
+ kind: rule
4
+ status: active
5
+ name: Telemetry vendor SDKs stay in infrastructure adapters
6
+ statement: >-
7
+ A layered source file outside the integration layer and infrastructure directories MUST NOT
8
+ import a telemetry vendor SDK (REQUIRED).
9
+ terms:
10
+ - term_id: vendor_sdk
11
+ text: >-
12
+ an exporter or agent package: @opentelemetry/*, @sentry/*, @datadog/*, dd-trace, newrelic,
13
+ segment (bare or @segment/*), mixpanel (bare or @mixpanel/*), amplitude (bare or
14
+ @amplitude/*), @posthog/*, @statsig/*, @braze/*, @heap/*, @rudderstack/*.
15
+ - term_id: adapter_location
16
+ text: >-
17
+ the integration layer, or an infrastructure/ directory — the outer ring where exporters are
18
+ wired to the neutral TelemetryPort.
19
+ content:
20
+ summary: >-
21
+ Core depends only on a TelemetryPort. The moment a domain module imports an exporter, every
22
+ wagon behind it inherits the vendor, and the plan's exporter-neutrality promise is gone.
23
+ normative_text: |
24
+ Files carrying a layer segment (domain, application, presentation, integration, assembly) may
25
+ import a vendor SDK only when the layer is integration or the path sits under infrastructure/.
26
+ Type-only imports count: the coupling is real even when erased. Unlayered entrypoints (a
27
+ composition root, a script) are outside this rule — they wire adapters, they do not become one.
28
+ fix_hint: |
29
+ Depend on the port; adapt at the edge:
30
+
31
+ // domain/accept-response.ts — no SDK, only the port parameter
32
+ export function acceptResponse(response, telemetry: TelemetryPort) { telemetry.emit(...); }
33
+
34
+ // infrastructure/otel-adapter.ts — the SDK lives here and only here
35
+ import { metrics } from "@opentelemetry/api";
36
+ exceptions:
37
+ - >-
38
+ The integration layer and infrastructure/ directories are exempt by design: that is where the
39
+ adapter belongs.
40
+ - >-
41
+ Inert until adoption, like every rule in this family.
42
+ metadata:
43
+ aliases:
44
+ - CODER-TELEMETRY-VENDOR-SDK-001
45
+ severity: 2
46
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
47
+ disposition: strict
48
+ introduced_in: 0.7.0
49
+ implementation:
50
+ type: validator
51
+ ref: bun_telemetry_code
@@ -0,0 +1,58 @@
1
+ schema_version: 1.1.0
2
+ rule_id: planner.telemetry.acceptance-decision
3
+ kind: rule
4
+ status: active
5
+ name: Every acceptance makes an explicit telemetry decision
6
+ statement: >-
7
+ Every acceptance declares telemetry with disposition required (naming at least one concrete
8
+ telemetry URN that resolves to a tracking-plan entry) or disposition not-applicable (with a
9
+ rationale), and every tracking-plan item's acceptance references resolve to declared acceptances
10
+ (REQUIRED).
11
+ terms:
12
+ - term_id: decision
13
+ text: >-
14
+ the acceptance's telemetry block: disposition plus, for required, concrete item lists
15
+ (events, metrics, traces, logs), or, for not-applicable, a rationale of at least 20 characters.
16
+ - term_id: required_item
17
+ text: >-
18
+ a concrete telemetry URN an acceptance demands be observed emitted. It must exist as a
19
+ tracking-plan item under the telemetry root.
20
+ content:
21
+ summary: >-
22
+ The decision removes the false choice between "test every debug log" and "have no observability
23
+ requirement". Not-applicable is a considered answer, not the absence of one.
24
+ normative_text: |
25
+ Once the capability is adopted, silence is not a disposition. An acceptance either requires
26
+ telemetry — and then each URN it lists must be a well-formed concrete URN present in the
27
+ tracking plan, or the requirement is unplannable — or it declares not-applicable with a
28
+ rationale long enough to be an answer (20 characters minimum). In the other direction, a
29
+ tracking-plan item that names an acceptance nothing declares is a dangling traceability link.
30
+ Resolution is checked in both directions; the lists are not required to be mirror images,
31
+ because an item may serve acceptances beyond those that first required it.
32
+ fix_hint: |
33
+ Decide, on every acceptance:
34
+
35
+ telemetry:
36
+ disposition: required
37
+ events: [telemetry:event:be:commons:response-invocation-accepted]
38
+
39
+ telemetry:
40
+ disposition: not-applicable
41
+ rationale: No externally useful observable outcome beyond the tested return value.
42
+ exceptions:
43
+ - >-
44
+ Diagnostic logs and internal spans are not automatically telemetry obligations. The decision is
45
+ per acceptance; nothing here requires a dedicated test for every debug-oriented record.
46
+ - >-
47
+ Inert until adoption: with no telemetry root and no acceptance telemetry declaration, this rule
48
+ emits nothing (see planner.telemetry.tracking-plan-schema).
49
+ metadata:
50
+ aliases:
51
+ - TELEMETRY-ACCEPTANCE-DECISION-001
52
+ severity: 2
53
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
54
+ disposition: strict
55
+ introduced_in: 0.7.0
56
+ implementation:
57
+ type: validator
58
+ ref: planner_telemetry_plan
@@ -0,0 +1,50 @@
1
+ schema_version: 1.1.0
2
+ rule_id: planner.telemetry.logical-ownership
3
+ kind: rule
4
+ status: active
5
+ name: Concrete telemetry resolves to exactly one wagon-owned logical artifact
6
+ statement: >-
7
+ Every concrete tracking-plan item's logical artifact is produced by exactly one wagon's
8
+ produce[] telemetry declaration, and the item's owner names that wagon (REQUIRED).
9
+ terms:
10
+ - term_id: ownership_declaration
11
+ text: >-
12
+ a wagon produce[] entry whose telemetry field (string or list) names the logical artifact
13
+ URN telemetry:{theme}:{artifact}. Ownership lives in the plan, not in the registry.
14
+ - term_id: owner
15
+ text: >-
16
+ the tracking-plan item field naming the wagon slug accountable for the artifact. It must equal
17
+ the wagon whose produce[] declares the logical artifact.
18
+ content:
19
+ summary: >-
20
+ A logical telemetry artifact with two producers is a coincidence waiting to diverge; with none,
21
+ it is emitted by code no plan stands behind. Both are plan defects, caught at the item.
22
+ normative_text: |
23
+ The registry does not grant ownership; it resolves against ownership the plan already declares.
24
+ For each item, the logical artifact must appear in exactly one wagon's produce[] telemetry, and
25
+ the item's owner field must equal that wagon's slug. Zero owners means no wagon has stood behind
26
+ the observable outcome; more than one means the emission contract has two authors with no
27
+ arbiter.
28
+ fix_hint: |
29
+ Declare the logical artifact on the producing wagon, and name that wagon as owner:
30
+
31
+ # plan/commons/_commons.yaml
32
+ produce:
33
+ - name: commons:response-invocation
34
+ contract: null
35
+ telemetry: telemetry:commons:response-invocation-accepted
36
+ exceptions:
37
+ - >-
38
+ Duplicate producers at the wagon level are also reported by planner.wagon.telemetry-filesystem
39
+ under the planner profile; this rule reports the same ambiguity at the item level under the
40
+ telemetry profile, so an adopting repository sees it whichever profile it runs.
41
+ metadata:
42
+ aliases:
43
+ - TELEMETRY-LOGICAL-OWNERSHIP-001
44
+ severity: 2
45
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
46
+ disposition: strict
47
+ introduced_in: 0.7.0
48
+ implementation:
49
+ type: validator
50
+ ref: planner_telemetry_plan
@@ -0,0 +1,47 @@
1
+ schema_version: 1.1.0
2
+ rule_id: planner.telemetry.metric-cardinality
3
+ kind: rule
4
+ status: active
5
+ name: High-cardinality identifiers are forbidden as metric labels
6
+ statement: >-
7
+ Metric tracking-plan items declare only low- or medium-cardinality dimensions, and no dimension
8
+ names a property declared cardinality high (REQUIRED).
9
+ terms:
10
+ - term_id: dimension
11
+ text: >-
12
+ a metric label: an entry of the metric item's dimensions list, with a name and a declared
13
+ cardinality (low, medium or high).
14
+ - term_id: high_cardinality_identifier
15
+ text: >-
16
+ a property whose distinct-value volume is unbounded or near-unbounded (request ids, user ids,
17
+ session tokens). Legal in event, log and trace properties where the plan justifies it; never as
18
+ a metric label.
19
+ content:
20
+ summary: >-
21
+ A high-cardinality metric label is a backend outage with a YAML file attached: every distinct
22
+ value becomes a new time series. The plan is where that mistake is cheapest to catch.
23
+ normative_text: |
24
+ Metric dimensions must declare cardinality, and high is rejected — both when the dimension
25
+ itself says high and when the dimension names a property the item declares cardinality high.
26
+ The identifier still belongs in the plan: carry it as an event, log or trace property, where
27
+ cardinality is the point rather than a liability.
28
+ fix_hint: |
29
+ Keep the identifier, change the signal:
30
+
31
+ # telemetry/commons/response-invocation-accepted/metric.be.duration.json
32
+ "dimensions": [{ "name": "outcome", "cardinality": "low" }]
33
+ # response_id moves to the sibling event's properties, declared cardinality high.
34
+ exceptions:
35
+ - >-
36
+ Event, log and trace properties may declare cardinality high where justified; this rule judges
37
+ metric dimensions only.
38
+ metadata:
39
+ aliases:
40
+ - TELEMETRY-METRIC-CARDINALITY-001
41
+ severity: 3
42
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
43
+ disposition: strict
44
+ introduced_in: 0.7.0
45
+ implementation:
46
+ type: validator
47
+ ref: planner_telemetry_plan
@@ -0,0 +1,75 @@
1
+ schema_version: 1.1.0
2
+ rule_id: planner.telemetry.tracking-plan-schema
3
+ kind: rule
4
+ status: active
5
+ name: Tracking-plan items are versioned, path-mirrored registry entries
6
+ statement: >-
7
+ Every JSON file under the telemetry root is one concrete tracking-plan item at
8
+ `telemetry/<theme>/<artifact>/{event|trace|metric|log}.<plane>[.<measure>].json`, shape-valid
9
+ against telemetry-plan.schema.json, with its id and logical artifact mirroring its path, a SemVer
10
+ version, and no concrete id declared twice (REQUIRED).
11
+ terms:
12
+ - term_id: tracking_plan_item
13
+ text: >-
14
+ one JSON file in the telemetry tree declaring a concrete observation: id, version,
15
+ logical_artifact, kind, plane, owner, purpose, acceptances, properties, required, and
16
+ optionally forbidden_properties and (for metrics) dimensions.
17
+ - term_id: concrete_item
18
+ text: >-
19
+ a telemetry URN with kind and plane: telemetry:{kind}:{plane}:{theme}:{artifact}[:{measure}].
20
+ Metric items carry a measure segment; event, trace and log items never do.
21
+ - term_id: logical_artifact
22
+ text: >-
23
+ the stable architectural identity of an observable outcome, telemetry:{theme}:{artifact}. Not
24
+ an exporter event; the concrete items under it are.
25
+ - term_id: telemetry_root
26
+ text: >-
27
+ the versioned tracking-plan source of truth, `telemetry/` by default, configurable as
28
+ topology.telemetry_root in atdd-bun.yaml.
29
+ content:
30
+ summary: >-
31
+ The telemetry tree is a registry, not a suggestion. Each file is schema-validated, and the
32
+ file's place in the tree is the authority for its identity: a renamed file or a hand-edited id
33
+ cannot drift apart silently.
34
+ normative_text: |
35
+ Local shape is the JSON Schema's job (telemetry-plan.schema.json): required identity fields, URN
36
+ grammar, SemVer core version, property declarations with classification and cardinality, and
37
+ metric dimensions that declare cardinality. Everything the Schema cannot see is this rule's
38
+ cross-artifact half: the file name's kind, plane and measure must match the declared fields; the
39
+ id must equal telemetry:{kind}:{plane}:{theme}:{artifact}[:{measure}] derived from the path; the
40
+ logical artifact must equal telemetry:{theme}:{artifact} from the path; every name in `required`
41
+ must be declared in `properties`; and no two files may declare the same concrete id.
42
+ fix_hint: |
43
+ Name the file for what it is and let the identity follow the path:
44
+
45
+ telemetry/commons/response-invocation-accepted/event.be.json
46
+ {
47
+ "id": "telemetry:event:be:commons:response-invocation-accepted",
48
+ "version": "1.0.0",
49
+ "logical_artifact": "telemetry:commons:response-invocation-accepted",
50
+ "kind": "event", "plane": "be", "owner": "commons",
51
+ "purpose": "Records that a response invocation passed acceptance so ingress health can be audited.",
52
+ "acceptances": ["acc:commons:E001-UNIT-001"],
53
+ "properties": { "response_id": { "type": "string", "classification": "internal", "cardinality": "high" } },
54
+ "required": ["response_id"],
55
+ "forbidden_properties": ["raw_payload", "secret", "prompt"]
56
+ }
57
+ exceptions:
58
+ - >-
59
+ The capability is inert until adopted. With no telemetry root and no acceptance telemetry
60
+ declaration anywhere, this rule emits nothing: upgrading the package must not fail a repository
61
+ that has not opted in. Adoption is a positive act — creating the tree, or making the first
62
+ acceptance decision.
63
+ - >-
64
+ The tree holds only tracking-plan items. Free-form JSON (notes, indexes, generated bundles)
65
+ does not belong under the telemetry root; every .json file is judged as an item.
66
+ metadata:
67
+ aliases:
68
+ - TELEMETRY-PLAN-SCHEMA-001
69
+ severity: 2
70
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
71
+ disposition: strict
72
+ introduced_in: 0.7.0
73
+ implementation:
74
+ type: validator
75
+ ref: planner_telemetry_plan
@@ -0,0 +1,47 @@
1
+ schema_version: 1.1.0
2
+ rule_id: tester.bun.telemetry-captured-sink
3
+ kind: rule
4
+ status: active
5
+ name: Telemetry tests assert on a captured sink
6
+ statement: >-
7
+ A telemetry test contains an assertion on the emission itself — `toHaveBeenCalled[With|Times]`, or
8
+ an expectation on `.emit` / `.emitted` / `.capture` / `.track` / `.record` (REQUIRED).
9
+ terms:
10
+ - term_id: captured_sink
11
+ text: >-
12
+ the spy, mock or captured array the emitter writes to: the thing whose CONTENT is the emission.
13
+ A return value is not a sink.
14
+ - term_id: telemetry_test
15
+ text: >-
16
+ a test file with a Telemetry: header, a TELEMETRY/EVENT/METRIC URN kind segment, or
17
+ *.telemetry.test.* colocation.
18
+ content:
19
+ summary: >-
20
+ Same obligation as tester.bun.telemetry-emit, carried into the telemetry profile and joined
21
+ with the exact-identity requirement: the event is the point, and only the sink saw it fire.
22
+ normative_text: |
23
+ A telemetry test that checks a return value has proven the function ran. It has not proven the
24
+ item fired — and dashboards, alerts and downstream analytics are built on the emission. The
25
+ predicate is deliberately the emission-assertion vocabulary, not a data-flow analysis: it names
26
+ what a test must contain, not what the runtime did.
27
+ fix_hint: |
28
+ Capture the emitter, assert on it:
29
+
30
+ const emit = mock(() => {});
31
+ acceptResponse({ id: "r-1" }, { emit });
32
+ expect(emit).toHaveBeenCalledWith("telemetry:event:be:commons:response-invocation-accepted", { response_id: "r-1" });
33
+ exceptions:
34
+ - >-
35
+ Only telemetry-identified tests are scanned; the tester profile's own rule governs the rest.
36
+ - >-
37
+ Inert until adoption, like every rule in this family.
38
+ metadata:
39
+ aliases:
40
+ - TESTER-TELEMETRY-CAPTURED-SINK-001
41
+ severity: 3
42
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
43
+ disposition: strict
44
+ introduced_in: 0.7.0
45
+ implementation:
46
+ type: validator
47
+ ref: bun_telemetry_test
@@ -0,0 +1,44 @@
1
+ schema_version: 1.1.0
2
+ rule_id: tester.bun.telemetry-identity-assertion
3
+ kind: rule
4
+ status: active
5
+ name: Telemetry tests assert the exact item identity
6
+ statement: >-
7
+ Every concrete telemetry URN a telemetry test claims in its `// Telemetry:` header appears in
8
+ the test's code as an assertion string; a telemetry test with no header asserts at least one
9
+ concrete telemetry URN (REQUIRED).
10
+ terms:
11
+ - term_id: exact_identity
12
+ text: >-
13
+ the concrete telemetry URN, verbatim, inside a string literal of the test. Not a prefix, not a
14
+ captured-anything matcher, not a comment.
15
+ content:
16
+ summary: >-
17
+ A test that mocks an emitter and asserts "called with anything" proves emission happened, not
18
+ that the planned item is the one that fired. Renames and copy-paste drift hide exactly there.
19
+ normative_text: |
20
+ The claimed URNs are the test's Telemetry: headers; each must appear in the comment-masked
21
+ source as a quoted string, so that the assertion and the plan cannot silently diverge. A
22
+ telemetry test identified only by URN segment or colocation must carry at least one concrete
23
+ telemetry: URN in code. When generated contracts ship, the contract identifier satisfies this
24
+ the same way.
25
+ fix_hint: |
26
+ Name the item in the assertion itself:
27
+
28
+ expect(emit).toHaveBeenCalledWith("telemetry:event:be:commons:response-invocation-accepted", { response_id: "r-1" });
29
+ exceptions:
30
+ - >-
31
+ Comments do not assert: a URN that appears only in the header or a comment does not satisfy
32
+ this rule.
33
+ - >-
34
+ Inert until adoption, like every rule in this family.
35
+ metadata:
36
+ aliases:
37
+ - TESTER-TELEMETRY-IDENTITY-ASSERTION-001
38
+ severity: 3
39
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
40
+ disposition: strict
41
+ introduced_in: 0.7.0
42
+ implementation:
43
+ type: validator
44
+ ref: bun_telemetry_test
@@ -0,0 +1,44 @@
1
+ schema_version: 1.1.0
2
+ rule_id: tester.bun.telemetry-required-item-coverage
3
+ kind: rule
4
+ status: active
5
+ name: Every required telemetry item has a telemetry test
6
+ statement: >-
7
+ Every concrete telemetry item an acceptance requires is bound by at least one test file's
8
+ `// Telemetry:` header (REQUIRED).
9
+ terms:
10
+ - term_id: required_item
11
+ text: >-
12
+ a concrete telemetry URN listed by an acceptance whose telemetry disposition is required and
13
+ that exists in the tracking plan.
14
+ content:
15
+ summary: >-
16
+ The evidence leg of the traceability tripod: planned, implemented, tested. The coder rule
17
+ binds the implementation; this binds the proof. A required item no test covers is a plan
18
+ nobody can rely on.
19
+ normative_text: |
20
+ Required items are collected from acceptance decisions; test bindings from every Telemetry:
21
+ header in test files. Their difference is reported at the tracking-plan item's own file.
22
+ Unresolvable required URNs belong to the acceptance-decision rule and are not double-reported.
23
+ fix_hint: |
24
+ Give the item its proof:
25
+
26
+ // tests/.../accept-response.telemetry.test.ts
27
+ // Acceptance: acc:commons:E001-UNIT-001
28
+ // Telemetry: telemetry:metric:be:commons:response-invocation-accepted:duration
29
+ exceptions:
30
+ - >-
31
+ Diagnostic logs and internal spans an acceptance does not require need no dedicated test —
32
+ only required items are covered by this closure.
33
+ - >-
34
+ Inert until adoption, like every rule in this family.
35
+ metadata:
36
+ aliases:
37
+ - TESTER-TELEMETRY-REQUIRED-ITEM-COVERAGE-001
38
+ severity: 2
39
+ # strict: atdd-bun fails on every finding; it has no advisory mode and no ratchet baseline.
40
+ disposition: strict
41
+ introduced_in: 0.7.0
42
+ implementation:
43
+ type: validator
44
+ ref: bun_telemetry_test