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.
- package/CHANGELOG.md +23 -0
- package/README.md +10 -0
- package/bin/rcf.js +2 -0
- package/blueprints/application-account-settings/CHANGELOG.md +35 -6
- package/blueprints/application-account-settings/README.md +1 -1
- package/blueprints/application-account-settings/blueprint.json +4 -2
- package/blueprints/application-account-settings/contributions/adrs/adr-2602-application-account-settings-security-surface-shape.json +1 -1
- package/blueprints/application-account-settings/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-account-settings/contributions/probes/profile-form-autocomplete.mjs +67 -0
- package/blueprints/application-account-settings/contributions/probes/run-profile-form-autocomplete.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-sessions-adapter-uniform.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-sessions-surface-shape.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-shell-tablist-per-capability.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-theme-radiogroup.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/sessions-adapter-uniform.mjs +130 -0
- package/blueprints/application-account-settings/contributions/probes/sessions-surface-shape.mjs +70 -0
- package/blueprints/application-account-settings/contributions/probes/shell-tablist-per-capability.mjs +60 -0
- package/blueprints/application-account-settings/contributions/probes/theme-radiogroup.mjs +126 -0
- package/blueprints/application-admin-console/CHANGELOG.md +32 -6
- package/blueprints/application-admin-console/README.md +1 -1
- package/blueprints/application-admin-console/blueprint.json +4 -2
- package/blueprints/application-admin-console/contributions/adrs/adr-2201-application-admin-console-capability-vocabulary.json +1 -1
- package/blueprints/application-admin-console/contributions/probes/audit-log-surface.mjs +79 -0
- package/blueprints/application-admin-console/contributions/probes/org-switcher-surface.mjs +83 -0
- package/blueprints/application-admin-console/contributions/probes/permission-matrix-grid.mjs +67 -0
- package/blueprints/application-admin-console/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-admin-console/contributions/probes/run-audit-log-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-org-switcher-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-permission-matrix-grid.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-sign-in-access-gated-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-users-directory-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/sign-in-access-gated-surface.mjs +117 -0
- package/blueprints/application-admin-console/contributions/probes/users-directory-surface.mjs +145 -0
- package/blueprints/application-api-rest/CHANGELOG.md +16 -8
- package/blueprints/application-api-rest/README.md +3 -3
- package/blueprints/application-api-rest/blueprint.json +3 -2
- package/blueprints/application-api-rest/contributions/adrs/adr-302-application-api-rest-auth-model.json +1 -1
- package/blueprints/application-api-rest/contributions/probes/cursor-pagination-round-trip.mjs +154 -0
- package/blueprints/application-api-rest/contributions/probes/health-probes-distinct.mjs +124 -0
- package/blueprints/application-api-rest/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-api-rest/contributions/probes/problem-details-on-error.mjs +77 -0
- package/blueprints/application-api-rest/contributions/probes/request-id-echoed.mjs +76 -0
- package/blueprints/application-api-rest/contributions/probes/run-cursor-pagination-round-trip.mjs +5 -0
- package/blueprints/application-api-rest/contributions/probes/run-health-probes-distinct.mjs +5 -0
- package/blueprints/application-api-rest/contributions/probes/run-problem-details-on-error.mjs +5 -0
- package/blueprints/application-api-rest/contributions/probes/run-request-id-echoed.mjs +5 -0
- package/blueprints/application-api-rest/docs/topics.md +1 -1
- package/blueprints/application-charts/CHANGELOG.md +35 -9
- package/blueprints/application-charts/README.md +6 -6
- package/blueprints/application-charts/blueprint.json +5 -3
- package/blueprints/application-charts/contributions/adrs/adr-1901-application-charts-engine-choice.json +1 -1
- package/blueprints/application-charts/contributions/probes/keyboard-traversal.mjs +151 -0
- package/blueprints/application-charts/contributions/probes/non-colour-distinction.mjs +67 -0
- package/blueprints/application-charts/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-charts/contributions/probes/run-keyboard-traversal.mjs +5 -0
- package/blueprints/application-charts/contributions/probes/run-non-colour-distinction.mjs +5 -0
- package/blueprints/application-charts/contributions/probes/run-text-alternative-table.mjs +5 -0
- package/blueprints/application-charts/contributions/probes/text-alternative-table.mjs +139 -0
- package/blueprints/application-charts/contributions/requirements/application-charts-req-001.json +1 -1
- package/blueprints/application-charts/contributions/requirements/application-charts-req-002.json +1 -1
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18101.json +5 -5
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18106.json +1 -1
- package/blueprints/application-charts/guide/application-charts.md +1 -1
- package/blueprints/application-dashboard/CHANGELOG.md +20 -10
- package/blueprints/application-dashboard/README.md +6 -6
- package/blueprints/application-dashboard/assets/guidance/dashboard-design.md +2 -2
- package/blueprints/application-dashboard/blueprint.json +3 -2
- package/blueprints/application-dashboard/contributions/adrs/adr-2001-application-dashboard-primary-kpi-enum.json +1 -1
- package/blueprints/application-dashboard/contributions/adrs/adr-2002-application-dashboard-timeframe-presets.json +1 -1
- package/blueprints/application-dashboard/contributions/probes/export-handle-formats.mjs +75 -0
- package/blueprints/application-dashboard/contributions/probes/four-tile-states.mjs +75 -0
- package/blueprints/application-dashboard/contributions/probes/primary-kpi-top-left.mjs +58 -0
- package/blueprints/application-dashboard/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-dashboard/contributions/probes/run-export-handle-formats.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/run-four-tile-states.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/run-primary-kpi-top-left.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/run-shell-five-regions.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/shell-five-regions.mjs +93 -0
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-002.json +1 -1
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-003.json +1 -1
- package/blueprints/application-dashboard/contributions/tacs/tac-2002-application-dashboard-filter-chrome.json +1 -1
- package/blueprints/application-dashboard/contributions/tacs/tac-2003-application-dashboard-export-handle.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19101.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19102.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19103.json +1 -1
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19104.json +1 -1
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19105.json +1 -1
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19106.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19107.json +5 -5
- package/blueprints/application-dashboard/docs/topics.md +2 -2
- package/blueprints/application-dashboard/guide/application-dashboard.md +2 -2
- package/blueprints/application-datatable/CHANGELOG.md +19 -9
- package/blueprints/application-datatable/README.md +2 -2
- package/blueprints/application-datatable/blueprint.json +3 -2
- package/blueprints/application-datatable/contributions/probes/apg-table-shape.mjs +57 -0
- package/blueprints/application-datatable/contributions/probes/four-states-regions.mjs +80 -0
- package/blueprints/application-datatable/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-datatable/contributions/probes/run-apg-table-shape.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/run-four-states-regions.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/run-search-adapter-round-trip.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/run-sort-adapter-round-trip.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/search-adapter-round-trip.mjs +58 -0
- package/blueprints/application-datatable/contributions/probes/sort-adapter-round-trip.mjs +60 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-006.json +1 -1
- package/blueprints/application-datatable/docs/topics.md +1 -1
- package/blueprints/application-empty-error-states/CHANGELOG.md +33 -3
- package/blueprints/application-empty-error-states/blueprint.json +4 -2
- package/blueprints/application-empty-error-states/contributions/probes/empty-list-and-no-search.mjs +66 -0
- package/blueprints/application-empty-error-states/contributions/probes/error-boundary-alert.mjs +65 -0
- package/blueprints/application-empty-error-states/contributions/probes/forbidden-and-server-error.mjs +167 -0
- package/blueprints/application-empty-error-states/contributions/probes/not-found-and-recovery.mjs +61 -0
- package/blueprints/application-empty-error-states/contributions/probes/permission-denied-and-offline.mjs +155 -0
- package/blueprints/application-empty-error-states/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-empty-list-and-no-search.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-error-boundary-alert.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-forbidden-and-server-error.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-not-found-and-recovery.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-permission-denied-and-offline.mjs +5 -0
- package/blueprints/application-error-handling/CHANGELOG.md +45 -6
- package/blueprints/application-error-handling/blueprint.json +3 -2
- package/blueprints/application-error-handling/contributions/probes/category-vocabulary.mjs +90 -0
- package/blueprints/application-error-handling/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-error-handling/contributions/probes/record-shape-adr-1701.mjs +78 -0
- package/blueprints/application-error-handling/contributions/probes/run-category-vocabulary.mjs +5 -0
- package/blueprints/application-error-handling/contributions/probes/run-record-shape-adr-1701.mjs +5 -0
- package/blueprints/application-error-handling/contributions/probes/run-two-boundaries-registered.mjs +5 -0
- package/blueprints/application-error-handling/contributions/probes/two-boundaries-registered.mjs +343 -0
- package/blueprints/application-file-upload/CHANGELOG.md +28 -6
- package/blueprints/application-file-upload/README.md +1 -1
- package/blueprints/application-file-upload/blueprint.json +3 -2
- package/blueprints/application-file-upload/contributions/probes/assertive-completion-slot.mjs +78 -0
- package/blueprints/application-file-upload/contributions/probes/chunked-transport-endpoints.mjs +173 -0
- package/blueprints/application-file-upload/contributions/probes/per-file-progressbar.mjs +124 -0
- package/blueprints/application-file-upload/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-file-upload/contributions/probes/run-assertive-completion-slot.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/run-chunked-transport-endpoints.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/run-per-file-progressbar.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/run-upload-surface-shape.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/upload-surface-shape.mjs +55 -0
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-001.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-002.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-003.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-004.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-005.json +1 -1
- package/blueprints/application-file-upload/probe-packs/application-file-upload.pack.mjs +1 -1
- package/blueprints/application-forms-wizard/CHANGELOG.md +20 -6
- package/blueprints/application-forms-wizard/README.md +2 -2
- package/blueprints/application-forms-wizard/blueprint.json +3 -2
- package/blueprints/application-forms-wizard/contributions/probes/draft-persistence-round-trip.mjs +110 -0
- package/blueprints/application-forms-wizard/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-forms-wizard/contributions/probes/run-draft-persistence-round-trip.mjs +4 -0
- package/blueprints/application-forms-wizard/contributions/probes/run-step-page-shape.mjs +4 -0
- package/blueprints/application-forms-wizard/contributions/probes/run-task-list-surface.mjs +4 -0
- package/blueprints/application-forms-wizard/contributions/probes/step-page-shape.mjs +61 -0
- package/blueprints/application-forms-wizard/contributions/probes/task-list-surface.mjs +109 -0
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-001.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-002.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-003.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-004.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-005.json +1 -1
- package/blueprints/application-forms-wizard/probe-packs/application-forms-wizard.pack.mjs +1 -1
- package/blueprints/application-notifications-in-app/CHANGELOG.md +38 -5
- package/blueprints/application-notifications-in-app/README.md +4 -4
- package/blueprints/application-notifications-in-app/blueprint.json +4 -2
- package/blueprints/application-notifications-in-app/contributions/adrs/adr-2101-application-notifications-in-app-live-region-role-mapping.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/probes/centre-acknowledge-round-trip.mjs +210 -0
- package/blueprints/application-notifications-in-app/contributions/probes/live-region-preseeding.mjs +93 -0
- package/blueprints/application-notifications-in-app/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-notifications-in-app/contributions/probes/run-centre-acknowledge-round-trip.mjs +5 -0
- package/blueprints/application-notifications-in-app/contributions/probes/run-live-region-preseeding.mjs +5 -0
- package/blueprints/application-notifications-in-app/contributions/probes/run-toast-contract.mjs +5 -0
- package/blueprints/application-notifications-in-app/contributions/probes/toast-contract.mjs +92 -0
- package/blueprints/application-notifications-in-app/contributions/tacs/tac-2101-application-notifications-in-app-live-region.json +3 -3
- package/blueprints/application-notifications-in-app/contributions/tacs/tac-2102-application-notifications-in-app-centre.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20101.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20102.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20103.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20105.json +1 -1
- package/blueprints/application-notifications-in-app/guide/application-notifications-in-app.md +3 -3
- package/blueprints/application-onboarding-tour/CHANGELOG.md +31 -5
- package/blueprints/application-onboarding-tour/README.md +1 -1
- package/blueprints/application-onboarding-tour/blueprint.json +5 -3
- package/blueprints/application-onboarding-tour/contributions/probes/checklist-anchor-open.mjs +81 -0
- package/blueprints/application-onboarding-tour/contributions/probes/completion-persistence.mjs +159 -0
- package/blueprints/application-onboarding-tour/contributions/probes/first-run-detection.mjs +67 -0
- package/blueprints/application-onboarding-tour/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-checklist-anchor-open.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-completion-persistence.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-first-run-detection.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-stepper-role-dialog.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/stepper-role-dialog.mjs +45 -0
- package/blueprints/application-spa/CHANGELOG.md +20 -11
- package/blueprints/application-spa/README.md +9 -10
- package/blueprints/application-spa/blueprint.json +3 -2
- package/blueprints/application-spa/contributions/adrs/adr-202-application-spa-theming.json +1 -1
- package/blueprints/application-spa/contributions/adrs/adr-206-application-spa-iconography.json +1 -1
- package/blueprints/application-spa/contributions/probes/designed-empty-state.mjs +72 -0
- package/blueprints/application-spa/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-spa/contributions/probes/route-inventory-published.mjs +185 -0
- package/blueprints/application-spa/contributions/probes/run-designed-empty-state.mjs +5 -0
- package/blueprints/application-spa/contributions/probes/run-route-inventory-published.mjs +5 -0
- package/blueprints/application-spa/contributions/probes/run-shell-nav-present.mjs +5 -0
- package/blueprints/application-spa/contributions/probes/shell-nav-present.mjs +69 -0
- package/blueprints/application-spa/contributions/tacs/tac-207-application-spa-token-adherence-probe.json +1 -1
- package/blueprints/application-spa/contributions/tacs/tac-208-application-spa-icon-adherence-probe.json +2 -2
- package/blueprints/application-spa/contributions/tacs/tac-209-application-spa-csp-styled-adherence-probe.json +3 -3
- package/blueprints/application-spa/contributions/tacs/tac-210-application-spa-external-dependency-provisioning-probe.json +2 -2
- package/blueprints/application-spa/contributions/tacs/tac-211-application-spa-core-flow-e2e-probe.json +2 -2
- package/blueprints/application-spa/contributions/user-stories/application-spa-us-1131.json +1 -1
- package/blueprints/application-spa/docs/topics.md +1 -1
- package/blueprints/delivery-ci-workflows/CHANGELOG.md +31 -11
- package/blueprints/delivery-ci-workflows/README.md +3 -3
- package/blueprints/delivery-ci-workflows/blueprint.json +3 -2
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-702-delivery-ci-workflows-strict-coverage-gate.json +2 -2
- package/blueprints/delivery-ci-workflows/contributions/probes/node-gate-entrypoint.mjs +76 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/probe-utils.mjs +56 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/real-account-github-actions-run-record.mjs +139 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/run-node-gate-entrypoint.mjs +3 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/run-real-account-github-actions-run-record.mjs +3 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/run-workflow-template-shape.mjs +3 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/workflow-template-shape.mjs +94 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-004.json +1 -1
- package/blueprints/delivery-ci-workflows/docs/topics.md +2 -3
- package/blueprints/deploy-cloudflare-workers/CHANGELOG.md +4 -4
- package/blueprints/deploy-cloudflare-workers/README.md +3 -3
- package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1306-deploy-cloudflare-workers-spa-shape.json +2 -2
- package/blueprints/deploy-cloudflare-workers/docs/topics.md +2 -2
- package/blueprints/deploy-hetzner-server/CHANGELOG.md +98 -7
- package/blueprints/deploy-hetzner-server/blueprint.json +7 -6
- package/blueprints/deploy-hetzner-server/contributions/probes/cloud-init-render-lint.mjs +46 -21
- package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +344 -83
- package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +211 -42
- package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +140 -22
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +111 -30
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-snapshot-on-demand.mjs +158 -30
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-throwaway-server-provision.mjs +154 -32
- package/blueprints/edge-cloudflare-access/CHANGELOG.md +6 -5
- package/blueprints/edge-cloudflare-access/blueprint.json +1 -1
- package/blueprints/edge-cloudflare-access/contributions/probes/admin-console-gate-surface.mjs +9 -1
- package/blueprints/edge-cloudflare-access/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-rate-limiting/CHANGELOG.md +4 -7
- package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +4 -6
- package/blueprints/edge-cloudflare-tunnel/README.md +1 -2
- package/blueprints/edge-cloudflare-tunnel/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-turnstile/CHANGELOG.md +2 -3
- package/blueprints/edge-cloudflare-turnstile/docs/topics.md +1 -1
- package/blueprints/email-smtp-resend/CHANGELOG.md +24 -8
- package/blueprints/email-smtp-resend/README.md +2 -2
- package/blueprints/email-smtp-resend/blueprint.json +3 -2
- package/blueprints/email-smtp-resend/contributions/probes/probe-utils.mjs +50 -0
- package/blueprints/email-smtp-resend/contributions/probes/real-account-resend-send.mjs +50 -0
- package/blueprints/email-smtp-resend/contributions/probes/run-real-account-resend-send.mjs +3 -0
- package/blueprints/email-smtp-resend/contributions/probes/run-smtp-round-trip.mjs +3 -0
- package/blueprints/email-smtp-resend/contributions/probes/run-unverified-sender-refusal.mjs +3 -0
- package/blueprints/email-smtp-resend/contributions/probes/smtp-round-trip.mjs +43 -0
- package/blueprints/email-smtp-resend/contributions/probes/unverified-sender-refusal.mjs +99 -0
- package/blueprints/email-smtp-resend/docs/topics.md +2 -3
- package/blueprints/jobs-background/CHANGELOG.md +66 -5
- package/blueprints/jobs-background/README.md +2 -2
- package/blueprints/jobs-background/blueprint.json +3 -2
- package/blueprints/jobs-background/contributions/probes/apply-time-override.mjs +38 -10
- package/blueprints/jobs-background/contributions/probes/apply-time-refusal.mjs +28 -6
- package/blueprints/jobs-background/contributions/probes/event-secrecy.mjs +16 -2
- package/blueprints/jobs-background/contributions/probes/fake-clock-cron.mjs +102 -21
- package/blueprints/jobs-background/contributions/probes/probe-utils.mjs +10 -2
- package/blueprints/jobs-background/contributions/probes/retry-and-fail-real-account.mjs +355 -0
- package/blueprints/jobs-background/contributions/probes/retry-and-fail.mjs +33 -2
- package/blueprints/jobs-background/contributions/probes/run-retry-and-fail-real-account.mjs +12 -0
- package/blueprints/jobs-background/docs/topics.md +1 -1
- package/blueprints/jobs-background/guide/jobs-background.md +1 -1
- package/blueprints/messaging-queue-cloudflare/CHANGELOG.md +25 -19
- package/blueprints/messaging-queue-cloudflare/README.md +12 -12
- package/blueprints/messaging-queue-cloudflare/blueprint.json +1 -1
- package/blueprints/messaging-queue-cloudflare/contributions/adrs/adr-3001-messaging-queue-cloudflare-adapter.json +2 -2
- package/blueprints/messaging-queue-cloudflare/contributions/adrs/adr-3002-messaging-queue-cloudflare-delivery-semantics.json +1 -1
- package/blueprints/messaging-queue-cloudflare/contributions/probes/real-account-concurrency-smoke.mjs +180 -12
- package/blueprints/messaging-queue-cloudflare/contributions/requirements/messaging-queue-cloudflare-req-006.json +1 -1
- package/blueprints/messaging-queue-cloudflare/docs/topics.md +2 -2
- package/blueprints/messaging-queue-cloudflare/guide/messaging-queue-cloudflare.md +2 -2
- package/blueprints/object-storage-s3/CHANGELOG.md +75 -4
- package/blueprints/object-storage-s3/blueprint.json +3 -2
- package/blueprints/object-storage-s3/contributions/probes/event-secrecy.mjs +73 -12
- package/blueprints/object-storage-s3/contributions/probes/facade-round-trip.mjs +55 -7
- package/blueprints/object-storage-s3/contributions/probes/hetzner-object-storage-round-trip.mjs +102 -18
- package/blueprints/object-storage-s3/contributions/probes/multipart-upload.mjs +105 -36
- package/blueprints/object-storage-s3/contributions/probes/presigned-url.mjs +104 -55
- package/blueprints/object-storage-s3/contributions/probes/probe-utils.mjs +129 -7
- package/blueprints/object-storage-s3/contributions/probes/put-get-round-trip.mjs +80 -18
- package/blueprints/object-storage-s3/contributions/probes/r2-real-account-smoke.mjs +291 -37
- package/blueprints/object-storage-s3/contributions/probes/run-event-secrecy.mjs +8 -2
- package/blueprints/object-storage-s3/contributions/probes/run-facade-round-trip.mjs +5 -5
- package/blueprints/object-storage-s3/contributions/probes/run-hetzner-object-storage-round-trip.mjs +8 -5
- package/blueprints/object-storage-s3/contributions/probes/run-multipart-upload.mjs +8 -2
- package/blueprints/object-storage-s3/contributions/probes/run-presigned-url.mjs +8 -2
- package/blueprints/object-storage-s3/contributions/probes/run-put-get-round-trip.mjs +6 -3
- package/blueprints/object-storage-s3/contributions/probes/run-r2-real-account-smoke.mjs +10 -2
- package/blueprints/object-storage-s3/contributions/requirements/object-storage-s3-req-006.json +1 -1
- package/blueprints/object-storage-s3/docs/topics.md +1 -1
- package/blueprints/object-storage-s3/guide/object-storage-s3.md +1 -1
- package/blueprints/observability-essentials/CHANGELOG.md +23 -7
- package/blueprints/observability-essentials/README.md +2 -2
- package/blueprints/observability-essentials/assets/probe-samples/probe-response-baseline.v2.json +1 -1
- package/blueprints/observability-essentials/blueprint.json +3 -2
- package/blueprints/observability-essentials/contributions/probes/liveness-probe.mjs +84 -0
- package/blueprints/observability-essentials/contributions/probes/metrics-endpoint.mjs +96 -0
- package/blueprints/observability-essentials/contributions/probes/probe-utils.mjs +52 -0
- package/blueprints/observability-essentials/contributions/probes/readiness-probe.mjs +171 -0
- package/blueprints/observability-essentials/contributions/probes/run-liveness-probe.mjs +3 -0
- package/blueprints/observability-essentials/contributions/probes/run-metrics-endpoint.mjs +3 -0
- package/blueprints/observability-essentials/contributions/probes/run-readiness-probe.mjs +3 -0
- package/blueprints/observability-essentials/docs/topics.md +2 -3
- package/blueprints/observability-logging/CHANGELOG.md +29 -4
- package/blueprints/observability-logging/README.md +2 -2
- package/blueprints/observability-logging/blueprint.json +3 -2
- package/blueprints/observability-logging/contributions/probes/correlation-id-flow.mjs +173 -0
- package/blueprints/observability-logging/contributions/probes/line-shape-and-fields.mjs +82 -0
- package/blueprints/observability-logging/contributions/probes/probe-utils.mjs +47 -0
- package/blueprints/observability-logging/contributions/probes/redaction-boundary.mjs +130 -0
- package/blueprints/observability-logging/contributions/probes/run-correlation-id-flow.mjs +3 -0
- package/blueprints/observability-logging/contributions/probes/run-line-shape-and-fields.mjs +3 -0
- package/blueprints/observability-logging/contributions/probes/run-redaction-boundary.mjs +3 -0
- package/blueprints/observability-probe-endpoints/CHANGELOG.md +23 -8
- package/blueprints/observability-probe-endpoints/README.md +3 -3
- package/blueprints/observability-probe-endpoints/blueprint.json +3 -2
- package/blueprints/observability-probe-endpoints/contributions/probes/kubernetes-startup-enabled.mjs +80 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/partial-profile-refusal.mjs +101 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/probe-utils.mjs +49 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/profile-boot-materialisation.mjs +69 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/run-kubernetes-startup-enabled.mjs +3 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/run-partial-profile-refusal.mjs +3 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/run-profile-boot-materialisation.mjs +3 -0
- package/blueprints/observability-probe-endpoints/docs/topics.md +2 -2
- package/blueprints/persistence-data-d1/CHANGELOG.md +16 -3
- package/blueprints/persistence-data-d1/README.md +2 -2
- package/blueprints/persistence-data-d1/blueprint.json +3 -2
- package/blueprints/persistence-data-d1/contributions/adrs/adr-1404-persistence-data-d1-store-boundary.json +1 -1
- package/blueprints/persistence-data-d1/contributions/probes/facade-round-trip.mjs +140 -0
- package/blueprints/persistence-data-d1/contributions/probes/migrations-forward-only.mjs +66 -0
- package/blueprints/persistence-data-d1/contributions/probes/probe-utils.mjs +51 -0
- package/blueprints/persistence-data-d1/contributions/probes/real-account-d1-round-trip.mjs +133 -0
- package/blueprints/persistence-data-d1/contributions/probes/run-facade-round-trip.mjs +3 -0
- package/blueprints/persistence-data-d1/contributions/probes/run-migrations-forward-only.mjs +3 -0
- package/blueprints/persistence-data-d1/contributions/probes/run-real-account-d1-round-trip.mjs +3 -0
- package/blueprints/persistence-data-d1/docs/topics.md +2 -3
- package/blueprints/persistence-data-postgres/CHANGELOG.md +84 -4
- package/blueprints/persistence-data-postgres/README.md +2 -2
- package/blueprints/persistence-data-postgres/blueprint.json +3 -2
- package/blueprints/persistence-data-postgres/contributions/adrs/adr-2801-persistence-data-postgres-driver.json +1 -1
- package/blueprints/persistence-data-postgres/contributions/probes/facade-round-trip.mjs +87 -12
- package/blueprints/persistence-data-postgres/contributions/probes/migration-apply.mjs +245 -19
- package/blueprints/persistence-data-postgres/contributions/probes/pool-posture-smoke.mjs +109 -26
- package/blueprints/persistence-data-postgres/contributions/probes/prepared-statement-scan.mjs +151 -38
- package/blueprints/persistence-data-postgres/contributions/probes/probe-utils.mjs +24 -6
- package/blueprints/persistence-data-postgres/contributions/probes/recovery-restore-round-trip.mjs +261 -48
- package/blueprints/persistence-data-postgres/contributions/probes/transaction-atomicity.mjs +76 -20
- package/blueprints/persistence-data-postgres/contributions/requirements/persistence-data-postgres-req-003.json +1 -1
- package/blueprints/persistence-data-postgres/docs/topics.md +1 -1
- package/blueprints/persistence-data-sqlite/CHANGELOG.md +16 -2
- package/blueprints/persistence-data-sqlite/README.md +2 -2
- package/blueprints/persistence-data-sqlite/blueprint.json +3 -2
- package/blueprints/persistence-data-sqlite/contributions/probes/boot-open-migrate.mjs +90 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/facade-round-trip.mjs +82 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/probe-utils.mjs +83 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/run-boot-open-migrate.mjs +5 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/run-facade-round-trip.mjs +5 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/run-wal-checkpoint.mjs +5 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/wal-checkpoint.mjs +137 -0
- package/blueprints/persistence-data-sqlite/docs/topics.md +2 -3
- package/blueprints/platform-cloudflare-cron-triggers/CHANGELOG.md +5 -5
- package/blueprints/platform-cloudflare-cron-triggers/README.md +2 -3
- package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +1 -1
- package/blueprints/platform-cloudflare-durable-objects/CHANGELOG.md +6 -10
- package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +1 -1
- package/blueprints/platform-cloudflare-kv/CHANGELOG.md +16 -12
- package/blueprints/platform-cloudflare-kv/README.md +2 -4
- package/blueprints/platform-cloudflare-kv/blueprint.json +2 -2
- package/blueprints/platform-cloudflare-kv/docs/topics.md +1 -1
- package/blueprints/platform-docker-compose-host/CHANGELOG.md +92 -5
- package/blueprints/platform-docker-compose-host/blueprint.json +2 -2
- package/blueprints/platform-docker-compose-host/contributions/probes/caddyfile-validate.mjs +116 -29
- package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs +181 -109
- package/blueprints/platform-docker-compose-host/contributions/probes/probe-utils.mjs +146 -26
- package/blueprints/platform-docker-compose-host/contributions/probes/real-account-minimal-stack-up.mjs +238 -24
- package/blueprints/platform-docker-compose-host/contributions/probes/real-account-reload-burst.mjs +207 -21
- package/blueprints/platform-docker-compose-host/contributions/probes/secrets-as-files-scan.mjs +246 -42
- package/blueprints/platform-docker-compose-host/docs/topics.md +1 -1
- package/blueprints/security-auth-clerk/CHANGELOG.md +18 -6
- package/blueprints/security-auth-clerk/README.md +3 -3
- package/blueprints/security-auth-clerk/assets/wiring/workers-wrangler-toml-shape.md +1 -1
- package/blueprints/security-auth-clerk/blueprint.json +3 -2
- package/blueprints/security-auth-clerk/contributions/probes/hosted-identity-ui-config.mjs +61 -0
- package/blueprints/security-auth-clerk/contributions/probes/probe-utils.mjs +188 -0
- package/blueprints/security-auth-clerk/contributions/probes/real-account-principal-directory-round-trip.mjs +216 -0
- package/blueprints/security-auth-clerk/contributions/probes/real-account-session-inventory.mjs +260 -0
- package/blueprints/security-auth-clerk/contributions/probes/role-model-adapter.mjs +53 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-hosted-identity-ui-config.mjs +4 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-real-account-principal-directory-round-trip.mjs +4 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-real-account-session-inventory.mjs +4 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-role-model-adapter.mjs +4 -0
- package/blueprints/security-auth-clerk/docs/topics.md +2 -3
- package/blueprints/security-auth-keycloak/CHANGELOG.md +18 -7
- package/blueprints/security-auth-keycloak/README.md +2 -2
- package/blueprints/security-auth-keycloak/blueprint.json +3 -2
- package/blueprints/security-auth-keycloak/contributions/probes/discovery-shape.mjs +68 -0
- package/blueprints/security-auth-keycloak/contributions/probes/introspection-shape.mjs +88 -0
- package/blueprints/security-auth-keycloak/contributions/probes/jwt-verifier-shape.mjs +86 -0
- package/blueprints/security-auth-keycloak/contributions/probes/probe-utils.mjs +180 -0
- package/blueprints/security-auth-keycloak/contributions/probes/real-account-realm-round-trip.mjs +91 -0
- package/blueprints/security-auth-keycloak/contributions/probes/role-adapter-shape.mjs +69 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-discovery-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-introspection-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-jwt-verifier-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-real-account-realm-round-trip.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-role-adapter-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/docs/topics.md +1 -2
- package/blueprints/security-auth-magic-link/CHANGELOG.md +15 -4
- package/blueprints/security-auth-magic-link/README.md +2 -2
- package/blueprints/security-auth-magic-link/assets/principal-registry-samples/single-address.md +1 -1
- package/blueprints/security-auth-magic-link/blueprint.json +3 -2
- package/blueprints/security-auth-magic-link/contributions/probes/probe-utils.mjs +174 -0
- package/blueprints/security-auth-magic-link/contributions/probes/real-account-magic-link-send.mjs +139 -0
- package/blueprints/security-auth-magic-link/contributions/probes/run-real-account-magic-link-send.mjs +4 -0
- package/blueprints/security-auth-magic-link/contributions/probes/run-token-entropy-shape.mjs +4 -0
- package/blueprints/security-auth-magic-link/contributions/probes/run-token-issue-verify.mjs +4 -0
- package/blueprints/security-auth-magic-link/contributions/probes/token-entropy-shape.mjs +60 -0
- package/blueprints/security-auth-magic-link/contributions/probes/token-issue-verify.mjs +90 -0
- package/blueprints/security-auth-magic-link/docs/topics.md +1 -1
- package/blueprints/security-auth-oauth2/CHANGELOG.md +16 -5
- package/blueprints/security-auth-oauth2/README.md +2 -2
- package/blueprints/security-auth-oauth2/blueprint.json +3 -2
- package/blueprints/security-auth-oauth2/contributions/probes/authorisation-code-flow-shape.mjs +259 -0
- package/blueprints/security-auth-oauth2/contributions/probes/pkce-challenge-shape.mjs +58 -0
- package/blueprints/security-auth-oauth2/contributions/probes/probe-utils.mjs +190 -0
- package/blueprints/security-auth-oauth2/contributions/probes/provider-adapter-shape.mjs +64 -0
- package/blueprints/security-auth-oauth2/contributions/probes/real-account-authorisation-code-flow.mjs +93 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-authorisation-code-flow-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-pkce-challenge-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-provider-adapter-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-real-account-authorisation-code-flow.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-session-bridge-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/session-bridge-shape.mjs +78 -0
- package/blueprints/security-auth-oauth2/docs/topics.md +1 -2
- package/blueprints/security-secrets-management/CHANGELOG.md +15 -4
- package/blueprints/security-secrets-management/README.md +2 -2
- package/blueprints/security-secrets-management/blueprint.json +3 -2
- package/blueprints/security-secrets-management/contributions/probes/add-recipient-rotation.mjs +107 -0
- package/blueprints/security-secrets-management/contributions/probes/encrypt-decrypt-round-trip.mjs +122 -0
- package/blueprints/security-secrets-management/contributions/probes/key-rotation.mjs +93 -0
- package/blueprints/security-secrets-management/contributions/probes/mismatched-key-refusal.mjs +74 -0
- package/blueprints/security-secrets-management/contributions/probes/probe-utils.mjs +214 -0
- package/blueprints/security-secrets-management/contributions/probes/run-add-recipient-rotation.mjs +4 -0
- package/blueprints/security-secrets-management/contributions/probes/run-encrypt-decrypt-round-trip.mjs +4 -0
- package/blueprints/security-secrets-management/contributions/probes/run-key-rotation.mjs +4 -0
- package/blueprints/security-secrets-management/contributions/probes/run-mismatched-key-refusal.mjs +4 -0
- package/blueprints/security-secrets-management/docs/topics.md +2 -3
- package/fixtures/canary-manifest.json +9 -9
- package/guidance/harness-template.md +25 -0
- package/guidance/managed/agent-instructions-block.hash +1 -1
- package/guidance/managed/agent-instructions-block.md +25 -0
- package/package.json +2 -2
- package/rcf/adrs/adr-4101-feedback-store-gitignored.json +20 -0
- package/rcf/adrs/adr-4102-fingerprint-and-labels.json +25 -0
- package/rcf/adrs/adr-4103-redaction-rule-based.json +20 -0
- package/rcf/adrs/adr-4104-destination-on-library.json +25 -0
- package/rcf/adrs/adr-4105-gh-ambient-auth.json +20 -0
- package/rcf/adrs/adr-4106-bundled-entries-terminal.json +20 -0
- package/rcf/adrs/adr-4107-stop-is-ask-event.json +30 -0
- package/rcf/adrs/adr-4108-hooks-in-committed-config.json +25 -0
- package/rcf/adrs/adr-4109-referee-validate-adr-crosslinks.json +25 -0
- package/rcf/adrs/adr-4110-referee-coverage-strict-by-default.json +25 -0
- package/rcf/adrs/adr-4111-referee-finalise-refusals.json +20 -0
- package/rcf/adrs/adr-4112-referee-finalise-verified-invariant.json +30 -0
- package/rcf/code-nodes/cn-600.json +45 -0
- package/rcf/code-nodes/cn-601.json +18 -0
- package/rcf/code-nodes/cn-602.json +15 -0
- package/rcf/code-nodes/cn-603.json +12 -0
- package/rcf/code-nodes/cn-604.json +14 -0
- package/rcf/code-nodes/cn-605.json +17 -0
- package/rcf/code-nodes/cn-606.json +12 -0
- package/rcf/code-nodes/cn-607.json +12 -0
- package/rcf/code-nodes/cn-608.json +14 -0
- package/rcf/code-nodes/cn-609.json +17 -0
- package/rcf/code-nodes/cn-620.json +18 -0
- package/rcf/code-nodes/cn-621.json +14 -0
- package/rcf/code-nodes/cn-622.json +15 -0
- package/rcf/code-nodes/cn-623.json +16 -0
- package/rcf/code-nodes/cn-624.json +14 -0
- package/rcf/code-nodes/cn-625.json +15 -0
- package/rcf/code-nodes/cn-626.json +13 -0
- package/rcf/code-nodes/cn-627.json +13 -0
- package/rcf/code-nodes/cn-628.json +14 -0
- package/rcf/code-nodes/cn-629.json +12 -0
- package/rcf/code-nodes/cn-630.json +12 -0
- package/rcf/code-nodes/cn-631.json +13 -0
- package/rcf/code-nodes/cn-632.json +13 -0
- package/rcf/fbs/fbs-180.json +45 -0
- package/rcf/fbs/fbs-181.json +45 -0
- package/rcf/fbs/fbs-182.json +42 -0
- package/rcf/fbs/fbs-183.json +50 -0
- package/rcf/fbs/fbs-184.json +49 -0
- package/rcf/fbs/fbs-185.json +34 -0
- package/rcf/fbs/fbs-186.json +16 -0
- package/rcf/fbs/fbs-187.json +17 -0
- package/rcf/fbs/fbs-188.json +17 -0
- package/rcf/fbs/fbs-189.json +17 -0
- package/rcf/requirements/req-155.json +13 -0
- package/rcf/requirements/req-156.json +13 -0
- package/rcf/requirements/req-157.json +13 -0
- package/rcf/requirements/req-158.json +13 -0
- package/rcf/requirements/req-159.json +13 -0
- package/rcf/requirements/req-160.json +13 -0
- package/rcf/requirements/req-161.json +13 -0
- package/rcf/requirements/req-162.json +13 -0
- package/rcf/requirements/req-163.json +13 -0
- package/rcf/requirements/req-164.json +13 -0
- package/rcf/requirements/req-165.json +13 -0
- package/rcf/tacs/tac-4101-feedback-cli.json +77 -0
- package/rcf/tacs/tac-4102-feedback-store.json +50 -0
- package/rcf/tacs/tac-4103-feedback-redact.json +39 -0
- package/rcf/tacs/tac-4104-feedback-fingerprint.json +33 -0
- package/rcf/tacs/tac-4105-feedback-render.json +45 -0
- package/rcf/tacs/tac-4106-feedback-destination.json +39 -0
- package/rcf/tacs/tac-4107-feedback-gh.json +43 -0
- package/rcf/tacs/tac-4108-feedback-hook.json +46 -0
- package/rcf/tacs/tac-4109-referee-validate-adr-crosslinks.json +20 -0
- package/rcf/tacs/tac-4110-referee-coverage-mode-selector.json +21 -0
- package/rcf/tacs/tac-4111-referee-finalise-per-ac-refusals.json +21 -0
- package/rcf/tacs/tac-4112-referee-finalise-verified-invariant.json +22 -0
- package/rcf/test-suites/ts-140.json +9 -9
- package/rcf/test-suites/ts-150.json +9 -9
- package/rcf/test-suites/ts-185.json +54 -0
- package/rcf/test-suites/ts-186.json +58 -0
- package/rcf/test-suites/ts-187.json +44 -0
- package/rcf/test-suites/ts-188.json +166 -0
- package/rcf/test-suites/ts-189.json +38 -0
- package/rcf/test-suites/ts-190.json +30 -0
- package/rcf/test-suites/ts-191.json +51 -0
- package/rcf/test-suites/ts-192.json +37 -0
- package/rcf/test-suites/ts-193.json +60 -0
- package/rcf/test-suites/ts-194.json +67 -0
- package/rcf/test-suites/ts-195.json +52 -0
- package/rcf/test-suites/ts-196.json +58 -0
- package/rcf/test-suites/ts-197.json +66 -0
- package/rcf/test-suites/ts-198.json +87 -0
- package/rcf/test-suites/ts-199.json +51 -0
- package/rcf/test-suites/ts-200.json +30 -0
- package/rcf/test-suites/ts-201.json +38 -0
- package/rcf/test-suites/ts-202.json +38 -0
- package/rcf/test-suites/ts-203.json +38 -0
- package/rcf/user-stories/us-15501.json +60 -0
- package/rcf/user-stories/us-15502.json +33 -0
- package/rcf/user-stories/us-15503.json +33 -0
- package/rcf/user-stories/us-15601.json +51 -0
- package/rcf/user-stories/us-15701.json +42 -0
- package/rcf/user-stories/us-15801.json +42 -0
- package/rcf/user-stories/us-15802.json +33 -0
- package/rcf/user-stories/us-15901.json +33 -0
- package/rcf/user-stories/us-15902.json +51 -0
- package/rcf/user-stories/us-16001.json +51 -0
- package/rcf/user-stories/us-16101.json +42 -0
- package/rcf/user-stories/us-16102.json +33 -0
- package/rcf/user-stories/us-16103.json +42 -0
- package/rcf/user-stories/us-16104.json +42 -0
- package/rcf/user-stories/us-16201.json +33 -0
- package/rcf/user-stories/us-16301.json +36 -0
- package/rcf/user-stories/us-16401.json +45 -0
- package/rcf/user-stories/us-16501.json +45 -0
- package/rcf/user-stories/us-16502.json +45 -0
- package/releases/releases.yaml +11 -1
- package/src/blueprint/library-loader.js +48 -0
- package/src/blueprint/library-registry.js +13 -0
- package/src/browser-verify/pack-runner.js +7 -12
- package/src/build/mark.js +1 -1
- package/src/cli/blueprint-library.js +149 -0
- package/src/cli/coverage.js +49 -24
- package/src/cli/doctor.js +195 -7
- package/src/cli/feedback.js +2204 -0
- package/src/cli/finalise.js +158 -1
- package/src/cli/help.js +6 -0
- package/src/cli/init.js +63 -0
- package/src/cli/update.js +21 -1
- package/src/core/store/walker.js +33 -0
- package/src/core/store/writer.js +23 -0
- package/src/feedback/destination.js +361 -0
- package/src/feedback/fingerprint.js +132 -0
- package/src/feedback/gh.js +376 -0
- package/src/feedback/hook.js +183 -0
- package/src/feedback/labels.js +87 -0
- package/src/feedback/redact-allowlist.json +14 -0
- package/src/feedback/redact.js +1108 -0
- package/src/feedback/render.js +267 -0
- package/src/feedback/store.js +604 -0
- package/src/finalise/index.js +4 -0
- package/src/finalise/ingest.js +61 -0
- package/src/finalise/ship-without-eval.js +19 -7
- package/src/mcp/tools.js +8 -2
- package/src/setup/feedback-hooks.js +397 -0
- package/src/setup/managed-gitignore.js +3 -0
- package/src/view/html-page.js +5 -5
- package/src/view/style.css +2 -2
package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs
CHANGED
|
@@ -1,52 +1,54 @@
|
|
|
1
|
-
// Probe: compose-config-lint.
|
|
1
|
+
// Probe: compose-config-lint (v1.1.8).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
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
|
-
//
|
|
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
|
|
31
|
-
import {
|
|
32
|
-
import { runShim, readCompose, COMPOSE_PATH, FIXTURE_DIR,
|
|
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
|
|
82
|
+
function scanHealthchecks(doc, extra, composeSha256) {
|
|
82
83
|
const results = [];
|
|
83
84
|
const services = doc.services ?? {};
|
|
84
|
-
const
|
|
85
|
-
for (const name of
|
|
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 (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
111
|
-
|
|
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:
|
|
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
|
-
|
|
170
|
+
observedDrivers.push(null);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (!ALLOWED_LOG_DRIVER.has(String(driver))) {
|
|
119
174
|
results.push({
|
|
120
|
-
anchorAcId:
|
|
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
|
|
136
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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:
|
|
266
|
+
anchorAcId: null,
|
|
267
|
+
conformanceOnly: true,
|
|
268
|
+
limitation: LIMIT_HC,
|
|
178
269
|
verdict: 'pass',
|
|
179
|
-
detail:
|
|
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:
|
|
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
|
|
16
|
-
// true
|
|
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 =
|
|
34
|
-
|
|
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
|
|
51
|
-
|
|
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 =
|
|
69
|
-
const
|
|
70
|
-
const
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|