rcf-lite 0.17.0 → 0.19.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 (161) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/blueprints/application-api-rest/README.md +5 -1
  3. package/blueprints/application-api-rest/blueprint.json +12 -4
  4. package/blueprints/application-api-rest/contributions/adrs/adr-304-application-api-rest-logging.json +3 -3
  5. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-006.json +4 -4
  6. package/blueprints/application-api-rest/contributions/tacs/tac-306-application-api-rest-operability.json +9 -8
  7. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2103.json +3 -3
  8. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2108.json +27 -27
  9. package/blueprints/application-api-rest/docs/topics.md +6 -4
  10. package/blueprints/application-api-rest/guide/application-api-rest.md +5 -1
  11. package/blueprints/application-error-handling/README.md +42 -0
  12. package/blueprints/application-error-handling/assets/schemas/error-record.schema.json +17 -0
  13. package/blueprints/application-error-handling/blueprint.json +27 -0
  14. package/blueprints/application-error-handling/contributions/adrs/adr-1701-application-error-handling-record-shape.json +25 -0
  15. package/blueprints/application-error-handling/contributions/adrs/adr-1702-application-error-handling-classification-vocabulary.json +20 -0
  16. package/blueprints/application-error-handling/contributions/adrs/adr-1703-application-error-handling-transport-mapping.json +20 -0
  17. package/blueprints/application-error-handling/contributions/requirements/application-error-handling-req-001.json +15 -0
  18. package/blueprints/application-error-handling/contributions/requirements/application-error-handling-req-002.json +15 -0
  19. package/blueprints/application-error-handling/contributions/requirements/application-error-handling-req-003.json +15 -0
  20. package/blueprints/application-error-handling/contributions/requirements/application-error-handling-req-004.json +15 -0
  21. package/blueprints/application-error-handling/contributions/tacs/tac-1701-application-error-handling-boundary.json +45 -0
  22. package/blueprints/application-error-handling/contributions/tacs/tac-1702-application-error-handling-record-factory.json +40 -0
  23. package/blueprints/application-error-handling/contributions/user-stories/application-error-handling-us-16101.json +25 -0
  24. package/blueprints/application-error-handling/contributions/user-stories/application-error-handling-us-16102.json +25 -0
  25. package/blueprints/application-error-handling/contributions/user-stories/application-error-handling-us-16103.json +34 -0
  26. package/blueprints/application-error-handling/contributions/user-stories/application-error-handling-us-16104.json +25 -0
  27. package/blueprints/application-error-handling/contributions/user-stories/application-error-handling-us-16105.json +25 -0
  28. package/blueprints/application-error-handling/contributions/user-stories/application-error-handling-us-16106.json +25 -0
  29. package/blueprints/application-error-handling/contributions/user-stories/application-error-handling-us-16107.json +25 -0
  30. package/blueprints/application-error-handling/docs/topics.md +24 -0
  31. package/blueprints/application-error-handling/guide/application-error-handling.md +35 -0
  32. package/blueprints/application-spa/blueprint.json +12 -2
  33. package/blueprints/application-spa/docs/topics.md +4 -2
  34. package/blueprints/delivery-ci-workflows/docs/topics.md +2 -2
  35. package/blueprints/deploy-cloudflare-workers/docs/topics.md +2 -2
  36. package/blueprints/email-smtp-resend/docs/topics.md +2 -2
  37. package/blueprints/observability-essentials/README.md +6 -2
  38. package/blueprints/observability-essentials/blueprint.json +133 -33
  39. package/blueprints/observability-essentials/contributions/adrs/adr-801-observability-essentials-health-probes.json +4 -4
  40. package/blueprints/observability-essentials/contributions/adrs/adr-802-observability-essentials-readiness-semantics.json +4 -4
  41. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-001.json +4 -4
  42. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-002.json +4 -4
  43. package/blueprints/observability-essentials/contributions/tacs/tac-801-observability-essentials-liveness-probe.json +10 -9
  44. package/blueprints/observability-essentials/contributions/tacs/tac-802-observability-essentials-readiness-probe.json +17 -11
  45. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7101.json +12 -3
  46. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7102.json +12 -3
  47. package/blueprints/observability-essentials/docs/topics.md +15 -8
  48. package/blueprints/observability-essentials/guide/observability-essentials.md +9 -3
  49. package/blueprints/observability-logging/README.md +44 -0
  50. package/blueprints/observability-logging/assets/samples/log-line.json +13 -0
  51. package/blueprints/observability-logging/blueprint.json +27 -0
  52. package/blueprints/observability-logging/contributions/adrs/adr-1601-observability-logging-line-shape.json +25 -0
  53. package/blueprints/observability-logging/contributions/adrs/adr-1602-observability-logging-correlation-id-header.json +25 -0
  54. package/blueprints/observability-logging/contributions/adrs/adr-1603-observability-logging-redaction-categories.json +25 -0
  55. package/blueprints/observability-logging/contributions/adrs/adr-1604-observability-logging-level-vocabulary.json +20 -0
  56. package/blueprints/observability-logging/contributions/requirements/observability-logging-req-001.json +15 -0
  57. package/blueprints/observability-logging/contributions/requirements/observability-logging-req-002.json +15 -0
  58. package/blueprints/observability-logging/contributions/requirements/observability-logging-req-003.json +15 -0
  59. package/blueprints/observability-logging/contributions/requirements/observability-logging-req-004.json +15 -0
  60. package/blueprints/observability-logging/contributions/requirements/observability-logging-req-005.json +15 -0
  61. package/blueprints/observability-logging/contributions/tacs/tac-1601-observability-logging-logger-factory.json +46 -0
  62. package/blueprints/observability-logging/contributions/tacs/tac-1602-observability-logging-redaction-boundary.json +27 -0
  63. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15101.json +34 -0
  64. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15102.json +43 -0
  65. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15103.json +34 -0
  66. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15104.json +25 -0
  67. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15105.json +34 -0
  68. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15106.json +25 -0
  69. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15107.json +25 -0
  70. package/blueprints/observability-logging/contributions/user-stories/observability-logging-us-15108.json +25 -0
  71. package/blueprints/observability-logging/docs/topics.md +21 -0
  72. package/blueprints/observability-logging/guide/observability-logging.md +36 -0
  73. package/blueprints/observability-probe-endpoints/README.md +5 -1
  74. package/blueprints/observability-probe-endpoints/blueprint.json +116 -24
  75. package/blueprints/observability-probe-endpoints/contributions/adrs/adr-1503-observability-probe-endpoints-kubernetes-default.json +5 -5
  76. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14102.json +11 -2
  77. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14107.json +11 -2
  78. package/blueprints/observability-probe-endpoints/docs/topics.md +6 -6
  79. package/blueprints/observability-probe-endpoints/guide/observability-probe-endpoints.md +10 -0
  80. package/blueprints/persistence-data-d1/docs/topics.md +2 -2
  81. package/blueprints/persistence-data-sqlite/docs/topics.md +2 -2
  82. package/blueprints/security-auth-clerk/docs/topics.md +2 -2
  83. package/blueprints/security-auth-keycloak/docs/topics.md +2 -2
  84. package/blueprints/security-auth-magic-link/docs/topics.md +2 -2
  85. package/blueprints/security-auth-oauth2/docs/topics.md +2 -2
  86. package/blueprints/security-secrets-management/docs/topics.md +2 -2
  87. package/fixtures/canary-manifest.json +6 -6
  88. package/guidance/harness-template.md +8 -0
  89. package/guidance/managed/agent-instructions-block.hash +1 -1
  90. package/guidance/managed/agent-instructions-block.md +8 -0
  91. package/package.json +1 -1
  92. package/rcf/code-nodes/cn-074.json +19 -0
  93. package/rcf/code-nodes/cn-075.json +15 -0
  94. package/rcf/code-nodes/cn-076.json +14 -0
  95. package/rcf/code-nodes/cn-077.json +14 -0
  96. package/rcf/code-nodes/cn-078.json +15 -0
  97. package/rcf/code-nodes/cn-079.json +14 -0
  98. package/rcf/code-nodes/cn-080.json +15 -0
  99. package/rcf/code-nodes/cn-081.json +15 -0
  100. package/rcf/code-nodes/cn-082.json +15 -0
  101. package/rcf/code-nodes/cn-083.json +14 -0
  102. package/rcf/code-nodes/cn-084.json +15 -0
  103. package/rcf/code-nodes/cn-085.json +14 -0
  104. package/rcf/code-nodes/cn-086.json +16 -0
  105. package/rcf/code-nodes/cn-087.json +14 -0
  106. package/rcf/code-nodes/cn-088.json +14 -0
  107. package/rcf/code-nodes/cn-089.json +14 -0
  108. package/rcf/code-nodes/cn-090.json +14 -0
  109. package/rcf/code-nodes/cn-091.json +14 -0
  110. package/rcf/code-nodes/cn-092.json +15 -0
  111. package/rcf/code-nodes/cn-093.json +15 -0
  112. package/rcf/code-nodes/cn-094.json +14 -0
  113. package/rcf/code-nodes/cn-095.json +14 -0
  114. package/rcf/code-nodes/cn-096.json +14 -0
  115. package/rcf/code-nodes/cn-097.json +14 -0
  116. package/rcf/fbs/fbs-024.json +24 -0
  117. package/rcf/fbs/fbs-025.json +25 -0
  118. package/rcf/fbs/fbs-026.json +27 -0
  119. package/rcf/fbs/fbs-027.json +25 -0
  120. package/rcf/fbs/fbs-028.json +27 -0
  121. package/rcf/fbs/fbs-029.json +27 -0
  122. package/rcf/fbs/fbs-030.json +27 -0
  123. package/rcf/fbs/fbs-031.json +27 -0
  124. package/rcf/fbs/fbs-032.json +27 -0
  125. package/rcf/fbs/fbs-033.json +27 -0
  126. package/rcf/fbs/fbs-034.json +27 -0
  127. package/rcf/requirements/req-012.json +22 -0
  128. package/rcf/requirements/req-013.json +22 -0
  129. package/rcf/requirements/req-014.json +22 -0
  130. package/rcf/requirements/req-015.json +21 -0
  131. package/rcf/test-suites/ts-034.json +32 -0
  132. package/rcf/test-suites/ts-035.json +23 -0
  133. package/rcf/test-suites/ts-036.json +65 -0
  134. package/rcf/test-suites/ts-037.json +55 -0
  135. package/rcf/test-suites/ts-038.json +41 -0
  136. package/rcf/test-suites/ts-039.json +41 -0
  137. package/rcf/test-suites/ts-040.json +57 -0
  138. package/rcf/test-suites/ts-041.json +57 -0
  139. package/rcf/test-suites/ts-042.json +57 -0
  140. package/rcf/test-suites/ts-043.json +49 -0
  141. package/rcf/test-suites/ts-044.json +41 -0
  142. package/rcf/user-stories/us-1201.json +34 -0
  143. package/rcf/user-stories/us-1202.json +25 -0
  144. package/rcf/user-stories/us-1203.json +43 -0
  145. package/rcf/user-stories/us-1204.json +25 -0
  146. package/rcf/user-stories/us-1301.json +43 -0
  147. package/rcf/user-stories/us-1302.json +43 -0
  148. package/rcf/user-stories/us-1401.json +43 -0
  149. package/rcf/user-stories/us-1402.json +43 -0
  150. package/rcf/user-stories/us-1403.json +43 -0
  151. package/rcf/user-stories/us-1404.json +43 -0
  152. package/rcf/user-stories/us-1501.json +43 -0
  153. package/releases/releases.yaml +21 -1
  154. package/src/blueprint/apply.js +8 -0
  155. package/src/blueprint/companions.js +485 -0
  156. package/src/blueprint/index.js +17 -0
  157. package/src/blueprint/loader.js +243 -1
  158. package/src/blueprint/remove-resolution.js +104 -0
  159. package/src/cli/blueprint.js +350 -0
  160. package/src/cli/doctor.js +76 -1
  161. package/src/cli/validate.js +7 -0
