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.
Files changed (229) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/blueprints/application-admin-console/CHANGELOG.md +11 -0
  3. package/blueprints/application-admin-console/README.md +98 -0
  4. package/blueprints/application-admin-console/blueprint.json +226 -0
  5. package/blueprints/application-admin-console/contributions/adrs/adr-2201-application-admin-console-capability-vocabulary.json +25 -0
  6. package/blueprints/application-admin-console/contributions/adrs/adr-2202-application-admin-console-baseline-roles.json +25 -0
  7. package/blueprints/application-admin-console/contributions/adrs/adr-2203-application-admin-console-invite-transport.json +20 -0
  8. package/blueprints/application-admin-console/contributions/adrs/adr-2204-application-admin-console-audit-retention.json +20 -0
  9. package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-001.json +18 -0
  10. package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-002.json +18 -0
  11. package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-003.json +18 -0
  12. package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-004.json +18 -0
  13. package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-005.json +18 -0
  14. package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-006.json +18 -0
  15. package/blueprints/application-admin-console/contributions/tacs/tac-2201-application-admin-console-shell.json +40 -0
  16. package/blueprints/application-admin-console/contributions/tacs/tac-2202-application-admin-console-capability-discovery.json +44 -0
  17. package/blueprints/application-admin-console/contributions/tacs/tac-2203-application-admin-console-permission-matrix.json +34 -0
  18. package/blueprints/application-admin-console/contributions/tacs/tac-2204-application-admin-console-audit-view.json +40 -0
  19. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21101.json +34 -0
  20. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21102.json +34 -0
  21. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21103.json +34 -0
  22. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21104.json +28 -0
  23. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21105.json +34 -0
  24. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21106.json +27 -0
  25. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21107.json +27 -0
  26. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21108.json +27 -0
  27. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21109.json +33 -0
  28. package/blueprints/application-admin-console/docs/topics.md +35 -0
  29. package/blueprints/application-admin-console/guide/application-admin-console.md +29 -0
  30. package/blueprints/application-admin-console/probe-packs/application-admin-console.pack.mjs +182 -0
  31. package/blueprints/application-api-rest/docs/topics.md +5 -0
  32. package/blueprints/application-charts/CHANGELOG.md +12 -0
  33. package/blueprints/application-charts/README.md +73 -0
  34. package/blueprints/application-charts/blueprint.json +26 -0
  35. package/blueprints/application-charts/contributions/adrs/adr-1901-application-charts-engine-choice.json +25 -0
  36. package/blueprints/application-charts/contributions/adrs/adr-1902-application-charts-accessible-palette.json +25 -0
  37. package/blueprints/application-charts/contributions/adrs/adr-1903-application-charts-motion.json +25 -0
  38. package/blueprints/application-charts/contributions/requirements/application-charts-req-001.json +15 -0
  39. package/blueprints/application-charts/contributions/requirements/application-charts-req-002.json +15 -0
  40. package/blueprints/application-charts/contributions/requirements/application-charts-req-003.json +15 -0
  41. package/blueprints/application-charts/contributions/requirements/application-charts-req-004.json +15 -0
  42. package/blueprints/application-charts/contributions/tacs/tac-1901-application-charts-render-shell.json +42 -0
  43. package/blueprints/application-charts/contributions/tacs/tac-1902-application-charts-keyboard-traversal.json +40 -0
  44. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18101.json +35 -0
  45. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18102.json +35 -0
  46. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18103.json +35 -0
  47. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18104.json +35 -0
  48. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18105.json +35 -0
  49. package/blueprints/application-charts/contributions/user-stories/application-charts-us-18106.json +35 -0
  50. package/blueprints/application-charts/docs/topics.md +37 -0
  51. package/blueprints/application-charts/guide/application-charts.md +44 -0
  52. package/blueprints/application-charts/probe-packs/application-charts.pack.mjs +191 -0
  53. package/blueprints/application-dashboard/CHANGELOG.md +10 -0
  54. package/blueprints/application-dashboard/README.md +102 -0
  55. package/blueprints/application-dashboard/assets/guidance/dashboard-design.md +124 -0
  56. package/blueprints/application-dashboard/blueprint.json +29 -0
  57. package/blueprints/application-dashboard/contributions/adrs/adr-2001-application-dashboard-primary-kpi-enum.json +25 -0
  58. package/blueprints/application-dashboard/contributions/adrs/adr-2002-application-dashboard-timeframe-presets.json +25 -0
  59. package/blueprints/application-dashboard/contributions/adrs/adr-2003-application-dashboard-export-formats.json +25 -0
  60. package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-001.json +15 -0
  61. package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-002.json +15 -0
  62. package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-003.json +15 -0
  63. package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-004.json +15 -0
  64. package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-005.json +15 -0
  65. package/blueprints/application-dashboard/contributions/tacs/tac-2001-application-dashboard-tile-grid.json +47 -0
  66. package/blueprints/application-dashboard/contributions/tacs/tac-2002-application-dashboard-filter-chrome.json +41 -0
  67. package/blueprints/application-dashboard/contributions/tacs/tac-2003-application-dashboard-export-handle.json +35 -0
  68. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19101.json +35 -0
  69. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19102.json +35 -0
  70. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19103.json +35 -0
  71. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19104.json +35 -0
  72. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19105.json +35 -0
  73. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19106.json +35 -0
  74. package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19107.json +35 -0
  75. package/blueprints/application-dashboard/docs/topics.md +43 -0
  76. package/blueprints/application-dashboard/guide/application-dashboard.md +48 -0
  77. package/blueprints/application-dashboard/probe-packs/application-dashboard.pack.mjs +231 -0
  78. package/blueprints/application-datatable/CHANGELOG.md +8 -0
  79. package/blueprints/application-datatable/README.md +43 -0
  80. package/blueprints/application-datatable/blueprint.json +33 -0
  81. package/blueprints/application-datatable/contributions/adrs/adr-1801-application-datatable-pattern-choice.json +25 -0
  82. package/blueprints/application-datatable/contributions/adrs/adr-1802-application-datatable-url-state.json +20 -0
  83. package/blueprints/application-datatable/contributions/adrs/adr-1803-application-datatable-page-size.json +20 -0
  84. package/blueprints/application-datatable/contributions/adrs/adr-1804-application-datatable-selection-persistence.json +20 -0
  85. package/blueprints/application-datatable/contributions/requirements/application-datatable-req-001.json +15 -0
  86. package/blueprints/application-datatable/contributions/requirements/application-datatable-req-002.json +15 -0
  87. package/blueprints/application-datatable/contributions/requirements/application-datatable-req-003.json +15 -0
  88. package/blueprints/application-datatable/contributions/requirements/application-datatable-req-004.json +15 -0
  89. package/blueprints/application-datatable/contributions/requirements/application-datatable-req-005.json +15 -0
  90. package/blueprints/application-datatable/contributions/requirements/application-datatable-req-006.json +15 -0
  91. package/blueprints/application-datatable/contributions/tacs/tac-1801-application-datatable-shell.json +50 -0
  92. package/blueprints/application-datatable/contributions/tacs/tac-1802-application-datatable-query-adapter.json +39 -0
  93. package/blueprints/application-datatable/contributions/tacs/tac-1803-application-datatable-selection-model.json +28 -0
  94. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17101.json +44 -0
  95. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17102.json +44 -0
  96. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17103.json +44 -0
  97. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17104.json +44 -0
  98. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17105.json +44 -0
  99. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17106.json +44 -0
  100. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17107.json +44 -0
  101. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17108.json +44 -0
  102. package/blueprints/application-datatable/contributions/user-stories/application-datatable-us-17109.json +44 -0
  103. package/blueprints/application-datatable/docs/topics.md +35 -0
  104. package/blueprints/application-datatable/guide/application-datatable.md +38 -0
  105. package/blueprints/application-datatable/probe-packs/application-datatable.pack.mjs +240 -0
  106. package/blueprints/application-error-handling/docs/topics.md +5 -0
  107. package/blueprints/application-notifications-in-app/CHANGELOG.md +9 -0
  108. package/blueprints/application-notifications-in-app/README.md +80 -0
  109. package/blueprints/application-notifications-in-app/blueprint.json +30 -0
  110. package/blueprints/application-notifications-in-app/contributions/adrs/adr-2101-application-notifications-in-app-live-region-role-mapping.json +25 -0
  111. package/blueprints/application-notifications-in-app/contributions/adrs/adr-2102-application-notifications-in-app-toast-timeout-floor.json +25 -0
  112. package/blueprints/application-notifications-in-app/contributions/adrs/adr-2103-application-notifications-in-app-retention-window.json +25 -0
  113. package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-001.json +15 -0
  114. package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-002.json +15 -0
  115. package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-003.json +15 -0
  116. package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-004.json +15 -0
  117. package/blueprints/application-notifications-in-app/contributions/requirements/application-notifications-in-app-req-005.json +15 -0
  118. package/blueprints/application-notifications-in-app/contributions/tacs/tac-2101-application-notifications-in-app-live-region.json +46 -0
  119. package/blueprints/application-notifications-in-app/contributions/tacs/tac-2102-application-notifications-in-app-centre.json +52 -0
  120. package/blueprints/application-notifications-in-app/contributions/tacs/tac-2103-application-notifications-in-app-preferences.json +40 -0
  121. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20101.json +35 -0
  122. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20102.json +35 -0
  123. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20103.json +35 -0
  124. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20104.json +35 -0
  125. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20105.json +35 -0
  126. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20106.json +26 -0
  127. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20107.json +26 -0
  128. package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20108.json +26 -0
  129. package/blueprints/application-notifications-in-app/docs/topics.md +39 -0
  130. package/blueprints/application-notifications-in-app/guide/application-notifications-in-app.md +47 -0
  131. package/blueprints/application-notifications-in-app/probe-packs/application-notifications-in-app.pack.mjs +328 -0
  132. package/blueprints/application-spa/docs/topics.md +5 -0
  133. package/blueprints/delivery-ci-workflows/docs/topics.md +5 -0
  134. package/blueprints/deploy-cloudflare-workers/docs/topics.md +5 -0
  135. package/blueprints/email-smtp-resend/docs/topics.md +5 -0
  136. package/blueprints/observability-essentials/docs/topics.md +5 -0
  137. package/blueprints/observability-logging/CHANGELOG.md +5 -0
  138. package/blueprints/observability-logging/README.md +1 -1
  139. package/blueprints/observability-logging/blueprint.json +113 -21
  140. package/blueprints/observability-logging/docs/topics.md +5 -0
  141. package/blueprints/observability-probe-endpoints/docs/topics.md +5 -0
  142. package/blueprints/persistence-data-d1/docs/topics.md +5 -0
  143. package/blueprints/persistence-data-sqlite/docs/topics.md +5 -0
  144. package/blueprints/security-auth-clerk/CHANGELOG.md +5 -0
  145. package/blueprints/security-auth-clerk/blueprint.json +2 -1
  146. package/blueprints/security-auth-clerk/docs/topics.md +5 -0
  147. package/blueprints/security-auth-keycloak/CHANGELOG.md +5 -0
  148. package/blueprints/security-auth-keycloak/blueprint.json +2 -1
  149. package/blueprints/security-auth-keycloak/docs/topics.md +5 -0
  150. package/blueprints/security-auth-magic-link/CHANGELOG.md +5 -0
  151. package/blueprints/security-auth-magic-link/blueprint.json +2 -1
  152. package/blueprints/security-auth-magic-link/docs/topics.md +5 -0
  153. package/blueprints/security-auth-oauth2/CHANGELOG.md +5 -0
  154. package/blueprints/security-auth-oauth2/blueprint.json +2 -1
  155. package/blueprints/security-auth-oauth2/docs/topics.md +5 -0
  156. package/blueprints/security-secrets-management/docs/topics.md +5 -0
  157. package/fixtures/canary-manifest.json +9 -9
  158. package/package.json +2 -2
  159. package/rcf/code-nodes/cn-106.json +13 -0
  160. package/rcf/code-nodes/cn-107.json +14 -0
  161. package/rcf/code-nodes/cn-108.json +13 -0
  162. package/rcf/code-nodes/cn-109.json +12 -0
  163. package/rcf/code-nodes/cn-110.json +12 -0
  164. package/rcf/code-nodes/cn-111.json +14 -0
  165. package/rcf/code-nodes/cn-112.json +14 -0
  166. package/rcf/code-nodes/cn-113.json +12 -0
  167. package/rcf/code-nodes/cn-114.json +13 -0
  168. package/rcf/code-nodes/cn-115.json +13 -0
  169. package/rcf/code-nodes/cn-116.json +12 -0
  170. package/rcf/code-nodes/cn-117.json +13 -0
  171. package/rcf/code-nodes/cn-118.json +13 -0
  172. package/rcf/code-nodes/cn-119.json +13 -0
  173. package/rcf/code-nodes/cn-120.json +13 -0
  174. package/rcf/code-nodes/cn-121.json +12 -0
  175. package/rcf/code-nodes/cn-122.json +12 -0
  176. package/rcf/code-nodes/cn-123.json +13 -0
  177. package/rcf/code-nodes/cn-124.json +13 -0
  178. package/rcf/code-nodes/cn-125.json +13 -0
  179. package/rcf/code-nodes/cn-126.json +13 -0
  180. package/rcf/code-nodes/cn-127.json +12 -0
  181. package/rcf/code-nodes/cn-128.json +14 -0
  182. package/rcf/code-nodes/cn-129.json +14 -0
  183. package/rcf/code-nodes/cn-130.json +14 -0
  184. package/rcf/code-nodes/cn-131.json +15 -0
  185. package/rcf/code-nodes/cn-132.json +12 -0
  186. package/rcf/code-nodes/cn-133.json +13 -0
  187. package/rcf/fbs/fbs-036.json +22 -0
  188. package/rcf/fbs/fbs-037.json +19 -0
  189. package/rcf/fbs/fbs-038.json +19 -0
  190. package/rcf/fbs/fbs-039.json +21 -0
  191. package/rcf/fbs/fbs-040.json +20 -0
  192. package/rcf/fbs/fbs-041.json +13 -0
  193. package/rcf/fbs/fbs-042.json +13 -0
  194. package/rcf/requirements/req-017.json +18 -0
  195. package/rcf/requirements/req-018.json +18 -0
  196. package/rcf/requirements/req-019.json +38 -0
  197. package/rcf/requirements/req-020.json +13 -0
  198. package/rcf/requirements/req-021.json +13 -0
  199. package/rcf/requirements/req-022.json +13 -0
  200. package/rcf/requirements/req-023.json +14 -0
  201. package/rcf/test-suites/ts-046.json +78 -0
  202. package/rcf/test-suites/ts-047.json +54 -0
  203. package/rcf/test-suites/ts-048.json +54 -0
  204. package/rcf/test-suites/ts-049.json +70 -0
  205. package/rcf/test-suites/ts-050.json +69 -0
  206. package/rcf/test-suites/ts-051.json +22 -0
  207. package/rcf/test-suites/ts-052.json +20 -0
  208. package/rcf/user-stories/us-1701.json +56 -0
  209. package/rcf/user-stories/us-1801.json +40 -0
  210. package/rcf/user-stories/us-1901.json +40 -0
  211. package/rcf/user-stories/us-2001.json +50 -0
  212. package/rcf/user-stories/us-2101.json +45 -0
  213. package/rcf/user-stories/us-2201.json +60 -0
  214. package/rcf/user-stories/us-2301.json +51 -0
  215. package/releases/releases.yaml +11 -1
  216. package/src/blueprint/apply.js +98 -1
  217. package/src/blueprint/capabilities.js +318 -0
  218. package/src/blueprint/loader.js +247 -0
  219. package/src/browser-verify/boot.js +180 -0
  220. package/src/browser-verify/index.js +13 -0
  221. package/src/browser-verify/manifest-writer.js +31 -6
  222. package/src/browser-verify/pack-browser.js +294 -0
  223. package/src/browser-verify/pack-loader.js +176 -0
  224. package/src/browser-verify/pack-runner.js +193 -0
  225. package/src/browser-verify/pack-schema.js +155 -0
  226. package/src/browser-verify/runner.js +24 -1
  227. package/src/cli/blueprint.js +92 -0
  228. package/src/cli/browser-verify.js +147 -7
  229. package/src/core/errors/index.js +7 -0
