rcf-lite 0.27.0 → 0.28.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 (601) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +10 -0
  3. package/bin/rcf.js +2 -0
  4. package/blueprints/application-account-settings/CHANGELOG.md +35 -6
  5. package/blueprints/application-account-settings/README.md +1 -1
  6. package/blueprints/application-account-settings/blueprint.json +4 -2
  7. package/blueprints/application-account-settings/contributions/adrs/adr-2602-application-account-settings-security-surface-shape.json +1 -1
  8. package/blueprints/application-account-settings/contributions/probes/probe-utils.mjs +177 -0
  9. package/blueprints/application-account-settings/contributions/probes/profile-form-autocomplete.mjs +67 -0
  10. package/blueprints/application-account-settings/contributions/probes/run-profile-form-autocomplete.mjs +5 -0
  11. package/blueprints/application-account-settings/contributions/probes/run-sessions-adapter-uniform.mjs +5 -0
  12. package/blueprints/application-account-settings/contributions/probes/run-sessions-surface-shape.mjs +5 -0
  13. package/blueprints/application-account-settings/contributions/probes/run-shell-tablist-per-capability.mjs +5 -0
  14. package/blueprints/application-account-settings/contributions/probes/run-theme-radiogroup.mjs +5 -0
  15. package/blueprints/application-account-settings/contributions/probes/sessions-adapter-uniform.mjs +130 -0
  16. package/blueprints/application-account-settings/contributions/probes/sessions-surface-shape.mjs +70 -0
  17. package/blueprints/application-account-settings/contributions/probes/shell-tablist-per-capability.mjs +60 -0
  18. package/blueprints/application-account-settings/contributions/probes/theme-radiogroup.mjs +126 -0
  19. package/blueprints/application-admin-console/CHANGELOG.md +32 -6
  20. package/blueprints/application-admin-console/README.md +1 -1
  21. package/blueprints/application-admin-console/blueprint.json +4 -2
  22. package/blueprints/application-admin-console/contributions/adrs/adr-2201-application-admin-console-capability-vocabulary.json +1 -1
  23. package/blueprints/application-admin-console/contributions/probes/audit-log-surface.mjs +79 -0
  24. package/blueprints/application-admin-console/contributions/probes/org-switcher-surface.mjs +83 -0
  25. package/blueprints/application-admin-console/contributions/probes/permission-matrix-grid.mjs +67 -0
  26. package/blueprints/application-admin-console/contributions/probes/probe-utils.mjs +177 -0
  27. package/blueprints/application-admin-console/contributions/probes/run-audit-log-surface.mjs +5 -0
  28. package/blueprints/application-admin-console/contributions/probes/run-org-switcher-surface.mjs +5 -0
  29. package/blueprints/application-admin-console/contributions/probes/run-permission-matrix-grid.mjs +5 -0
  30. package/blueprints/application-admin-console/contributions/probes/run-sign-in-access-gated-surface.mjs +5 -0
  31. package/blueprints/application-admin-console/contributions/probes/run-users-directory-surface.mjs +5 -0
  32. package/blueprints/application-admin-console/contributions/probes/sign-in-access-gated-surface.mjs +117 -0
  33. package/blueprints/application-admin-console/contributions/probes/users-directory-surface.mjs +145 -0
  34. package/blueprints/application-api-rest/CHANGELOG.md +16 -8
  35. package/blueprints/application-api-rest/README.md +3 -3
  36. package/blueprints/application-api-rest/blueprint.json +3 -2
  37. package/blueprints/application-api-rest/contributions/adrs/adr-302-application-api-rest-auth-model.json +1 -1
  38. package/blueprints/application-api-rest/contributions/probes/cursor-pagination-round-trip.mjs +154 -0
  39. package/blueprints/application-api-rest/contributions/probes/health-probes-distinct.mjs +124 -0
  40. package/blueprints/application-api-rest/contributions/probes/probe-utils.mjs +177 -0
  41. package/blueprints/application-api-rest/contributions/probes/problem-details-on-error.mjs +77 -0
  42. package/blueprints/application-api-rest/contributions/probes/request-id-echoed.mjs +76 -0
  43. package/blueprints/application-api-rest/contributions/probes/run-cursor-pagination-round-trip.mjs +5 -0
  44. package/blueprints/application-api-rest/contributions/probes/run-health-probes-distinct.mjs +5 -0
  45. package/blueprints/application-api-rest/contributions/probes/run-problem-details-on-error.mjs +5 -0
  46. package/blueprints/application-api-rest/contributions/probes/run-request-id-echoed.mjs +5 -0
  47. package/blueprints/application-api-rest/docs/topics.md +1 -1
  48. package/blueprints/application-charts/CHANGELOG.md +35 -9
  49. package/blueprints/application-charts/README.md +6 -6
  50. package/blueprints/application-charts/blueprint.json +5 -3
  51. package/blueprints/application-charts/contributions/adrs/adr-1901-application-charts-engine-choice.json +1 -1
  52. package/blueprints/application-charts/contributions/probes/keyboard-traversal.mjs +151 -0
  53. package/blueprints/application-charts/contributions/probes/non-colour-distinction.mjs +67 -0
  54. package/blueprints/application-charts/contributions/probes/probe-utils.mjs +177 -0
  55. package/blueprints/application-charts/contributions/probes/run-keyboard-traversal.mjs +5 -0
  56. package/blueprints/application-charts/contributions/probes/run-non-colour-distinction.mjs +5 -0
  57. package/blueprints/application-charts/contributions/probes/run-text-alternative-table.mjs +5 -0
  58. package/blueprints/application-charts/contributions/probes/text-alternative-table.mjs +139 -0
  59. package/blueprints/application-charts/contributions/requirements/application-charts-req-001.json +1 -1
  60. package/blueprints/application-charts/contributions/requirements/application-charts-req-002.json +1 -1
  61. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18101.json +5 -5
  62. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18106.json +1 -1
  63. package/blueprints/application-charts/guide/application-charts.md +1 -1
  64. package/blueprints/application-dashboard/CHANGELOG.md +20 -10
  65. package/blueprints/application-dashboard/README.md +6 -6
  66. package/blueprints/application-dashboard/assets/guidance/dashboard-design.md +2 -2
  67. package/blueprints/application-dashboard/blueprint.json +3 -2
  68. package/blueprints/application-dashboard/contributions/adrs/adr-2001-application-dashboard-primary-kpi-enum.json +1 -1
  69. package/blueprints/application-dashboard/contributions/adrs/adr-2002-application-dashboard-timeframe-presets.json +1 -1
  70. package/blueprints/application-dashboard/contributions/probes/export-handle-formats.mjs +75 -0
  71. package/blueprints/application-dashboard/contributions/probes/four-tile-states.mjs +75 -0
  72. package/blueprints/application-dashboard/contributions/probes/primary-kpi-top-left.mjs +58 -0
  73. package/blueprints/application-dashboard/contributions/probes/probe-utils.mjs +177 -0
  74. package/blueprints/application-dashboard/contributions/probes/run-export-handle-formats.mjs +4 -0
  75. package/blueprints/application-dashboard/contributions/probes/run-four-tile-states.mjs +4 -0
  76. package/blueprints/application-dashboard/contributions/probes/run-primary-kpi-top-left.mjs +4 -0
  77. package/blueprints/application-dashboard/contributions/probes/run-shell-five-regions.mjs +4 -0
  78. package/blueprints/application-dashboard/contributions/probes/shell-five-regions.mjs +93 -0
  79. package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-002.json +1 -1
  80. package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-003.json +1 -1
  81. package/blueprints/application-dashboard/contributions/tacs/tac-2002-application-dashboard-filter-chrome.json +1 -1
  82. package/blueprints/application-dashboard/contributions/tacs/tac-2003-application-dashboard-export-handle.json +2 -2
  83. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19101.json +2 -2
  84. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19102.json +2 -2
  85. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19103.json +1 -1
  86. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19104.json +1 -1
  87. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19105.json +1 -1
  88. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19106.json +2 -2
  89. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19107.json +5 -5
  90. package/blueprints/application-dashboard/docs/topics.md +2 -2
  91. package/blueprints/application-dashboard/guide/application-dashboard.md +2 -2
  92. package/blueprints/application-datatable/CHANGELOG.md +19 -9
  93. package/blueprints/application-datatable/README.md +2 -2
  94. package/blueprints/application-datatable/blueprint.json +3 -2
  95. package/blueprints/application-datatable/contributions/probes/apg-table-shape.mjs +57 -0
  96. package/blueprints/application-datatable/contributions/probes/four-states-regions.mjs +80 -0
  97. package/blueprints/application-datatable/contributions/probes/probe-utils.mjs +177 -0
  98. package/blueprints/application-datatable/contributions/probes/run-apg-table-shape.mjs +4 -0
  99. package/blueprints/application-datatable/contributions/probes/run-four-states-regions.mjs +4 -0
  100. package/blueprints/application-datatable/contributions/probes/run-search-adapter-round-trip.mjs +4 -0
  101. package/blueprints/application-datatable/contributions/probes/run-sort-adapter-round-trip.mjs +4 -0
  102. package/blueprints/application-datatable/contributions/probes/search-adapter-round-trip.mjs +58 -0
  103. package/blueprints/application-datatable/contributions/probes/sort-adapter-round-trip.mjs +60 -0
  104. package/blueprints/application-datatable/contributions/requirements/application-datatable-req-006.json +1 -1
  105. package/blueprints/application-datatable/docs/topics.md +1 -1
  106. package/blueprints/application-empty-error-states/CHANGELOG.md +33 -3
  107. package/blueprints/application-empty-error-states/blueprint.json +4 -2
  108. package/blueprints/application-empty-error-states/contributions/probes/empty-list-and-no-search.mjs +66 -0
  109. package/blueprints/application-empty-error-states/contributions/probes/error-boundary-alert.mjs +65 -0
  110. package/blueprints/application-empty-error-states/contributions/probes/forbidden-and-server-error.mjs +167 -0
  111. package/blueprints/application-empty-error-states/contributions/probes/not-found-and-recovery.mjs +61 -0
  112. package/blueprints/application-empty-error-states/contributions/probes/permission-denied-and-offline.mjs +155 -0
  113. package/blueprints/application-empty-error-states/contributions/probes/probe-utils.mjs +177 -0
  114. package/blueprints/application-empty-error-states/contributions/probes/run-empty-list-and-no-search.mjs +5 -0
  115. package/blueprints/application-empty-error-states/contributions/probes/run-error-boundary-alert.mjs +5 -0
  116. package/blueprints/application-empty-error-states/contributions/probes/run-forbidden-and-server-error.mjs +5 -0
  117. package/blueprints/application-empty-error-states/contributions/probes/run-not-found-and-recovery.mjs +5 -0
  118. package/blueprints/application-empty-error-states/contributions/probes/run-permission-denied-and-offline.mjs +5 -0
  119. package/blueprints/application-error-handling/CHANGELOG.md +45 -6
  120. package/blueprints/application-error-handling/blueprint.json +3 -2
  121. package/blueprints/application-error-handling/contributions/probes/category-vocabulary.mjs +90 -0
  122. package/blueprints/application-error-handling/contributions/probes/probe-utils.mjs +177 -0
  123. package/blueprints/application-error-handling/contributions/probes/record-shape-adr-1701.mjs +78 -0
  124. package/blueprints/application-error-handling/contributions/probes/run-category-vocabulary.mjs +5 -0
  125. package/blueprints/application-error-handling/contributions/probes/run-record-shape-adr-1701.mjs +5 -0
  126. package/blueprints/application-error-handling/contributions/probes/run-two-boundaries-registered.mjs +5 -0
  127. package/blueprints/application-error-handling/contributions/probes/two-boundaries-registered.mjs +343 -0
  128. package/blueprints/application-file-upload/CHANGELOG.md +28 -6
  129. package/blueprints/application-file-upload/README.md +1 -1
  130. package/blueprints/application-file-upload/blueprint.json +3 -2
  131. package/blueprints/application-file-upload/contributions/probes/assertive-completion-slot.mjs +78 -0
  132. package/blueprints/application-file-upload/contributions/probes/chunked-transport-endpoints.mjs +173 -0
  133. package/blueprints/application-file-upload/contributions/probes/per-file-progressbar.mjs +124 -0
  134. package/blueprints/application-file-upload/contributions/probes/probe-utils.mjs +177 -0
  135. package/blueprints/application-file-upload/contributions/probes/run-assertive-completion-slot.mjs +4 -0
  136. package/blueprints/application-file-upload/contributions/probes/run-chunked-transport-endpoints.mjs +4 -0
  137. package/blueprints/application-file-upload/contributions/probes/run-per-file-progressbar.mjs +4 -0
  138. package/blueprints/application-file-upload/contributions/probes/run-upload-surface-shape.mjs +4 -0
  139. package/blueprints/application-file-upload/contributions/probes/upload-surface-shape.mjs +55 -0
  140. package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-001.json +1 -1
  141. package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-002.json +1 -1
  142. package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-003.json +1 -1
  143. package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-004.json +1 -1
  144. package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-005.json +1 -1
  145. package/blueprints/application-file-upload/probe-packs/application-file-upload.pack.mjs +1 -1
  146. package/blueprints/application-forms-wizard/CHANGELOG.md +20 -6
  147. package/blueprints/application-forms-wizard/README.md +2 -2
  148. package/blueprints/application-forms-wizard/blueprint.json +3 -2
  149. package/blueprints/application-forms-wizard/contributions/probes/draft-persistence-round-trip.mjs +110 -0
  150. package/blueprints/application-forms-wizard/contributions/probes/probe-utils.mjs +177 -0
  151. package/blueprints/application-forms-wizard/contributions/probes/run-draft-persistence-round-trip.mjs +4 -0
  152. package/blueprints/application-forms-wizard/contributions/probes/run-step-page-shape.mjs +4 -0
  153. package/blueprints/application-forms-wizard/contributions/probes/run-task-list-surface.mjs +4 -0
  154. package/blueprints/application-forms-wizard/contributions/probes/step-page-shape.mjs +61 -0
  155. package/blueprints/application-forms-wizard/contributions/probes/task-list-surface.mjs +109 -0
  156. package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-001.json +1 -1
  157. package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-002.json +1 -1
  158. package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-003.json +1 -1
  159. package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-004.json +1 -1
  160. package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-005.json +1 -1
  161. package/blueprints/application-forms-wizard/probe-packs/application-forms-wizard.pack.mjs +1 -1
  162. package/blueprints/application-notifications-in-app/CHANGELOG.md +38 -5
  163. package/blueprints/application-notifications-in-app/README.md +4 -4
  164. package/blueprints/application-notifications-in-app/blueprint.json +4 -2
  165. package/blueprints/application-notifications-in-app/contributions/adrs/adr-2101-application-notifications-in-app-live-region-role-mapping.json +1 -1
  166. package/blueprints/application-notifications-in-app/contributions/probes/centre-acknowledge-round-trip.mjs +210 -0
  167. package/blueprints/application-notifications-in-app/contributions/probes/live-region-preseeding.mjs +93 -0
  168. package/blueprints/application-notifications-in-app/contributions/probes/probe-utils.mjs +177 -0
  169. package/blueprints/application-notifications-in-app/contributions/probes/run-centre-acknowledge-round-trip.mjs +5 -0
  170. package/blueprints/application-notifications-in-app/contributions/probes/run-live-region-preseeding.mjs +5 -0
  171. package/blueprints/application-notifications-in-app/contributions/probes/run-toast-contract.mjs +5 -0
  172. package/blueprints/application-notifications-in-app/contributions/probes/toast-contract.mjs +92 -0
  173. package/blueprints/application-notifications-in-app/contributions/tacs/tac-2101-application-notifications-in-app-live-region.json +3 -3
  174. package/blueprints/application-notifications-in-app/contributions/tacs/tac-2102-application-notifications-in-app-centre.json +1 -1
  175. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20101.json +1 -1
  176. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20102.json +1 -1
  177. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20103.json +1 -1
  178. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20105.json +1 -1
  179. package/blueprints/application-notifications-in-app/guide/application-notifications-in-app.md +3 -3
  180. package/blueprints/application-onboarding-tour/CHANGELOG.md +31 -5
  181. package/blueprints/application-onboarding-tour/README.md +1 -1
  182. package/blueprints/application-onboarding-tour/blueprint.json +5 -3
  183. package/blueprints/application-onboarding-tour/contributions/probes/checklist-anchor-open.mjs +81 -0
  184. package/blueprints/application-onboarding-tour/contributions/probes/completion-persistence.mjs +159 -0
  185. package/blueprints/application-onboarding-tour/contributions/probes/first-run-detection.mjs +67 -0
  186. package/blueprints/application-onboarding-tour/contributions/probes/probe-utils.mjs +177 -0
  187. package/blueprints/application-onboarding-tour/contributions/probes/run-checklist-anchor-open.mjs +5 -0
  188. package/blueprints/application-onboarding-tour/contributions/probes/run-completion-persistence.mjs +5 -0
  189. package/blueprints/application-onboarding-tour/contributions/probes/run-first-run-detection.mjs +5 -0
  190. package/blueprints/application-onboarding-tour/contributions/probes/run-stepper-role-dialog.mjs +5 -0
  191. package/blueprints/application-onboarding-tour/contributions/probes/stepper-role-dialog.mjs +45 -0
  192. package/blueprints/application-spa/CHANGELOG.md +20 -11
  193. package/blueprints/application-spa/README.md +9 -10
  194. package/blueprints/application-spa/blueprint.json +3 -2
  195. package/blueprints/application-spa/contributions/adrs/adr-202-application-spa-theming.json +1 -1
  196. package/blueprints/application-spa/contributions/adrs/adr-206-application-spa-iconography.json +1 -1
  197. package/blueprints/application-spa/contributions/probes/designed-empty-state.mjs +72 -0
  198. package/blueprints/application-spa/contributions/probes/probe-utils.mjs +177 -0
  199. package/blueprints/application-spa/contributions/probes/route-inventory-published.mjs +185 -0
  200. package/blueprints/application-spa/contributions/probes/run-designed-empty-state.mjs +5 -0
  201. package/blueprints/application-spa/contributions/probes/run-route-inventory-published.mjs +5 -0
  202. package/blueprints/application-spa/contributions/probes/run-shell-nav-present.mjs +5 -0
  203. package/blueprints/application-spa/contributions/probes/shell-nav-present.mjs +69 -0
  204. package/blueprints/application-spa/contributions/tacs/tac-207-application-spa-token-adherence-probe.json +1 -1
  205. package/blueprints/application-spa/contributions/tacs/tac-208-application-spa-icon-adherence-probe.json +2 -2
  206. package/blueprints/application-spa/contributions/tacs/tac-209-application-spa-csp-styled-adherence-probe.json +3 -3
  207. package/blueprints/application-spa/contributions/tacs/tac-210-application-spa-external-dependency-provisioning-probe.json +2 -2
  208. package/blueprints/application-spa/contributions/tacs/tac-211-application-spa-core-flow-e2e-probe.json +2 -2
  209. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1131.json +1 -1
  210. package/blueprints/application-spa/docs/topics.md +1 -1
  211. package/blueprints/delivery-ci-workflows/CHANGELOG.md +31 -11
  212. package/blueprints/delivery-ci-workflows/README.md +3 -3
  213. package/blueprints/delivery-ci-workflows/blueprint.json +3 -2
  214. package/blueprints/delivery-ci-workflows/contributions/adrs/adr-702-delivery-ci-workflows-strict-coverage-gate.json +2 -2
  215. package/blueprints/delivery-ci-workflows/contributions/probes/node-gate-entrypoint.mjs +76 -0
  216. package/blueprints/delivery-ci-workflows/contributions/probes/probe-utils.mjs +56 -0
  217. package/blueprints/delivery-ci-workflows/contributions/probes/real-account-github-actions-run-record.mjs +139 -0
  218. package/blueprints/delivery-ci-workflows/contributions/probes/run-node-gate-entrypoint.mjs +3 -0
  219. package/blueprints/delivery-ci-workflows/contributions/probes/run-real-account-github-actions-run-record.mjs +3 -0
  220. package/blueprints/delivery-ci-workflows/contributions/probes/run-workflow-template-shape.mjs +3 -0
  221. package/blueprints/delivery-ci-workflows/contributions/probes/workflow-template-shape.mjs +94 -0
  222. package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-004.json +1 -1
  223. package/blueprints/delivery-ci-workflows/docs/topics.md +2 -3
  224. package/blueprints/deploy-cloudflare-workers/CHANGELOG.md +4 -4
  225. package/blueprints/deploy-cloudflare-workers/README.md +3 -3
  226. package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1306-deploy-cloudflare-workers-spa-shape.json +2 -2
  227. package/blueprints/deploy-cloudflare-workers/docs/topics.md +2 -2
  228. package/blueprints/deploy-hetzner-server/CHANGELOG.md +98 -7
  229. package/blueprints/deploy-hetzner-server/blueprint.json +7 -6
  230. package/blueprints/deploy-hetzner-server/contributions/probes/cloud-init-render-lint.mjs +46 -21
  231. package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +344 -83
  232. package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +211 -42
  233. package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +140 -22
  234. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +111 -30
  235. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-snapshot-on-demand.mjs +158 -30
  236. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-throwaway-server-provision.mjs +154 -32
  237. package/blueprints/edge-cloudflare-access/CHANGELOG.md +6 -5
  238. package/blueprints/edge-cloudflare-access/blueprint.json +1 -1
  239. package/blueprints/edge-cloudflare-access/contributions/probes/admin-console-gate-surface.mjs +9 -1
  240. package/blueprints/edge-cloudflare-access/docs/topics.md +1 -1
  241. package/blueprints/edge-cloudflare-rate-limiting/CHANGELOG.md +4 -7
  242. package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +1 -1
  243. package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +4 -6
  244. package/blueprints/edge-cloudflare-tunnel/README.md +1 -2
  245. package/blueprints/edge-cloudflare-tunnel/docs/topics.md +1 -1
  246. package/blueprints/edge-cloudflare-turnstile/CHANGELOG.md +2 -3
  247. package/blueprints/edge-cloudflare-turnstile/docs/topics.md +1 -1
  248. package/blueprints/email-smtp-resend/CHANGELOG.md +24 -8
  249. package/blueprints/email-smtp-resend/README.md +2 -2
  250. package/blueprints/email-smtp-resend/blueprint.json +3 -2
  251. package/blueprints/email-smtp-resend/contributions/probes/probe-utils.mjs +50 -0
  252. package/blueprints/email-smtp-resend/contributions/probes/real-account-resend-send.mjs +50 -0
  253. package/blueprints/email-smtp-resend/contributions/probes/run-real-account-resend-send.mjs +3 -0
  254. package/blueprints/email-smtp-resend/contributions/probes/run-smtp-round-trip.mjs +3 -0
  255. package/blueprints/email-smtp-resend/contributions/probes/run-unverified-sender-refusal.mjs +3 -0
  256. package/blueprints/email-smtp-resend/contributions/probes/smtp-round-trip.mjs +43 -0
  257. package/blueprints/email-smtp-resend/contributions/probes/unverified-sender-refusal.mjs +99 -0
  258. package/blueprints/email-smtp-resend/docs/topics.md +2 -3
  259. package/blueprints/jobs-background/CHANGELOG.md +66 -5
  260. package/blueprints/jobs-background/README.md +2 -2
  261. package/blueprints/jobs-background/blueprint.json +3 -2
  262. package/blueprints/jobs-background/contributions/probes/apply-time-override.mjs +38 -10
  263. package/blueprints/jobs-background/contributions/probes/apply-time-refusal.mjs +28 -6
  264. package/blueprints/jobs-background/contributions/probes/event-secrecy.mjs +16 -2
  265. package/blueprints/jobs-background/contributions/probes/fake-clock-cron.mjs +102 -21
  266. package/blueprints/jobs-background/contributions/probes/probe-utils.mjs +10 -2
  267. package/blueprints/jobs-background/contributions/probes/retry-and-fail-real-account.mjs +355 -0
  268. package/blueprints/jobs-background/contributions/probes/retry-and-fail.mjs +33 -2
  269. package/blueprints/jobs-background/contributions/probes/run-retry-and-fail-real-account.mjs +12 -0
  270. package/blueprints/jobs-background/docs/topics.md +1 -1
  271. package/blueprints/jobs-background/guide/jobs-background.md +1 -1
  272. package/blueprints/messaging-queue-cloudflare/CHANGELOG.md +25 -19
  273. package/blueprints/messaging-queue-cloudflare/README.md +12 -12
  274. package/blueprints/messaging-queue-cloudflare/blueprint.json +1 -1
  275. package/blueprints/messaging-queue-cloudflare/contributions/adrs/adr-3001-messaging-queue-cloudflare-adapter.json +2 -2
  276. package/blueprints/messaging-queue-cloudflare/contributions/adrs/adr-3002-messaging-queue-cloudflare-delivery-semantics.json +1 -1
  277. package/blueprints/messaging-queue-cloudflare/contributions/probes/real-account-concurrency-smoke.mjs +180 -12
  278. package/blueprints/messaging-queue-cloudflare/contributions/requirements/messaging-queue-cloudflare-req-006.json +1 -1
  279. package/blueprints/messaging-queue-cloudflare/docs/topics.md +2 -2
  280. package/blueprints/messaging-queue-cloudflare/guide/messaging-queue-cloudflare.md +2 -2
  281. package/blueprints/object-storage-s3/CHANGELOG.md +75 -4
  282. package/blueprints/object-storage-s3/blueprint.json +3 -2
  283. package/blueprints/object-storage-s3/contributions/probes/event-secrecy.mjs +73 -12
  284. package/blueprints/object-storage-s3/contributions/probes/facade-round-trip.mjs +55 -7
  285. package/blueprints/object-storage-s3/contributions/probes/hetzner-object-storage-round-trip.mjs +102 -18
  286. package/blueprints/object-storage-s3/contributions/probes/multipart-upload.mjs +105 -36
  287. package/blueprints/object-storage-s3/contributions/probes/presigned-url.mjs +104 -55
  288. package/blueprints/object-storage-s3/contributions/probes/probe-utils.mjs +129 -7
  289. package/blueprints/object-storage-s3/contributions/probes/put-get-round-trip.mjs +80 -18
  290. package/blueprints/object-storage-s3/contributions/probes/r2-real-account-smoke.mjs +291 -37
  291. package/blueprints/object-storage-s3/contributions/probes/run-event-secrecy.mjs +8 -2
  292. package/blueprints/object-storage-s3/contributions/probes/run-facade-round-trip.mjs +5 -5
  293. package/blueprints/object-storage-s3/contributions/probes/run-hetzner-object-storage-round-trip.mjs +8 -5
  294. package/blueprints/object-storage-s3/contributions/probes/run-multipart-upload.mjs +8 -2
  295. package/blueprints/object-storage-s3/contributions/probes/run-presigned-url.mjs +8 -2
  296. package/blueprints/object-storage-s3/contributions/probes/run-put-get-round-trip.mjs +6 -3
  297. package/blueprints/object-storage-s3/contributions/probes/run-r2-real-account-smoke.mjs +10 -2
  298. package/blueprints/object-storage-s3/contributions/requirements/object-storage-s3-req-006.json +1 -1
  299. package/blueprints/object-storage-s3/docs/topics.md +1 -1
  300. package/blueprints/object-storage-s3/guide/object-storage-s3.md +1 -1
  301. package/blueprints/observability-essentials/CHANGELOG.md +23 -7
  302. package/blueprints/observability-essentials/README.md +2 -2
  303. package/blueprints/observability-essentials/assets/probe-samples/probe-response-baseline.v2.json +1 -1
  304. package/blueprints/observability-essentials/blueprint.json +3 -2
  305. package/blueprints/observability-essentials/contributions/probes/liveness-probe.mjs +84 -0
  306. package/blueprints/observability-essentials/contributions/probes/metrics-endpoint.mjs +96 -0
  307. package/blueprints/observability-essentials/contributions/probes/probe-utils.mjs +52 -0
  308. package/blueprints/observability-essentials/contributions/probes/readiness-probe.mjs +171 -0
  309. package/blueprints/observability-essentials/contributions/probes/run-liveness-probe.mjs +3 -0
  310. package/blueprints/observability-essentials/contributions/probes/run-metrics-endpoint.mjs +3 -0
  311. package/blueprints/observability-essentials/contributions/probes/run-readiness-probe.mjs +3 -0
  312. package/blueprints/observability-essentials/docs/topics.md +2 -3
  313. package/blueprints/observability-logging/CHANGELOG.md +29 -4
  314. package/blueprints/observability-logging/README.md +2 -2
  315. package/blueprints/observability-logging/blueprint.json +3 -2
  316. package/blueprints/observability-logging/contributions/probes/correlation-id-flow.mjs +173 -0
  317. package/blueprints/observability-logging/contributions/probes/line-shape-and-fields.mjs +82 -0
  318. package/blueprints/observability-logging/contributions/probes/probe-utils.mjs +47 -0
  319. package/blueprints/observability-logging/contributions/probes/redaction-boundary.mjs +130 -0
  320. package/blueprints/observability-logging/contributions/probes/run-correlation-id-flow.mjs +3 -0
  321. package/blueprints/observability-logging/contributions/probes/run-line-shape-and-fields.mjs +3 -0
  322. package/blueprints/observability-logging/contributions/probes/run-redaction-boundary.mjs +3 -0
  323. package/blueprints/observability-probe-endpoints/CHANGELOG.md +23 -8
  324. package/blueprints/observability-probe-endpoints/README.md +3 -3
  325. package/blueprints/observability-probe-endpoints/blueprint.json +3 -2
  326. package/blueprints/observability-probe-endpoints/contributions/probes/kubernetes-startup-enabled.mjs +80 -0
  327. package/blueprints/observability-probe-endpoints/contributions/probes/partial-profile-refusal.mjs +101 -0
  328. package/blueprints/observability-probe-endpoints/contributions/probes/probe-utils.mjs +49 -0
  329. package/blueprints/observability-probe-endpoints/contributions/probes/profile-boot-materialisation.mjs +69 -0
  330. package/blueprints/observability-probe-endpoints/contributions/probes/run-kubernetes-startup-enabled.mjs +3 -0
  331. package/blueprints/observability-probe-endpoints/contributions/probes/run-partial-profile-refusal.mjs +3 -0
  332. package/blueprints/observability-probe-endpoints/contributions/probes/run-profile-boot-materialisation.mjs +3 -0
  333. package/blueprints/observability-probe-endpoints/docs/topics.md +2 -2
  334. package/blueprints/persistence-data-d1/CHANGELOG.md +16 -3
  335. package/blueprints/persistence-data-d1/README.md +2 -2
  336. package/blueprints/persistence-data-d1/blueprint.json +3 -2
  337. package/blueprints/persistence-data-d1/contributions/adrs/adr-1404-persistence-data-d1-store-boundary.json +1 -1
  338. package/blueprints/persistence-data-d1/contributions/probes/facade-round-trip.mjs +140 -0
  339. package/blueprints/persistence-data-d1/contributions/probes/migrations-forward-only.mjs +66 -0
  340. package/blueprints/persistence-data-d1/contributions/probes/probe-utils.mjs +51 -0
  341. package/blueprints/persistence-data-d1/contributions/probes/real-account-d1-round-trip.mjs +133 -0
  342. package/blueprints/persistence-data-d1/contributions/probes/run-facade-round-trip.mjs +3 -0
  343. package/blueprints/persistence-data-d1/contributions/probes/run-migrations-forward-only.mjs +3 -0
  344. package/blueprints/persistence-data-d1/contributions/probes/run-real-account-d1-round-trip.mjs +3 -0
  345. package/blueprints/persistence-data-d1/docs/topics.md +2 -3
  346. package/blueprints/persistence-data-postgres/CHANGELOG.md +84 -4
  347. package/blueprints/persistence-data-postgres/README.md +2 -2
  348. package/blueprints/persistence-data-postgres/blueprint.json +3 -2
  349. package/blueprints/persistence-data-postgres/contributions/adrs/adr-2801-persistence-data-postgres-driver.json +1 -1
  350. package/blueprints/persistence-data-postgres/contributions/probes/facade-round-trip.mjs +87 -12
  351. package/blueprints/persistence-data-postgres/contributions/probes/migration-apply.mjs +245 -19
  352. package/blueprints/persistence-data-postgres/contributions/probes/pool-posture-smoke.mjs +109 -26
  353. package/blueprints/persistence-data-postgres/contributions/probes/prepared-statement-scan.mjs +151 -38
  354. package/blueprints/persistence-data-postgres/contributions/probes/probe-utils.mjs +24 -6
  355. package/blueprints/persistence-data-postgres/contributions/probes/recovery-restore-round-trip.mjs +261 -48
  356. package/blueprints/persistence-data-postgres/contributions/probes/transaction-atomicity.mjs +76 -20
  357. package/blueprints/persistence-data-postgres/contributions/requirements/persistence-data-postgres-req-003.json +1 -1
  358. package/blueprints/persistence-data-postgres/docs/topics.md +1 -1
  359. package/blueprints/persistence-data-sqlite/CHANGELOG.md +16 -2
  360. package/blueprints/persistence-data-sqlite/README.md +2 -2
  361. package/blueprints/persistence-data-sqlite/blueprint.json +3 -2
  362. package/blueprints/persistence-data-sqlite/contributions/probes/boot-open-migrate.mjs +90 -0
  363. package/blueprints/persistence-data-sqlite/contributions/probes/facade-round-trip.mjs +82 -0
  364. package/blueprints/persistence-data-sqlite/contributions/probes/probe-utils.mjs +83 -0
  365. package/blueprints/persistence-data-sqlite/contributions/probes/run-boot-open-migrate.mjs +5 -0
  366. package/blueprints/persistence-data-sqlite/contributions/probes/run-facade-round-trip.mjs +5 -0
  367. package/blueprints/persistence-data-sqlite/contributions/probes/run-wal-checkpoint.mjs +5 -0
  368. package/blueprints/persistence-data-sqlite/contributions/probes/wal-checkpoint.mjs +137 -0
  369. package/blueprints/persistence-data-sqlite/docs/topics.md +2 -3
  370. package/blueprints/platform-cloudflare-cron-triggers/CHANGELOG.md +5 -5
  371. package/blueprints/platform-cloudflare-cron-triggers/README.md +2 -3
  372. package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +1 -1
  373. package/blueprints/platform-cloudflare-durable-objects/CHANGELOG.md +6 -10
  374. package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +1 -1
  375. package/blueprints/platform-cloudflare-kv/CHANGELOG.md +16 -12
  376. package/blueprints/platform-cloudflare-kv/README.md +2 -4
  377. package/blueprints/platform-cloudflare-kv/blueprint.json +2 -2
  378. package/blueprints/platform-cloudflare-kv/docs/topics.md +1 -1
  379. package/blueprints/platform-docker-compose-host/CHANGELOG.md +92 -5
  380. package/blueprints/platform-docker-compose-host/blueprint.json +2 -2
  381. package/blueprints/platform-docker-compose-host/contributions/probes/caddyfile-validate.mjs +116 -29
  382. package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs +181 -109
  383. package/blueprints/platform-docker-compose-host/contributions/probes/probe-utils.mjs +146 -26
  384. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-minimal-stack-up.mjs +238 -24
  385. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-reload-burst.mjs +207 -21
  386. package/blueprints/platform-docker-compose-host/contributions/probes/secrets-as-files-scan.mjs +246 -42
  387. package/blueprints/platform-docker-compose-host/docs/topics.md +1 -1
  388. package/blueprints/security-auth-clerk/CHANGELOG.md +18 -6
  389. package/blueprints/security-auth-clerk/README.md +3 -3
  390. package/blueprints/security-auth-clerk/assets/wiring/workers-wrangler-toml-shape.md +1 -1
  391. package/blueprints/security-auth-clerk/blueprint.json +3 -2
  392. package/blueprints/security-auth-clerk/contributions/probes/hosted-identity-ui-config.mjs +61 -0
  393. package/blueprints/security-auth-clerk/contributions/probes/probe-utils.mjs +188 -0
  394. package/blueprints/security-auth-clerk/contributions/probes/real-account-principal-directory-round-trip.mjs +216 -0
  395. package/blueprints/security-auth-clerk/contributions/probes/real-account-session-inventory.mjs +260 -0
  396. package/blueprints/security-auth-clerk/contributions/probes/role-model-adapter.mjs +53 -0
  397. package/blueprints/security-auth-clerk/contributions/probes/run-hosted-identity-ui-config.mjs +4 -0
  398. package/blueprints/security-auth-clerk/contributions/probes/run-real-account-principal-directory-round-trip.mjs +4 -0
  399. package/blueprints/security-auth-clerk/contributions/probes/run-real-account-session-inventory.mjs +4 -0
  400. package/blueprints/security-auth-clerk/contributions/probes/run-role-model-adapter.mjs +4 -0
  401. package/blueprints/security-auth-clerk/docs/topics.md +2 -3
  402. package/blueprints/security-auth-keycloak/CHANGELOG.md +18 -7
  403. package/blueprints/security-auth-keycloak/README.md +2 -2
  404. package/blueprints/security-auth-keycloak/blueprint.json +3 -2
  405. package/blueprints/security-auth-keycloak/contributions/probes/discovery-shape.mjs +68 -0
  406. package/blueprints/security-auth-keycloak/contributions/probes/introspection-shape.mjs +88 -0
  407. package/blueprints/security-auth-keycloak/contributions/probes/jwt-verifier-shape.mjs +86 -0
  408. package/blueprints/security-auth-keycloak/contributions/probes/probe-utils.mjs +180 -0
  409. package/blueprints/security-auth-keycloak/contributions/probes/real-account-realm-round-trip.mjs +91 -0
  410. package/blueprints/security-auth-keycloak/contributions/probes/role-adapter-shape.mjs +69 -0
  411. package/blueprints/security-auth-keycloak/contributions/probes/run-discovery-shape.mjs +4 -0
  412. package/blueprints/security-auth-keycloak/contributions/probes/run-introspection-shape.mjs +4 -0
  413. package/blueprints/security-auth-keycloak/contributions/probes/run-jwt-verifier-shape.mjs +4 -0
  414. package/blueprints/security-auth-keycloak/contributions/probes/run-real-account-realm-round-trip.mjs +4 -0
  415. package/blueprints/security-auth-keycloak/contributions/probes/run-role-adapter-shape.mjs +4 -0
  416. package/blueprints/security-auth-keycloak/docs/topics.md +1 -2
  417. package/blueprints/security-auth-magic-link/CHANGELOG.md +15 -4
  418. package/blueprints/security-auth-magic-link/README.md +2 -2
  419. package/blueprints/security-auth-magic-link/assets/principal-registry-samples/single-address.md +1 -1
  420. package/blueprints/security-auth-magic-link/blueprint.json +3 -2
  421. package/blueprints/security-auth-magic-link/contributions/probes/probe-utils.mjs +174 -0
  422. package/blueprints/security-auth-magic-link/contributions/probes/real-account-magic-link-send.mjs +139 -0
  423. package/blueprints/security-auth-magic-link/contributions/probes/run-real-account-magic-link-send.mjs +4 -0
  424. package/blueprints/security-auth-magic-link/contributions/probes/run-token-entropy-shape.mjs +4 -0
  425. package/blueprints/security-auth-magic-link/contributions/probes/run-token-issue-verify.mjs +4 -0
  426. package/blueprints/security-auth-magic-link/contributions/probes/token-entropy-shape.mjs +60 -0
  427. package/blueprints/security-auth-magic-link/contributions/probes/token-issue-verify.mjs +90 -0
  428. package/blueprints/security-auth-magic-link/docs/topics.md +1 -1
  429. package/blueprints/security-auth-oauth2/CHANGELOG.md +16 -5
  430. package/blueprints/security-auth-oauth2/README.md +2 -2
  431. package/blueprints/security-auth-oauth2/blueprint.json +3 -2
  432. package/blueprints/security-auth-oauth2/contributions/probes/authorisation-code-flow-shape.mjs +259 -0
  433. package/blueprints/security-auth-oauth2/contributions/probes/pkce-challenge-shape.mjs +58 -0
  434. package/blueprints/security-auth-oauth2/contributions/probes/probe-utils.mjs +190 -0
  435. package/blueprints/security-auth-oauth2/contributions/probes/provider-adapter-shape.mjs +64 -0
  436. package/blueprints/security-auth-oauth2/contributions/probes/real-account-authorisation-code-flow.mjs +93 -0
  437. package/blueprints/security-auth-oauth2/contributions/probes/run-authorisation-code-flow-shape.mjs +4 -0
  438. package/blueprints/security-auth-oauth2/contributions/probes/run-pkce-challenge-shape.mjs +4 -0
  439. package/blueprints/security-auth-oauth2/contributions/probes/run-provider-adapter-shape.mjs +4 -0
  440. package/blueprints/security-auth-oauth2/contributions/probes/run-real-account-authorisation-code-flow.mjs +4 -0
  441. package/blueprints/security-auth-oauth2/contributions/probes/run-session-bridge-shape.mjs +4 -0
  442. package/blueprints/security-auth-oauth2/contributions/probes/session-bridge-shape.mjs +78 -0
  443. package/blueprints/security-auth-oauth2/docs/topics.md +1 -2
  444. package/blueprints/security-secrets-management/CHANGELOG.md +15 -4
  445. package/blueprints/security-secrets-management/README.md +2 -2
  446. package/blueprints/security-secrets-management/blueprint.json +3 -2
  447. package/blueprints/security-secrets-management/contributions/probes/add-recipient-rotation.mjs +107 -0
  448. package/blueprints/security-secrets-management/contributions/probes/encrypt-decrypt-round-trip.mjs +122 -0
  449. package/blueprints/security-secrets-management/contributions/probes/key-rotation.mjs +93 -0
  450. package/blueprints/security-secrets-management/contributions/probes/mismatched-key-refusal.mjs +74 -0
  451. package/blueprints/security-secrets-management/contributions/probes/probe-utils.mjs +214 -0
  452. package/blueprints/security-secrets-management/contributions/probes/run-add-recipient-rotation.mjs +4 -0
  453. package/blueprints/security-secrets-management/contributions/probes/run-encrypt-decrypt-round-trip.mjs +4 -0
  454. package/blueprints/security-secrets-management/contributions/probes/run-key-rotation.mjs +4 -0
  455. package/blueprints/security-secrets-management/contributions/probes/run-mismatched-key-refusal.mjs +4 -0
  456. package/blueprints/security-secrets-management/docs/topics.md +2 -3
  457. package/fixtures/canary-manifest.json +9 -9
  458. package/guidance/harness-template.md +25 -0
  459. package/guidance/managed/agent-instructions-block.hash +1 -1
  460. package/guidance/managed/agent-instructions-block.md +25 -0
  461. package/package.json +2 -2
  462. package/rcf/adrs/adr-4101-feedback-store-gitignored.json +20 -0
  463. package/rcf/adrs/adr-4102-fingerprint-and-labels.json +25 -0
  464. package/rcf/adrs/adr-4103-redaction-rule-based.json +20 -0
  465. package/rcf/adrs/adr-4104-destination-on-library.json +25 -0
  466. package/rcf/adrs/adr-4105-gh-ambient-auth.json +20 -0
  467. package/rcf/adrs/adr-4106-bundled-entries-terminal.json +20 -0
  468. package/rcf/adrs/adr-4107-stop-is-ask-event.json +30 -0
  469. package/rcf/adrs/adr-4108-hooks-in-committed-config.json +25 -0
  470. package/rcf/adrs/adr-4109-referee-validate-adr-crosslinks.json +25 -0
  471. package/rcf/adrs/adr-4110-referee-coverage-strict-by-default.json +25 -0
  472. package/rcf/adrs/adr-4111-referee-finalise-refusals.json +20 -0
  473. package/rcf/adrs/adr-4112-referee-finalise-verified-invariant.json +30 -0
  474. package/rcf/code-nodes/cn-600.json +45 -0
  475. package/rcf/code-nodes/cn-601.json +18 -0
  476. package/rcf/code-nodes/cn-602.json +15 -0
  477. package/rcf/code-nodes/cn-603.json +12 -0
  478. package/rcf/code-nodes/cn-604.json +14 -0
  479. package/rcf/code-nodes/cn-605.json +17 -0
  480. package/rcf/code-nodes/cn-606.json +12 -0
  481. package/rcf/code-nodes/cn-607.json +12 -0
  482. package/rcf/code-nodes/cn-608.json +14 -0
  483. package/rcf/code-nodes/cn-609.json +17 -0
  484. package/rcf/code-nodes/cn-620.json +18 -0
  485. package/rcf/code-nodes/cn-621.json +14 -0
  486. package/rcf/code-nodes/cn-622.json +15 -0
  487. package/rcf/code-nodes/cn-623.json +16 -0
  488. package/rcf/code-nodes/cn-624.json +14 -0
  489. package/rcf/code-nodes/cn-625.json +15 -0
  490. package/rcf/code-nodes/cn-626.json +13 -0
  491. package/rcf/code-nodes/cn-627.json +13 -0
  492. package/rcf/code-nodes/cn-628.json +14 -0
  493. package/rcf/code-nodes/cn-629.json +12 -0
  494. package/rcf/code-nodes/cn-630.json +12 -0
  495. package/rcf/code-nodes/cn-631.json +13 -0
  496. package/rcf/code-nodes/cn-632.json +13 -0
  497. package/rcf/fbs/fbs-180.json +45 -0
  498. package/rcf/fbs/fbs-181.json +45 -0
  499. package/rcf/fbs/fbs-182.json +42 -0
  500. package/rcf/fbs/fbs-183.json +50 -0
  501. package/rcf/fbs/fbs-184.json +49 -0
  502. package/rcf/fbs/fbs-185.json +34 -0
  503. package/rcf/fbs/fbs-186.json +16 -0
  504. package/rcf/fbs/fbs-187.json +17 -0
  505. package/rcf/fbs/fbs-188.json +17 -0
  506. package/rcf/fbs/fbs-189.json +17 -0
  507. package/rcf/requirements/req-155.json +13 -0
  508. package/rcf/requirements/req-156.json +13 -0
  509. package/rcf/requirements/req-157.json +13 -0
  510. package/rcf/requirements/req-158.json +13 -0
  511. package/rcf/requirements/req-159.json +13 -0
  512. package/rcf/requirements/req-160.json +13 -0
  513. package/rcf/requirements/req-161.json +13 -0
  514. package/rcf/requirements/req-162.json +13 -0
  515. package/rcf/requirements/req-163.json +13 -0
  516. package/rcf/requirements/req-164.json +13 -0
  517. package/rcf/requirements/req-165.json +13 -0
  518. package/rcf/tacs/tac-4101-feedback-cli.json +77 -0
  519. package/rcf/tacs/tac-4102-feedback-store.json +50 -0
  520. package/rcf/tacs/tac-4103-feedback-redact.json +39 -0
  521. package/rcf/tacs/tac-4104-feedback-fingerprint.json +33 -0
  522. package/rcf/tacs/tac-4105-feedback-render.json +45 -0
  523. package/rcf/tacs/tac-4106-feedback-destination.json +39 -0
  524. package/rcf/tacs/tac-4107-feedback-gh.json +43 -0
  525. package/rcf/tacs/tac-4108-feedback-hook.json +46 -0
  526. package/rcf/tacs/tac-4109-referee-validate-adr-crosslinks.json +20 -0
  527. package/rcf/tacs/tac-4110-referee-coverage-mode-selector.json +21 -0
  528. package/rcf/tacs/tac-4111-referee-finalise-per-ac-refusals.json +21 -0
  529. package/rcf/tacs/tac-4112-referee-finalise-verified-invariant.json +22 -0
  530. package/rcf/test-suites/ts-140.json +9 -9
  531. package/rcf/test-suites/ts-150.json +9 -9
  532. package/rcf/test-suites/ts-185.json +54 -0
  533. package/rcf/test-suites/ts-186.json +58 -0
  534. package/rcf/test-suites/ts-187.json +44 -0
  535. package/rcf/test-suites/ts-188.json +166 -0
  536. package/rcf/test-suites/ts-189.json +38 -0
  537. package/rcf/test-suites/ts-190.json +30 -0
  538. package/rcf/test-suites/ts-191.json +51 -0
  539. package/rcf/test-suites/ts-192.json +37 -0
  540. package/rcf/test-suites/ts-193.json +60 -0
  541. package/rcf/test-suites/ts-194.json +67 -0
  542. package/rcf/test-suites/ts-195.json +52 -0
  543. package/rcf/test-suites/ts-196.json +58 -0
  544. package/rcf/test-suites/ts-197.json +66 -0
  545. package/rcf/test-suites/ts-198.json +87 -0
  546. package/rcf/test-suites/ts-199.json +51 -0
  547. package/rcf/test-suites/ts-200.json +30 -0
  548. package/rcf/test-suites/ts-201.json +38 -0
  549. package/rcf/test-suites/ts-202.json +38 -0
  550. package/rcf/test-suites/ts-203.json +38 -0
  551. package/rcf/user-stories/us-15501.json +60 -0
  552. package/rcf/user-stories/us-15502.json +33 -0
  553. package/rcf/user-stories/us-15503.json +33 -0
  554. package/rcf/user-stories/us-15601.json +51 -0
  555. package/rcf/user-stories/us-15701.json +42 -0
  556. package/rcf/user-stories/us-15801.json +42 -0
  557. package/rcf/user-stories/us-15802.json +33 -0
  558. package/rcf/user-stories/us-15901.json +33 -0
  559. package/rcf/user-stories/us-15902.json +51 -0
  560. package/rcf/user-stories/us-16001.json +51 -0
  561. package/rcf/user-stories/us-16101.json +42 -0
  562. package/rcf/user-stories/us-16102.json +33 -0
  563. package/rcf/user-stories/us-16103.json +42 -0
  564. package/rcf/user-stories/us-16104.json +42 -0
  565. package/rcf/user-stories/us-16201.json +33 -0
  566. package/rcf/user-stories/us-16301.json +36 -0
  567. package/rcf/user-stories/us-16401.json +45 -0
  568. package/rcf/user-stories/us-16501.json +45 -0
  569. package/rcf/user-stories/us-16502.json +45 -0
  570. package/releases/releases.yaml +11 -1
  571. package/src/blueprint/library-loader.js +48 -0
  572. package/src/blueprint/library-registry.js +13 -0
  573. package/src/browser-verify/pack-runner.js +7 -12
  574. package/src/build/mark.js +1 -1
  575. package/src/cli/blueprint-library.js +149 -0
  576. package/src/cli/coverage.js +49 -24
  577. package/src/cli/doctor.js +195 -7
  578. package/src/cli/feedback.js +2204 -0
  579. package/src/cli/finalise.js +158 -1
  580. package/src/cli/help.js +6 -0
  581. package/src/cli/init.js +63 -0
  582. package/src/cli/update.js +21 -1
  583. package/src/core/store/walker.js +33 -0
  584. package/src/core/store/writer.js +23 -0
  585. package/src/feedback/destination.js +361 -0
  586. package/src/feedback/fingerprint.js +132 -0
  587. package/src/feedback/gh.js +376 -0
  588. package/src/feedback/hook.js +183 -0
  589. package/src/feedback/labels.js +87 -0
  590. package/src/feedback/redact-allowlist.json +14 -0
  591. package/src/feedback/redact.js +1108 -0
  592. package/src/feedback/render.js +267 -0
  593. package/src/feedback/store.js +604 -0
  594. package/src/finalise/index.js +4 -0
  595. package/src/finalise/ingest.js +61 -0
  596. package/src/finalise/ship-without-eval.js +19 -7
  597. package/src/mcp/tools.js +8 -2
  598. package/src/setup/feedback-hooks.js +397 -0
  599. package/src/setup/managed-gitignore.js +3 -0
  600. package/src/view/html-page.js +5 -5
  601. package/src/view/style.css +2 -2