@@ -0,0 +1,44 @@
1
+ # Logging blueprint (v1.0.0)
2
+
3
+ Structured single-line JSON emission for a rcf-lite project. General enterprise practice, no vendor lock-in. Ships the shelf-canonical `logging` global topic on `ADR-1601-observability-logging-line-shape` (transferred from `application-api-rest-ADR-304`, retained there as superseded history). Provides the `logging` role for the companion-suggestion mechanism.
4
+
5
+ ## Apply
6
+
7
+ ```
8
+ rcf define blueprint add <path-to>/blueprints/observability-logging
9
+ ```
10
+
11
+ Applies namespaced contributions into the project tree and records `manifest.blueprints[]`. Apply is idempotent; `rcf define blueprint list` shows the applied entry; `rcf define blueprint remove observability-logging` cleanly removes an unreferenced application.
12
+
13
+ ## Anatomy
14
+
15
+ | Piece | Where | What |
16
+ |---|---|---|
17
+ | Metadata | `blueprint.json` | Slug, version 1.0.0, category `observability`, `providesRoles: ["logging"]`, and the 19 contributions with scope/topic on `ADR-1601` |
18
+ | Doc set | `contributions/` | 5 REQs, 8 USs (13 ACs), 2 TACs, 4 ADRs, schema-valid and namespaced (`observability-logging-REQ-001` prefix family; `ADR-1601-observability-logging-line-shape` suffix family) |
19
+ | Log line sample | `assets/samples/log-line.json` | Exact JSON shape of one emitted line carrying the shared minimum field set |
20
+ | Guide | `guide/observability-logging.md` | Operator-facing: when to use it, when not, what stays your call, mechanism-reach gaps |
21
+ | Coordination vocabulary | `docs/topics.md` | The `logging` global-topic string this blueprint claims, plus the shelf-wide id band registry update |
22
+
23
+ The doc set is contributions (copied into the project tree by `rcf define blueprint add`); the guide, assets and docs are package-resident references.
24
+
25
+ ## What it contributes, and what it deliberately does not
26
+
27
+ Contributed kinds: REQ, US (with inline ACs), TAC, ADR. Adherence is expressed as ACs; the blueprint ships no test files (ratified decision 5) and no code.
28
+
29
+ Deliberately not contributed: a specific log-shipping transport (Elastic Common Schema over Logstash, Loki push endpoints, Datadog agent, Splunk HEC); a rotation policy for on-disk log files (the shipped emission target is stdout, and the surrounding platform owns the pipe); a metrics-export surface (out of scope, sits with the metrics-store companion when the shelf ships one); a distributed-tracing surface (spans, propagation, sampling; out of scope for v1). The choice of ECS library (Elastic's, `pino-elastic-common-schema`, a project-authored shim) stays project-side; the blueprint names the ECS-neutral field set the AC binds to.
30
+
31
+ ## The one global decision
32
+
33
+ `ADR-1601-observability-logging-line-shape` ships `scope: global` on topic `logging`. This is the project's structured emission contract: one JSON object per line to stdout with the shared minimum field set (message, level, timestamp, correlationId, environment, serviceName, serviceVersion). A composing blueprint that holds a different opinion (structured multi-line, per-service special format, non-JSON) conflicts here by design; the operator resolves at project level with `rcf define blueprint supersede logging --incoming <source>`.
34
+
35
+ See `docs/topics.md` for the exact string, the expected resolutions, and the AC id band allocation (observability-logging owns 15101-15899, ADR/TAC suffix block 16xx).
36
+
37
+ ## Quality bar
38
+
39
+ One JSON object per line on stdout carrying the shared minimum field set; serialisation failures caught at the boundary with a stderr notice and no service crash; correlation identifier accepted from an inbound header (default `X-Correlation-Id`, elicited alternative), propagated onto every log line, reflected on outbound requests, `null` when no ambient context; PII fields redacted at the emission boundary by named category (recommended defaults: credential, pii.email, pii.name, pii.address, token, bearer; elicited additions); operator-elicited minimum level per environment filtered per line; environment, serviceName and serviceVersion supplied at boot and stamped on every line; an in-memory capture mode for hermetic tests. Every bar is carried by ACs in the doc set, not by this README.
40
+
41
+ ## Known mechanism-reach gaps
42
+
43
+ - **Call-site coverage.** The blueprint cannot prove every log-emitting call site uses the factory. A rogue `console.log` from any code path bypasses the boundary and lands unstructured text on stdout. Turning the discipline into a runtime-observable AC either becomes document-observable ("the source contains no `console.log`") or requires a project-side gate the blueprint does not ship. Named here rather than smuggled in as a v1 requirement. Promotion signal: a lint-rule contribution in a future rcf-lite blueprint or in the `delivery-ci-workflows` blueprint's shipped ruleset. Project-side workaround: a grep gate as a project-authored TC bound to `AC-15101-1` that walks the source tree for bare `console.` calls outside a whitelist.
44
+ - **Text-level redaction.** ADR-1603 redacts at field-name granularity. A payload string that concatenates an email into the `message` field carries the email through the emission. Field-name redaction is fast and predictable; text-level redaction is expensive and prone to false positives. Project-side workaround: wrap the log call sites in a project-authored redaction pass; long-term, a future companion blueprint may claim a text-level `logRedaction` topic.
@@ -0,0 +1,13 @@
1
+ {
2
+ "message": "request completed",
3
+ "level": "info",
4
+ "timestamp": "2026-09-04T12:20:00.123Z",
5
+ "correlationId": "c3b8d0f1-4a2e-4a6f-9c1a-1234567890ab",
6
+ "environment": "production",
7
+ "serviceName": "orders-api",
8
+ "serviceVersion": "1.4.2",
9
+ "httpStatus": 200,
10
+ "requestPath": "/orders/42",
11
+ "durationMs": 37,
12
+ "userId": "[REDACTED:pii.name]"
13
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "slug": "observability-logging",
3
+ "version": "1.0.0",
4
+ "category": "observability",
5
+ "providesRoles": ["logging"],
6
+ "contributions": [
7
+ { "id": "observability-logging-REQ-001", "kind": "req", "path": "requirements/observability-logging-req-001.json" },
8
+ { "id": "observability-logging-REQ-002", "kind": "req", "path": "requirements/observability-logging-req-002.json" },
9
+ { "id": "observability-logging-REQ-003", "kind": "req", "path": "requirements/observability-logging-req-003.json" },
10
+ { "id": "observability-logging-REQ-004", "kind": "req", "path": "requirements/observability-logging-req-004.json" },
11
+ { "id": "observability-logging-REQ-005", "kind": "req", "path": "requirements/observability-logging-req-005.json" },
12
+ { "id": "observability-logging-US-15101", "kind": "us", "path": "user-stories/observability-logging-us-15101.json" },
13
+ { "id": "observability-logging-US-15102", "kind": "us", "path": "user-stories/observability-logging-us-15102.json" },
14
+ { "id": "observability-logging-US-15103", "kind": "us", "path": "user-stories/observability-logging-us-15103.json" },
15
+ { "id": "observability-logging-US-15104", "kind": "us", "path": "user-stories/observability-logging-us-15104.json" },
16
+ { "id": "observability-logging-US-15105", "kind": "us", "path": "user-stories/observability-logging-us-15105.json" },
17
+ { "id": "observability-logging-US-15106", "kind": "us", "path": "user-stories/observability-logging-us-15106.json" },
18
+ { "id": "observability-logging-US-15107", "kind": "us", "path": "user-stories/observability-logging-us-15107.json" },
19
+ { "id": "observability-logging-US-15108", "kind": "us", "path": "user-stories/observability-logging-us-15108.json" },
20
+ { "id": "TAC-1601-observability-logging-logger-factory", "kind": "tac", "path": "tacs/tac-1601-observability-logging-logger-factory.json" },
21
+ { "id": "TAC-1602-observability-logging-redaction-boundary", "kind": "tac", "path": "tacs/tac-1602-observability-logging-redaction-boundary.json" },
22
+ { "id": "ADR-1601-observability-logging-line-shape", "kind": "adr", "path": "adrs/adr-1601-observability-logging-line-shape.json", "scope": "global", "topic": "logging" },
23
+ { "id": "ADR-1602-observability-logging-correlation-id-header", "kind": "adr", "path": "adrs/adr-1602-observability-logging-correlation-id-header.json", "recommendedDefault": true, "elicited": true, "standardsTraceClause": "generic enterprise practice" },
24
+ { "id": "ADR-1603-observability-logging-redaction-categories", "kind": "adr", "path": "adrs/adr-1603-observability-logging-redaction-categories.json", "recommendedDefault": true, "elicited": true, "standardsTraceClause": "generic enterprise practice" },
25
+ { "id": "ADR-1604-observability-logging-level-vocabulary", "kind": "adr", "path": "adrs/adr-1604-observability-logging-level-vocabulary.json", "recommendedDefault": true, "elicited": true, "standardsTraceClause": "generic enterprise practice" }
26
+ ]
27
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "adrId": "ADR-1601-observability-logging-line-shape",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Structured single-line JSON to standard output with the shared minimum field set",
8
+ "context": "Every log ingestion pipeline the applying project's operator might reach for (Elastic Common Schema pipelines, Loki, Datadog, Splunk) parses one JSON object per line. Emissions that break this shape (multi-line stack traces, unstructured text, non-JSON) either drop from the pipeline silently or produce a per-service special case the operator maintains forever. The core shelf owned the 'logging' topic on application-api-rest-ADR-304 through the 0.18.0 line; from this blueprint the topic transfers to observability-logging so a shelf project without api-rest still gets a logging contract, and api-rest becomes a consumer.",
9
+ "decision": "Every log emission from the applying application lands as exactly one JSON object per line on process stdout. Every line carries the shared minimum field set: message (string), level (one string from the ADR-1604 vocabulary), timestamp (ISO-8601 with millisecond precision), correlationId (string or null), environment (string), serviceName (string), serviceVersion (string). Payload fields fold onto the top level of the object. A JSON serialisation failure at the boundary drops the emission with one stderr notice and returns to the caller without throwing.",
10
+ "consequences": "Any log ingestion pipeline that parses one JSON object per line consumes the applying application's stdout without a service-specific parser. Message search, level filtering, environment partitioning and service-version debugging all become one-field queries. The shared minimum field set is stable across every applying project; a project cannot silently rename or drop one of the seven without a major version bump. This ADR is the shelf-canonical owner of the 'logging' topic; the previously superseded application-api-rest-ADR-304 defers to it. Composing blueprints that want a different logging surface (structured multi-line, per-service special format, non-JSON) conflict on the 'logging' topic and the operator resolves at project level.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "Free-form text emission",
14
+ "summary": "The application writes plain text log messages with no structural discipline.",
15
+ "reasonNotChosen": "Every ingestion pipeline needs a per-service parser, and every parser breaks the moment a caller changes a phrase. The industry-standard shape for the tier this blueprint targets is one JSON object per line."
16
+ },
17
+ {
18
+ "name": "Multi-line JSON with pretty-printing",
19
+ "summary": "One JSON object per emission but pretty-printed across multiple lines for human readability.",
20
+ "reasonNotChosen": "Breaks the one-line assumption every log pipeline holds. Pretty printing is a rendering concern of the consuming tool, not of the emitter."
21
+ }
22
+ ],
23
+ "createdAt": "2026-09-04T12:20:00Z",
24
+ "updatedAt": "2026-09-04T12:20:00Z"
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "adrId": "ADR-1602-observability-logging-correlation-id-header",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Correlation identifier header name is X-Correlation-Id by recommended default; operator elicits an alternative at apply",
8
+ "context": "Inbound requests may arrive with correlation identifiers under different header names depending on the surrounding platform: X-Correlation-Id in generic HTTP tooling, X-Request-Id in reverse proxies, traceparent in W3C Trace Context clients. Picking one at the code level forces every applying project to align; making it operator-elicited lets the applying platform's convention win.",
9
+ "decision": "The recommended default correlation header is X-Correlation-Id. The applying operator elicits an alternative at apply time; the elicited value is recorded on the applied ADR. The application accepts the elicited header on inbound requests and reflects it as the SAME header name on outbound requests.",
10
+ "consequences": "A project sitting behind a reverse proxy that already stamps X-Request-Id sets the elicited value to X-Request-Id and inherits the proxy's identifier without a code change. A project with no upstream stamping keeps the recommended default and mints v4 UUIDs. The header name is fixed for the process lifetime; changing it is a re-apply.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "Hard-code X-Correlation-Id",
14
+ "summary": "The application accepts only X-Correlation-Id and no elicited alternative.",
15
+ "reasonNotChosen": "Projects sitting behind a proxy that stamps a different header lose the upstream identifier and mint a new one on every request; the whole point of the mechanism is to inherit an existing identifier when one is present."
16
+ },
17
+ {
18
+ "name": "Adopt W3C Trace Context traceparent as the sole surface",
19
+ "summary": "The application requires the traceparent header for correlation, following the W3C Trace Context standard.",
20
+ "reasonNotChosen": "The applying tier for this blueprint is small greenfield applications; requiring a Trace Context client on every calling service would force upstream tooling the tier does not otherwise need. A project that operates in a Trace Context ecosystem elicits traceparent as the header name and inherits the standard."
21
+ }
22
+ ],
23
+ "createdAt": "2026-09-04T12:20:00Z",
24
+ "updatedAt": "2026-09-04T12:20:00Z"
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "adrId": "ADR-1603-observability-logging-redaction-categories",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Redaction categories: credential, pii.email, pii.name, pii.address, token, bearer by recommended default; operator elicits additions",
8
+ "context": "PII redaction in logs is one of the most common compliance surfaces (GDPR, PCI DSS, HIPAA); a blueprint that ships no default vocabulary forces every applying project to invent its own on day one, which reliably produces a coverage gap. A vocabulary that pretends to be exhaustive fights every project's actual field surface. Splitting recommended defaults from elicited additions gets both.",
9
+ "decision": "The recommended default category set is credential, pii.email, pii.name, pii.address, token, bearer. A field whose name equals or path-tail-matches any of these categories is redacted at the emission boundary. The applying operator elicits additional category names at apply (kebab or dotted-namespace grammar; refused with a boot error otherwise); elicited additions apply alongside the defaults. The applied ADR records the final category list the applying project uses.",
10
+ "consequences": "A project's logs never emit a field named 'credential', 'pii.email', 'pii.name', 'pii.address', 'token' or 'bearer' with a real value out of the box, before the operator has thought about redaction. Additional per-domain categories (ssn, iban, jwt) land through the elicited additions at apply without a code change. Free-text redaction inside the message string (an email pasted into an error message) is out of scope; the mechanism reach is field-level, not text-level.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "Ship no default vocabulary",
14
+ "summary": "The blueprint ships a redaction contract but no default category list; every applying project supplies its own from scratch.",
15
+ "reasonNotChosen": "Reliably produces a coverage gap on day one because the operator has not yet enumerated the field surface. Recommended defaults with elicited additions give a working floor and a project-side extension path in the same round."
16
+ },
17
+ {
18
+ "name": "Redact by field-value pattern rather than field-name category",
19
+ "summary": "The boundary walks every value and redacts against pattern lists (regex for emails, credit cards, tokens).",
20
+ "reasonNotChosen": "Pattern-matching every value on every emission is expensive at scale and produces false positives that make logs unusable. Field-name category matching is fast and predictable; the trade-off is that a field with an unrelated name that holds a PII value is not redacted, which is what the field-name grammar exists to catch."
21
+ }
22
+ ],
23
+ "createdAt": "2026-09-04T12:20:00Z",
24
+ "updatedAt": "2026-09-04T12:20:00Z"
25
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "adrId": "ADR-1604-observability-logging-level-vocabulary",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Level vocabulary is trace, debug, info, warn, error, fatal; operator elicits the minimum level per environment",
8
+ "context": "The industry has settled on a six-level vocabulary (trace, debug, info, warn, error, fatal); most log tooling (structured or otherwise) already understands these strings without translation. Production and development want different minimum levels; hard-coding info-and-above helps neither.",
9
+ "decision": "The recommended default vocabulary is exactly trace, debug, info, warn, error, fatal, in that severity order. The applying operator elicits a minimum level per environment at apply; the applied ADR records the elicited value. The factory refuses to construct with an unknown level string and drops emissions below the minimum before serialisation.",
10
+ "consequences": "The vocabulary is stable across every applying project; log tooling wires its level filter once. Per-environment minimums honour the production-development split without changing call sites. A future project that wants a different vocabulary (Apache-style FATAL/ERROR/WARN/INFO/DEBUG/TRACE) supersedes this ADR at project level; the vocabulary transfer is a mechanical string rename in call sites.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "Adopt Apache log4j-style ALL/TRACE/DEBUG/INFO/WARN/ERROR/FATAL/OFF vocabulary",
14
+ "summary": "The blueprint ships the historically-familiar seven-plus-two vocabulary from log4j.",
15
+ "reasonNotChosen": "ALL and OFF are configuration meta-levels, not emission levels; conflating them with severities has produced enough confusion that modern structured-logging tools omit them. The six emission levels above match what today's tools accept out of the box."
16
+ }
17
+ ],
18
+ "createdAt": "2026-09-04T12:20:00Z",
19
+ "updatedAt": "2026-09-04T12:20:00Z"
20
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "reqId": "observability-logging-REQ-001",
3
+ "prdId": "PRD-001",
4
+ "title": "Every service surface emits structured single-line JSON log lines through the shared factory",
5
+ "description": "Every log emission from the applying application passes through the logger factory contract TAC-1601 produces, writes one JSON object per line to standard output, and includes at minimum the fields message (string), level (one of the level vocabulary in ADR-1604), timestamp (ISO-8601 with millisecond precision), correlationId (string, may be null when no ambient context), environment (string), serviceName (string) and serviceVersion (string). A log emission that fails to serialise (a value that is not JSON-representable, a circular reference) is caught, dropped with a one-time stderr notice, and the calling code path continues; a serialisation failure never crashes the service.",
6
+ "category": "functional",
7
+ "domain": "observability",
8
+ "priority": "must",
9
+ "rationale": "Downstream log ingestion tooling (Elastic Common Schema pipelines, Loki, Datadog, Splunk) parses one JSON object per line. Multi-line stack traces or unstructured text break the pipeline and produce silent data loss. Threading every emission through one factory closes the drift class where a stray console.log lands unstructured text alongside structured lines.",
10
+ "tags": ["blueprint:observability-logging", "category:01-structured-emission"],
11
+ "version": "1.0.0",
12
+ "status": "approved",
13
+ "createdAt": "2026-09-04T12:20:00Z",
14
+ "updatedAt": "2026-09-04T12:20:00Z"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "reqId": "observability-logging-REQ-002",
3
+ "prdId": "PRD-001",
4
+ "title": "The application accepts, propagates and reflects a correlation identifier on every log line",
5
+ "description": "The application reads a correlation identifier from an inbound request header (name governed by ADR-1602; default X-Correlation-Id, elicited per project), threads it through the request-scoped context using the mechanism the TAC exposes, includes it on every log line emitted during that request, and reflects it on any outbound request initiated inside the same context. When no inbound identifier is present, the application mints a v4 UUID and treats it as the ambient value. Requests processed with no ambient context (a scheduled task boot, a background worker startup line) log with correlationId equal to null; the field is always present.",
6
+ "category": "functional",
7
+ "domain": "observability",
8
+ "priority": "must",
9
+ "rationale": "Correlation identifiers are the mechanism that stitches together the log lines of a single logical request across multiple services and multiple retries. Without acceptance-and-reflection at every hop, log search cannot follow the chain. Present-but-null is a legitimate ambient state (boot-time logs, cron); refusing to log without an identifier fights the operator when they need the emission most.",
10
+ "tags": ["blueprint:observability-logging", "category:02-correlation-id"],
11
+ "version": "1.0.0",
12
+ "status": "approved",
13
+ "createdAt": "2026-09-04T12:20:00Z",
14
+ "updatedAt": "2026-09-04T12:20:00Z"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "reqId": "observability-logging-REQ-003",
3
+ "prdId": "PRD-001",
4
+ "title": "PII fields are redacted by named category at the emission boundary, not at the call site",
5
+ "description": "The logger factory exposes a redaction contract (ADR-1603 recommended-default categories: credential, pii.email, pii.name, pii.address, token, bearer; elicited additions per project). A field named or path-labelled with a category is replaced with the string [REDACTED:<category>] before the line is serialised, whether the log call site remembered to redact or not. Redaction happens at the emission boundary (TAC-1602): a rogue field name in a caller-supplied object is redacted by rule, not by hope. The redaction pass never mutates the caller's original object; the emission pipeline copies the object first.",
6
+ "category": "nonFunctional",
7
+ "domain": "observability",
8
+ "priority": "must",
9
+ "rationale": "PII leaks through logs are one of the most common data-exposure defects in production systems. Placing the redaction responsibility at the call site produces a coverage gap the size of the codebase; placing it at the boundary is the only scalable enforcement. Named categories keep the vocabulary auditable and let a project add its own categories at apply time.",
10
+ "tags": ["blueprint:observability-logging", "category:03-pii-redaction"],
11
+ "version": "1.0.0",
12
+ "status": "approved",
13
+ "createdAt": "2026-09-04T12:20:00Z",
14
+ "updatedAt": "2026-09-04T12:20:00Z"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "reqId": "observability-logging-REQ-004",
3
+ "prdId": "PRD-001",
4
+ "title": "Every environment has an operator-elicited minimum log level; the factory refuses to emit below it",
5
+ "description": "The logger factory reads a minimum level per environment (ADR-1604 recommended default vocabulary: trace, debug, info, warn, error, fatal; the minimum level per environment is elicited at apply). Emissions below the minimum are dropped before serialisation; emissions at or above the minimum land on stdout. The environment name is supplied at boot (REQ-005) and the level filter is applied per line, not batched. Changing the minimum level mid-run through a documented hook (SIGHUP re-read, admin endpoint, config reload) is out of scope for v1; the minimum is fixed for the process lifetime.",
6
+ "category": "functional",
7
+ "domain": "observability",
8
+ "priority": "must",
9
+ "rationale": "Production log volume is dominated by debug and trace lines; enabling them everywhere buries the signal. Development wants them; production does not. A per-environment operator-elicited minimum keeps the discipline explicit at apply time and lets the same code path serve both environments with the same call sites.",
10
+ "tags": ["blueprint:observability-logging", "category:04-level-vocabulary"],
11
+ "version": "1.0.0",
12
+ "status": "approved",
13
+ "createdAt": "2026-09-04T12:20:00Z",
14
+ "updatedAt": "2026-09-04T12:20:00Z"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "reqId": "observability-logging-REQ-005",
3
+ "prdId": "PRD-001",
4
+ "title": "Every log line carries environment, service name and service version, all operator-elicited at boot",
5
+ "description": "The three fields (environment, serviceName, serviceVersion) are supplied to the logger factory at boot from operator-elicited configuration (environment variables, config file, deployment manifest); the factory refuses to construct a logger when any of the three is missing or blank. Every emitted line carries all three at fixed field names so log ingestion can partition by environment and service without prose parsing.",
6
+ "category": "functional",
7
+ "domain": "observability",
8
+ "priority": "must",
9
+ "rationale": "A shared log destination sees lines from every environment and every service. Without machine-readable partitioning fields, filtering by environment or service requires string search on the message field, which is fragile and prone to false positives. Requiring the three fields at boot puts the discipline at the earliest possible surface.",
10
+ "tags": ["blueprint:observability-logging", "category:05-boot-fields"],
11
+ "version": "1.0.0",
12
+ "status": "approved",
13
+ "createdAt": "2026-09-04T12:20:00Z",
14
+ "updatedAt": "2026-09-04T12:20:00Z"
15
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "tacId": "TAC-1601-observability-logging-logger-factory",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Logger factory",
8
+ "purpose": "Owns the construction of every logger the application uses. Threads the operator-elicited boot fields (environment, serviceName, serviceVersion, minimum level, correlation header name, redaction categories) into a shared factory contract; every call site takes a logger from the factory and never constructs one directly. Provides a hermetic in-memory capture mode for tests.",
9
+ "responsibilities": [
10
+ "Construct a logger from operator-elicited boot fields; refuse construction when environment, serviceName or serviceVersion is missing or blank (AC-15105-1), and refuse when the minimum level is not one of the recommended-default vocabulary (AC-15106-1).",
11
+ "Emit each log call as one JSON line to standard output carrying the shared minimum field set (AC-15101-1), applying the level filter per line (AC-15104-1).",
12
+ "Expose an in-memory capture mode returning an ordered array of the structured objects the same emission would have written to stdout (AC-15107-1), with zero stdout side effects.",
13
+ "Catch serialisation failures (BigInt, function reference, circular object) at the boundary, drop the emission with one stderr notice naming the offending field, and return without throwing to the caller (AC-15101-2).",
14
+ "Read the ambient correlation context per emission and thread it onto the correlationId field (AC-15102-1), or null when no context is active (AC-15102-3)."
15
+ ],
16
+ "internalStructure": "One module exporting createLoggerFactory({ environment, serviceName, serviceVersion, minimumLevel, correlationHeader, redactionCategories, now, sink }) -> { getLogger(name) -> Logger, capturedLines() }. The factory closes over boot fields, a now() clock (default () -> new Date()) and a pluggable sink (default process.stdout; test-mode sink is an in-memory array). Loggers are lightweight wrappers around the sink. Serialisation is JSON.stringify with a replacer function that folds BigInt to string and short-circuits on circularity via a WeakSet, throwing a controlled SerializationRefusedError caught at the boundary.",
17
+ "interfaces": [
18
+ {
19
+ "name": "createLoggerFactory",
20
+ "kind": "factory",
21
+ "description": "createLoggerFactory({ environment, serviceName, serviceVersion, minimumLevel, correlationHeader, redactionCategories, now, sink }) -> { getLogger(name), capturedLines() }. The factory is constructed exactly once at boot; getLogger returns a per-module logger with .trace/.debug/.info/.warn/.error/.fatal methods each taking (message, payload?)."
22
+ },
23
+ {
24
+ "name": "Logger",
25
+ "kind": "interface",
26
+ "description": "One method per level; each takes (message: string, payload?: object). Emits one JSON line carrying message, level, timestamp, correlationId, environment, serviceName, serviceVersion, and every payload field folded onto the top level."
27
+ },
28
+ {
29
+ "name": "correlationHeader",
30
+ "kind": "config",
31
+ "description": "Operator-elicited inbound header name governing the correlation identifier acceptance and outbound propagation; recommended default X-Correlation-Id per ADR-1602."
32
+ }
33
+ ],
34
+ "dependencies": [
35
+ {
36
+ "name": "Redaction boundary",
37
+ "kind": "tac",
38
+ "tacId": "TAC-1602-observability-logging-redaction-boundary",
39
+ "description": "The factory calls the redaction boundary on every serialised payload before the line is written to the sink; the boundary encapsulates the category vocabulary and the [REDACTED:<category>] rewriting."
40
+ }
41
+ ],
42
+ "tradeoffs": "The factory owns the field vocabulary; a project that wants an additional top-level field beyond the shared minimum authors it as a payload field (which appears on the line) rather than as a factory-side hook. Capture mode is a first-class interface, not a test-only afterthought: the runtime sink is one strategy the same interface serves, so the capture path exercises the same serialisation logic as the runtime path. The v1 blueprint does not ship a dynamic level-change interface; the level is fixed for the process lifetime (REQ-004).",
43
+ "notes": "Known mechanism-reach gap: the blueprint cannot prove every emission call site uses the factory. A project-side grep gate ('every log call is a factory-returned logger method, not a bare console.log') or a lint rule closes the class; the blueprint's guide names this gap.",
44
+ "createdAt": "2026-09-04T12:20:00Z",
45
+ "updatedAt": "2026-09-04T12:20:00Z"
46
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "tacId": "TAC-1602-observability-logging-redaction-boundary",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Redaction boundary",
8
+ "purpose": "Owns the redaction pass every emission passes through before the line lands on the sink. Encapsulates the category vocabulary from ADR-1603 (recommended defaults plus elicited additions) and the field-name matching rule; a payload field whose name or path label matches a category is replaced with the literal string [REDACTED:<category>] in the SERIALISED line, never in the caller's original object.",
9
+ "responsibilities": [
10
+ "Apply the redaction rule on a deep-cloned copy of the emission payload so the caller's original object is not mutated (AC-15103-1).",
11
+ "Accept elicited additional category names at boot (kebab or dotted-namespace grammar) and apply them alongside the recommended defaults (AC-15103-2).",
12
+ "Refuse construction when an elicited category name fails the accepted grammar (AC-15108-1)."
13
+ ],
14
+ "internalStructure": "One module exporting createRedactionBoundary({ categories }) -> { redact(payload) -> payload }. Categories is a Set of strings; matching walks payload keys (dot-path in nested objects) and replaces the value on the returned clone when the key equals or ends in a category name. The clone is a structuredClone unless the payload includes non-cloneable values, in which case the boundary falls back to a JSON round-trip clone with the serialisation-refused fold from TAC-1601.",
15
+ "interfaces": [
16
+ {
17
+ "name": "createRedactionBoundary",
18
+ "kind": "factory",
19
+ "description": "createRedactionBoundary({ categories: string[] }) -> { redact(payload: object): object }. Called once at boot from the logger factory; the factory holds the returned redact function and applies it on every emission."
20
+ }
21
+ ],
22
+ "dependencies": [],
23
+ "tradeoffs": "Redaction runs at the serialisation boundary and therefore only redacts fields the payload declares under a category-named key. Free-text redaction in the message string (an email pasted into a warning message) is out of scope for v1: the mechanism reach is field-level, not text-level. A project that needs text-level redaction wires a project-side wrapper around the logger call sites; the blueprint's guide names this gap.",
24
+ "notes": "",
25
+ "createdAt": "2026-09-04T12:20:00Z",
26
+ "updatedAt": "2026-09-04T12:20:00Z"
27
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "usId": "observability-logging-US-15101",
3
+ "prdId": "PRD-001",
4
+ "reqId": "observability-logging-REQ-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Every log emission lands as one JSON object per line on stdout with the ratified minimum field set",
8
+ "asA": "operator running the application and consuming its stdout through a log ingestion pipeline",
9
+ "iWant": "every log emission to arrive as one JSON object on one line, carrying the shared minimum field set the pipeline expects",
10
+ "soThat": "the ingestion pipeline parses every line without special cases and no emission escapes as unstructured text",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-15101-1",
14
+ "description": "A log emission at any level from any call site produces exactly one line on stdout, that line parses as valid JSON, and the parsed object carries every field of the shared minimum set: message, level, timestamp, correlationId, environment, serviceName, serviceVersion.",
15
+ "given": "a running application with the logger factory constructed and an ambient correlation context",
16
+ "when": "any call site emits at any level",
17
+ "then": "one stdout line results, JSON.parse succeeds, and the object has all seven minimum fields present with the correct types",
18
+ "testable": true,
19
+ "scope": "runtime"
20
+ },
21
+ {
22
+ "id": "AC-15101-2",
23
+ "description": "A log emission whose payload contains a non-JSON-representable value (a BigInt, a function reference, a circular object) is caught at the boundary, dropped with one stderr notice naming the offending field, and the calling code path returns as if the emission had succeeded; the service does not crash.",
24
+ "given": "a running application and a caller passing a BigInt or a circular object as part of a log payload",
25
+ "when": "the emission is attempted",
26
+ "then": "stdout carries no line for this emission, stderr carries one notice naming the offending field, and the caller's next line of code executes without throwing",
27
+ "testable": true,
28
+ "scope": "runtime"
29
+ }
30
+ ],
31
+ "tacIds": ["TAC-1601-observability-logging-logger-factory"],
32
+ "createdAt": "2026-09-04T12:20:00Z",
33
+ "updatedAt": "2026-09-04T12:20:00Z"
34
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "usId": "observability-logging-US-15102",
3
+ "prdId": "PRD-001",
4
+ "reqId": "observability-logging-REQ-002",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "A correlation identifier read from the inbound request header rides every log line and every outbound request until the request ends",
8
+ "asA": "operator debugging a request path that spans multiple services",
9
+ "iWant": "the correlation identifier from the inbound request to appear on every log line the application emits during the request AND on every outbound request the application initiates within the same context",
10
+ "soThat": "log search returns the whole chain of a logical request across services from one string query",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-15102-1",
14
+ "description": "A request arriving with the elicited correlation header (default X-Correlation-Id per ADR-1602) causes every log line emitted during the handler and its callees to carry correlationId equal to the header value; a request arriving without the header causes the application to mint a v4 UUID and the same value appears on every log line for the request.",
15
+ "given": "a running application with the correlation header configured and an inbound request",
16
+ "when": "the request is processed (with or without the header set)",
17
+ "then": "every log line emitted during the request carries the same correlationId value; header value wins when present; a v4 UUID is minted otherwise",
18
+ "testable": true,
19
+ "scope": "runtime"
20
+ },
21
+ {
22
+ "id": "AC-15102-2",
23
+ "description": "An outbound HTTP request initiated from within a request-scoped handler carries the ambient correlationId as the same elicited header name.",
24
+ "given": "an inbound request handler that issues an outbound HTTP call",
25
+ "when": "the outbound request is sent",
26
+ "then": "the outbound request headers include the correlation header with the ambient value",
27
+ "testable": true,
28
+ "scope": "runtime"
29
+ },
30
+ {
31
+ "id": "AC-15102-3",
32
+ "description": "A log emission with no ambient correlation context (boot line, scheduled task, background worker startup) carries correlationId equal to null; the field is present.",
33
+ "given": "a running application with no ambient request context",
34
+ "when": "a boot-time or background emission occurs",
35
+ "then": "the line carries correlationId: null",
36
+ "testable": true,
37
+ "scope": "runtime"
38
+ }
39
+ ],
40
+ "tacIds": ["TAC-1601-observability-logging-logger-factory"],
41
+ "createdAt": "2026-09-04T12:20:00Z",
42
+ "updatedAt": "2026-09-04T12:20:00Z"
43
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "usId": "observability-logging-US-15103",
3
+ "prdId": "PRD-001",
4
+ "reqId": "observability-logging-REQ-003",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Fields named or path-labelled with a PII category are redacted at the boundary regardless of call-site discipline",
8
+ "asA": "operator concerned that PII leaks through logs",
9
+ "iWant": "the emission boundary to redact fields with named PII categories automatically",
10
+ "soThat": "a rogue caller that forgets to redact does not leak PII to the log destination",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-15103-1",
14
+ "description": "A log payload containing a field named or path-tagged with any of the recommended-default categories (credential, pii.email, pii.name, pii.address, token, bearer) is replaced with the literal string [REDACTED:<category>] in the emitted line; the caller's original object is not mutated (a subsequent read of the same object still shows the original value).",
15
+ "given": "an emission at any level whose payload includes a field tagged as pii.email",
16
+ "when": "the emission passes through the boundary",
17
+ "then": "the stdout line's field value equals [REDACTED:pii.email] and the caller-side object still holds the original address",
18
+ "testable": true,
19
+ "scope": "runtime"
20
+ },
21
+ {
22
+ "id": "AC-15103-2",
23
+ "description": "A project may add its own redaction categories at apply through the ADR-1603 elicited-additions parameter; a new category name (kebab or dotted-namespace) is accepted and applied by the boundary in the same round.",
24
+ "given": "a project that elicited an additional category ssn on ADR-1603",
25
+ "when": "an emission includes a field tagged ssn",
26
+ "then": "the stdout line's field value equals [REDACTED:ssn]",
27
+ "testable": true,
28
+ "scope": "runtime"
29
+ }
30
+ ],
31
+ "tacIds": ["TAC-1602-observability-logging-redaction-boundary"],
32
+ "createdAt": "2026-09-04T12:20:00Z",
33
+ "updatedAt": "2026-09-04T12:20:00Z"
34
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "usId": "observability-logging-US-15104",
3
+ "prdId": "PRD-001",
4
+ "reqId": "observability-logging-REQ-004",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Emissions below the operator-elicited minimum level are dropped before serialisation",
8
+ "asA": "operator sizing log volume for a production environment",
9
+ "iWant": "emissions below the minimum level to be dropped without serialising them",
10
+ "soThat": "log volume matches the operator's chosen signal budget and the level filter is honoured line-by-line",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-15104-1",
14
+ "description": "With the elicited minimum level set to info, an emission at trace or debug produces no stdout line; an emission at info, warn, error or fatal produces one stdout line each.",
15
+ "given": "a running application with minimum level set to info",
16
+ "when": "one emission is issued at each of the six levels",
17
+ "then": "the stdout line count equals 4 (info, warn, error, fatal), and no line at trace or debug appears",
18
+ "testable": true,
19
+ "scope": "runtime"
20
+ }
21
+ ],
22
+ "tacIds": ["TAC-1601-observability-logging-logger-factory"],
23
+ "createdAt": "2026-09-04T12:20:00Z",
24
+ "updatedAt": "2026-09-04T12:20:00Z"
25
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "usId": "observability-logging-US-15105",
3
+ "prdId": "PRD-001",
4
+ "reqId": "observability-logging-REQ-005",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Every log line reflects the operator-elicited environment, serviceName and serviceVersion",
8
+ "asA": "operator debugging in a shared log destination",
9
+ "iWant": "every log line to carry the environment, serviceName and serviceVersion the operator set at boot",
10
+ "soThat": "log queries partition by those three fields without prose parsing on the message string",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-15105-1",
14
+ "description": "The logger factory refuses to construct when environment, serviceName or serviceVersion is missing or blank; construction failure throws with a message naming the missing field.",
15
+ "given": "an application booting with one of the three fields absent",
16
+ "when": "the logger factory is constructed",
17
+ "then": "the construction throws, the error message names the missing field, and the process exits before any emission is possible",
18
+ "testable": true,
19
+ "scope": "runtime"
20
+ },
21
+ {
22
+ "id": "AC-15105-2",
23
+ "description": "Every emitted line carries environment, serviceName and serviceVersion at fixed field names and equal to the values the operator supplied at boot.",
24
+ "given": "a running application constructed with the three fields set",
25
+ "when": "any emission is issued",
26
+ "then": "the stdout line's three fields equal the operator-supplied values verbatim",
27
+ "testable": true,
28
+ "scope": "runtime"
29
+ }
30
+ ],
31
+ "tacIds": ["TAC-1601-observability-logging-logger-factory"],
32
+ "createdAt": "2026-09-04T12:20:00Z",
33
+ "updatedAt": "2026-09-04T12:20:00Z"
34
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "usId": "observability-logging-US-15106",
3
+ "prdId": "PRD-001",
4
+ "reqId": "observability-logging-REQ-004",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "The logger factory refuses to construct with an unknown level string",
8
+ "asA": "operator wiring the logger factory at boot with a mis-configured minimum level",
9
+ "iWant": "the factory to refuse construction with a message naming the unknown level and the accepted vocabulary",
10
+ "soThat": "a typo in the environment variable is caught at boot rather than silently dropping every emission",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-15106-1",
14
+ "description": "The factory refuses to construct when the elicited minimum level is not one of the recommended-default vocabulary (trace, debug, info, warn, error, fatal); the thrown error names the offending value and lists the accepted set.",
15
+ "given": "an application booting with minimum level set to 'verbose'",
16
+ "when": "the factory is constructed",
17
+ "then": "the construction throws with a message naming 'verbose' as the offending value and listing the accepted vocabulary",
18
+ "testable": true,
19
+ "scope": "runtime"
20
+ }
21
+ ],
22
+ "tacIds": ["TAC-1601-observability-logging-logger-factory"],
23
+ "createdAt": "2026-09-04T12:20:00Z",
24
+ "updatedAt": "2026-09-04T12:20:00Z"
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "usId": "observability-logging-US-15107",
3
+ "prdId": "PRD-001",
4
+ "reqId": "observability-logging-REQ-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "A test double captures emitted lines for assertion without touching stdout",
8
+ "asA": "test author verifying that the application emits the expected log lines during a test",
9
+ "iWant": "a test double the factory produces that captures emitted lines in memory instead of writing to stdout",
10
+ "soThat": "assertions on log emissions do not depend on stdout capture and do not pollute test output",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-15107-1",
14
+ "description": "The blueprint's guide names an in-memory capture mode of the factory contract that returns an ordered array of the emitted structured objects; the capture mode is a documented API of TAC-1601 and can be constructed in a test without any stdout redirection.",
15
+ "given": "a test that constructs the factory in capture mode",
16
+ "when": "the code under test emits any number of log lines",
17
+ "then": "the returned array contains one entry per emission with the same object shape the runtime writes to stdout, and stdout carries no line",
18
+ "testable": true,
19
+ "scope": "runtime"
20
+ }
21
+ ],
22
+ "tacIds": ["TAC-1601-observability-logging-logger-factory"],
23
+ "createdAt": "2026-09-04T12:20:00Z",
24
+ "updatedAt": "2026-09-04T12:20:00Z"
25
+ }