@@ -0,0 +1,51 @@
1
+ {
2
+ "createdAt": "2026-09-05T01:30:00.000Z",
3
+ "updatedAt": "2026-09-05T01:30:00.000Z",
4
+ "usId": "US-2301",
5
+ "prdId": "PRD-001",
6
+ "reqId": "REQ-023",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "title": "The application-admin-console blueprint ships on the shelf; its probe pack fires only on applied capabilities and refuses on a broken admin surface",
10
+ "asA": "shelf consumer applying the application-admin-console v1.0.0 blueprint",
11
+ "iWant": "the shipped blueprint files with a users, roles, orgs, audit-log surface set that self-configures from the applied capability declarations, the Playwright probe pack whose four checks each gate their fire on the applied capability, and a sample-app fixture proving the pack passes on a compliant surface, records applicable: false where the capability is absent, and refuses on the three break switches",
12
+ "soThat": "a project consuming rcf-lite gets a vendor-neutral admin console conditional on what identity blueprint is applied, no dead links to unapplied capabilities, the pack that proves the ARIA APG grid pattern on the permission matrix, the request-log ACs on the audit view, and a golden fixture the pack refuses on a surface that loses the grid roles, drops the request-access control, or writes an audit entry missing the correlation id",
13
+ "acceptanceCriteria": [
14
+ {
15
+ "id": "AC-2301-1",
16
+ "description": "The shelf carries blueprints/application-admin-console/blueprint.json declaring slug application-admin-console, version 1.0.0, category application, 23 contributions in the 6 REQ / 9 US / 4 TAC / 4 ADR groups per spec section 5.5; requiresAppliedCapabilities.capabilities is [principalDirectory] with allowSkipFlag: allow-no-auth-yet; elicits[] declares baselineRoles (when roleModel), tenancyShape (when tenancy), inviteTransport, and auditRetentionDays (when auditLog); suggestedCompanions declares logging and errorHandling with reasons from spec section 5.5; providesRoles is absent per spec 5.5.3.",
17
+ "testable": true
18
+ },
19
+ {
20
+ "id": "AC-2301-2",
21
+ "description": "rcf define blueprint add ./blueprints/application-admin-console --allow-no-auth-yet applies the 23 contributions cleanly on a fresh init project with only application-spa applied; rcf define validate reports the tree clean; both rcf audit coverage --strict and rcf audit eval coverage --strict exit 0 on the scratch project; the sidecar rcf/blueprints/application-admin-console.applied.json declares allowNoAuthYet: true and an appliedCapabilities: [] union.",
22
+ "testable": true
23
+ },
24
+ {
25
+ "id": "AC-2301-3",
26
+ "description": "rcf define blueprint add ./blueprints/application-admin-console on a project with only application-spa applied exits 3 with a stderr message beginning 'application-admin-console requires at least one applied security-auth-*' and naming the currently-applied blueprints; nothing is written to disk (no contributions, no sidecar, no manifest entry).",
27
+ "testable": true
28
+ },
29
+ {
30
+ "id": "AC-2301-4",
31
+ "description": "rcf define blueprint add ./blueprints/application-admin-console on a project with security-auth-magic-link 1.1.0 applied applies cleanly; the sidecar's appliedCapabilities reads exactly [principalDirectory] with no roleModel; rcf define blueprint add on a project with security-auth-clerk 1.2.0 + observability-logging applied produces appliedCapabilities [principalDirectory, roleModel, auditLog] (order-agnostic).",
32
+ "testable": true
33
+ },
34
+ {
35
+ "id": "AC-2301-5",
36
+ "description": "The shipped probe pack blueprints/application-admin-console/probe-packs/application-admin-console.pack.mjs loads via T-0's pack-loader against a scratch project with the blueprint applied; each of its four check ids (AC-21102-1, AC-21103-1, AC-21104-1, AC-21105-1) resolves to an AC id contributed by the blueprint per T-0 AC-1701-3; every check carries a description field per spec section 9; each check's appliesTo predicate reads manifest.blueprints[]/${slug}.applied.json capabilities and returns false when the required capability is absent so the check records applicable: false; the pack-level appliesTo predicate references BOTH tacIds and route.",
37
+ "testable": true
38
+ },
39
+ {
40
+ "id": "AC-2301-6",
41
+ "description": "The sample-app fixture at packages/rcf-lite/test/fixtures/probe-pack-application-admin-console/ ships a dependency-free Node HTTP server (server.js) with a start script; CAPS env or ?caps= query switch selects which surfaces exist mirroring the applied capability set (principalDirectory, roleModel, tenancy, auditLog, or comma combinations thereof); the console shell nav only links applied surfaces; the users directory table enumerates every row by data-user-id inside the role=table region; the permission matrix renders under role=grid with role=row on every rank, role=rowheader on the leftmost cell of each role rank, role=columnheader on every permission header, role=gridcell on inner cells, and each cell announces the exact permission string on focus via aria-label; the org switcher is present only when tenancy is applied; the audit-log table enumerates every row by data-audit-id and every audit entry carries actor, target, before, after, timestamp, and correlationId in inspectable columns; an access-denied route renders a request-access control routed to a POST /api/request-access endpoint; window.__adminFetches and /__requests mirror every request.",
42
+ "testable": true
43
+ },
44
+ {
45
+ "id": "AC-2301-7",
46
+ "description": "The probe pack refuses ship on each broken fixture: with ?break=matrix-grid the permission matrix drops the role=grid attributes so AC-21103-1 fails at severity block; with ?break=denied the access-denied route drops the request-access control so AC-21102-1's negative branch (a principal without admin scope) fails at severity block; with ?break=audit-fields the audit entries omit the correlationId column so AC-21105-1 fails at severity block."
47
+ ,
48
+ "testable": true
49
+ }
50
+ ]
51
+ }
@@ -40,8 +40,18 @@
40
40
  # `npm install rcf-lite`.
