rcf-lite 0.20.0 → 0.21.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/blueprints/application-admin-console/CHANGELOG.md +11 -0
- package/blueprints/application-admin-console/README.md +98 -0
- package/blueprints/application-admin-console/blueprint.json +226 -0
- package/blueprints/application-admin-console/contributions/adrs/adr-2201-application-admin-console-capability-vocabulary.json +25 -0
- package/blueprints/application-admin-console/contributions/adrs/adr-2202-application-admin-console-baseline-roles.json +25 -0
- package/blueprints/application-admin-console/contributions/adrs/adr-2203-application-admin-console-invite-transport.json +20 -0
- package/blueprints/application-admin-console/contributions/adrs/adr-2204-application-admin-console-audit-retention.json +20 -0
- package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-001.json +18 -0
- package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-002.json +18 -0
- package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-003.json +18 -0
- package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-004.json +18 -0
- package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-005.json +18 -0
- package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-006.json +18 -0
- package/blueprints/application-admin-console/contributions/tacs/tac-2201-application-admin-console-shell.json +40 -0
- package/blueprints/application-admin-console/contributions/tacs/tac-2202-application-admin-console-capability-discovery.json +44 -0
- package/blueprints/application-admin-console/contributions/tacs/tac-2203-application-admin-console-permission-matrix.json +34 -0
- package/blueprints/application-admin-console/contributions/tacs/tac-2204-application-admin-console-audit-view.json +40 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21101.json +34 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21102.json +34 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21103.json +34 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21104.json +28 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21105.json +34 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21106.json +27 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21107.json +27 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21108.json +27 -0
- package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21109.json +33 -0
- package/blueprints/application-admin-console/docs/topics.md +35 -0
- package/blueprints/application-admin-console/guide/application-admin-console.md +29 -0
- package/blueprints/application-admin-console/probe-packs/application-admin-console.pack.mjs +182 -0
- package/blueprints/application-api-rest/docs/topics.md +5 -0
- package/blueprints/application-charts/CHANGELOG.md +12 -0
- package/blueprints/application-charts/README.md +73 -0
- package/blueprints/application-charts/blueprint.json +26 -0
- package/blueprints/application-charts/contributions/adrs/adr-1901-application-charts-engine-choice.json +25 -0
- package/blueprints/application-charts/contributions/adrs/adr-1902-application-charts-accessible-palette.json +25 -0
- package/blueprints/application-charts/contributions/adrs/adr-1903-application-charts-motion.json +25 -0
- package/blueprints/application-charts/contributions/requirements/application-charts-req-001.json +15 -0
- package/blueprints/application-charts/contributions/requirements/application-charts-req-002.json +15 -0
- package/blueprints/application-charts/contributions/requirements/application-charts-req-003.json +15 -0
- package/blueprints/application-charts/contributions/requirements/application-charts-req-004.json +15 -0
- package/blueprints/application-charts/contributions/tacs/tac-1901-application-charts-render-shell.json +42 -0
- package/blueprints/application-charts/contributions/tacs/tac-1902-application-charts-keyboard-traversal.json +40 -0
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18101.json +35 -0
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18102.json +35 -0
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18103.json +35 -0
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18104.json +35 -0
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18105.json +35 -0
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18106.json +35 -0
- package/blueprints/application-charts/docs/topics.md +37 -0
- package/blueprints/application-charts/guide/application-charts.md +44 -0
- package/blueprints/application-charts/probe-packs/application-charts.pack.mjs +191 -0
- package/blueprints/application-dashboard/CHANGELOG.md +10 -0
- package/blueprints/application-dashboard/README.md +102 -0
- package/blueprints/application-dashboard/assets/guidance/dashboard-design.md +124 -0
- package/blueprints/application-dashboard/blueprint.json +29 -0
- package/blueprints/application-dashboard/contributions/adrs/adr-2001-application-dashboard-primary-kpi-enum.json +25 -0
- package/blueprints/application-dashboard/contributions/adrs/adr-2002-application-dashboard-timeframe-presets.json +25 -0
- package/blueprints/application-dashboard/contributions/adrs/adr-2003-application-dashboard-export-formats.json +25 -0
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-001.json +15 -0
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-002.json +15 -0
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-003.json +15 -0
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-004.json +15 -0
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-005.json +15 -0
- package/blueprints/application-dashboard/contributions/tacs/tac-2001-application-dashboard-tile-grid.json +47 -0
- package/blueprints/application-dashboard/contributions/tacs/tac-2002-application-dashboard-filter-chrome.json +41 -0
- package/blueprints/application-dashboard/contributions/tacs/tac-2003-application-dashboard-export-handle.json +35 -0
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19101.json +35 -0
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19102.json +35 -0
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19103.json +35 -0
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19104.json +35 -0
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19105.json +35 -0
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19106.json +35 -0
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19107.json +35 -0
- package/blueprints/application-dashboard/docs/topics.md +43 -0
- package/blueprints/application-dashboard/guide/application-dashboard.md +48 -0
- package/blueprints/application-dashboard/probe-packs/application-dashboard.pack.mjs +231 -0
- package/blueprints/application-datatable/CHANGELOG.md +8 -0
- package/blueprints/application-datatable/README.md +43 -0
- package/blueprints/application-datatable/blueprint.json +33 -0
- package/blueprints/application-datatable/contributions/adrs/adr-1801-application-datatable-pattern-choice.json +25 -0
- package/blueprints/application-datatable/contributions/adrs/adr-1802-application-datatable-url-state.json +20 -0
- package/blueprints/application-datatable/contributions/adrs/adr-1803-application-datatable-page-size.json +20 -0
- package/blueprints/application-datatable/contributions/adrs/adr-1804-application-datatable-selection-persistence.json +20 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-001.json +15 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-002.json +15 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-003.json +15 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-004.json +15 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-005.json +15 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-006.json +15 -0
- package/blueprints/application-datatable/contributions/tacs/tac-1801-application-datatable-shell.json +50 -0
- package/blueprints/application-datatable/contributions/tacs/tac-1802-application-datatable-query-adapter.json +39 -0
- package/blueprints/application-datatable/contributions/tacs/tac-1803-application-datatable-selection-model.json +28 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17101.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17102.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17103.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17104.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17105.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17106.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17107.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17108.json +44 -0
- package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17109.json +44 -0
- package/blueprints/application-datatable/docs/topics.md +35 -0
- package/blueprints/application-datatable/guide/application-datatable.md +38 -0
- package/blueprints/application-datatable/probe-packs/application-datatable.pack.mjs +240 -0
- package/blueprints/application-error-handling/docs/topics.md +5 -0
- package/blueprints/application-notifications-in-app/CHANGELOG.md +9 -0
- package/blueprints/application-notifications-in-app/README.md +80 -0
- package/blueprints/application-notifications-in-app/blueprint.json +30 -0
- package/blueprints/application-notifications-in-app/contributions/adrs/adr-2101-application-notifications-in-app-live-region-role-mapping.json +25 -0
- package/blueprints/application-notifications-in-app/contributions/adrs/adr-2102-application-notifications-in-app-toast-timeout-floor.json +25 -0
- package/blueprints/application-notifications-in-app/contributions/adrs/adr-2103-application-notifications-in-app-retention-window.json +25 -0
- package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-001.json +15 -0
- package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-002.json +15 -0
- package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-003.json +15 -0
- package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-004.json +15 -0
- package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-005.json +15 -0
- package/blueprints/application-notifications-in-app/contributions/tacs/tac-2101-application-notifications-in-app-live-region.json +46 -0
- package/blueprints/application-notifications-in-app/contributions/tacs/tac-2102-application-notifications-in-app-centre.json +52 -0
- package/blueprints/application-notifications-in-app/contributions/tacs/tac-2103-application-notifications-in-app-preferences.json +40 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20101.json +35 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20102.json +35 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20103.json +35 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20104.json +35 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20105.json +35 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20106.json +26 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20107.json +26 -0
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20108.json +26 -0
- package/blueprints/application-notifications-in-app/docs/topics.md +39 -0
- package/blueprints/application-notifications-in-app/guide/application-notifications-in-app.md +47 -0
- package/blueprints/application-notifications-in-app/probe-packs/application-notifications-in-app.pack.mjs +328 -0
- package/blueprints/application-spa/docs/topics.md +5 -0
- package/blueprints/delivery-ci-workflows/docs/topics.md +5 -0
- package/blueprints/deploy-cloudflare-workers/docs/topics.md +5 -0
- package/blueprints/email-smtp-resend/docs/topics.md +5 -0
- package/blueprints/observability-essentials/docs/topics.md +5 -0
- package/blueprints/observability-logging/CHANGELOG.md +5 -0
- package/blueprints/observability-logging/README.md +1 -1
- package/blueprints/observability-logging/blueprint.json +113 -21
- package/blueprints/observability-logging/docs/topics.md +5 -0
- package/blueprints/observability-probe-endpoints/docs/topics.md +5 -0
- package/blueprints/persistence-data-d1/docs/topics.md +5 -0
- package/blueprints/persistence-data-sqlite/docs/topics.md +5 -0
- package/blueprints/security-auth-clerk/CHANGELOG.md +5 -0
- package/blueprints/security-auth-clerk/blueprint.json +2 -1
- package/blueprints/security-auth-clerk/docs/topics.md +5 -0
- package/blueprints/security-auth-keycloak/CHANGELOG.md +5 -0
- package/blueprints/security-auth-keycloak/blueprint.json +2 -1
- package/blueprints/security-auth-keycloak/docs/topics.md +5 -0
- package/blueprints/security-auth-magic-link/CHANGELOG.md +5 -0
- package/blueprints/security-auth-magic-link/blueprint.json +2 -1
- package/blueprints/security-auth-magic-link/docs/topics.md +5 -0
- package/blueprints/security-auth-oauth2/CHANGELOG.md +5 -0
- package/blueprints/security-auth-oauth2/blueprint.json +2 -1
- package/blueprints/security-auth-oauth2/docs/topics.md +5 -0
- package/blueprints/security-secrets-management/docs/topics.md +5 -0
- package/fixtures/canary-manifest.json +9 -9
- package/package.json +2 -2
- package/rcf/code-nodes/cn-106.json +13 -0
- package/rcf/code-nodes/cn-107.json +14 -0
- package/rcf/code-nodes/cn-108.json +13 -0
- package/rcf/code-nodes/cn-109.json +12 -0
- package/rcf/code-nodes/cn-110.json +12 -0
- package/rcf/code-nodes/cn-111.json +14 -0
- package/rcf/code-nodes/cn-112.json +14 -0
- package/rcf/code-nodes/cn-113.json +12 -0
- package/rcf/code-nodes/cn-114.json +13 -0
- package/rcf/code-nodes/cn-115.json +13 -0
- package/rcf/code-nodes/cn-116.json +12 -0
- package/rcf/code-nodes/cn-117.json +13 -0
- package/rcf/code-nodes/cn-118.json +13 -0
- package/rcf/code-nodes/cn-119.json +13 -0
- package/rcf/code-nodes/cn-120.json +13 -0
- package/rcf/code-nodes/cn-121.json +12 -0
- package/rcf/code-nodes/cn-122.json +12 -0
- package/rcf/code-nodes/cn-123.json +13 -0
- package/rcf/code-nodes/cn-124.json +13 -0
- package/rcf/code-nodes/cn-125.json +13 -0
- package/rcf/code-nodes/cn-126.json +13 -0
- package/rcf/code-nodes/cn-127.json +12 -0
- package/rcf/code-nodes/cn-128.json +14 -0
- package/rcf/code-nodes/cn-129.json +14 -0
- package/rcf/code-nodes/cn-130.json +14 -0
- package/rcf/code-nodes/cn-131.json +15 -0
- package/rcf/code-nodes/cn-132.json +12 -0
- package/rcf/code-nodes/cn-133.json +13 -0
- package/rcf/fbs/fbs-036.json +22 -0
- package/rcf/fbs/fbs-037.json +19 -0
- package/rcf/fbs/fbs-038.json +19 -0
- package/rcf/fbs/fbs-039.json +21 -0
- package/rcf/fbs/fbs-040.json +20 -0
- package/rcf/fbs/fbs-041.json +13 -0
- package/rcf/fbs/fbs-042.json +13 -0
- package/rcf/requirements/req-017.json +18 -0
- package/rcf/requirements/req-018.json +18 -0
- package/rcf/requirements/req-019.json +38 -0
- package/rcf/requirements/req-020.json +13 -0
- package/rcf/requirements/req-021.json +13 -0
- package/rcf/requirements/req-022.json +13 -0
- package/rcf/requirements/req-023.json +14 -0
- package/rcf/test-suites/ts-046.json +78 -0
- package/rcf/test-suites/ts-047.json +54 -0
- package/rcf/test-suites/ts-048.json +54 -0
- package/rcf/test-suites/ts-049.json +70 -0
- package/rcf/test-suites/ts-050.json +69 -0
- package/rcf/test-suites/ts-051.json +22 -0
- package/rcf/test-suites/ts-052.json +20 -0
- package/rcf/user-stories/us-1701.json +56 -0
- package/rcf/user-stories/us-1801.json +40 -0
- package/rcf/user-stories/us-1901.json +40 -0
- package/rcf/user-stories/us-2001.json +50 -0
- package/rcf/user-stories/us-2101.json +45 -0
- package/rcf/user-stories/us-2201.json +60 -0
- package/rcf/user-stories/us-2301.json +51 -0
- package/releases/releases.yaml +11 -1
- package/src/blueprint/apply.js +98 -1
- package/src/blueprint/capabilities.js +318 -0
- package/src/blueprint/loader.js +247 -0
- package/src/browser-verify/boot.js +180 -0
- package/src/browser-verify/index.js +13 -0
- package/src/browser-verify/manifest-writer.js +31 -6
- package/src/browser-verify/pack-browser.js +294 -0
- package/src/browser-verify/pack-loader.js +176 -0
- package/src/browser-verify/pack-runner.js +193 -0
- package/src/browser-verify/pack-schema.js +155 -0
- package/src/browser-verify/runner.js +24 -1
- package/src/cli/blueprint.js +92 -0
- package/src/cli/browser-verify.js +147 -7
- package/src/core/errors/index.js +7 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# application-notifications-in-app: coordination vocabulary
|
|
2
|
+
|
|
3
|
+
## Global topics
|
|
4
|
+
|
|
5
|
+
The blueprint claims no new global topics at v1.0.0. The in-app-notifications contract is a composed application shape; every ADR (priority-to-role mapping, toast timeout floor, centre retention window) is non-global.
|
|
6
|
+
|
|
7
|
+
## Deliberately unclaimed topics
|
|
8
|
+
|
|
9
|
+
- `notificationsChannel` (the channel this blueprint owns). Not a global topic; the family-prefix reservation is the discoverable coordination mechanism (see below). A future blueprint that composes across all in-app-adjacent channels (a shared preferences store, for instance) would either share TAC-2103 or supersede this blueprint.
|
|
10
|
+
- `livingRegionMapping` (the priority-to-role mapping). Reserved for the applying project as the ADR-2101 decision, not a global topic; a project that supersedes the mapping authors its own ADR.
|
|
11
|
+
|
|
12
|
+
## Family-prefix reservation
|
|
13
|
+
|
|
14
|
+
The `application-notifications-` prefix is reserved for the sibling channel blueprints the shelf will grow into: `-email`, `-push`, `-webhook`. This is a doc-only reservation in v1.0.0 (per spec Q3 default). No sibling channel ships in this round. See the family-prefix row in the shelf band registry below and the blueprint README's "Family-prefix reservation" section.
|
|
15
|
+
|
|
16
|
+
## Shelf id band and suffix block
|
|
17
|
+
|
|
18
|
+
| Blueprint | US band | ADR/TAC suffix block | Status | Global topics |
|
|
19
|
+
|---|---|---|---|---|
|
|
20
|
+
| application-datatable | 17101-17899 | 18xx | shipped v1.0.0 | none |
|
|
21
|
+
| application-charts | 18101-18899 | 19xx | shipped v1.0.0 | none |
|
|
22
|
+
| application-dashboard | 19101-19899 | 20xx | shipped v1.0.0 | none |
|
|
23
|
+
| application-notifications-in-app | 20101-20899 | 21xx | shipped v1.0.0 | none |
|
|
24
|
+
| application-admin-console | 21101-21899 | 22xx | shipped v1.0.0 | none |
|
|
25
|
+
| application-notifications-email (reserved) | to be claimed at ship | to be claimed at ship | name-only doc reservation (not shipped) | tba |
|
|
26
|
+
| application-notifications-push (reserved) | to be claimed at ship | to be claimed at ship | name-only doc reservation (not shipped) | tba |
|
|
27
|
+
| application-notifications-webhook (reserved) | to be claimed at ship | to be claimed at ship | name-only doc reservation (not shipped) | tba |
|
|
28
|
+
|
|
29
|
+
The shelf-wide band registry lives in `packages/rcf-lite/docs/blueprint-authoring.md` section 5.
|
|
30
|
+
|
|
31
|
+
## Probe pack
|
|
32
|
+
|
|
33
|
+
`probe-packs/application-notifications-in-app.pack.mjs` ships three checks anchored to blueprint AC ids:
|
|
34
|
+
|
|
35
|
+
- `AC-20101-1` live-region preseeding on every declared route (both wrappers present AND empty at load)
|
|
36
|
+
- `AC-20102-1` transient toast contract with priority-to-role mapping and timeout measured against the six-second WCAG 2.2.1 floor
|
|
37
|
+
- `AC-20103-1` centre acknowledge round-trip enumerated by `data-notification-id`, reconciled through client and server request logs
|
|
38
|
+
|
|
39
|
+
Every check carries a `description` field per spec section 9. `appliesTo` binds `TAC-2101-application-notifications-in-app-live-region` OR any FBS route matching `notifications-centre`. See `probe-packs/application-notifications-in-app.pack.mjs` for the source; see `packages/rcf-lite/test/fixtures/probe-pack-application-notifications-in-app/README.md` for the sample-app fixture and the four negative-run break switches.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Guide: application-notifications-in-app (v1.0.0)
|
|
2
|
+
|
|
3
|
+
## What it is
|
|
4
|
+
|
|
5
|
+
A shipped, vendor-neutral in-app-notifications contract for a rcf-lite application. Ships the live-region preseeding discipline (TAC-2101), the transient-toast contract with the priority-to-role mapping (ADR-2101) and the WCAG 2.2.1 six-second timeout floor (ADR-2102), the notification-centre inbox with the ADR-2103 retention window and the per-item acknowledge round-trip (TAC-2102), the per-user preferences UI with category silence and sibling-channel delegation (TAC-2103), and the delivery-attempt log write path (REQ-005, TAC-2102). Ships a Playwright probe pack that gates ship on the three runtime-observable surfaces the shelf assures: both live-region wrappers are preseeded on every declared route before any notification fires, every toast maps priority to the correct wrapper and role with an announcement measured against the six-second floor, and every centre acknowledge control round-trips to the server with the DOM updating on the response.
|
|
6
|
+
|
|
7
|
+
## What it deliberately is not
|
|
8
|
+
|
|
9
|
+
- Not a notifications transport. The in-app slug is the discriminator; sibling channel blueprints under the reserved `application-notifications-` family prefix ship their own transports. This blueprint ships no email, no Web Push, no webhook.
|
|
10
|
+
- Not a notifications store. The delivery-attempt log's persistence layer is the applied `logging` companion's; this blueprint owns the row shape and delegates the write path.
|
|
11
|
+
- Not a preferences store. The preferences UI records intent on the surface (category silence, digest opt-in); the store where those preferences persist is the applying project's, and each sibling channel blueprint reads its own share of the intent.
|
|
12
|
+
|
|
13
|
+
## When to reach for it
|
|
14
|
+
|
|
15
|
+
- Any shipped rcf-lite application whose operators need transient toasts, a durable notification centre and per-user category silence. Web SaaS applications, admin consoles, dashboards with alerting.
|
|
16
|
+
- Projects that want the WCAG 4.1.3 status-message discipline and the WCAG 2.2.1 timeout floor enforced at ship without hand-authoring the live-region contract.
|
|
17
|
+
- Projects that expect to add sibling channels (email digest, Web Push, webhooks) later: the family-prefix reservation and the preferences UI's sibling-delegation shape land the discipline early.
|
|
18
|
+
|
|
19
|
+
## When it does not fit
|
|
20
|
+
|
|
21
|
+
- Server-to-server notifications with no operator surface. If nobody reads the notification on a screen, this blueprint's contract is expensive noise; use a logging blueprint alone.
|
|
22
|
+
- A project whose notifications are single-channel and channel-first (an SMS-only alert product) where the in-app slot is not the primary surface. The sibling channel blueprint is the fit; the in-app blueprint composes later if the surface grows.
|
|
23
|
+
- Highly bespoke chat-like surfaces (a support-inbox blueprint, a workflow-review inbox) where the accessibility contract differs from the WCAG 4.1.3 status-message discipline. Supersede this blueprint with a project-authored REQ naming the residual, or apply a different chat-oriented blueprint.
|
|
24
|
+
|
|
25
|
+
## What a good outcome looks like
|
|
26
|
+
|
|
27
|
+
Every route the applying project ships preseeds both live-region wrappers in the DOM at page load; no announcement race is possible. Every toast writes into exactly one wrapper based on its priority (info to polite as `role="status"`, error to assertive as `role="alert"`) and stays visible for six seconds unless dismissed; the reader hears an interruption exactly when the application asserts one. The notification centre lists notifications within the retention window with every item Tab-reachable and every acknowledge round-tripping to the server. The preferences UI carries per-category silence toggles and clearly labels any sibling channel that is not yet applied. The shipped probe pack runs green on the delivery-ci-workflows gate.
|
|
28
|
+
|
|
29
|
+
## The operator decisions that remain open
|
|
30
|
+
|
|
31
|
+
- **Toast timeout floor** (ADR-2102). Six seconds by default; operator elicits a longer dwell time if the surface has a very high notification volume.
|
|
32
|
+
- **Notification categories** (REQ-004). Payment, security and product are the shipped default categories on the fixture; the operator elicits their own category list at apply.
|
|
33
|
+
- **Centre retention window** (ADR-2103). Thirty days by default; operator elicits a longer or shorter window at apply.
|
|
34
|
+
- **Notification centre route path** (REQ-003 note). `/notifications-centre` by default; operator elicits an override for a bespoke route.
|
|
35
|
+
- **Sibling channels applied** (REQ-004 note). Which of `-email`, `-push`, `-webhook` (or a project-authored sibling) is applied on the project; discovered, not elicited, from the manifest.
|
|
36
|
+
|
|
37
|
+
## Common gotchas
|
|
38
|
+
|
|
39
|
+
- The two live-region wrappers must be preseeded before the first notification. A lazy-mount defect is silent at the DOM (the wrapper appears "eventually") but loses the first announcement at the assistive-technology layer. The pack refuses ship on this shape.
|
|
40
|
+
- The priority-to-role mapping is fixed at v1.0.0 (info to polite/status, error to assertive/alert). A third priority (`warning`) is a v1.1 minor bump because it changes the role vocabulary; free-form priorities are refused at project-side review.
|
|
41
|
+
- The toast timeout floor is a floor, not a ceiling. Six seconds is the default; an operator-elicited value above the floor is honoured (thirty seconds), a value below the floor is refused at apply. The pack measures the actual elapsed time against the shell root's declared floor and against the six-second baseline; both must clear.
|
|
42
|
+
- The centre enumerates items by `data-notification-id`, per the T-3 gate discipline. A centre implementation that uses only `role="listitem"` on the children (without a per-element id attribute) passes the accessibility contract but does not let the pack enumerate individual items reliably; ship both.
|
|
43
|
+
- The family-prefix reservation is documented; naming a future notifications blueprint outside the family (`application-alerts-email`) reads against the reservation and stops at author-side review.
|
|
44
|
+
|
|
45
|
+
## Cost honesty
|
|
46
|
+
|
|
47
|
+
Shipping this blueprint costs the project a design pass on the notification categories and retention window, and a build pass on the live-region factory and the centre inbox surface. It buys the WCAG 4.1.3 and WCAG 2.2.1 disciplines enforced at ship, the ARIA APG alert pattern on the assertive slot, the centre inbox with the acknowledge round-trip, the preferences UI with sibling delegation, and the family-prefix reservation that survives when the shelf grows a second notifications channel. The gate reviewer verifies the pack runs green against the sample-app fixture and reviews the applying project's own notifications surfaces against the ADR set.
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// application-notifications-in-app probe pack (v1.0.0).
|
|
2
|
+
//
|
|
3
|
+
// Three runtime-observable checks anchored to the blueprint's ACs:
|
|
4
|
+
// AC-20101-1 live-region preseeding (both wrappers present AND empty
|
|
5
|
+
// at load on every declared route)
|
|
6
|
+
// AC-20102-1 transient toast contract (priority-to-role mapping,
|
|
7
|
+
// one-shot announcement, timeout measured against the
|
|
8
|
+
// ADR-2102 six-second floor via data-shown-at /
|
|
9
|
+
// data-dismissed-at timestamps rather than a sleep in
|
|
10
|
+
// the pack)
|
|
11
|
+
// AC-20103-1 centre acknowledge round-trip (Tab-reachable acknowledge
|
|
12
|
+
// control POSTs to the server, the server responds, the
|
|
13
|
+
// DOM updates in response; enumerated by data-notification-id
|
|
14
|
+
// per the T-3 gate discipline, never by role alone)
|
|
15
|
+
//
|
|
16
|
+
// Every check drives the real Playwright browser the T-0 runner
|
|
17
|
+
// injects (packages/rcf-lite/src/browser-verify/pack-browser.js) and
|
|
18
|
+
// asserts on the DOM the applying project renders under TAC-2101
|
|
19
|
+
// (live-region wrappers and toast factory), TAC-2102 (centre inbox
|
|
20
|
+
// and acknowledge round-trip) and the priority-to-role mapping per
|
|
21
|
+
// ADR-2101.
|
|
22
|
+
//
|
|
23
|
+
// The pack has no network-interception seam (T-0 shipped none); the
|
|
24
|
+
// acknowledge round-trip is captured through the request log the
|
|
25
|
+
// sample app exposes on window.__notificationFetches and at
|
|
26
|
+
// GET /__requests. The toast timeout is measured with browser.evaluate
|
|
27
|
+
// reading timestamps the sample app records on the toast element
|
|
28
|
+
// (data-shown-at and data-dismissed-at), polled with a bounded loop
|
|
29
|
+
// so the pack does not sleep for six seconds on the happy path.
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
packName: 'application-notifications-in-app',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
blueprintSlug: 'application-notifications-in-app',
|
|
35
|
+
// Applies to any FBS that realises the live-region TAC or whose
|
|
36
|
+
// navModel routes include a notifications-centre path. The source
|
|
37
|
+
// references BOTH `tacIds` and `route` so the T-0 loader's
|
|
38
|
+
// applicability source-scan sees the two legal seams (route is one
|
|
39
|
+
// of the three legal predicates).
|
|
40
|
+
appliesTo: ({ fbs }) => {
|
|
41
|
+
const routes = fbs?.designStage?.navModel?.routes ?? [];
|
|
42
|
+
const tacIds = fbs?.contextRequirements?.tacIds ?? [];
|
|
43
|
+
if (tacIds.includes('TAC-2101-application-notifications-in-app-live-region')) return true;
|
|
44
|
+
return routes.some((r) => typeof r?.path === 'string' && /notifications-centre/.test(r.path));
|
|
45
|
+
},
|
|
46
|
+
boot: { bootCommand: null, waitForUrl: null, waitForSelector: null },
|
|
47
|
+
checks: [
|
|
48
|
+
{
|
|
49
|
+
id: 'AC-20101-1',
|
|
50
|
+
severity: 'block',
|
|
51
|
+
description: 'Live-region preseeded on every declared route: both the polite wrapper ([aria-live="polite"] [data-live-region="polite"]) and the assertive wrapper ([role="alert"] [data-live-region="assertive"]) are preseeded and empty in the DOM at page load, before any notification fires. Enumerated by the per-element [data-live-region] attribute (never by aria-live or role alone).',
|
|
52
|
+
run: async ({ browser, runtimeUrl }) => {
|
|
53
|
+
if (!browser) return { verdict: 'fail', detail: 'no packBrowser wired' };
|
|
54
|
+
const routes = ['/', '/notifications-centre', '/notifications-preferences'];
|
|
55
|
+
const perRoute = [];
|
|
56
|
+
for (const path of routes) {
|
|
57
|
+
// Preserve the reviewer's --url query verbatim across every
|
|
58
|
+
// route navigation: swap the pathname, keep the search.
|
|
59
|
+
const target = new URL(runtimeUrl);
|
|
60
|
+
target.pathname = path;
|
|
61
|
+
await browser.goto(target.toString());
|
|
62
|
+
const measurement = await browser.evaluate(() => {
|
|
63
|
+
const polite = document.querySelector('[data-live-region="polite"]');
|
|
64
|
+
const assertive = document.querySelector('[data-live-region="assertive"]');
|
|
65
|
+
function inspect(el) {
|
|
66
|
+
if (!el) return null;
|
|
67
|
+
return {
|
|
68
|
+
tag: el.tagName,
|
|
69
|
+
ariaLive: el.getAttribute('aria-live'),
|
|
70
|
+
role: el.getAttribute('role'),
|
|
71
|
+
dataLiveRegion: el.getAttribute('data-live-region'),
|
|
72
|
+
childCount: el.childElementCount,
|
|
73
|
+
textLength: (el.textContent || '').trim().length,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return { polite: inspect(polite), assertive: inspect(assertive) };
|
|
77
|
+
});
|
|
78
|
+
perRoute.push({ path, measurement });
|
|
79
|
+
}
|
|
80
|
+
const failures = [];
|
|
81
|
+
for (const entry of perRoute) {
|
|
82
|
+
if (!entry.measurement.polite) {
|
|
83
|
+
failures.push({ path: entry.path, reason: 'polite wrapper missing at page load (preseeded wrappers are required per Sara Soueidan normative guidance)' });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (!entry.measurement.assertive) {
|
|
87
|
+
failures.push({ path: entry.path, reason: 'assertive wrapper missing at page load' });
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (entry.measurement.polite.ariaLive !== 'polite') {
|
|
91
|
+
failures.push({ path: entry.path, reason: 'polite wrapper missing aria-live="polite"' });
|
|
92
|
+
}
|
|
93
|
+
if (entry.measurement.assertive.role !== 'alert') {
|
|
94
|
+
failures.push({ path: entry.path, reason: 'assertive wrapper missing role="alert"' });
|
|
95
|
+
}
|
|
96
|
+
if (entry.measurement.polite.textLength > 0 || entry.measurement.polite.childCount > 0) {
|
|
97
|
+
failures.push({ path: entry.path, reason: 'polite wrapper carries content at page load (must be empty until a notification fires)' });
|
|
98
|
+
}
|
|
99
|
+
if (entry.measurement.assertive.textLength > 0 || entry.measurement.assertive.childCount > 0) {
|
|
100
|
+
failures.push({ path: entry.path, reason: 'assertive wrapper carries content at page load (must be empty until a notification fires)' });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (failures.length > 0) {
|
|
104
|
+
return { verdict: 'fail', detail: 'live-region preseeded contract broken: ' + JSON.stringify(failures) };
|
|
105
|
+
}
|
|
106
|
+
return { verdict: 'pass', detail: 'live-region wrappers preseeded and empty on every declared route: ' + JSON.stringify(perRoute.map((r) => ({ path: r.path, polite: r.measurement.polite, assertive: r.measurement.assertive }))) };
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: 'AC-20102-1',
|
|
111
|
+
severity: 'block',
|
|
112
|
+
description: 'Transient toast contract: an info-priority toast renders inside the polite wrapper with role="status", an error-priority toast renders inside the assertive wrapper with role="alert" (per ADR-2101 priority-to-role mapping); every toast body appears in exactly one wrapper (never both); every toast records data-shown-at and data-dismissed-at, and a timeout-dismissed toast carries an elapsed time at or above the ADR-2102 six-second floor per WCAG 2.2.1.',
|
|
113
|
+
run: async ({ browser, runtimeUrl }) => {
|
|
114
|
+
if (!browser) return { verdict: 'fail', detail: 'no packBrowser wired' };
|
|
115
|
+
const target = new URL('', runtimeUrl).toString();
|
|
116
|
+
await browser.goto(target);
|
|
117
|
+
// Read the shell's declared timeout floor (defaults to six
|
|
118
|
+
// seconds per ADR-2102; may be overridden above the floor).
|
|
119
|
+
const shellFloorSeconds = await browser.evaluate(() => {
|
|
120
|
+
const root = document.querySelector('[data-region="shell-root"]');
|
|
121
|
+
const value = root && root.getAttribute('data-toast-timeout-floor-seconds');
|
|
122
|
+
return value ? Number(value) : 6;
|
|
123
|
+
});
|
|
124
|
+
// Fire one info toast and one error toast through the triggers
|
|
125
|
+
// the fixture ships; a real applying project may drive the
|
|
126
|
+
// same shape via a click on its own trigger, an evaluate hook,
|
|
127
|
+
// or a background-event dispatch.
|
|
128
|
+
await browser.click('[data-toast-trigger="info"]');
|
|
129
|
+
await browser.click('[data-toast-trigger="error"]');
|
|
130
|
+
// Poll for both toasts to dismiss (bounded loop; cap slightly
|
|
131
|
+
// above the shell's floor so a six-second floor passes and a
|
|
132
|
+
// two-second break-timeout run still finishes in time).
|
|
133
|
+
const capMs = Math.max(shellFloorSeconds * 1000, 6000) + 3500;
|
|
134
|
+
const outcome = await browser.evaluate(async (capMs) => {
|
|
135
|
+
const start = Date.now();
|
|
136
|
+
function toastState() {
|
|
137
|
+
const infoToast = document.querySelector('[data-toast-priority="info"]');
|
|
138
|
+
const errorToast = document.querySelector('[data-toast-priority="error"]');
|
|
139
|
+
return { infoToast, errorToast };
|
|
140
|
+
}
|
|
141
|
+
return await new Promise((resolve) => {
|
|
142
|
+
function tick() {
|
|
143
|
+
const { infoToast, errorToast } = toastState();
|
|
144
|
+
const infoDismissedAt = infoToast && infoToast.getAttribute('data-dismissed-at');
|
|
145
|
+
const errorDismissedAt = errorToast && errorToast.getAttribute('data-dismissed-at');
|
|
146
|
+
if (infoDismissedAt && errorDismissedAt) {
|
|
147
|
+
resolve({ done: true });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (Date.now() - start > capMs) {
|
|
151
|
+
resolve({ done: false });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
setTimeout(tick, 60);
|
|
155
|
+
}
|
|
156
|
+
tick();
|
|
157
|
+
});
|
|
158
|
+
}, capMs);
|
|
159
|
+
if (!outcome || !outcome.done) {
|
|
160
|
+
return { verdict: 'fail', detail: 'toasts did not dismiss within the polling cap (' + capMs + 'ms); the shell may have failed to fire the toast timer' };
|
|
161
|
+
}
|
|
162
|
+
const evidence = await browser.evaluate(() => {
|
|
163
|
+
const politeWrapper = document.querySelector('[data-live-region="polite"]');
|
|
164
|
+
const assertiveWrapper = document.querySelector('[data-live-region="assertive"]');
|
|
165
|
+
function inspect(el) {
|
|
166
|
+
if (!el) return null;
|
|
167
|
+
return {
|
|
168
|
+
id: el.getAttribute('data-toast-id'),
|
|
169
|
+
priority: el.getAttribute('data-toast-priority'),
|
|
170
|
+
role: el.getAttribute('role'),
|
|
171
|
+
parentDataLiveRegion: el.parentElement && el.parentElement.getAttribute('data-live-region'),
|
|
172
|
+
shownAt: el.getAttribute('data-shown-at'),
|
|
173
|
+
dismissedAt: el.getAttribute('data-dismissed-at'),
|
|
174
|
+
body: (el.textContent || '').trim(),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function texts(wrapper) {
|
|
178
|
+
if (!wrapper) return [];
|
|
179
|
+
return Array.from(wrapper.querySelectorAll('[data-toast-id]')).map((t) => (t.textContent || '').trim());
|
|
180
|
+
}
|
|
181
|
+
const infoToast = document.querySelector('[data-toast-priority="info"]');
|
|
182
|
+
const errorToast = document.querySelector('[data-toast-priority="error"]');
|
|
183
|
+
return {
|
|
184
|
+
info: inspect(infoToast),
|
|
185
|
+
error: inspect(errorToast),
|
|
186
|
+
politeBodies: texts(politeWrapper),
|
|
187
|
+
assertiveBodies: texts(assertiveWrapper),
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
if (!evidence.info) return { verdict: 'fail', detail: 'no info-priority toast rendered after trigger' };
|
|
191
|
+
if (!evidence.error) return { verdict: 'fail', detail: 'no error-priority toast rendered after trigger' };
|
|
192
|
+
if (evidence.info.parentDataLiveRegion !== 'polite') {
|
|
193
|
+
return { verdict: 'fail', detail: 'info toast rendered in wrapper ' + JSON.stringify(evidence.info.parentDataLiveRegion) + ' instead of polite (ADR-2101 mapping broken)' };
|
|
194
|
+
}
|
|
195
|
+
if (evidence.info.role !== 'status') {
|
|
196
|
+
return { verdict: 'fail', detail: 'info toast role is ' + JSON.stringify(evidence.info.role) + ' instead of status (ADR-2101 mapping broken)' };
|
|
197
|
+
}
|
|
198
|
+
if (evidence.error.parentDataLiveRegion !== 'assertive') {
|
|
199
|
+
return { verdict: 'fail', detail: 'error toast rendered in wrapper ' + JSON.stringify(evidence.error.parentDataLiveRegion) + ' instead of assertive (ADR-2101 mapping broken)' };
|
|
200
|
+
}
|
|
201
|
+
if (evidence.error.role !== 'alert') {
|
|
202
|
+
return { verdict: 'fail', detail: 'error toast role is ' + JSON.stringify(evidence.error.role) + ' instead of alert (ADR-2101 mapping broken)' };
|
|
203
|
+
}
|
|
204
|
+
// Disjoint-content check (AC-20108-1 support): the info body
|
|
205
|
+
// does not appear in the assertive wrapper and vice versa.
|
|
206
|
+
if (evidence.assertiveBodies.some((body) => body === evidence.info.body)) {
|
|
207
|
+
return { verdict: 'fail', detail: 'info toast body appears in the assertive wrapper (double-post defect)' };
|
|
208
|
+
}
|
|
209
|
+
if (evidence.politeBodies.some((body) => body === evidence.error.body)) {
|
|
210
|
+
return { verdict: 'fail', detail: 'error toast body appears in the polite wrapper (double-post defect)' };
|
|
211
|
+
}
|
|
212
|
+
// Timeout floor check (WCAG 2.2.1): elapsed time between
|
|
213
|
+
// data-shown-at and data-dismissed-at at or above the ADR-2102
|
|
214
|
+
// floor. Six seconds baseline; an operator-elicited override
|
|
215
|
+
// raises the floor (never lowers it).
|
|
216
|
+
const infoElapsed = new Date(evidence.info.dismissedAt).getTime() - new Date(evidence.info.shownAt).getTime();
|
|
217
|
+
const errorElapsed = new Date(evidence.error.dismissedAt).getTime() - new Date(evidence.error.shownAt).getTime();
|
|
218
|
+
const floorMs = shellFloorSeconds * 1000;
|
|
219
|
+
if (infoElapsed < 6000) {
|
|
220
|
+
return { verdict: 'fail', detail: 'info toast dismissed after ' + infoElapsed + 'ms, below the WCAG 2.2.1 six-second floor (shell declared floor: ' + shellFloorSeconds + 's)' };
|
|
221
|
+
}
|
|
222
|
+
if (errorElapsed < 6000) {
|
|
223
|
+
return { verdict: 'fail', detail: 'error toast dismissed after ' + errorElapsed + 'ms, below the WCAG 2.2.1 six-second floor (shell declared floor: ' + shellFloorSeconds + 's)' };
|
|
224
|
+
}
|
|
225
|
+
if (shellFloorSeconds < 6) {
|
|
226
|
+
return { verdict: 'fail', detail: 'shell root declared a toast timeout floor of ' + shellFloorSeconds + 's, below the ADR-2102 six-second floor' };
|
|
227
|
+
}
|
|
228
|
+
return { verdict: 'pass', detail: 'toast contract honoured: info in polite/status (elapsed ' + infoElapsed + 'ms), error in assertive/alert (elapsed ' + errorElapsed + 'ms), floor ' + shellFloorSeconds + 's, ' + evidence.politeBodies.length + ' polite bodies + ' + evidence.assertiveBodies.length + ' assertive bodies, disjoint' };
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
id: 'AC-20103-1',
|
|
233
|
+
severity: 'block',
|
|
234
|
+
description: 'Notification centre acknowledge round-trip: the centre lists notifications within the ADR-2103 retention window (default thirty days), each item carries a stable data-notification-id (enumerated per-element, never by role alone), and a Tab-reachable [data-action="acknowledge"] control activates a POST /api/notifications/acknowledge round-trip whose 2xx response flips the item data-acknowledged to true. Reconciled against window.__notificationFetches and /__requests.',
|
|
235
|
+
run: async ({ browser, runtimeUrl }) => {
|
|
236
|
+
if (!browser) return { verdict: 'fail', detail: 'no packBrowser wired' };
|
|
237
|
+
// Preserve the reviewer's --url query across the route swap.
|
|
238
|
+
const target = new URL(runtimeUrl);
|
|
239
|
+
target.pathname = '/notifications-centre';
|
|
240
|
+
await browser.goto(target.toString());
|
|
241
|
+
const initial = await browser.evaluate(() => {
|
|
242
|
+
// Enumerate only the notification wrappers (articles carrying
|
|
243
|
+
// data-notification-id AND data-acknowledged; the acknowledge
|
|
244
|
+
// and mark-read controls also carry data-notification-id so
|
|
245
|
+
// scope the query to article elements).
|
|
246
|
+
const items = Array.from(document.querySelectorAll('article[data-notification-id]'));
|
|
247
|
+
return {
|
|
248
|
+
itemCount: items.length,
|
|
249
|
+
itemIds: items.map((el) => el.getAttribute('data-notification-id')),
|
|
250
|
+
itemAcknowledged: items.map((el) => el.getAttribute('data-acknowledged')),
|
|
251
|
+
centreRegion: Boolean(document.querySelector('[data-region="notifications-centre"]')),
|
|
252
|
+
markAllRead: Boolean(document.querySelector('[data-action="mark-all-read"]')),
|
|
253
|
+
initialFetchCount: (window.__notificationFetches || []).length,
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
if (!initial.centreRegion) {
|
|
257
|
+
return { verdict: 'fail', detail: 'centre region not found (missing [data-region="notifications-centre"])' };
|
|
258
|
+
}
|
|
259
|
+
if (initial.itemCount === 0) {
|
|
260
|
+
return { verdict: 'fail', detail: 'centre inbox has zero items; the seeded backlog should render within the retention window' };
|
|
261
|
+
}
|
|
262
|
+
if (!initial.markAllRead) {
|
|
263
|
+
return { verdict: 'fail', detail: 'mark-all-read control missing ([data-action="mark-all-read"])' };
|
|
264
|
+
}
|
|
265
|
+
// Pick the first not-yet-acknowledged item and activate its
|
|
266
|
+
// acknowledge control by clicking. A Tab-driven activation
|
|
267
|
+
// would exercise the same handler; the runner has no keyboard
|
|
268
|
+
// seam beyond click / type / press but the fixture places the
|
|
269
|
+
// acknowledge control inside a <button> so the click is the
|
|
270
|
+
// same click a keyboard activation would trigger.
|
|
271
|
+
const firstUnackIndex = initial.itemAcknowledged.findIndex((v) => v !== 'true');
|
|
272
|
+
if (firstUnackIndex === -1) {
|
|
273
|
+
return { verdict: 'fail', detail: 'every seeded notification is already acknowledged; cannot exercise the round-trip on an unacknowledged item' };
|
|
274
|
+
}
|
|
275
|
+
const targetId = initial.itemIds[firstUnackIndex];
|
|
276
|
+
await browser.click('[data-action="acknowledge"][data-notification-id="' + targetId + '"]');
|
|
277
|
+
// Poll for the DOM update (bounded loop; the acknowledge
|
|
278
|
+
// round-trip should complete in well under a second).
|
|
279
|
+
const outcome = await browser.evaluate(async (targetId) => {
|
|
280
|
+
const start = Date.now();
|
|
281
|
+
return await new Promise((resolve) => {
|
|
282
|
+
function tick() {
|
|
283
|
+
const item = document.querySelector('[data-notification-id="' + targetId + '"]');
|
|
284
|
+
if (item && item.getAttribute('data-acknowledged') === 'true') {
|
|
285
|
+
resolve({ done: true });
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (Date.now() - start > 3000) {
|
|
289
|
+
resolve({ done: false });
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
setTimeout(tick, 50);
|
|
293
|
+
}
|
|
294
|
+
tick();
|
|
295
|
+
});
|
|
296
|
+
}, targetId);
|
|
297
|
+
if (!outcome || !outcome.done) {
|
|
298
|
+
return { verdict: 'fail', detail: 'acknowledge on notification ' + JSON.stringify(targetId) + ' did not flip data-acknowledged to true within 3s; the POST or the DOM update path is broken' };
|
|
299
|
+
}
|
|
300
|
+
// Reconcile the round-trip via window.__notificationFetches
|
|
301
|
+
// and /__requests: an `acknowledge` client record and an
|
|
302
|
+
// `acknowledge-server` server record should both exist for
|
|
303
|
+
// this notificationId.
|
|
304
|
+
const clientRecords = await browser.evaluate((targetId) => {
|
|
305
|
+
const fetches = window.__notificationFetches || [];
|
|
306
|
+
return fetches.filter((f) => f.type === 'acknowledge' && f.notificationId === targetId);
|
|
307
|
+
}, targetId);
|
|
308
|
+
if (!clientRecords || clientRecords.length === 0) {
|
|
309
|
+
return { verdict: 'fail', detail: 'no client-side acknowledge fetch recorded for ' + JSON.stringify(targetId) };
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
const requestsRes = await fetch(new URL('/__requests', runtimeUrl).toString());
|
|
313
|
+
if (requestsRes.status !== 200) {
|
|
314
|
+
return { verdict: 'fail', detail: '/__requests endpoint returned ' + requestsRes.status };
|
|
315
|
+
}
|
|
316
|
+
const requestsBody = await requestsRes.json();
|
|
317
|
+
const serverAcks = (Array.isArray(requestsBody) ? requestsBody : []).filter((r) => r.kind === 'acknowledge-server' && r.notificationId === targetId);
|
|
318
|
+
if (serverAcks.length === 0) {
|
|
319
|
+
return { verdict: 'fail', detail: 'no server-side acknowledge record for ' + JSON.stringify(targetId) + ' at /__requests' };
|
|
320
|
+
}
|
|
321
|
+
} catch (err) {
|
|
322
|
+
return { verdict: 'fail', detail: '/__requests endpoint unreachable: ' + err.message };
|
|
323
|
+
}
|
|
324
|
+
return { verdict: 'pass', detail: 'acknowledge round-trip on ' + JSON.stringify(targetId) + ' completed: ' + initial.itemCount + ' items seeded, client + server records reconciled, data-acknowledged flipped to true' };
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
};
|
|
@@ -43,6 +43,11 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
43
43
|
| observability-probe-endpoints | 14101-14899 | 15xx | shipped v1.1.0 | `healthProbes`, `readinessSemantics` |
|
|
44
44
|
| observability-logging | 15101-15899 | 16xx | shipped v1.0.0 | `logging` |
|
|
45
45
|
| application-error-handling | 16101-16899 | 17xx | shipped v1.0.0 | `errorHandling` |
|
|
46
|
+
| application-datatable | 17101-17899 | 18xx | shipped v1.0.0 | none |
|
|
47
|
+
| application-charts | 18101-18899 | 19xx | shipped v1.0.0 | none |
|
|
48
|
+
| application-dashboard | 19101-19899 | 20xx | shipped v1.0.0 | none |
|
|
49
|
+
| application-notifications-in-app | 20101-20899 | 21xx | shipped v1.0.0 | none |
|
|
50
|
+
| application-admin-console | 21101-21899 | 22xx | shipped v1.0.0 | none |
|
|
46
51
|
|
|
47
52
|
SPA v1.1.0 stays on the LOW end of its band: v1.0.0 occupied US-1101 through US-1128; v1.1.0 adds US-1129 and US-1130, leaving headroom above 1130 for future minor bumps and for project-side stories that mechanically derive to the 11xx numeric range.
|
|
48
53
|
|
|
@@ -49,6 +49,11 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
49
49
|
| deploy-cloudflare-workers | 12101-12899 | 13xx | shipped v1.0.0 | `deploymentTarget` |
|
|
50
50
|
| persistence-data-d1 | 13101-13899 | 14xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
51
51
|
| observability-probe-endpoints | 14101-14899 | 15xx | shipped v1.1.0 | `healthProbes`, `readinessSemantics` |
|
|
52
|
+
| application-datatable | 17101-17899 | 18xx | shipped v1.0.0 | none |
|
|
53
|
+
| application-charts | 18101-18899 | 19xx | shipped v1.0.0 | none |
|
|
54
|
+
| application-dashboard | 19101-19899 | 20xx | shipped v1.0.0 | none |
|
|
55
|
+
| application-notifications-in-app | 20101-20899 | 21xx | shipped v1.0.0 | none |
|
|
56
|
+
| application-admin-console | 21101-21899 | 22xx | shipped v1.0.0 | none |
|
|
52
57
|
|
|
53
58
|
US 6101-6110 sit at the LOW end of the 6101-6899 band on purpose. A project-side story that mechanically derives from `delivery-ci-workflows-REQ-011` into the number `6111` would collide against delivery-ci-workflows-US-6111 in this package; the band leaves headroom at the HIGH end (US 6181-6899) so a project's own stories anchored to delivery-ci-workflows REQs can allocate without conflict. The watchpost run4 lesson applies here too.
|
|
54
59
|
|
|
@@ -35,6 +35,11 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
35
35
|
| deploy-cloudflare-workers | 12101-12899 | 13xx | shipped v1.0.0 | `deploymentTarget` |
|
|
36
36
|
| persistence-data-d1 | 13101-13899 | 14xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
37
37
|
| observability-probe-endpoints | 14101-14899 | 15xx | shipped v1.1.0 | `healthProbes`, `readinessSemantics` |
|
|
38
|
+
| application-datatable | 17101-17899 | 18xx | shipped v1.0.0 | none |
|
|
39
|
+
| application-charts | 18101-18899 | 19xx | shipped v1.0.0 | none |
|
|
40
|
+
| application-dashboard | 19101-19899 | 20xx | shipped v1.0.0 | none |
|
|
41
|
+
| application-notifications-in-app | 20101-20899 | 21xx | shipped v1.0.0 | none |
|
|
42
|
+
| application-admin-console | 21101-21899 | 22xx | shipped v1.0.0 | none |
|
|
38
43
|
|
|
39
44
|
US 12101-12112 sit at the LOW end of the 12101-12899 band on purpose. A project-side story that mechanically derives from a deploy-cloudflare-workers REQ id into the number `12112` would collide against deploy-cloudflare-workers-US-12112 in this package; the band leaves headroom at the HIGH end (US 12181-12899) so a project's own stories anchored to deploy-cloudflare-workers REQs can allocate without conflict. The watchpost run4 lesson applies here too. Band spacing between the last shipped block (security-secrets-management, 8101-8899, 9xx) and this one (12101-12899, 13xx) leaves the 9xxx-11xxx US bands and the 10xx-12xx suffix blocks open for concurrent-lane authoring so a peer authoring another blueprint in the same PR window does not collide with this one.
|
|
40
45
|
|
|
@@ -31,6 +31,11 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
31
31
|
| deploy-cloudflare-workers | 12101-12899 | 13xx | shipped v1.0.0 | `deploymentTarget` |
|
|
32
32
|
| persistence-data-d1 | 13101-13899 | 14xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
33
33
|
| observability-probe-endpoints | 14101-14899 | 15xx | shipped v1.1.0 | `healthProbes`, `readinessSemantics` |
|
|
34
|
+
| application-datatable | 17101-17899 | 18xx | shipped v1.0.0 | none |
|
|
35
|
+
| application-charts | 18101-18899 | 19xx | shipped v1.0.0 | none |
|
|
36
|
+
| application-dashboard | 19101-19899 | 20xx | shipped v1.0.0 | none |
|
|
37
|
+
| application-notifications-in-app | 20101-20899 | 21xx | shipped v1.0.0 | none |
|
|
38
|
+
| application-admin-console | 21101-21899 | 22xx | shipped v1.0.0 | none |
|
|
34
39
|
|
|
35
40
|
US 4101-4106 sit at the LOW end of the 4101-4899 band on purpose. A project-side story that mechanically derives from an email-smtp-resend REQ id into the number `4106` (leading `4` + sequence `106`) would collide against email-smtp-resend-US-4106 in this package; the band leaves headroom at the HIGH end (US 4181-4899) so a project's own stories anchored to email-smtp-resend REQs can allocate without conflict. The watchpost run4 lesson applies here too.
|
|
36
41
|
|
|
@@ -50,6 +50,11 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
50
50
|
| deploy-cloudflare-workers | 12101-12899 | 13xx | shipped v1.0.0 | `deploymentTarget` |
|
|
51
51
|
| persistence-data-d1 | 13101-13899 | 14xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
52
52
|
| observability-probe-endpoints | 14101-14899 | 15xx | shipped v1.1.0 | `healthProbes`, `readinessSemantics` |
|
|
53
|
+
| application-datatable | 17101-17899 | 18xx | shipped v1.0.0 | none |
|
|
54
|
+
| application-charts | 18101-18899 | 19xx | shipped v1.0.0 | none |
|
|
55
|
+
| application-dashboard | 19101-19899 | 20xx | shipped v1.0.0 | none |
|
|
56
|
+
| application-notifications-in-app | 20101-20899 | 21xx | shipped v1.0.0 | none |
|
|
57
|
+
| application-admin-console | 21101-21899 | 22xx | shipped v1.0.0 | none |
|
|
53
58
|
|
|
54
59
|
US 7101-7110 sit at the LOW end of the 7101-7899 band on purpose. A project-side story that mechanically derives from an observability-essentials REQs id into the number `7110` would collide against observability-essentials-US-7110 in this package; the band leaves headroom at the HIGH end (US 7181-7899) so a project's own stories anchored to observability-essentials REQs can allocate without conflict. The watchpost run4 lesson applies here too.
|
|
55
60
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# observability-logging CHANGELOG
|
|
2
|
+
|
|
3
|
+
## 1.1.0 (visual round T-5, spec 2026-09-04 section 5.5.2, Baz Q2 default)
|
|
4
|
+
|
|
5
|
+
- Declares `capabilities: [auditLog]` on `blueprint.json`. No other change. The new field is additive per section 6 of `blueprint-authoring.md` (an additive optional field with no global-topic change is a minor bump). Keeps a single grammar for capability declaration: consumer blueprints read the union of declared `capabilities[]` on applied blueprints, never the `providesRoles[]` -> capability inference. This is the ratified rule (one grammar, no role-to-capability inference); the section 6a table previously noted `auditLog` as "implicit through the `logging` role" and now names `observability-logging` as the explicit shelf provider. Consumed at apply time by the visual round T-5 `application-admin-console` blueprint to gate the audit-log surface (probe pack check `AC-21105-1`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Logging blueprint (v1.0.0)
|
|
2
2
|
|
|
3
|
-
Structured single-line JSON emission for a rcf-lite project. General enterprise practice, no vendor lock-in. Ships the shelf-canonical `logging` global topic on `ADR-1601-observability-logging-line-shape` (transferred from `application-api-rest-ADR-304`, retained there as superseded history). Provides the `logging` role for the companion-suggestion mechanism.
|
|
3
|
+
Structured single-line JSON emission for a rcf-lite project. General enterprise practice, no vendor lock-in. Ships the shelf-canonical `logging` global topic on `ADR-1601-observability-logging-line-shape` (transferred from `application-api-rest-ADR-304`, retained there as superseded history). Provides the `logging` role for the companion-suggestion mechanism. From 1.1.0, declares `capabilities: [auditLog]` on `blueprint.json` so the application-admin-console probe pack activates the audit-log surface check `AC-21105-1` when this blueprint is applied on the project (one grammar; consumer blueprints read declared capabilities, never a role-to-capability inference).
|
|
4
4
|
|
|
5
5
|
## Apply
|
|
6
6
|
|