@@ -1,52 +1,54 @@
1
- // Probe: compose-config-lint.
1
+ // Probe: compose-config-lint (v1.1.8).
2
2
  //
3
- // anchorAcId list (multi): AC-composeHost-healthcheckLint (primary),
4
- // AC-composeHost-restartClassification, AC-composeHost-logDriverClassification.
5
- // accountBound: false.
3
+ // This is an offline validator: it parses the shipped compose.yaml
4
+ // and (when a Docker daemon is reachable) shells out to
5
+ // `docker compose config`. It has no engine-minted identifier - the
6
+ // sha256 of compose.yaml is computed by this probe with Node's
7
+ // `createHash` and the service names are Compose's own choices, not
8
+ // engine-returned identities. Every result row is a
9
+ // `conformanceOnly` de-claim naming the shipped AC clause the
10
+ // offline check does not observe; the live observation for each
11
+ // property lives on the account-bound probes that stand a real
12
+ // compose stack up.
13
+ //
14
+ // Splits per-property observations into their own result rows:
15
+ // - AC-composeHost-healthcheckLint per HTTP-terminating service.
16
+ // - AC-composeHost-restartClassification per service.
17
+ // - AC-composeHost-logDriverClassification per service, and one
18
+ // top-level check that every service uses the single elicited
19
+ // driver (LOG_DRIVER env, default journald).
20
+ // - AC-composeHost-healthcheckLint one summary docker-compose-config
21
+ // exit row when the daemon is reachable.
6
22
  //