41
41
 
42
42
  feedVersion: 1
43
- latest: "0.20.0"
43
+ latest: "0.21.0"
44
44
  releases:
45
+ - version: "0.21.0"
46
+ date: "2026-09-05"
47
+ breaking: false
48
+ headlines:
49
+ - "rcf-lite 0.21.0 adds five visual-feature blueprints to the shelf (data table, charts, dashboard with packaged design guidance, in-app notifications, admin console) and the mechanism that proves them: a blueprint can now ship a Playwright probe pack that rcf verify runs against your running app in a real browser, and the ship gate refuses while a surface check fails."
50
+ - "The admin console only offers the surfaces your applied auth blueprint can support. Blueprints can now ask you questions at apply time."
51
+ - "The schema floor moves to rcf-schemas 0.6.1."
52
+ minAgentAction: null
53
+ notesUrl: "https://stravica.ai/docs/rcf/changelog/"
54
+
45
55
  - version: "0.20.0"
46
56
  date: "2026-09-04"
47
57
  breaking: true
@@ -20,6 +20,7 @@ import { loadBlueprint } from './loader.js';
20
20
  import { updateManifest } from './manifest-writer.js';
21
21
  import { stampId } from './namespace.js';
22
22
  import { nextResolutionId } from './resolutions.js';
