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
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# application-notifications-in-app CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.2.8 - 2026-09-12
|
|
4
|
+
|
|
5
|
+
- Fixture centre now carries the active query-scoped break switch into the served acknowledge form action: `/notifications-centre?break=ack` renders each per-item form with `action="/actions/acknowledge-notification?notification-id=<id>&break=ack"`, so a JS-off native form submission originating from a broken centre reaches the same broken handler branch as the API and cannot escape the `ack` refusal. Fixture README lists `POST /actions/acknowledge-notification` alongside the JSON API in the routes table and states both refuse under `?break=ack` (or `PROBE_BREAK=ack`), and the `?break=ack` row in the query-switch table now names both HTTP 502 error codes (`ACKNOWLEDGE_ROUND_TRIP_REFUSED` on the JSON API, `ACKNOWLEDGE_FORM_REFUSED` on the form action). The `/actions/acknowledge-notification` route comment corrected: with JS off the native form submission POSTs to the form action, with JS on the client script intercepts the click, calls `preventDefault` on the form, and drives the JSON API instead (previous wording claimed JS-on activation POSTs to the form route). centre-acknowledge-round-trip probe wording ("next principal load"), row detail ("next principal load"), and the 1.2.7 CHANGELOG line ("next principal load") now say "next served load of the centre" - the fixture has no notification-principal key so calling an ordinary refetch a principal load misdescribed the observation.
|
|
6
|
+
|
|
7
|
+
## 1.2.7 - 2026-09-12
|
|
8
|
+
|
|
9
|
+
- centre-acknowledge-round-trip AC-20103-1 positive row now observes activation causality through a served progressive-enhancement form action, not a bare API call. Fixture centre wraps each acknowledge button in `<form method="post" action="/actions/acknowledge-notification">` with the notification-id in a hidden input; a new server route accepts the form-encoded submission, flips the delivery-log `acknowledgedAt`, records both an `acknowledge-server` and an `acknowledge-form` entry on `/__requests`, and returns 200 with the applied `acknowledgedAt`; the JS-on client script now calls `preventDefault()` on the button click so only one POST fires per activation (the JSON API path stays for SPA parity). The probe fetches `/notifications-centre`, verifies the form + hidden input for the picked id, SUBMITS the served form action (content-type `application/x-www-form-urlencoded`), verifies the delivery-log flip and the request-log counts, and refetches `/notifications-centre` to observe `data-acknowledged="true"` on the same article on the next served load of the centre (proof that the served control's activation causes the acknowledged state, not just that a hand-crafted API POST does). The companion null-anchored conformance row now narrows to the browser-only same-page in-place DOM mutation the client script performs after fetch resolves (the pack browser check owns that). Fixture `?break=ack` refuses the form action too (`ACKNOWLEDGE_FORM_REFUSED`, HTTP 502) as well as the JSON API, so the anatomy negative-variant assertion continues to drive the probe to fail under `PROBE_BREAK=ack` on both paths.
|
|
10
|
+
|
|
11
|
+
## 1.2.6 - 2026-09-12
|
|
12
|
+
|
|
13
|
+
- centre-acknowledge-round-trip upgraded from a null-anchored conformance-only row into TWO rows: a POSITIVE row anchored to AC-20103-1 observing the surface enumeration (per-item `[data-notification-id]` wrappers, matching per-item acknowledge and mark-read controls, exactly one mark-all-read control) AND the server-side round-trip (POST `/api/notifications/acknowledge` returns 200 with the echoed `notificationId`, the delivery-log `acknowledgedAt` for the row flips from null to an ISO timestamp, `/__requests` records exactly one `acknowledge-server` entry), plus a companion CONFORMANCE-ONLY row (null anchor, `limitation` naming AC-20103-1) for the browser-only DOM `data-acknowledged="true"` flip after the fetch resolves. Fixture `?break=ack` extended to also refuse the server-side POST with HTTP 502 `ACKNOWLEDGE_ROUND_TRIP_REFUSED`, so the anatomy negative-variant map now drives the centre-acknowledge probe to fail under `PROBE_BREAK=ack` (added to `brokenExpectations`). Toast-contract row split so AC-20102-1 (browser-only priority-to-role mapping, one-shot announcement, elapsed timeout on `data-shown-at`/`data-dismissed-at`) remains `notObservableHere` without any focus-pause language (that clause is AC-20102-4), and a second null-anchored conformance row names AC-20102-4 in the `limitation` for the focus-pause + dismissal-removal properties (browser-only and unimplemented in the fixture); both rows now carry an evidence object from the fixture shell reachability fetch so the rule-7d shape and the evidence tally accept them. Positive-evidence row shape tightened in the anatomy helper: a positive row now requires a non-empty engine-returned `requestId` AND (a non-empty `bodyExcerpt` OR a non-empty `derived` object); a `{derived:{}}` alone or a `requestId` alone no longer counts, with two new negative-case tests covering the empty-derived and id-only patterns.
|
|
14
|
+
|
|
15
|
+
## 1.2.5 - 2026-09-12
|
|
16
|
+
|
|
17
|
+
- Rule-7d shape fix on the centre-acknowledge row: the AC-20103-1 de-claim now carries `anchorAcId: null` and names AC-20103-1 in the `limitation` field per the shipped shape (a `conformanceOnly:true` row must not simultaneously claim an anchor). Fixture README env-var manifest now declares `PROBE_BREAK` (the fixture reads it as an alternate to a per-request `?break=`) and the probe-utils `DECLARED_ENV` list mirrors the addition. Anatomy shape check tightened to reject an anchored `conformanceOnly` row (a positive evidence field no longer bypasses the null-anchor + limitation contract); a new negative-case test constructs an anchored `conformanceOnly` row and asserts the shape helper throws. Product-term usage of "round trip" / "round-trip" (request/response cycle in AC-20103-1 and in the `centre-acknowledge-round-trip` module name) stays as shipped.
|
|
18
|
+
|
|
19
|
+
## 1.2.4 - 2026-09-11
|
|
20
|
+
|
|
21
|
+
- Shared `aggregate()` no longer promotes warn rows to pass: any row with verdict warn (including honest de-claim rows) lifts the aggregate to warn; a probe whose rows are all warn aggregates to warn, not pass. Shared teardown propagates a SIGKILL failure through the returned kill() promise instead of swallowing it. centre-acknowledge AC-20103-1 row de-claimed to `conformanceOnly:true` with `verdict:'warn'` and a `limitation` naming the browser-only clause (control activation and DOM `data-acknowledged` mutation are not observed by a Node HTTP probe). toast-contract row keeps `notObservableHere:true` + `verdict:'warn'`; live-region-preseeding stays as a positive-observation row that fails under `?break=preseed`. Anatomy test extended with a negative-variant assertion that the shipped preseed break switch drives the live-region-preseeding aggregate to fail.
|
|
22
|
+
|
|
23
|
+
## 1.2.3 - 2026-09-11
|
|
24
|
+
|
|
25
|
+
- toast-contract `notObservableHere` row for AC-20102-1 now emits `verdict:'warn'` (previously `pass`); the aggregate correctly reports amber for the browser-only halves of AC-20102-1 (role mapping, elapsed timeout and focus-pause behaviour on `data-shown-at`/`data-dismissed-at`) rather than a false pass. Anatomy test extended to (a) enumerate the shipped AC/REQ id set from `contributions/user-stories/*.json` and refuse any row whose `anchorAcId` or `notObservableAcId` is not in that set, and (b) accept the conformance-only row shape (null anchor + non-empty `limitation`).
|
|
26
|
+
|
|
27
|
+
## 1.2.2 - 2026-09-11
|
|
28
|
+
|
|
29
|
+
- Probes rewritten to observe declared AC properties instead of static constants. live-region-preseeding now enumerates elements by `[data-live-region]` and asserts polite (aria-live="polite", empty) and assertive (role="alert", empty) semantics per AC-20101-1; positive-anchor-on-absence broken-variant row removed. toast-contract row de-claimed to notObservableHere for AC-20102-1 (role mapping, elapsed timeout and focus pausing are client-JS-driven and only observable in a browser); the static timeout-floor constant-echo row is dropped. centre-acknowledge broken-variant shortcut row removed; the positive centre-acknowledge row keeps derived evidence (acknowledgedAt flips null to ISO plus the server-side request log entry). Rule 10 applied to every row detail. Anatomy test extended to accept the notObservableHere row shape (non-empty reason + non-empty anchorAcId) alongside evidence-object and honest-skip. Register cleanup.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## 1.2.1 - 2026-09-11
|
|
33
|
+
|
|
34
|
+
- Added a criterion-e probe pack (`contributions/probes/`) covering 3 properties the fixture engine at `packages/rcf-lite/test/fixtures/probe-pack-application-notifications-in-app/server.js` answers: `live-region-preseeding`, `toast-contract`, `centre-acknowledge-round-trip`. Every response now carries an `x-fixture-request-id` header; probes record the id, the HTTP status and a body excerpt as positive evidence per rule 7d. Fixture README declares every env var the pack reads. Anatomy test at `packages/rcf-lite/test/blueprint/application-notifications-in-app-anatomy.test.js` pins the new pack files and asserts each result carries one of the four 7d evidence shapes.
|
|
35
|
+
|
|
36
|
+
|
|
3
37
|
## 1.2.0 (2026-09-10)
|
|
4
38
|
|
|
5
39
|
### Added
|
|
@@ -19,23 +53,22 @@
|
|
|
19
53
|
|
|
20
54
|
- Register: neutral wording in shipped prose (no capability change).
|
|
21
55
|
|
|
22
|
-
## 1.1.0 (2026-09-10
|
|
56
|
+
## 1.1.0 (2026-09-10)
|
|
23
57
|
|
|
24
58
|
### Added
|
|
25
59
|
|
|
26
|
-
- Failure-path acceptance criteria on US-20101 (live-region wrappers pre-seeded before any state-driven mount), US-20102 (sub-6s timeout apply refusal with TOAST_TIMEOUT_TOO_SHORT, focused-toast pause per WCAG 2.2.1), US-20103 (non-2xx acknowledgement error toast with unchanged data-acknowledged, paginated fetch failure), US-20104 (failed preference write error toast with toggle rollback, idempotency of retried writes), US-20105 (delivery-row incomplete refusal), US-20106 (page-load backlog failure with cached-row banner), US-20107 (invalid toast payload refusal), US-20108 (invalid priority drop).
|
|
60
|
+
- Failure-path acceptance criteria on US-20101 (live-region wrappers pre-seeded before any state-driven mount), US-20102 (sub-6s timeout apply refusal with TOAST_TIMEOUT_TOO_SHORT, focused-toast pause per WCAG 2.2.1), US-20103 (non-2xx acknowledgement error toast with unchanged data-acknowledged, paginated fetch failure), US-20104 (failed preference write error toast with toggle rollback, idempotency of retried writes), US-20105 (delivery-row incomplete refusal), US-20106 (page-load backlog failure with cached-row banner), US-20107 (invalid toast payload refusal), US-20108 (invalid priority drop).
|
|
27
61
|
- Vendor citations on the WCAG 2.2.1 Timing Adjustable and status-messages acceptance criteria (verified 2026-09-10).
|
|
28
62
|
|
|
29
63
|
### Changed
|
|
30
64
|
|
|
31
65
|
- Every REQ now carries a deliveredBy link into TAC-2101 (live-region), TAC-2102 (centre) or TAC-2103 (preferences).
|
|
32
66
|
- Every AC on every user story now carries an explicit disposition (fixed or template); ACs that observe writeDeliveryRow, renderLiveRegions, renderToast or renderCentre carry ownerRef into the owning TAC field.
|
|
33
|
-
- Review fix pass (2026-09-10): README, TAC-2103 and REQ-004 register scrubbed (no operator names or work-item ids). Closes review-fix-pass findings O-2, O-3.
|
|
34
67
|
|
|
35
68
|
## 1.0.0 (visual round, spec 2026-09-04)
|
|
36
69
|
|
|
37
|
-
-
|
|
70
|
+
- Initial shipped version of the shelf's application-notifications-in-app blueprint. Five REQs (live-region preseeding on every declared route; transient toast contract with priority-to-role mapping, one-shot announcement and timeout floor; notification centre inbox with retention and acknowledge round-trip; per-user preferences UI with category silence and sibling delegation; delivery-attempt log with operator-readable rows), eight USs binding runtime-observable ACs plus three cross-cutting cases (page load with backlog, background event, disjoint-content contract), three TACs (live-region wrappers with the toast factory; centre inbox with retention and acknowledge round-trip; preferences UI with sibling delegation), three ADRs (priority-to-role mapping polite/status vs assertive/alert per WCAG 4.1.3 and the ARIA APG alert pattern; six-second toast timeout floor per WCAG 2.2.1 with elicited overrides above the floor; thirty-day centre retention window with elicited overrides). No new global topics.
|
|
38
71
|
- Reserves the `application-notifications-` family prefix for the sibling channel blueprints the shelf will grow into (`-email`, `-push`, `-webhook`) as a doc-only reservation on the blueprint README and every applying blueprint's `docs/topics.md` shelf band registry per the 2026-09-04 shelf decision to make the transport type explicit in the blueprint name.
|
|
39
72
|
- Ships `probe-packs/application-notifications-in-app.pack.mjs`: three browser-verify checks anchored to blueprint AC ids, every check carrying a description field per spec section 9. `AC-20101-1` live-region preseeded on every declared route (wrappers present AND empty at load, enumerated by `[data-live-region]`). `AC-20102-1` transient toast contract with priority-to-role mapping and timeout measured against the ADR-2102 six-second floor via `data-shown-at`/`data-dismissed-at` timestamps (no six-second sleep in the pack; bounded polling). `AC-20103-1` centre acknowledge round-trip enumerated by `data-notification-id` (per-element attribute per gate discipline), reconciled through both `window.__notificationFetches` and the server-side request log. Fourth blueprint on the shelf that ships a Playwright probe pack under the runner extension.
|
|
40
73
|
- Ships a sample-app fixture at `packages/rcf-lite/test/fixtures/probe-pack-application-notifications-in-app/` (dependency-free Node HTTP server) so the pack can be probed at the shelf's own gate; the fixture is the golden the gate reviewer drives, with `?break=preseed`, `?break=role`, `?break=timeout` and `?break=ack` query switches for the negative runs. Three routes (`/`, `/notifications-centre`, `/notifications-preferences`); every route preseeds both live-region wrappers.
|
|
41
|
-
- Suggests the `logging` and `errorHandling` companions with the
|
|
74
|
+
- Suggests the `logging` and `errorHandling` companions with the shipped spec's reasons. `providesRoles` absent, leaf blueprint per the loader contract (an empty array is refused, an omitted field marks the blueprint as a leaf).
|
|
@@ -34,7 +34,7 @@ The `application-notifications-` prefix is reserved for the sibling in-app-adjac
|
|
|
34
34
|
- `application-notifications-push`: Web Push and platform APN/FCM notifications. Owns the subscription lifecycle and the platform-token store.
|
|
35
35
|
- `application-notifications-webhook`: outbound HTTP notifications to operator-configured endpoints. Owns the delivery-retry semantics and the endpoint registry.
|
|
36
36
|
|
|
37
|
-
The reservation is doc-only in v1.0.0
|
|
37
|
+
The reservation is doc-only in v1.0.0: the family prefix is named in this README and in every shipped blueprint's `docs/topics.md` shelf band registry as a reserved-slot row. No sibling channel currently ships. Any proposal for a name outside the family (`application-alerts-email`, `application-messages-push`) reads against this reservation and is refused at author-side check.
|
|
38
38
|
|
|
39
39
|
Digest opt-in on the preferences UI is elicited on the sibling channel, not here: the preferences UI carries a disabled section per sibling channel that is not yet applied, labelled `data-preference-status="sibling not applied"`. When the sibling ships and the applying project applies it, that sibling's own blueprint reads the operator's preference state and drives the digest transport.
|
|
40
40
|
|
|
@@ -47,8 +47,8 @@ Every route the applying project ships renders with both live-region wrappers pr
|
|
|
47
47
|
Every runtime-observable AC that is not bound to a pack check appears here individually per the gate discipline (`blueprint-authoring-checklist.md` section 6.g): categories are not enough. The pack has three checks; every other runtime-observable AC on this blueprint is a mechanism-reach gap named below.
|
|
48
48
|
|
|
49
49
|
- **AC-20101-2 live-region factory identity stable across route changes**. The pack asserts the wrappers are preseeded on every declared route (AC-20101-1); it does not assert the two wrappers are the same DOM nodes across route changes (a factory that recreates the wrappers on route change passes AC-20101-1 but fails AC-20101-2). A v1.1 minor bump could persist a `data-live-region-id` on each wrapper and assert identity across a `browser.click` on a nav link.
|
|
50
|
-
- **AC-20102-2 toast factory library-scope shape**. The pack drives the runtime toast surface (AC-20102-1); the library-scope AC on the factory's own signature is a project-side
|
|
51
|
-
- **AC-20103-2 centre factory reads through the delivery-log endpoint**. The pack drives the item enumeration and the acknowledge round-trip (AC-20103-1); the library-scope AC on the factory's own delivery-log read is a project-side
|
|
50
|
+
- **AC-20102-2 toast factory library-scope shape**. The pack drives the runtime toast surface (AC-20102-1); the library-scope AC on the factory's own signature is a project-side check concern.
|
|
51
|
+
- **AC-20103-2 centre factory reads through the delivery-log endpoint**. The pack drives the item enumeration and the acknowledge round-trip (AC-20103-1); the library-scope AC on the factory's own delivery-log read is a project-side check concern.
|
|
52
52
|
- **AC-20104-1 preferences category silence**. The preferences surface renders per-category toggles but the pack does not itself activate a silence toggle and assert the round-trip today. A v1.1 minor bump could add a preferences-round-trip check paralleling AC-20103-1.
|
|
53
53
|
- **AC-20104-2 sibling-channel digest section disabled when no sibling applied**. The fixture renders the disabled sections and the pack could inspect them, but the AC's real bite is when a sibling channel ships and the disabled state must lift; this class stays a v1.1 minor bump candidate for when the first sibling ships.
|
|
54
54
|
- **AC-20105-1 delivery-log endpoint shape**. The pack does not enumerate every row on the log (the delivery-log's persistence is delegated to the applied `logging` companion). A v1.1 minor bump could add a log-shape probe that reads `/api/delivery-log`, asserts the row shape and confirms append-only order.
|
|
@@ -74,7 +74,7 @@ WCAG 4.1.3 Status Messages on the live-region contract (REQ-001, ADR-2101). ARIA
|
|
|
74
74
|
|
|
75
75
|
## Consumers and dependencies
|
|
76
76
|
|
|
77
|
-
- Consumes the applied `logging` companion (or the shelf provider `observability-logging`) for the delivery-attempt log write path (REQ-005, TAC-2102) and every acknowledge round-trip record. A project without a `logging` companion applied sees the delivery-log endpoint returning an empty rows array, which fails project-side
|
|
77
|
+
- Consumes the applied `logging` companion (or the shelf provider `observability-logging`) for the delivery-attempt log write path (REQ-005, TAC-2102) and every acknowledge round-trip record. A project without a `logging` companion applied sees the delivery-log endpoint returning an empty rows array, which fails project-side check.
|
|
78
78
|
- Consumes the applied `errorHandling` companion (or the shelf provider `application-error-handling`) for the assertive-slot alert's internal error record when an acknowledge round-trip fails at the server.
|
|
79
79
|
- Reserves the `application-notifications-` family prefix for sibling channel blueprints (see above).
|
|
80
80
|
- Reuses the pack-browser `resize(width, height)` seam only for future minor bumps; v1.0.0 does not exercise breakpoint reflow in the pack.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"slug": "application-notifications-in-app",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.8",
|
|
4
4
|
"category": "application",
|
|
5
5
|
"suggestedCompanions": [
|
|
6
6
|
{
|
|
@@ -154,5 +154,7 @@
|
|
|
154
154
|
"kind": "string",
|
|
155
155
|
"default": "20"
|
|
156
156
|
}
|
|
157
|
-
]
|
|
157
|
+
],
|
|
158
|
+
"updatedAt": "2026-09-12T02:05:15Z",
|
|
159
|
+
"updatedIso": "2026-09-11T22:30:00Z"
|
|
158
160
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"status": "accepted",
|
|
7
7
|
"title": "Map toast priority to live-region wrapper and ARIA role from a fixed two-value enum: info to polite plus status, error to assertive plus alert",
|
|
8
8
|
"context": "A transient toast needs to reach a screen reader on the queue that matches the reader's attention regime: an informational confirmation belongs on the polite queue, an error interruption belongs on the assertive queue. Free-form role assignment lets a well-meaning implementation route an error into the polite wrapper (the reader misses the interruption) or route an informational message into the assertive wrapper (the reader is interrupted for a confirmation). The WCAG 4.1.3 Status Messages clause and the ARIA APG alert pattern name the correct mapping; the problem is turning that guidance into a runtime discipline the pack can drive.",
|
|
9
|
-
"decision": "Toast priority is a two-value enum: `info` and `error`. `info` renders inside the polite wrapper (`[aria-live=\"polite\"] [data-live-region=\"polite\"]`) and carries `role=\"status\"` on the toast element. `error` renders inside the assertive wrapper (`[role=\"alert\"] [data-live-region=\"assertive\"]`) and carries `role=\"alert\"` on the toast element. A priority outside the enum is refused at project-side
|
|
9
|
+
"decision": "Toast priority is a two-value enum: `info` and `error`. `info` renders inside the polite wrapper (`[aria-live=\"polite\"] [data-live-region=\"polite\"]`) and carries `role=\"status\"` on the toast element. `error` renders inside the assertive wrapper (`[role=\"alert\"] [data-live-region=\"assertive\"]`) and carries `role=\"alert\"` on the toast element. A priority outside the enum is refused at project-side check. The pack asserts the priority-to-role mapping on both branches and asserts the disjoint-content rule (a body text in exactly one wrapper).",
|
|
10
10
|
"consequences": "Every toast maps to a queue readers already understand; the reader hears an interruption exactly when the application asserts one. The two-value enum is honest about the shipped shelf's known cases; a third priority (`warning`) would land as a v1.1 minor bump with a shelf-wide review because it changes the role vocabulary. The disjoint-content rule keeps a double-post defect visible at ship rather than at incident.",
|
|
11
11
|
"alternativesConsidered": [
|
|
12
12
|
{
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// application-notifications-in-app probe: centre-acknowledge-round-trip
|
|
2
|
+
// (AC-20103-1).
|
|
3
|
+
//
|
|
4
|
+
// AC-20103-1 first eight words:
|
|
5
|
+
// "Given the notification centre route with a seeded".
|
|
6
|
+
//
|
|
7
|
+
// AC-20103-1 has three clauses:
|
|
8
|
+
// 1. Surface enumeration: /notifications-centre lists notifications
|
|
9
|
+
// with per-item [data-notification-id], [data-action="acknowledge"]
|
|
10
|
+
// and [data-action="mark-read"] controls, plus exactly one
|
|
11
|
+
// [data-action="mark-all-read"] control.
|
|
12
|
+
// 2. Activation causes a POST: activating the per-item acknowledge
|
|
13
|
+
// control POSTs to the acknowledge endpoint; the server responds
|
|
14
|
+
// 200 { ok:true } and the delivery log's acknowledgedAt for the
|
|
15
|
+
// row flips from null to an ISO timestamp; the round-trip is
|
|
16
|
+
// recorded on the server request log.
|
|
17
|
+
// 3. Post-response DOM update on the CURRENT page: the item's
|
|
18
|
+
// data-acknowledged reads "true" after the response (client-side
|
|
19
|
+
// DOM mutation without a page reload).
|
|
20
|
+
//
|
|
21
|
+
// Clauses 1 and 2 are server-observable via the shipped
|
|
22
|
+
// progressive-enhancement form action: the centre wraps each
|
|
23
|
+
// acknowledge button in a <form method="post"
|
|
24
|
+
// action="/actions/acknowledge-notification"> with the notification-id
|
|
25
|
+
// in a hidden input, so a Node HTTP probe can fetch the served page,
|
|
26
|
+
// find the form action, SUBMIT that form action, and observe both the
|
|
27
|
+
// server-side flip (delivery-log acknowledgedAt) and the next served
|
|
28
|
+
// centre page rendering data-acknowledged="true" on the same article
|
|
29
|
+
// (proof that the served control's activation causes the acknowledged
|
|
30
|
+
// state, not just that a bare API POST does). Clause 3 (the
|
|
31
|
+
// same-page DOM mutation triggered by the JS-on client script) is
|
|
32
|
+
// browser-only and covered as a null-anchored conformance de-claim.
|
|
33
|
+
//
|
|
34
|
+
// This probe emits TWO rows:
|
|
35
|
+
// - Row A: POSITIVE anchor to AC-20103-1 covering clauses 1 and 2
|
|
36
|
+
// (surface enumeration + activation-of-the-served-control causing
|
|
37
|
+
// the server-side round-trip and the served acknowledged state on
|
|
38
|
+
// the next centre fetch). It picks a notificationId derived from
|
|
39
|
+
// the centre HTML, verifies the article carries a form whose
|
|
40
|
+
// action points at /actions/acknowledge-notification with the
|
|
41
|
+
// principal-id in a hidden input, SUBMITS that form action, and
|
|
42
|
+
// asserts derived outputs the fixture had no choice about (the
|
|
43
|
+
// delivery-log acknowledgedAt flipping from null to an ISO
|
|
44
|
+
// timestamp for THAT id, the server request log carrying exactly
|
|
45
|
+
// one acknowledge-server entry and one acknowledge-form entry for
|
|
46
|
+
// that id, and the next-load centre HTML rendering
|
|
47
|
+
// data-acknowledged="true" on that same article).
|
|
48
|
+
// - Row B: CONFORMANCE-ONLY (null anchor) naming AC-20103-1 in the
|
|
49
|
+
// limitation for the browser-only same-page DOM flip (the client
|
|
50
|
+
// script's fetch-then-setAttribute mutation happens post-response
|
|
51
|
+
// without a reload; deferred to the browser check).
|
|
52
|
+
|
|
53
|
+
import { fixtureFetch, startFixture, excerpt } from './probe-utils.mjs';
|
|
54
|
+
|
|
55
|
+
export const anchorAcId = 'application-notifications-in-app-AC-20103-1';
|
|
56
|
+
export const accountBound = false;
|
|
57
|
+
|
|
58
|
+
const FIRST_EIGHT = 'Given the notification centre route with a seeded';
|
|
59
|
+
|
|
60
|
+
export default async function runProbe() {
|
|
61
|
+
const fixture = await startFixture();
|
|
62
|
+
const results = [];
|
|
63
|
+
try {
|
|
64
|
+
const centre = await fixtureFetch(fixture.url, '/notifications-centre');
|
|
65
|
+
// Strip inline <script> blocks so the DOM-shape enumeration below
|
|
66
|
+
// does not match literal attribute strings that live inside the
|
|
67
|
+
// client script (for example a `data-notification-id="' +
|
|
68
|
+
// notificationId + '"` template string or an
|
|
69
|
+
// `[data-action="mark-all-read"]` selector inside JS).
|
|
70
|
+
const centreDom = centre.body.replace(/<script\b[\s\S]*?<\/script>/gi, '');
|
|
71
|
+
const ids = Array.from(centreDom.matchAll(/data-notification-id="([^"]+)"/g)).map((m) => m[1]);
|
|
72
|
+
const uniqueIds = Array.from(new Set(ids));
|
|
73
|
+
const notificationId = uniqueIds[0] ?? null;
|
|
74
|
+
const perItemAckControls = Array.from(centreDom.matchAll(/<button[^>]*data-action="acknowledge"[^>]*data-notification-id="([^"]+)"/g)).map((m) => m[1]);
|
|
75
|
+
const markReadControls = Array.from(centreDom.matchAll(/<button[^>]*data-action="mark-read"[^>]*data-notification-id="([^"]+)"/g)).map((m) => m[1]);
|
|
76
|
+
const markAllReadCount = (centreDom.match(/<button[^>]*data-action="mark-all-read"/g) || []).length;
|
|
77
|
+
// Find the acknowledge form wrapping the picked notificationId's
|
|
78
|
+
// button. The form action is the observable activation surface
|
|
79
|
+
// (JS-off progressive enhancement); the hidden input carries the
|
|
80
|
+
// notification-id so the POST body identifies the row without
|
|
81
|
+
// relying on the query string.
|
|
82
|
+
const ackFormMatch = notificationId
|
|
83
|
+
? centreDom.match(new RegExp('<form[^>]*method="post"[^>]*action="([^"]*\\/actions\\/acknowledge-notification[^"]*)"[^>]*data-notification-id="' + notificationId + '"[^>]*>[\\s\\S]*?<input[^>]*type="hidden"[^>]*name="notification-id"[^>]*value="' + notificationId + '"[\\s\\S]*?<button[^>]*data-action="acknowledge"[^>]*data-notification-id="' + notificationId + '"[\\s\\S]*?<\\/form>', 'i'))
|
|
84
|
+
: null;
|
|
85
|
+
const ackFormAction = ackFormMatch ? ackFormMatch[1] : null;
|
|
86
|
+
const ackFormPresent = !!ackFormMatch && !!ackFormAction;
|
|
87
|
+
const surfaceOk = centre.status === 200 && !!centre.requestId
|
|
88
|
+
&& uniqueIds.length >= 1
|
|
89
|
+
&& perItemAckControls.length === uniqueIds.length
|
|
90
|
+
&& markReadControls.length === uniqueIds.length
|
|
91
|
+
&& markAllReadCount === 1
|
|
92
|
+
&& ackFormPresent;
|
|
93
|
+
|
|
94
|
+
const beforeLog = await fixtureFetch(fixture.url, '/api/delivery-log');
|
|
95
|
+
let beforeRow = null;
|
|
96
|
+
try { beforeRow = (JSON.parse(beforeLog.body).rows || []).find((r) => r.notificationId === notificationId) || null; } catch (_) { /* parse */ }
|
|
97
|
+
|
|
98
|
+
// Submit the served form action as a native form submission would:
|
|
99
|
+
// a POST with content-type application/x-www-form-urlencoded and
|
|
100
|
+
// the hidden input's name=value pair in the body. This proves
|
|
101
|
+
// activation causality: the served form action is what caused the
|
|
102
|
+
// server-side acknowledge, not a hand-crafted API call.
|
|
103
|
+
const formBody = 'notification-id=' + encodeURIComponent(notificationId ?? '');
|
|
104
|
+
const ack = await fixtureFetch(fixture.url, ackFormAction ?? '/actions/acknowledge-notification', {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
107
|
+
body: formBody,
|
|
108
|
+
});
|
|
109
|
+
let ackBody = null;
|
|
110
|
+
try { ackBody = JSON.parse(ack.body); } catch (_) { /* parse */ }
|
|
111
|
+
|
|
112
|
+
const afterLog = await fixtureFetch(fixture.url, '/api/delivery-log');
|
|
113
|
+
let afterRow = null;
|
|
114
|
+
try { afterRow = (JSON.parse(afterLog.body).rows || []).find((r) => r.notificationId === notificationId) || null; } catch (_) { /* parse */ }
|
|
115
|
+
|
|
116
|
+
// Refetch the served centre page: the SAME article should now render
|
|
117
|
+
// data-acknowledged="true" (proof that activation of the served
|
|
118
|
+
// control produces the acknowledged state on the next served load
|
|
119
|
+
// of the centre - the AC's "the item's data-acknowledged reads true
|
|
120
|
+
// after the response" clause, observed via the server-rendered
|
|
121
|
+
// next-load rather than the browser-only same-page DOM mutation).
|
|
122
|
+
const centreAfter = await fixtureFetch(fixture.url, '/notifications-centre');
|
|
123
|
+
const centreAfterDom = centreAfter.body.replace(/<script\b[\s\S]*?<\/script>/gi, '');
|
|
124
|
+
const acknowledgedArticleMatch = notificationId
|
|
125
|
+
? centreAfterDom.match(new RegExp('<article[^>]*data-notification-id="' + notificationId + '"[^>]*data-acknowledged="([^"]+)"'))
|
|
126
|
+
: null;
|
|
127
|
+
const nextLoadAcknowledgedAttr = acknowledgedArticleMatch ? acknowledgedArticleMatch[1] : null;
|
|
128
|
+
|
|
129
|
+
const requestsAfter = await fixtureFetch(fixture.url, '/__requests');
|
|
130
|
+
let requestEntries = [];
|
|
131
|
+
try { requestEntries = JSON.parse(requestsAfter.body); } catch (_) { /* parse */ }
|
|
132
|
+
const ackServerLogs = requestEntries.filter((e) => e.kind === 'acknowledge-server' && e.notificationId === notificationId);
|
|
133
|
+
const ackFormLogs = requestEntries.filter((e) => e.kind === 'acknowledge-form' && e.notificationId === notificationId);
|
|
134
|
+
|
|
135
|
+
const roundTripPass = ack.status === 200
|
|
136
|
+
&& !!ack.requestId
|
|
137
|
+
&& ackBody && ackBody.ok === true && ackBody.notificationId === notificationId
|
|
138
|
+
&& typeof ackBody.acknowledgedAt === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(ackBody.acknowledgedAt)
|
|
139
|
+
&& !!beforeRow && beforeRow.acknowledgedAt === null
|
|
140
|
+
&& !!afterRow && typeof afterRow.acknowledgedAt === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(afterRow.acknowledgedAt)
|
|
141
|
+
&& ackServerLogs.length === 1
|
|
142
|
+
&& ackFormLogs.length === 1
|
|
143
|
+
&& nextLoadAcknowledgedAttr === 'true';
|
|
144
|
+
|
|
145
|
+
const positivePass = surfaceOk && roundTripPass;
|
|
146
|
+
|
|
147
|
+
results.push({
|
|
148
|
+
anchorAcId,
|
|
149
|
+
verdict: positivePass ? 'pass' : 'fail',
|
|
150
|
+
detail: positivePass
|
|
151
|
+
? `${FIRST_EIGHT} backlog, /notifications-centre enumerated ${uniqueIds.length} [data-notification-id] wrappers with matching per-item acknowledge and mark-read controls and exactly one mark-all-read control; the acknowledge control for notificationId=${notificationId} was wrapped in a <form method="post" action="${ackFormAction}"> with the notification-id in a hidden input; submitting that served form action (content-type application/x-www-form-urlencoded, body notification-id=${notificationId}) returned 200 { ok:true, notificationId, acknowledgedAt:${ackBody?.acknowledgedAt} }; the delivery-log acknowledgedAt for that id flipped from null to ${afterRow?.acknowledgedAt}; the /__requests log recorded exactly one acknowledge-server entry AND one acknowledge-form entry for that id; refetching /notifications-centre rendered data-acknowledged="true" on the same article (proof that activation of the served control causes the acknowledged state on the next served load of the centre); x-fixture-request-id on the form POST=${ack.requestId}`
|
|
152
|
+
: `${FIRST_EIGHT} backlog, evidence gap: surfaceOk=${surfaceOk} (uniqueIds=${uniqueIds.length} perItemAck=${perItemAckControls.length} markRead=${markReadControls.length} markAllRead=${markAllReadCount} ackFormPresent=${ackFormPresent}) notificationId=${notificationId} ackFormAction=${ackFormAction} ackStatus=${ack.status} ackBody=${JSON.stringify(ackBody)} beforeAck=${beforeRow ? beforeRow.acknowledgedAt : 'missing'} afterAck=${afterRow ? afterRow.acknowledgedAt : 'missing'} ackServerLogs=${ackServerLogs.length} ackFormLogs=${ackFormLogs.length} nextLoadAcknowledgedAttr=${nextLoadAcknowledgedAttr}`,
|
|
153
|
+
evidence: {
|
|
154
|
+
requestId: ack.requestId,
|
|
155
|
+
responseStatus: ack.status,
|
|
156
|
+
bodyExcerpt: excerpt(ack.body),
|
|
157
|
+
derived: {
|
|
158
|
+
notificationId,
|
|
159
|
+
uniqueIdCount: uniqueIds.length,
|
|
160
|
+
perItemAckControlCount: perItemAckControls.length,
|
|
161
|
+
markReadControlCount: markReadControls.length,
|
|
162
|
+
markAllReadCount,
|
|
163
|
+
ackFormPresent,
|
|
164
|
+
ackFormAction,
|
|
165
|
+
beforeAck: beforeRow ? beforeRow.acknowledgedAt : null,
|
|
166
|
+
afterAck: afterRow ? afterRow.acknowledgedAt : null,
|
|
167
|
+
ackServerLogCount: ackServerLogs.length,
|
|
168
|
+
ackFormLogCount: ackFormLogs.length,
|
|
169
|
+
nextLoadAcknowledgedAttr,
|
|
170
|
+
surfaceOk,
|
|
171
|
+
roundTripPass,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Conformance-only companion row: the same-page DOM
|
|
177
|
+
// data-acknowledged flip that AC-20103-1 requires ("the item's
|
|
178
|
+
// data-acknowledged reads true after the response") when the JS
|
|
179
|
+
// client script mutates the article element in place after the
|
|
180
|
+
// fetch resolves (no page reload) is browser-only; the Node HTTP
|
|
181
|
+
// probe cannot observe the client script's in-place DOM mutation.
|
|
182
|
+
// The positive row above already covers the served next-load
|
|
183
|
+
// acknowledged state (the server-observable equivalent); this
|
|
184
|
+
// de-claim narrows to the client-side in-place mutation the
|
|
185
|
+
// browser check owns. Recorded as a null-anchored conformance
|
|
186
|
+
// de-claim per the rule-7d shape.
|
|
187
|
+
const centreArticleExcerpt = (centreDom.match(new RegExp(`<article[^>]*data-notification-id="${notificationId}"[^]{0,240}`)) || [''])[0];
|
|
188
|
+
results.push({
|
|
189
|
+
anchorAcId: null,
|
|
190
|
+
conformanceOnly: true,
|
|
191
|
+
limitation: 'application-notifications-in-app-AC-20103-1: the client-side, same-page DOM data-acknowledged="true" flip on the /notifications-centre article (the fetch()-then-setAttribute mutation the JS-on client script performs without a page reload) is browser-only. The positive row above covers the served next-load equivalent (activation of the served form action produces data-acknowledged="true" on the same article on the next /notifications-centre fetch). The in-place same-page DOM mutation is deferred to the pack browser check.',
|
|
192
|
+
verdict: 'warn',
|
|
193
|
+
detail: `${FIRST_EIGHT} backlog, the client-side same-page DOM data-acknowledged="true" flip on the notification article after the fetch resolves (without a page reload) is browser-only; recorded as a null-anchored conformance de-claim so the AC-20103-1 in-place-mutation clause the positive row does not cover is on the record; fixture centre reachability confirmed via the same /notifications-centre fetch (x-fixture-request-id=${centre.requestId}).`,
|
|
194
|
+
evidence: {
|
|
195
|
+
requestId: centre.requestId,
|
|
196
|
+
responseStatus: centre.status,
|
|
197
|
+
bodyExcerpt: excerpt(centreArticleExcerpt),
|
|
198
|
+
derived: {
|
|
199
|
+
notificationId,
|
|
200
|
+
articleServerRenderedAcknowledgedAttr: (centreArticleExcerpt.match(/data-acknowledged="([^"]+)"/) || [null, null])[1],
|
|
201
|
+
inPlaceDomFlipObserved: false,
|
|
202
|
+
reason: 'browser-only in-place DOM mutation clause of AC-20103-1',
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
} finally {
|
|
207
|
+
await fixture.kill();
|
|
208
|
+
}
|
|
209
|
+
return { results };
|
|
210
|
+
}
|
package/blueprints/application-notifications-in-app/contributions/probes/live-region-preseeding.mjs
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// application-notifications-in-app probe: live-region preseeding
|
|
2
|
+
// (AC-20101-1). Boots the fixture, GETs the shell root and the two
|
|
3
|
+
// nested surfaces (/notifications-centre, /notifications-preferences),
|
|
4
|
+
// enumerates elements by the per-element attribute [data-live-region]
|
|
5
|
+
// (per AC-20101-1 wording), and observes the polite/assertive semantics
|
|
6
|
+
// on each preseeded wrapper.
|
|
7
|
+
//
|
|
8
|
+
// AC-20101-1 requires BOTH wrappers to be present at page load on
|
|
9
|
+
// every declared route, enumerated by [data-live-region]:
|
|
10
|
+
// the polite wrapper carries aria-live="polite" AND
|
|
11
|
+
// data-live-region="polite" AND is empty (no descendant text nodes);
|
|
12
|
+
// the assertive wrapper carries role="alert" AND
|
|
13
|
+
// data-live-region="assertive" AND is empty.
|
|
14
|
+
//
|
|
15
|
+
// A broken-variant absence row is deliberately not emitted
|
|
16
|
+
// (positive-anchor rule: broken-variant rows must not be
|
|
17
|
+
// positive-anchored on absence, removed here per the charts pattern).
|
|
18
|
+
|
|
19
|
+
import { fixtureFetch, startFixture, excerpt } from './probe-utils.mjs';
|
|
20
|
+
|
|
21
|
+
export const anchorAcId = 'application-notifications-in-app-AC-20101-1';
|
|
22
|
+
export const accountBound = false;
|
|
23
|
+
|
|
24
|
+
// Enumerate every element that carries a data-live-region attribute
|
|
25
|
+
// in the served HTML. Returns per-element observed semantics.
|
|
26
|
+
function enumerateLiveRegions(body) {
|
|
27
|
+
const rx = /<([a-z][a-z0-9]*)\b([^>]*\bdata-live-region="([^"]+)"[^>]*)>([\s\S]*?)<\/\1>/gi;
|
|
28
|
+
const out = [];
|
|
29
|
+
let m;
|
|
30
|
+
while ((m = rx.exec(body)) !== null) {
|
|
31
|
+
const attrs = m[2];
|
|
32
|
+
const kind = m[3];
|
|
33
|
+
const inner = m[4];
|
|
34
|
+
const ariaLiveMatch = /\baria-live="([^"]+)"/.exec(attrs);
|
|
35
|
+
const roleMatch = /\brole="([^"]+)"/.exec(attrs);
|
|
36
|
+
out.push({
|
|
37
|
+
kind,
|
|
38
|
+
empty: inner.length === 0,
|
|
39
|
+
ariaLive: ariaLiveMatch ? ariaLiveMatch[1] : null,
|
|
40
|
+
role: roleMatch ? roleMatch[1] : null,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function observePoliteAssertive(elements) {
|
|
47
|
+
const polite = elements.find((e) => e.kind === 'polite') || null;
|
|
48
|
+
const assertive = elements.find((e) => e.kind === 'assertive') || null;
|
|
49
|
+
const politeOk = !!polite && polite.ariaLive === 'polite' && polite.empty === true;
|
|
50
|
+
const assertiveOk = !!assertive && assertive.role === 'alert' && assertive.empty === true;
|
|
51
|
+
return { polite, assertive, politeOk, assertiveOk };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default async function runProbe() {
|
|
55
|
+
const fixture = await startFixture();
|
|
56
|
+
const results = [];
|
|
57
|
+
try {
|
|
58
|
+
const routes = ['/', '/notifications-centre', '/notifications-preferences'];
|
|
59
|
+
for (const route of routes) {
|
|
60
|
+
const r = await fixtureFetch(fixture.url, route);
|
|
61
|
+
const elements = enumerateLiveRegions(r.body);
|
|
62
|
+
const { polite, assertive, politeOk, assertiveOk } = observePoliteAssertive(elements);
|
|
63
|
+
const pass = r.status === 200 && !!r.requestId && politeOk && assertiveOk;
|
|
64
|
+
const politeShape = polite
|
|
65
|
+
? `ariaLive=${polite.ariaLive} dataLiveRegion=polite empty=${polite.empty}`
|
|
66
|
+
: 'missing';
|
|
67
|
+
const assertiveShape = assertive
|
|
68
|
+
? `role=${assertive.role} dataLiveRegion=assertive empty=${assertive.empty}`
|
|
69
|
+
: 'missing';
|
|
70
|
+
results.push({
|
|
71
|
+
anchorAcId,
|
|
72
|
+
verdict: pass ? 'pass' : 'fail',
|
|
73
|
+
detail: pass
|
|
74
|
+
? `Given a page load on any declared route; observed on GET ${route}: enumerated by [data-live-region] found ${elements.length} elements; polite {${politeShape}}; assertive {${assertiveShape}}; x-fixture-request-id=${r.requestId}`
|
|
75
|
+
: `Given a page load on any declared route; evidence gap on GET ${route}: status=${r.status} rid=${r.requestId} polite {${politeShape}} assertive {${assertiveShape}}`,
|
|
76
|
+
evidence: {
|
|
77
|
+
requestId: r.requestId,
|
|
78
|
+
responseStatus: r.status,
|
|
79
|
+
bodyExcerpt: excerpt((r.body.match(/<[^>]+data-live-region="polite"[^>]*>[\s\S]*?<\/[a-z]+>/i) || [''])[0]),
|
|
80
|
+
derived: {
|
|
81
|
+
route,
|
|
82
|
+
elementCount: elements.length,
|
|
83
|
+
polite: polite && { ariaLive: polite.ariaLive, role: polite.role, empty: polite.empty },
|
|
84
|
+
assertive: assertive && { ariaLive: assertive.ariaLive, role: assertive.role, empty: assertive.empty },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} finally {
|
|
90
|
+
await fixture.kill();
|
|
91
|
+
}
|
|
92
|
+
return { results };
|
|
93
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Shared helpers for application-notifications-in-app contributions/probes/.
|
|
2
|
+
//
|
|
3
|
+
// The real engine for every probe in this pack is the fixture HTTP
|
|
4
|
+
// server at packages/rcf-lite/test/fixtures/probe-pack-application-notifications-in-app/server.js
|
|
5
|
+
// booted on a scratch port from the family range 47640-47649.
|
|
6
|
+
// The fixture echoes a per-request x-fixture-request-id header on
|
|
7
|
+
// every response; probes record that id and a response body excerpt
|
|
8
|
+
// as positive evidence per rule 7d.
|
|
9
|
+
//
|
|
10
|
+
// No account credentials or network calls: every probe is
|
|
11
|
+
// local-fixture-driven, so no probe carries an account-bound branch
|
|
12
|
+
// or a CI_HAS_* gate. Declared env vars are the fixture's own
|
|
13
|
+
// PORT and (where the fixture reads them) the CAPS/state switches
|
|
14
|
+
// the probes drive via query string.
|
|
15
|
+
|
|
16
|
+
import { spawn } from 'node:child_process';
|
|
17
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { createServer } from 'node:net';
|
|
19
|
+
import { dirname, resolve } from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
|
|
24
|
+
export const SLUG = 'application-notifications-in-app';
|
|
25
|
+
export const PROJECT_ROOT = resolve(HERE, '..', '..', '..', '..');
|
|
26
|
+
export const FIXTURE_DIR = resolve(PROJECT_ROOT, 'packages/rcf-lite/test/fixtures/probe-pack-application-notifications-in-app');
|
|
27
|
+
export const REPORT_DIR = resolve(PROJECT_ROOT, '.rcf/reports/blueprints/application-notifications-in-app');
|
|
28
|
+
export const PORT_RANGE_START = 47640;
|
|
29
|
+
export const PORT_RANGE_END = 47649;
|
|
30
|
+
export const DECLARED_ENV = Object.freeze(['PORT', 'PROBE_BREAK']);
|
|
31
|
+
export const REQUEST_ID_HEADER = 'x-fixture-request-id';
|
|
32
|
+
|
|
33
|
+
// Pick a free port from the fixture family range. Falls back to
|
|
34
|
+
// asking the OS for an ephemeral port only if the whole range is
|
|
35
|
+
// held by other workers (never expected on a single-machine run).
|
|
36
|
+
export async function pickPort() {
|
|
37
|
+
for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port += 1) {
|
|
38
|
+
if (await canBind(port)) return port;
|
|
39
|
+
}
|
|
40
|
+
return await bindEphemeral();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function canBind(port) {
|
|
44
|
+
return new Promise((res) => {
|
|
45
|
+
const s = createServer();
|
|
46
|
+
s.once('error', () => res(false));
|
|
47
|
+
s.listen(port, '127.0.0.1', () => {
|
|
48
|
+
const bound = s.address();
|
|
49
|
+
s.close(() => res(!!bound));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function bindEphemeral() {
|
|
55
|
+
return new Promise((res) => {
|
|
56
|
+
const s = createServer();
|
|
57
|
+
s.listen(0, '127.0.0.1', () => {
|
|
58
|
+
const port = s.address().port;
|
|
59
|
+
s.close(() => res(port));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Spawns the fixture server as a child on `port` with the given
|
|
65
|
+
// env overlay; resolves once the fixture has printed LISTENING and
|
|
66
|
+
// returns { child, url, kill } (kill is synchronous best-effort).
|
|
67
|
+
export async function startFixture({ port, env = {} } = {}) {
|
|
68
|
+
const boundPort = port ?? await pickPort();
|
|
69
|
+
const child = spawn(process.execPath, ['server.js'], {
|
|
70
|
+
cwd: FIXTURE_DIR,
|
|
71
|
+
env: { ...process.env, PORT: String(boundPort), ...env },
|
|
72
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
73
|
+
});
|
|
74
|
+
await new Promise((res, rej) => {
|
|
75
|
+
const timer = setTimeout(() => rej(new Error(`fixture did not print LISTENING within 4s on port ${boundPort}`)), 4000);
|
|
76
|
+
let stderrBuf = '';
|
|
77
|
+
child.stderr.on('data', (chunk) => { stderrBuf += String(chunk); });
|
|
78
|
+
child.on('exit', (code) => rej(new Error(`fixture exited early (code=${code}) stderr=${stderrBuf.slice(0, 400)}`)));
|
|
79
|
+
child.stdout.on('data', (chunk) => {
|
|
80
|
+
if (String(chunk).includes(`LISTENING ${boundPort}`)) { clearTimeout(timer); res(); }
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
const url = new URL(`http://127.0.0.1:${boundPort}`);
|
|
84
|
+
return {
|
|
85
|
+
child,
|
|
86
|
+
url,
|
|
87
|
+
port: boundPort,
|
|
88
|
+
// Await child exit and propagate any error. A teardown failure
|
|
89
|
+
// must fail the verdict (the teardown-propagation rule);
|
|
90
|
+
// no swallowed errors here.
|
|
91
|
+
kill: () => new Promise((res, rej) => {
|
|
92
|
+
let settled = false;
|
|
93
|
+
const done = (err) => { if (settled) return; settled = true; err ? rej(err) : res(); };
|
|
94
|
+
child.once('exit', () => done());
|
|
95
|
+
child.once('error', (err) => done(err));
|
|
96
|
+
try { child.kill('SIGTERM'); } catch (err) { return done(err); }
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
if (settled) return;
|
|
99
|
+
try { child.kill('SIGKILL'); } catch (err) { return done(err); }
|
|
100
|
+
}, 4000);
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Issue an HTTP request against the fixture and return
|
|
106
|
+
// { status, headers, body, requestId }.
|
|
107
|
+
export async function fixtureFetch(url, pathAndQuery, init = {}) {
|
|
108
|
+
const target = new URL(pathAndQuery, url);
|
|
109
|
+
const res = await fetch(target, init);
|
|
110
|
+
const body = await res.text();
|
|
111
|
+
const requestId = res.headers.get(REQUEST_ID_HEADER);
|
|
112
|
+
return { status: res.status, headers: Object.fromEntries(res.headers.entries()), body, requestId };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function excerpt(body, maxLen = 240) {
|
|
116
|
+
if (typeof body !== 'string') return '';
|
|
117
|
+
return body.length <= maxLen ? body : body.slice(0, maxLen) + '…';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function aggregate(results) {
|
|
121
|
+
// Positive-evidence rule: no-checks-ran is a fail.
|
|
122
|
+
if (!Array.isArray(results) || results.length === 0) return 'fail';
|
|
123
|
+
if (results.some((r) => r.verdict === 'fail')) return 'fail';
|
|
124
|
+
// Any warn row lifts the aggregate to warn, including honest
|
|
125
|
+
// notObservableHere de-claims. A probe whose rows are all warn
|
|
126
|
+
// aggregates to warn, not pass; the shelf reads amber and the tally
|
|
127
|
+
// accepts a warn aggregate as problems=0 when no fail row is present.
|
|
128
|
+
if (results.some((r) => r && r.verdict === 'warn')) return 'warn';
|
|
129
|
+
return 'pass';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function writeReport({ probeName, engine, results, extra }) {
|
|
133
|
+
await mkdir(REPORT_DIR, { recursive: true });
|
|
134
|
+
const report = {
|
|
135
|
+
slug: SLUG,
|
|
136
|
+
probeName,
|
|
137
|
+
runAt: new Date().toISOString(),
|
|
138
|
+
engine,
|
|
139
|
+
results,
|
|
140
|
+
aggregateVerdict: aggregate(results),
|
|
141
|
+
...(extra ?? {}),
|
|
142
|
+
};
|
|
143
|
+
const filepath = resolve(REPORT_DIR, `${probeName}.json`);
|
|
144
|
+
await writeFile(filepath, JSON.stringify(report, null, 2) + '\n', 'utf8');
|
|
145
|
+
return { report, path: filepath };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function runShim(probeName, engine, mainFn) {
|
|
149
|
+
try {
|
|
150
|
+
const outcome = (await mainFn()) ?? { results: [] };
|
|
151
|
+
const { results, extra } = outcome;
|
|
152
|
+
const { report, path: filepath } = await writeReport({ probeName, engine, results, extra });
|
|
153
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
154
|
+
process.stdout.write(`report written to ${filepath}\n`);
|
|
155
|
+
if (report.aggregateVerdict === 'fail') process.exitCode = 1;
|
|
156
|
+
} catch (err) {
|
|
157
|
+
// Positive-evidence rule (exception fallback): exception rows have no
|
|
158
|
+
// observed AC; anchor null (never a fabricated id like
|
|
159
|
+
// "unknown"), carry an evidence object with the error excerpt.
|
|
160
|
+
const message = err && err.message ? err.message : String(err);
|
|
161
|
+
const stack = err && err.stack ? err.stack : String(err);
|
|
162
|
+
const results = [{
|
|
163
|
+
anchorAcId: null,
|
|
164
|
+
verdict: 'fail',
|
|
165
|
+
detail: `probe threw: ${message}`,
|
|
166
|
+
evidence: {
|
|
167
|
+
errorMessage: message,
|
|
168
|
+
errorExcerpt: excerpt(stack, 480),
|
|
169
|
+
},
|
|
170
|
+
}];
|
|
171
|
+
const { report, path: filepath } = await writeReport({ probeName, engine, results });
|
|
172
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
173
|
+
process.stderr.write(`probe error: ${stack}\n`);
|
|
174
|
+
process.stderr.write(`report written to ${filepath}\n`);
|
|
175
|
+
process.exitCode = 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import runProbe from './centre-acknowledge-round-trip.mjs';
|
|
2
|
+
import { runShim } from './probe-utils.mjs';
|
|
3
|
+
|
|
4
|
+
const engine = { kind: 'fixture', image: 'test/fixtures/probe-pack-application-notifications-in-app/server.js', healthy: true };
|
|
5
|
+
runShim('centre-acknowledge-round-trip', engine, runProbe);
|