7
- // Passes when:
8
- // - docker compose config exits 0 against the applied fixture compose.yaml
9
- // (the daemon must be reachable; when the docker CLI is missing, the
10
- // lint stage records a warn result so the source-tree assertions still
11
- // surface).
12
- // - every HTTP-terminating service (web) declares a healthcheck block.
13
- // - every service declares a restart policy whose value is unless-stopped
14
- // or on-failure.
15
- // - every service declares a logging driver whose value is journald or loki.
16
- // - a scan of the composeStackReady lifecycle event body captured by the
17
- // in-process facade proves no secret literal leaks.
23
+ // The compose.yaml bytes hash and the observed values (driver,
24
+ // restart policy, healthcheck keys, docker compose config exit)
25
+ // stay on the row as derived context.
18
26
  //
19
- // Mutations (each fails; each names the offending service or literal):
20
- // - SIMULATE_MISSING_HEALTHCHECK=true strips the healthcheck: block from
21
- // the web service before the compose-config-lint scan.
22
- // - SIMULATE_UNCLASSIFIED_RESTART=true rewrites restart: unless-stopped
23
- // on the web service to restart: always.
24
- // - SIMULATE_UNCLASSIFIED_LOG_DRIVER=true rewrites the web service
25
- // logging.driver from journald to syslog.
26
- // - SIMULATE_EVENT_SECRECY_LEAK=true injects the fixture web-token value
27
- // into the composeStackReady event body; the event-secrecy scan FAILS.
27
+ // accountBound: false.
28
28
 