23
+ import { buildRefusalMessage, discoverAppliedCapabilities, runCustomAuthCapabilityElicits, runElicitationPhase, writeSidecar } from './capabilities.js';
23
24
 
24
25
  /**
25
26
  * @typedef {object} ApplyResult
@@ -79,7 +80,7 @@ import { nextResolutionId } from './resolutions.js';
79
80
  * prove the rollback runs. Never used in production.
80
81
  * @returns {Promise<ApplyResult | import('../core/errors/index.js').RcfError>}
81
82
  */
82
- export async function applyBlueprint({ projectRoot, tree, source, displaySource, namespaceOverride, effectiveSlug, libraryPrefix, libraryBands, resolveDeclarations, now = new Date(), dryRun = false, _copyFileForTest }) {
83
+ export async function applyBlueprint({ projectRoot, tree, source, displaySource, namespaceOverride, effectiveSlug, libraryPrefix, libraryBands, resolveDeclarations, allowNoAuthYet = false, elicitAnswers = null, customAuthReadLine = null, now = new Date(), dryRun = false, _copyFileForTest }) {
83
84
  const hintSource = typeof displaySource === 'string' && displaySource.length > 0 ? displaySource : source;
84
85
  const blueprint = await loadBlueprint(source);
85
86
  if (blueprint.kind) return blueprint; // RcfError
@@ -105,6 +106,69 @@ export async function applyBlueprint({ projectRoot, tree, source, displaySource,
105
106
  if (bandErr) return bandErr;
106
107
  }
107
108
 
109
+ // Capability-declaration mechanism (visual round T-5 spec 5.5).
110
+ // Discover the union of applied capabilities across every applied
111
+ // blueprint via source read-back; the applied-blueprint-record
112
+ // schema is closed (rcf-schemas 0.6.0) and does not carry a
113
+ // capabilities field, so the source blueprint.json is the ground
114
+ // truth for what each applied blueprint declares.
115
+ const discovery = await discoverAppliedCapabilities(applied, projectRoot);
116
+ const discoveredCapabilities = discovery.union;
117
+
118
+ // Custom-auth capability elicitation (visual round T-5 spec 5.5
119
+ // "Risks": custom auth elicits per capability). Fires BEFORE the
120
+ // requiresAppliedCapabilities refusal gate: providesCapability
121
+ // elicits let a project with no shelf auth blueprint declare which
122
+ // capabilities its own custom auth carries. Truthy answers fold
123
+ // into the effective applied set; that set is what the refusal gate
124
+ // and the standard elicitation phase see.
125
+ const customAuth = await runCustomAuthCapabilityElicits({
126
+ elicits: blueprint.elicits,
127
+ answers: elicitAnswers ?? {},
128
+ readLine: customAuthReadLine ?? undefined,
129
+ });
130
+ if (customAuth.kind) return customAuth; // RcfError
131
+ const appliedCapabilities = [...new Set([...discoveredCapabilities, ...customAuth.capsProvided])].sort();
132
+ const customAuthElicitations = customAuth.answered;
133
+
134
+ // Refusal gate for consumer blueprints that require at least one
135
+ // applied capability. When the effective set has no overlap AND
136
+ // the CLI did not pass the allow-skip flag AND no custom-auth
137
+ // capability answers were supplied that satisfy the gate, refuse
138
+ // with the templated message (spec 5.5.1 verbatim for admin-console).
139
+ if (blueprint.requiresAppliedCapabilities && !allowNoAuthYet) {
140
+ const required = blueprint.requiresAppliedCapabilities.capabilities;
141
+ const overlap = required.some((c) => appliedCapabilities.includes(c));
142
+ if (!overlap) {
143
+ const message = buildRefusalMessage({
144
+ slug: appliedSlug,
145
+ refusalMessageId: blueprint.requiresAppliedCapabilities.refusalMessageId,
146
+ requiredCapabilities: required,
147
+ appliedRecords: applied,
148
+ allowSkipFlag: blueprint.requiresAppliedCapabilities.allowSkipFlag,
149
+ });
150
+ return rcfError({ kind: 'requiresAppliedCapabilities', message });
151
+ }
152
+ }
153
+
154
+ // Elicitation phase (visual round T-5 spec section 5.5, T-2 gate).
155
+ // Evaluate each declared elicit against the effective capability set
156
+ // (union of applied blueprints and custom-auth answers). Skip
157
+ // providesCapability entries: those were resolved above and their
158
+ // answers already landed in customAuthElicitations. Coerce operator
159
+ // answers per kind. A missing required answer refuses before any
160
+ // write.
161
+ const regularElicits = Array.isArray(blueprint.elicits)
162
+ ? blueprint.elicits.filter((e) => typeof e.providesCapability !== 'string')
163
+ : [];
164
+ const elicitResult = runElicitationPhase({
165
+ elicits: regularElicits,
166
+ appliedCapabilities,
167
+ answers: elicitAnswers ?? {},
168
+ });
169
+ if (elicitResult.kind) return elicitResult; // RcfError
170
+ const appliedElicitations = { ...customAuthElicitations, ...elicitResult.value };
171
+
108
172
  // Pre-detection: fold operator-supplied --resolve declarations into a
109
173
  // WORKING COPY of the manifest so the detector honours them on this
110
174
  // run. The final manifest write later composes the same resolution
@@ -302,11 +366,44 @@ export async function applyBlueprint({ projectRoot, tree, source, displaySource,
302
366
  });
303
367
  if (manifestResult.kind) return manifestResult; // RcfError
304
368
 
369
+ // Sidecar write for the capability-declaration mechanism (visual
370
+ // round T-5 spec 5.5.1). Per-project apply-state that the applied-
371
+ // blueprint-record schema cannot carry today: the discovered
372
+ // appliedCapabilities snapshot, the elicit answers, and (when the
373
+ // gate was skipped) an allowNoAuthYet flag plus a notes string that
374
+ // rcf define validate later reads to flag ungated surfaces. The
375
+ // sidecar is written on ANY apply that carries capabilities-related
376
+ // data (declared capabilities, requiresAppliedCapabilities gate,
377
+ // elicits, or an override) so probe packs and audit tooling have
378
+ // one deterministic file to read. dryRun skips the write.
379
+ const wroteSidecar = !dryRun && (
380
+ blueprint.requiresAppliedCapabilities !== undefined ||
381
+ (Array.isArray(blueprint.elicits) && blueprint.elicits.length > 0) ||
382
+ Array.isArray(blueprint.capabilities) ||
383
+ allowNoAuthYet === true
384
+ );
385
+ let sidecarPath = null;
386
+ if (wroteSidecar) {
387
+ sidecarPath = await writeSidecar({
388
+ projectRoot,
389
+ slug: appliedSlug,
390
+ version: blueprint.version,
391
+ appliedAt: now.toISOString(),
392
+ appliedCapabilities,
393
+ appliedElicitations,
394
+ allowNoAuthYet: allowNoAuthYet === true ? true : undefined,
395
+ notes: allowNoAuthYet === true
396
+ ? `no auth yet: applied under --${blueprint.requiresAppliedCapabilities?.allowSkipFlag ?? 'allow-no-auth-yet'}; surfaces gated on ${(blueprint.requiresAppliedCapabilities?.capabilities ?? []).join(', ')} will refuse at runtime until an auth blueprint is applied.`
397
+ : undefined,
398
+ });
399
+ }
400
+
305
401
  return {
306
402
  applied: true,
307
403
  slug: appliedSlug,
308
404
  version: blueprint.version,
309
405
  contributions: writtenContributions,
406
+ ...(wroteSidecar ? { sidecarPath, appliedCapabilities, appliedElicitations } : {}),
310
407
  // Companion-suggestion mechanism (spec 2.6). Bubble the source
311
408
  // blueprint's `suggestedCompanions[]` up on the result so the CLI
312
409
  // can render the resolved-suggestion block after a successful
@@ -0,0 +1,318 @@
1
+ // Capability-declaration mechanism (visual round T-5 spec section
2
+ // 5.5.1, 5.5.2). Consumer blueprints gate their surfaces on the union
3
+ // of currently-applied blueprints' `capabilities[]` declarations.
4
+ //
5
+ // Why source read-back. The applied-blueprint-record schema in
6
+ // @stravica-ai/rcf-schemas is closed under additionalProperties:false
7
+ // (0.6.0), so `appliedCapabilities[]` cannot land on the manifest
8
+ // record without a schema bump. Rather than block the round on that
9
+ // dependency, this file adopts the core-companions T-2 gate precedent:
10
+ // read the source blueprint.json via manifest.blueprints[].source and
11
+ // union its declared capabilities[]. Per-project answers (elicits) and
12
+ // a per-project applied-capability snapshot are written to a sidecar
13
+ // file at rcf/blueprints/${slug}.applied.json.
14
+ //
15
+ // This file is imported by apply.js at apply time and by the shipped
16
+ // probe pack (blueprints/application-admin-console/probe-packs/) at
17
+ // verify time.
18
+
19
+ import { readFile, writeFile, mkdir, stat } from 'node:fs/promises';
20
+ import { dirname, join, resolve, isAbsolute } from 'node:path';
21
+
22
+ import { rcfError } from '../core/errors/index.js';
23
+
24
+ /**
25
+ * Read a single applied blueprint's declared capabilities via
26
+ * source read-back. Silently ignores an applied blueprint whose
27
+ * source path no longer exists (the record's ownership fact is the
28
+ * slug on the manifest; the source file is provenance and can move).
29
+ *
30
+ * @param {{ slug: string, source: string }} appliedRecord
31
+ * @param {string} projectRoot
32
+ * @returns {Promise<string[]>}
33
+ */
34
+ export async function readAppliedBlueprintCapabilities(appliedRecord, projectRoot) {
35
+ if (!appliedRecord || typeof appliedRecord.source !== 'string') return [];
36
+ const src = appliedRecord.source;
37
+ // A qualified typed ref (`wsd:auth-oauth2`) never resolves as a file
38
+ // path. Skip it; the library-resolved source blueprint is not on
39
+ // this local tree.
40
+ if (/^[a-z][a-z0-9-]*:[a-z]/i.test(src) && !src.startsWith('/') && !src.startsWith('.')) return [];
41
+ const abs = isAbsolute(src) ? src : resolve(projectRoot, src);
42
+ const metaPath = join(abs, 'blueprint.json');
43
+ let raw;
44
+ try {
45
+ raw = await readFile(metaPath, 'utf8');
46
+ } catch {
47
+ return [];
48
+ }
49
+ try {
50
+ const doc = JSON.parse(raw);
51
+ if (!Array.isArray(doc.capabilities)) return [];
52
+ return doc.capabilities.filter((s) => typeof s === 'string');
53
+ } catch {
54
+ return [];
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Discover the union of applied capabilities across every applied
60
+ * blueprint on the tree.
61
+ *
62
+ * @param {Array<{ slug: string, source: string }>} appliedRecords
63
+ * @param {string} projectRoot
64
+ * @returns {Promise<{ union: string[], perSlug: Record<string, string[]> }>}
65
+ */
66
+ export async function discoverAppliedCapabilities(appliedRecords, projectRoot) {
67
+ const perSlug = {};
68
+ const union = new Set();
69
+ for (const rec of appliedRecords ?? []) {
70
+ const caps = await readAppliedBlueprintCapabilities(rec, projectRoot);
71
+ perSlug[rec.slug] = caps;
72
+ for (const c of caps) union.add(c);
73
+ }
74
+ return { union: [...union].sort(), perSlug };
75
+ }
76
+
77
+ /**
78
+ * Build the refusal message for a requiresAppliedCapabilities gate.
79
+ * refusalMessageId maps to a known template; a message the loader
80
+ * accepts is used verbatim if the id is unknown (so a future
81
+ * blueprint can ship a bespoke refusal without a core code change).
82
+ *
83
+ * @param {object} args
84
+ * @param {string} args.slug applying blueprint slug
85
+ * @param {string} args.refusalMessageId
86
+ * @param {string[]} args.requiredCapabilities
87
+ * @param {Array<{ slug: string, version: string }>} args.appliedRecords
88
+ * @param {string} args.allowSkipFlag the CLI flag the operator can use to override
89
+ * @returns {string}
90
+ */
91
+ export function buildRefusalMessage({ slug, refusalMessageId, requiredCapabilities, appliedRecords, allowSkipFlag }) {
92
+ // Applied blueprints list, one per line (indented 2 spaces per spec
93
+ // 5.5.1 formatting).
94
+ const appliedLines = (appliedRecords ?? [])
95
+ .map((r) => ` ${r.slug} v${r.version}`)
96
+ .join('\n');
97
+ const appliedBlock = appliedLines.length > 0 ? appliedLines : ' (none)';
98
+ if (refusalMessageId === 'application-admin-console-bare-spa') {
99
+ return [
100
+ `${slug} requires at least one applied security-auth-* blueprint,`,
101
+ `or an operator override with --${allowSkipFlag}.`,
102
+ '',
103
+ 'Applied blueprints on this project:',
104
+ appliedBlock,
105
+ '',
106
+ 'Suggested next steps:',
107
+ ' 1. Apply an auth blueprint first:',
108
+ ' rcf define blueprint add security-auth-magic-link',
109
+ ' (or security-auth-clerk, security-auth-oauth2, security-auth-keycloak)',
110
+ ` 2. Override for a scaffolding pass (surfaces will refuse at apply until an`,
111
+ ` auth blueprint is applied):`,
112
+ ` rcf define blueprint add ${slug} --${allowSkipFlag}`,
113
+ ].join('\n');
114
+ }
115
+ // Generic fallback. Names the required capabilities and applied set;
116
+ // the operator has enough to diagnose without a bespoke template.
117
+ return [
118
+ `${slug} requires an applied blueprint declaring at least one of:`,
119
+ ` ${requiredCapabilities.join(', ')}`,
120
+ `or an operator override with --${allowSkipFlag}.`,
121
+ '',
122
+ 'Applied blueprints on this project:',
123
+ appliedBlock,
124
+ ].join('\n');
125
+ }
126
+
127
+ /**
128
+ * Run the elicitation phase. Reads a bag of operator-supplied answers
129
+ * (`--answer <id>=<value>` merged with `--answers <file>` in the CLI),
130
+ * evaluates each elicit's `when` predicate against the discovered
131
+ * capability set, coerces the answer per kind, and returns a
132
+ * `{id: value}` map. A missing required answer for an elicit whose
133
+ * predicate fires refuses with a validation rcfError.
134
+ *
135
+ * @param {object} args
136
+ * @param {Array<object>} args.elicits from loader
137
+ * @param {string[]} args.appliedCapabilities discovered union
138
+ * @param {Record<string, string|boolean>} args.answers operator bag
139
+ * @returns {{value: Record<string, string|boolean|null>} | import('../core/errors/index.js').RcfError}
140
+ */
141
+ export function runElicitationPhase({ elicits, appliedCapabilities, answers }) {
142
+ if (!Array.isArray(elicits) || elicits.length === 0) {
143
+ return { value: {} };
144
+ }
145
+ const capSet = new Set(appliedCapabilities ?? []);
146
+ const bag = answers ?? {};
147
+ const out = {};
148
+ for (const e of elicits) {
149
+ const gated = e.when ? Array.isArray(e.when.requiresCapability) && !e.when.requiresCapability.some((c) => capSet.has(c)) : false;
150
+ if (gated) continue;
151
+ const raw = bag[e.id];
152
+ let coerced;
153
+ if (raw === undefined || raw === null || raw === '') {
154
+ if (e.default === undefined) {
155
+ return rcfError({
156
+ kind: 'validation',
157
+ message: `blueprint apply: elicit '${e.id}' is required (no default declared) and no answer was supplied. Pass --answer ${e.id}=<value> or --answers <file>.`,
158
+ });
159
+ }
160
+ coerced = e.default;
161
+ } else if (e.kind === 'boolean') {
162
+ if (typeof raw === 'boolean') coerced = raw;
163
+ else {
164
+ const s = String(raw).trim().toLowerCase();
165
+ if (s === 'y' || s === 'yes' || s === 'true' || s === '1') coerced = true;
166
+ else if (s === 'n' || s === 'no' || s === 'false' || s === '0') coerced = false;
167
+ else return rcfError({ kind: 'validation', message: `blueprint apply: elicit '${e.id}' expects a boolean; got '${raw}'.` });
168
+ }
169
+ } else if (e.kind === 'enum') {
170
+ const s = String(raw).trim();
171
+ if (!e.options.includes(s)) {
172
+ return rcfError({ kind: 'validation', message: `blueprint apply: elicit '${e.id}' expects one of [${e.options.join(', ')}]; got '${raw}'.` });
173
+ }
174
+ coerced = s;
175
+ } else {
176
+ coerced = String(raw).trim();
177
+ }
178
+ out[e.id] = coerced;
179
+ }
180
+ return { value: out };
181
+ }
182
+
183
+ /**
184
+ * Sidecar path for a blueprint's per-project apply-state (relative to
185
+ * the project root). One file per applied blueprint slug.
186
+ */
187
+ export function sidecarRelPath(slug) {
188
+ return join('rcf', 'blueprints', `${slug}.applied.json`);
189
+ }
190
+
191
+ /**
192
+ * Write the sidecar. Overwrites the file so re-apply on the same
193
+ * version is idempotent.
194
+ *
195
+ * @param {object} args
196
+ * @param {string} args.projectRoot
197
+ * @param {string} args.slug
198
+ * @param {string} args.version
199
+ * @param {string} args.appliedAt
200
+ * @param {string[]} args.appliedCapabilities
201
+ * @param {Record<string, string|boolean|null>} args.appliedElicitations
202
+ * @param {boolean} [args.allowNoAuthYet]
203
+ * @param {string} [args.notes]
204
+ */
205
+ export async function writeSidecar({ projectRoot, slug, version, appliedAt, appliedCapabilities, appliedElicitations, allowNoAuthYet, notes }) {
206
+ const rel = sidecarRelPath(slug);
207
+ const abs = join(projectRoot, rel);
208
+ await mkdir(dirname(abs), { recursive: true });
209
+ const payload = { slug, version, appliedAt, appliedCapabilities: [...appliedCapabilities].sort(), appliedElicitations };
210
+ if (allowNoAuthYet === true) payload.allowNoAuthYet = true;
211
+ if (typeof notes === 'string' && notes.length > 0) payload.notes = notes;
212
+ await writeFile(abs, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
213
+ return rel;
214
+ }
215
+
216
+ /**
217
+ * Read the sidecar. Missing file returns null (never throws).
218
+ */
219
+ export async function readSidecar(projectRoot, slug) {
220
+ const abs = join(projectRoot, sidecarRelPath(slug));
221
+ try {
222
+ const raw = await readFile(abs, 'utf8');
223
+ return JSON.parse(raw);
224
+ } catch {
225
+ return null;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Presence check for the sidecar file (does not parse).
231
+ */
232
+ export async function sidecarExists(projectRoot, slug) {
233
+ try {
234
+ await stat(join(projectRoot, sidecarRelPath(slug)));
235
+ return true;
236
+ } catch {
237
+ return false;
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Custom-auth capability elicitation (visual round T-5 spec 5.5
243
+ * "Risks": custom auth elicits per capability). Runs BEFORE the
244
+ * requiresAppliedCapabilities refusal gate. Each elicit entry with
245
+ * a truthy `providesCapability` string is a boolean prompt asking
246
+ * whether the operator's own (non-shelf) auth blueprint provides
247
+ * the named capability. A truthy answer folds that capability into
248
+ * the effective applied set, so a project with no shelf auth
249
+ * blueprint can still pass the refusal gate without the allowNoAuthYet
250
+ * override, and the resulting sidecar records the operator-declared
251
+ * appliedCapabilities honestly.
252
+ *
253
+ * Interactive vs non-interactive:
254
+ * - Non-interactive: only the elicits whose id appears in `answers`
255
+ * resolve; the rest are treated as unanswered.
256
+ * - Interactive (tty=true): unanswered custom-auth elicits are read
257
+ * from stdin via readline with a `y/N` prompt; this branch is
258
+ * driven synchronously by the CLI's `apply` wrapper when it
259
+ * detects `process.stdin.isTTY === true`.
260
+ *
261
+ * A malformed boolean answer refuses with a validation rcfError.
262
+ *
263
+ * @param {object} args
264
+ * @param {Array<object>} args.elicits from loader (may include non-providesCapability entries; they are ignored here)
265
+ * @param {Record<string, string|boolean>} args.answers operator bag
266
+ * @param {(prompt: string) => Promise<string>} [args.readLine] test seam for interactive input
267
+ * @returns {Promise<{ capsProvided: string[], answered: Record<string, boolean>, elicitsPresent: boolean, anyAnswered: boolean } | import('../core/errors/index.js').RcfError>}
268
+ */
269
+ export async function runCustomAuthCapabilityElicits({ elicits, answers, readLine }) {
270
+ const capsProvided = new Set();
271
+ const answered = {};
272
+ let elicitsPresent = false;
273
+ const bag = answers ?? {};
274
+ for (const e of elicits ?? []) {
275
+ if (typeof e.providesCapability !== 'string' || e.providesCapability.length === 0) continue;
276
+ elicitsPresent = true;
277
+ const raw = bag[e.id];
278
+ let coerced;
279
+ if (raw === undefined || raw === null || raw === '') {
280
+ if (typeof readLine !== 'function') {
281
+ // Non-interactive branch, no answer supplied: skip. Default
282
+ // remains false in that case so a missing operator answer does
283
+ // not silently declare a capability.
284
+ continue;
285
+ }
286
+ const reply = await readLine(`Custom auth: does your auth provide '${e.providesCapability}'? [y/N]: `);
287
+ const s = String(reply ?? '').trim().toLowerCase();
288
+ if (s === '' || s === 'n' || s === 'no' || s === 'false' || s === '0') coerced = false;
289
+ else if (s === 'y' || s === 'yes' || s === 'true' || s === '1') coerced = true;
290
+ else {
291
+ return rcfError({
292
+ kind: 'validation',
293
+ message: `blueprint apply: elicit '${e.id}' expects a boolean; got '${reply}'.`,
294
+ });
295
+ }
296
+ } else if (typeof raw === 'boolean') {
297
+ coerced = raw;
298
+ } else {
299
+ const s = String(raw).trim().toLowerCase();
300
+ if (s === 'y' || s === 'yes' || s === 'true' || s === '1') coerced = true;
301
+ else if (s === 'n' || s === 'no' || s === 'false' || s === '0') coerced = false;
302
+ else {
303
+ return rcfError({
304
+ kind: 'validation',
305
+ message: `blueprint apply: elicit '${e.id}' expects a boolean; got '${raw}'.`,
306
+ });
307
+ }
308
+ }
309
+ answered[e.id] = coerced;
310
+ if (coerced === true) capsProvided.add(e.providesCapability);
311
+ }
312
+ return {
313
+ capsProvided: [...capsProvided].sort(),
314
+ answered,
315
+ elicitsPresent,
316
+ anyAnswered: Object.keys(answered).length > 0,
317
+ };
318
+ }