29
29
  import { spawnSync } from 'node:child_process';
30
- import { readFile, readdir } from 'node:fs/promises';
31
- import { resolve } from 'node:path';
32
- import { runShim, readCompose, COMPOSE_PATH, FIXTURE_DIR, SECRET_PATH, whichDocker } from './probe-utils.mjs';
30
+ import { readFile } from 'node:fs/promises';
31
+ import { createHash } from 'node:crypto';
32
+ import { runShim, readCompose, COMPOSE_PATH, FIXTURE_DIR, whichDocker, parseComposeYaml } from './probe-utils.mjs';
33
+
34
+ function sha256(text) {
35
+ return createHash('sha256').update(text, 'utf8').digest('hex');
36
+ }
33
37
 
34
- export const anchorAcIds = [
35
- 'AC-composeHost-healthcheckLint',
36
- 'AC-composeHost-restartClassification',
37
- 'AC-composeHost-logDriverClassification',
38
- ];
38
+ export const anchorAcIds = [];
39
39
  export const accountBound = false;
40
40
 
41
41
  const HTTP_SERVICES = new Set(['web', 'caddy']);
42
42
  const ALLOWED_RESTART = new Set(['unless-stopped', 'on-failure']);
43
43
  const ALLOWED_LOG_DRIVER = new Set(['journald', 'loki']);
44
44
 
45
+ const LIMIT_HC = 'AC-composeHost-healthcheckLint: healthcheck presence is validated offline against the shipped compose.yaml; the live observation (a healthy compose service reporting healthy in `docker compose ps`) is carried by real-account-minimal-stack-up.';
46
+ const LIMIT_RESTART = 'AC-composeHost-restartClassification: restart-policy classification is validated offline against the shipped compose.yaml; the live observation (a compose stack applying and honouring the policy) is carried by real-account-minimal-stack-up.';
47
+ const LIMIT_LOG = 'AC-composeHost-logDriverClassification: log-driver classification is validated offline against the shipped compose.yaml against the elicited LOG_DRIVER; the live observation (a compose stack routing logs through the applied driver) lives on the account-bound branch.';
48
+
45
49
  function applyMutations(text) {
46
50
  let out = text;
47
51
  if (process.env.SIMULATE_MISSING_HEALTHCHECK === 'true') {
48
- // Strip the healthcheck: block from the web service by removing
49
- // the healthcheck: line and its indented children.
50
52
  const lines = out.split('\n');
51
53
  const trimmed = [];
52
54
  let stripping = false;
@@ -58,7 +60,6 @@ function applyMutations(text) {
58
60
  if (line.trim() === '' || indent > stripIndent) continue;
59
61
  stripping = false;
60
62
  }
61
- // Only strip inside the web service, not caddy.
62
63
  if (line.match(/^\s{4}healthcheck:\s*$/) && trimmed.slice(-30).some((l) => l.match(/^\s{2}web:\s*$/))) {
63
64
  const m = line.match(/^(\s*)/);
64
65
  stripIndent = m ? m[1].length : 0;
@@ -78,50 +79,139 @@ function applyMutations(text) {
78
79
  return out;
79
80
  }
80
81
 
81
- function scanServices(doc) {
82
+ function scanHealthchecks(doc, extra, composeSha256) {
82
83
  const results = [];
83
84
  const services = doc.services ?? {};
84
- const names = Object.keys(services);
85
- for (const name of names) {
85
+ const httpNames = Object.keys(services).filter((n) => HTTP_SERVICES.has(n));
86
+ for (const name of httpNames) {
86
87
  const svc = services[name] ?? {};
87
- if (HTTP_SERVICES.has(name)) {
88
- if (!svc.healthcheck) {
89
- results.push({
90
- anchorAcId: 'AC-composeHost-healthcheckLint',
91
- verdict: 'fail',
92
- detail: `HTTP-terminating service '${name}' is missing a healthcheck: block`,
93
- });
94
- }
88
+ if (!svc.healthcheck) {
89
+ results.push({
90
+ anchorAcId: null,
91
+ conformanceOnly: true,
92
+ limitation: LIMIT_HC,
93
+ verdict: 'fail',
94
+ detail: `offline compose-config-lint: HTTP-terminating service '${name}' is missing a healthcheck: block`,
95
+ evidence: { composeSha256, serviceName: name, missingServices: [name], expectedShape: 'healthcheck block', observedShape: 'absent' },
96
+ });
97
+ } else {
98
+ results.push({
99
+ anchorAcId: null,
100
+ conformanceOnly: true,
101
+ limitation: LIMIT_HC,
102
+ verdict: 'pass',
103
+ detail: `offline compose-config-lint: HTTP-terminating service '${name}' carries a healthcheck: block`,
104
+ evidence: {
105
+ composeSha256,
106
+ serviceName: name,
107
+ healthcheckKeys: Object.keys(svc.healthcheck || {}).sort(),
108
+ },
109
+ });
95
110
  }
111
+ }
112
+ return results;
113
+ }
114
+
115
+ function scanRestart(doc, composeSha256) {
116
+ const results = [];
117
+ const services = doc.services ?? {};
118
+ for (const name of Object.keys(services)) {
119
+ const svc = services[name] ?? {};
96
120
  const restart = svc.restart;
97
121
  if (!restart) {
98
122
  results.push({
99
- anchorAcId: 'AC-composeHost-restartClassification',
123
+ anchorAcId: null,
124
+ conformanceOnly: true,
125
+ limitation: LIMIT_RESTART,
100
126
  verdict: 'fail',
101
- detail: `service '${name}' declares no restart policy; allowed: unless-stopped, on-failure`,
127
+ detail: `offline compose-config-lint: service '${name}' declares no restart policy; allowed: unless-stopped, on-failure`,
128
+ evidence: { composeSha256, serviceName: name, restart: null },
102
129
  });
103
130
  } else if (!ALLOWED_RESTART.has(String(restart))) {
104
131
  results.push({
105
- anchorAcId: 'AC-composeHost-restartClassification',
132
+ anchorAcId: null,
133
+ conformanceOnly: true,
134
+ limitation: LIMIT_RESTART,
106
135
  verdict: 'fail',
107
- detail: `service '${name}' declares restart: ${restart}; allowed: unless-stopped, on-failure`,
136
+ detail: `offline compose-config-lint: service '${name}' declares restart: ${restart}; allowed: unless-stopped, on-failure`,
137
+ evidence: { composeSha256, serviceName: name, restart: String(restart), allowed: ['unless-stopped', 'on-failure'] },
138
+ });
139
+ } else {
140
+ results.push({
141
+ anchorAcId: null,
142
+ conformanceOnly: true,
143
+ limitation: LIMIT_RESTART,
144
+ verdict: 'pass',
145
+ detail: `offline compose-config-lint: service '${name}' declares restart: ${restart}`,
146
+ evidence: { composeSha256, serviceName: name, restart: String(restart) },
108
147
  });
109
148
  }
110
- const logging = svc.logging;
111
- const driver = logging && logging.driver;
149
+ }
150
+ return results;
151
+ }
152
+
153
+ function scanLogging(doc, composeSha256) {
154
+ const results = [];
155
+ const services = doc.services ?? {};
156
+ const elicitedDriver = process.env.LOG_DRIVER || 'journald';
157
+ const observedDrivers = [];
158
+ for (const name of Object.keys(services)) {
159
+ const svc = services[name] ?? {};
160
+ const driver = svc.logging && svc.logging.driver;
112
161
  if (!driver) {
113
162
  results.push({
114
- anchorAcId: 'AC-composeHost-logDriverClassification',
163
+ anchorAcId: null,
164
+ conformanceOnly: true,
165
+ limitation: LIMIT_LOG,
115
166
  verdict: 'fail',
116
- detail: `service '${name}' declares no logging driver; allowed: journald, loki`,
167
+ detail: `offline compose-config-lint: service '${name}' declares no logging driver; allowed: journald, loki`,
168
+ evidence: { composeSha256, serviceName: name, driver: null },
117
169
  });
118
- } else if (!ALLOWED_LOG_DRIVER.has(String(driver))) {
170
+ observedDrivers.push(null);
171
+ continue;
172
+ }
173
+ if (!ALLOWED_LOG_DRIVER.has(String(driver))) {
119
174
  results.push({
120
- anchorAcId: 'AC-composeHost-logDriverClassification',
175
+ anchorAcId: null,
176
+ conformanceOnly: true,
177
+ limitation: LIMIT_LOG,
121
178
  verdict: 'fail',
122
- detail: `service '${name}' declares logging driver '${driver}'; allowed: journald, loki`,
179
+ detail: `offline compose-config-lint: service '${name}' declares logging driver '${driver}'; allowed: journald, loki`,
180
+ evidence: { composeSha256, serviceName: name, driver: String(driver), allowed: ['journald', 'loki'] },
123
181
  });
182
+ observedDrivers.push(String(driver));
183
+ continue;
124
184
  }
185
+ observedDrivers.push(String(driver));
186
+ }
187
+ const distinct = new Set(observedDrivers.filter(Boolean));
188
+ if (distinct.size > 1) {
189
+ results.push({
190
+ anchorAcId: null,
191
+ conformanceOnly: true,
192
+ limitation: LIMIT_LOG,
193
+ verdict: 'fail',
194
+ detail: `offline compose-config-lint: services declare more than one log driver (${[...distinct].join(', ')}); the elicited log-driver=${elicitedDriver} requires every service to match`,
195
+ evidence: { composeSha256, composeFile: 'compose.yaml', elicitedDriver, observedDrivers, distinct: [...distinct] },
196
+ });
197
+ } else if (distinct.size === 1 && !distinct.has(elicitedDriver)) {
198
+ results.push({
199
+ anchorAcId: null,
200
+ conformanceOnly: true,
201
+ limitation: LIMIT_LOG,
202
+ verdict: 'fail',
203
+ detail: `offline compose-config-lint: services declare log driver ${[...distinct][0]} but the elicited log-driver is ${elicitedDriver}`,
204
+ evidence: { composeSha256, composeFile: 'compose.yaml', elicitedDriver, observedDrivers },
205
+ });
206
+ } else if (distinct.size === 1) {
207
+ results.push({
208
+ anchorAcId: null,
209
+ conformanceOnly: true,
210
+ limitation: LIMIT_LOG,
211
+ verdict: 'pass',
212
+ detail: `offline compose-config-lint: every service uses the elicited log driver ${[...distinct][0]}`,
213
+ evidence: { composeSha256, composeFile: 'compose.yaml', elicitedDriver, observedDrivers },
214
+ });
125
215
  }
126
216
  return results;
127
217
  }
@@ -129,44 +219,43 @@ function scanServices(doc) {
129
219
  export default async function runProbe() {
130
220
  const results = [];
131
221
  const extra = {};
132
- // Source-tree scan on the (possibly mutated) compose file.
133
222
  const originalText = await readFile(COMPOSE_PATH, 'utf8');
134
223
  const mutatedText = applyMutations(originalText);
135
- const { doc: origDoc } = await readCompose();
136
- const { parseComposeYaml } = await import('./probe-utils.mjs');
224
+ const composeSha256 = sha256(mutatedText);
225
+ extra.composeSha256 = composeSha256;
137
226
  const mutatedDoc = parseComposeYaml(mutatedText);
138
- const scanResults = scanServices(mutatedDoc);
139
- results.push(...scanResults);
140
227
  extra.serviceNames = Object.keys(mutatedDoc.services ?? {});
141
228
  extra.mutations = {
142
229
  missingHealthcheck: process.env.SIMULATE_MISSING_HEALTHCHECK === 'true',
143
230
  unclassifiedRestart: process.env.SIMULATE_UNCLASSIFIED_RESTART === 'true',
144
231
  unclassifiedLogDriver: process.env.SIMULATE_UNCLASSIFIED_LOG_DRIVER === 'true',
145
- eventSecrecyLeak: process.env.SIMULATE_EVENT_SECRECY_LEAK === 'true',
146
232
  };
147
- // docker compose config lint (on the mutated text, via a scratch file).
233
+ results.push(...scanHealthchecks(mutatedDoc, extra, composeSha256));
234
+ results.push(...scanRestart(mutatedDoc, composeSha256));
235
+ results.push(...scanLogging(mutatedDoc, composeSha256));
148
236
  const dockerVersion = whichDocker();
149
237
  extra.dockerVersion = dockerVersion;
150
238
  if (dockerVersion === null) {
151
239
  results.push({
152
- anchorAcId: 'AC-composeHost-healthcheckLint',
240
+ anchorAcId: null,
241
+ conformanceOnly: true,
242
+ limitation: LIMIT_HC,
153
243
  verdict: 'warn',
154
- detail: 'docker CLI absent on PATH; source-tree assertions ran but docker compose config was not exercised',
244
+ detail: 'offline compose-config-lint: docker CLI absent on PATH; source-tree assertions ran but docker compose config was not exercised',
245
+ evidence: { composeSha256, dockerLocal: null },
155
246
  });
156
247
  } else {
157
- const { writeFile, mkdir } = await import('node:fs/promises');
248
+ const { writeFile, mkdir, cp } = await import('node:fs/promises');
158
249
  const { tmpdir } = await import('node:os');
159
250
  const { join } = await import('node:path');
160
251
  const scratch = join(tmpdir(), `rcf-lite-t2-compose-config-lint-${Date.now()}`);
161
252
  await mkdir(scratch, { recursive: true });
162
253
  await writeFile(join(scratch, 'compose.yaml'), mutatedText, 'utf8');
163
- // Copy .env, caddy/, secrets/, src/ so docker compose config can resolve references
164
- const { cp } = await import('node:fs/promises');
165
254
  for (const rel of ['.env', 'caddy', 'secrets', 'src']) {
166
255
  try {
167
256
  await cp(join(FIXTURE_DIR, rel), join(scratch, rel), { recursive: true });
168
257
  } catch (err) {
169
- // best-effort; docker compose config still validates syntax
258
+ // best-effort
170
259
  }
171
260
  }
172
261
  const cfg = spawnSync('docker', ['compose', '-f', join(scratch, 'compose.yaml'), 'config'], {
@@ -174,54 +263,37 @@ export default async function runProbe() {
174
263
  });
175
264
  if (cfg.status === 0) {
176
265
  results.push({
177
- anchorAcId: 'AC-composeHost-healthcheckLint',
266
+ anchorAcId: null,
267
+ conformanceOnly: true,
268
+ limitation: LIMIT_HC,
178
269
  verdict: 'pass',
179
- detail: 'docker compose config exit 0 on the applied compose.yaml',
270
+ detail: `offline compose-config-lint: docker compose config exit 0 (docker ${dockerVersion})`,
271
+ evidence: {
272
+ composeSha256,
273
+ composeFile: 'compose.yaml',
274
+ dockerVersion,
275
+ engineNote: 'Docker Engine (compose sub-command); vendor documentation https://docs.docker.com/compose/compose-file/, verifiedOn 2026-09-11',
276
+ exitStatus: 0,
277
+ },
180
278
  });
181
279
  } else {
182
280
  results.push({
183
- anchorAcId: 'AC-composeHost-healthcheckLint',
281
+ anchorAcId: null,
282
+ conformanceOnly: true,
283
+ limitation: LIMIT_HC,
184
284
  verdict: 'fail',
185
- detail: `docker compose config exit ${cfg.status}: ${(cfg.stderr || cfg.stdout || '').slice(0, 400)}`,
285
+ detail: `offline compose-config-lint: docker compose config exit ${cfg.status}: ${(cfg.stderr || cfg.stdout || '').slice(0, 400)}`,
286
+ evidence: {
287
+ composeSha256,
288
+ composeFile: 'compose.yaml',
289
+ dockerVersion,
290
+ exitStatus: cfg.status,
291
+ stderr: (cfg.stderr || '').slice(0, 400),
292
+ },
186
293
  });
187
294
  }
188
295
  extra.dockerComposeConfigExit = cfg.status;
189
296
  }
190
- // Event-secrecy scan: capture a composeStackReady event body and assert
191
- // no secret literal appears; SIMULATE_EVENT_SECRECY_LEAK injects one.
192
- const secretValue = (await readFile(SECRET_PATH, 'utf8')).trim();
193
- const event = {
194
- kind: 'composeStackReady',
195
- services: extra.serviceNames,
196
- imageDigests: extra.serviceNames.map((n) => `sha256:fixture-${n}`),
197
- healthTimesMs: extra.serviceNames.map(() => 42),
198
- };
199
- if (process.env.SIMULATE_EVENT_SECRECY_LEAK === 'true') {
200
- event.debug = { boundToken: secretValue };
201
- }
202
- const eventBody = JSON.stringify(event);
203
- if (eventBody.includes(secretValue)) {
204
- results.push({
205
- anchorAcId: 'AC-composeHost-healthcheckLint',
206
- verdict: 'fail',
207
- detail: `composeStackReady event body carries the fixture secret literal (event-secrecy leak): ${eventBody.slice(0, 200)}`,
208
- });
209
- } else {
210
- results.push({
211
- anchorAcId: 'AC-composeHost-healthcheckLint',
212
- verdict: 'pass',
213
- detail: 'composeStackReady event body carries no secret literal (event-secrecy scan clean)',
214
- });
215
- }
216
- extra.eventBodyByteLength = eventBody.length;
217
- // Passing summary line when nothing failed.
218
- if (!results.some((r) => r.verdict === 'fail')) {
219
- results.push({
220
- anchorAcId: 'AC-composeHost-healthcheckLint',
221
- verdict: 'pass',
222
- detail: `compose-config-lint pass: services ${extra.serviceNames.join(', ')}; healthcheck + restart + logging + event-secrecy scans all clean`,
223
- });
224
- }
225
297
  return { results, extra };
226
298
  }
227
299
 
@@ -1,19 +1,32 @@
1
- // Shared helpers for platform-docker-compose-host probes.
2
- //
1
+ // Shared helpers for platform-docker-compose-host probes (v1.1.4,
2
+ // v1.1.5, 2026-09-11).
3
+
4
+ // Every result row a probe returns MUST carry either an `evidence`
5
+ // object naming the observed artefact (compose service list, on-server
6
+ // response body excerpt, burst counters, event body sample) OR
7
+ // `accountBoundSkipped: true` with a `reason` field naming exactly
8
+ // one unset variable (the shape rule). Empty or null probe outcomes
9
+ // FAIL with detail exactly `no checks ran`.
10
+
3
11
  // Runtime-dependency posture:
4
12
  // - compose-config-lint shells to docker compose config against the applied
5
13
  // fixture compose.yaml; the docker daemon must be reachable locally for
6
14
  // the lint stage (skipped-with-warn when the CLI is absent so a
7
15
  // review machine without docker can still see the source-tree assertions).
8
16
  // - secrets-as-files-scan runs in-process against the fixture; no external
9
- // engine required.
17
+ // engine required. Scans compose.yaml, .env, every service config
18
+ // under caddy/, secrets/ metadata and the compose secret-shape.
10
19
  // - caddyfile-validate shells to caddy validate. When a local caddy binary
11
20
  // is not on PATH the probe runs the vendor container caddy:2 via docker
12
- // run --rm; when docker is also absent it skips-with-warn.
21
+ // run --rm; when docker is also absent it skips-with-warn. The probe
22
+ // also asserts the compose file bind-mounts the Caddyfile read-only.
13
23
  // - real-account-* probes call the fixture's provision.mjs, drive docker
14
24
  // compose over ssh against the throwaway server, and tear down in
15
- // always(). Without CI_HAS_HETZNER_ACCOUNT they record accountBoundSkipped:
16
- // true and the aggregate flips to pass per the real-account gate contract, section 3.5.
25
+ // always(). Without CI_HAS_HETZNER_ACCOUNT set to exactly the string
26
+ // "true" the probe records accountBoundSkipped: true and a reason
27
+ // naming CI_HAS_HETZNER_ACCOUNT (set-but-not-true reported as such,
28
+ // distinguished from unset). A second-tier HCLOUD_TOKEN unset once
29
+ // past the first-tier gate has its own honest skip row.
17
30
 
18
31
  import { mkdir, writeFile, readFile } from 'node:fs/promises';
19
32
  import { dirname, resolve, join } from 'node:path';
@@ -30,31 +43,75 @@ export const COMPOSE_PATH = resolve(FIXTURE_DIR, 'compose.yaml');
30
43
  export const CADDYFILE_PATH = resolve(FIXTURE_DIR, 'caddy/Caddyfile');
31
44
  export const SECRET_PATH = resolve(FIXTURE_DIR, 'secrets/web-token');
32
45
  export const ENV_PATH = resolve(FIXTURE_DIR, '.env');
33
- export const REPORT_DIR = resolve(
34
- PROJECT_ROOT,
35
- '.rcf/reports/blueprints/platform-docker-compose-host',
36
- );
46
+ export const REPORT_DIR = process.env.RCF_REPORT_DIR_OVERRIDE
47
+ ? resolve(process.env.RCF_REPORT_DIR_OVERRIDE, 'blueprints/platform-docker-compose-host')
48
+ : resolve(PROJECT_ROOT, '.rcf/reports/blueprints/platform-docker-compose-host');
49
+ export const BLUEPRINT_JSON_PATH = resolve(HERE, '..', '..', 'blueprint.json');
50
+
51
+ // The report's `version` field is read from the blueprint's own
52
+ // blueprint.json at write time; every regenerated record therefore
53
+ // carries the version the shipped code was at when the record was
54
+ // written. The strict walker compares each record's `version` to
55
+ // blueprint.json's `version` and FAILS on any mismatch (a record
56
+ // at an older version fails the walk). The read is deliberately
57
+ // per-call so a version bump between two writes in one process is
58
+ // picked up honestly.
59
+ async function readBlueprintVersion() {
60
+ const raw = await readFile(BLUEPRINT_JSON_PATH, 'utf8');
61
+ const doc = JSON.parse(raw);
62
+ if (typeof doc.version !== 'string' || doc.version.length === 0) {
63
+ throw new Error(`blueprint.json at ${BLUEPRINT_JSON_PATH} carries no string version`);
64
+ }
65
+ return doc.version;
66
+ }
37
67
 
38
68
  export function aggregate(results) {
69
+ if (!Array.isArray(results) || results.length === 0) return 'fail';
39
70
  if (results.some((r) => r.verdict === 'fail')) return 'fail';
40
71
  if (results.some((r) => r.verdict === 'warn')) return 'warn';
41
72
  return 'pass';
42
73
  }
43
74
 
75
+ // An empty results array is an honest failure of the probe: no anchor
76
+ // can be manufactured. Callers that KNOW the anchor may pass it in;
77
+ // otherwise the row carries anchorAcId: null so downstream tallies do
78
+ // not see an invented AC id.
79
+ export function emptyResultsFail(anchorAcId = null) {
80
+ return {
81
+ anchorAcId: anchorAcId ?? null,
82
+ verdict: 'fail',
83
+ detail: 'no checks ran',
84
+ evidence: { reason: 'the probe returned zero result rows', probeName: '(unknown at empty-fail construction)' },
85
+ };
86
+ }
87
+
44
88
  export function isSkipped(results) {
45
- return results.length > 0 && results.every((r) => r.accountBoundSkipped === true);
89
+ return Array.isArray(results) && results.length > 0 && results.every((r) => r.accountBoundSkipped === true);
46
90
  }
47
91
 
48
92
  export async function writeReport({ probeName, engine, results, extra }) {
49
93
  await mkdir(REPORT_DIR, { recursive: true });
50
- const rawVerdict = aggregate(results);
51
- const aggregateVerdict = isSkipped(results) ? 'pass' : rawVerdict;
94
+ const rows = Array.isArray(results) && results.length > 0 ? results : [emptyResultsFail()];
95
+ // the anchor-prefix rule: every row's detail starts with the first eight
96
+ // words of the anchored AC text. The prepend is idempotent so a
97
+ // caller that already wrapped its detail via `anchored()` does not
98
+ // double up.
99
+ for (const r of rows) {
100
+ const prefix = r && r.anchorAcId && AC_ANCHOR_PREFIX[r.anchorAcId];
101
+ if (prefix && typeof r.detail === 'string' && !r.detail.startsWith(prefix)) {
102
+ r.detail = `${prefix}. ${r.detail}`;
103
+ }
104
+ }
105
+ const rawVerdict = aggregate(rows);
106
+ const aggregateVerdict = isSkipped(rows) ? 'pass' : rawVerdict;
107
+ const version = await readBlueprintVersion();
52
108
  const report = {
53
109
  slug: 'platform-docker-compose-host',
54
110
  probeName,
111
+ version,
55
112
  runAt: new Date().toISOString(),
56
113
  engine,
57
- results,
114
+ results: rows,
58
115
  aggregateVerdict,
59
116
  ...(extra ?? {}),
60
117
  };
@@ -65,17 +122,26 @@ export async function writeReport({ probeName, engine, results, extra }) {
65
122
 
66
123
  export async function runShim(probeName, engine, mainFn) {
67
124
  try {
68
- const outcome = (await mainFn()) ?? { results: [] };
69
- const { results, extra } = outcome;
70
- const { report, path } = await writeReport({ probeName, engine, results, extra });
125
+ const outcome = await mainFn();
126
+ const results = (outcome && Array.isArray(outcome.results)) ? outcome.results : null;
127
+ const extra = outcome && outcome.extra;
128
+ const rows = results && results.length > 0 ? results : [emptyResultsFail()];
129
+ const { report, path } = await writeReport({ probeName, engine, results: rows, extra });
71
130
  process.stdout.write(JSON.stringify(report, null, 2) + '\n');
72
131
  process.stdout.write(`report written to ${path}\n`);
73
132
  if (report.aggregateVerdict === 'fail') process.exitCode = 1;
74
133
  } catch (err) {
134
+ // A probe that throws has no known anchor at this layer; use null
135
+ // rather than the invented "unknown" fallback (defect).
75
136
  const results = [{
76
- anchorAcId: 'unknown',
137
+ anchorAcId: null,
77
138
  verdict: 'fail',
78
139
  detail: `probe threw: ${err && err.message ? err.message : String(err)}`,
140
+ evidence: {
141
+ probeName,
142
+ errorMessage: err && err.message ? err.message : String(err),
143
+ errorStack: (err && err.stack ? err.stack : String(err)).slice(0, 800),
144
+ },
79
145
  }];
80
146
  const { report, path } = await writeReport({ probeName, engine, results });
81
147
  process.stdout.write(JSON.stringify(report, null, 2) + '\n');
@@ -85,21 +151,46 @@ export async function runShim(probeName, engine, mainFn) {
85
151
  }
86
152
  }
87
153
 
88
- export function accountBoundSkippedResult(anchorAcId, note) {
154
+ // Skip helper (the shape rule): the reason field names exactly one
155
+ // unset variable; the gateState field distinguishes unset from
156
+ // set-but-not-true, so a variable set to `false` is never reported as
157
+ // unset. Callers hand the anchor plus a short note.
158
+ export function firstTierGateSkipResult(anchorAcId, varName = 'CI_HAS_HETZNER_ACCOUNT', note = '') {
159
+ const observed = process.env[varName];
160
+ const state = observed === undefined
161
+ ? `unset`
162
+ : `set-but-not-true (observed value ${JSON.stringify(observed)})`;
89
163
  return {
90
164
  anchorAcId,
91
165
  verdict: 'skipped',
92
166
  accountBoundSkipped: true,
93
- detail: `accountBound: CI_HAS_HETZNER_ACCOUNT unset; ${note}`,
167
+ reason: varName,
168
+ gateState: state,
169
+ detail: `accountBoundSkipped: gate variable ${varName} ${state}; ${note || 'run with CI_HAS_HETZNER_ACCOUNT=true to exercise the account-bound branch.'}`,
94
170
  };
95
171
  }
96
172
 
173
+ // Second-tier skip helper (the shape rule): the account-bound branch
174
+ // requires HCLOUD_TOKEN once the first-tier gate is true. When only the
175
+ // second-tier is missing the skip row names HCLOUD_TOKEN literally and
176
+ // still aggregates to pass; missing configuration is a skip, not a
177
+ // failure.
178
+ export function secondTierMissingSkipResult(anchorAcId, varName, note = '') {
179
+ return {
180
+ anchorAcId,
181
+ verdict: 'skipped',
182
+ accountBoundSkipped: true,
183
+ reason: varName,
184
+ gateState: 'unset',
185
+ detail: `accountBoundSkipped: second-tier variable ${varName} unset while CI_HAS_HETZNER_ACCOUNT=true; ${note || 'the probe cannot open the vendor client.'}`,
186
+ };
187
+ }
188
+
189
+ export function accountBoundSkippedResult(anchorAcId, note) {
190
+ return firstTierGateSkipResult(anchorAcId, 'CI_HAS_HETZNER_ACCOUNT', note);
191
+ }
192
+
97
193
  // Small YAML subset parser tuned for the fixture compose.yaml shape.
98
- // The fixture keeps compose.yaml intentionally simple (two-space indent,
99
- // no anchors, no flow-style scalars) so the probes stay dependency-free.
100
- // If a probe needs a full parser later, this returns a shape sufficient
101
- // for the assertions the ACs make (services, networks, volumes, secrets,
102
- // env_file, healthcheck presence, restart, logging.driver).
103
194
  export function parseComposeYaml(text) {
104
195
  const doc = {};
105
196
  const lines = text.split(/\r?\n/);
@@ -135,6 +226,15 @@ export function parseComposeYaml(text) {
135
226
  } else {
136
227
  obj[key] = decodeScalar(val.trim());
137
228
  parent.push(obj);
229
+ // Push the list-item object onto the stack so subsequent
230
+ // lines indented deeper than the list marker are added as
231
+ // sibling fields on the same object (e.g. the compose
232
+ // long-form service-secret entry:
233
+ // - source: web-token
234
+ // target: web-token
235
+ // mode: 0400
236
+ // must land as {source, target, mode} on one item).
237
+ stack.push({ indent, node: obj });
138
238
  }
139
239
  } else {
140
240
  parent.push(decodeScalar(rest.trim()));
@@ -145,7 +245,6 @@ export function parseComposeYaml(text) {
145
245
  if (!kv) continue;
146
246
  const [, key, val] = kv;
147
247
  if (val === '') {
148
- // Peek: next non-empty non-comment line starting with '- ' means list.
149
248
  let peekIsList = false;
150
249
  for (let j = lines.indexOf(raw) + 1; j < lines.length; j++) {
151
250
  const nxt = lines[j];
@@ -183,3 +282,24 @@ export function whichCaddy() {
183
282
  const r = spawnSync('caddy', ['version'], { encoding: 'utf8' });
184
283
  return r.status === 0 ? (r.stdout || '').trim() : null;
185
284
  }
285
+
286
+ // the anchor-prefix rule: every result row's detail starts with the first
287
+ // eight words of the anchored AC text. This map holds those prefixes
288
+ // for the platform-docker-compose-host ACs; callers wrap their detail
289
+ // via `anchored(anchorAcId, detail)`.
290
+ export const AC_ANCHOR_PREFIX = {
291
+ 'AC-composeHost-upClean': 'The real-account-minimal-stack-up probe, when CI_HAS_HETZNER_ACCOUNT is set, scps the',
292
+ 'AC-composeHost-zeroDowntimeReload': 'The real-account-reload-burst probe, when CI_HAS_HETZNER_ACCOUNT is set, runs undici',
293
+ 'AC-composeHost-healthcheckLint': 'The compose-config-lint probe shells to docker compose config against',
294
+ 'AC-composeHost-restartClassification': 'The compose-config-lint probe asserts every service in the compose.yaml',
295
+ 'AC-composeHost-logDriverClassification': 'Every service in the applied compose.yaml declares a logging',
296
+ 'AC-composeHost-reverseProxyArtefactValid': 'The applied fixture ships caddy/Caddyfile under a caddy/ directory',
297
+ 'AC-composeHost-secretsAreFiles': 'The secrets-as-files-scan probe walks compose.yaml plus every referenced service',
298
+ 'AC-composeHost-secretShape': 'The applied compose.yaml declares at least one secret under',
299
+ 'AC-38107-4': 'Read-only bind-mount: the reverse-proxy config bind-mount in compose.yaml is',
300
+ };
301
+
302
+ export function anchored(anchorAcId, body) {
303
+ const prefix = AC_ANCHOR_PREFIX[anchorAcId];
304
+ return prefix ? `${prefix}. ${body}` : body;
305
+ }