@pie-players/pie-assessment-toolkit 0.3.74 → 1.0.0-beta.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 (257) hide show
  1. package/README.md +658 -714
  2. package/custom-elements.json +1315 -0
  3. package/dist/attempt/TestSession.d.ts +1 -21
  4. package/dist/attempt/TestSession.js +16 -102
  5. package/dist/components/ItemScope.custom-element.d.ts +1 -0
  6. package/dist/components/ItemScope.custom-element.js +1 -0
  7. package/dist/components/ItemToolBar.custom-element.js +1 -1
  8. package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
  9. package/dist/components/SectionToolBar.custom-element.js +1 -1
  10. package/dist/components/chunks/ItemToolBar-3zrc4p3d.js +4 -0
  11. package/dist/components/chunks/ItemToolBar-66hcwmfw.js +2 -0
  12. package/dist/components/chunks/ItemToolBar-9gqhfxtn.js +40 -0
  13. package/dist/components/chunks/ItemToolBar-hzc5wyjp.js +2 -0
  14. package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-kkj69ptr.js} +1 -1
  15. package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
  16. package/dist/components/chunks/sre-engine-bj997w4b.js +1 -0
  17. package/dist/components/item-scope-element.d.ts +2 -0
  18. package/dist/components/item-scope-element.js +2 -0
  19. package/dist/context/assessment-toolkit-context.d.ts +33 -6
  20. package/dist/index.d.ts +25 -50
  21. package/dist/index.js +15 -45
  22. package/dist/policy/core/PolicySource.d.ts +2 -2
  23. package/dist/policy/core/ToolPolicyEngine.d.ts +118 -34
  24. package/dist/policy/core/ToolPolicyEngine.js +203 -56
  25. package/dist/policy/core/compose-decision.d.ts +28 -7
  26. package/dist/policy/core/compose-decision.js +95 -74
  27. package/dist/policy/core/decision-types.d.ts +98 -55
  28. package/dist/policy/core/feature-decision.d.ts +28 -19
  29. package/dist/policy/core/feature-decision.js +15 -11
  30. package/dist/policy/core/pnp-policy-inputs.d.ts +29 -35
  31. package/dist/policy/core/pnp-policy-inputs.js +40 -88
  32. package/dist/policy/core/provenance.d.ts +10 -5
  33. package/dist/policy/core/provenance.js +1 -1
  34. package/dist/policy/core/tool-parameters.d.ts +11 -0
  35. package/dist/policy/core/tool-parameters.js +15 -0
  36. package/dist/policy/engine.d.ts +7 -23
  37. package/dist/policy/engine.js +5 -20
  38. package/dist/policy/sources/PnpPolicySource.d.ts +43 -68
  39. package/dist/policy/sources/PnpPolicySource.js +133 -132
  40. package/dist/runtime/SectionControllerBinding.d.ts +283 -0
  41. package/dist/runtime/SectionControllerBinding.js +456 -0
  42. package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
  43. package/dist/runtime/SectionRuntimeEngine.js +30 -372
  44. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
  45. package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
  46. package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
  47. package/dist/runtime/adapter/dom-event-bridge.js +17 -44
  48. package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -3
  49. package/dist/runtime/adapter/subscriber-fanout.js +2 -6
  50. package/dist/runtime/catalog-context.d.ts +19 -0
  51. package/dist/runtime/catalog-context.js +28 -0
  52. package/dist/runtime/composition-emit-scheduler.d.ts +12 -18
  53. package/dist/runtime/composition-emit-scheduler.js +10 -16
  54. package/dist/runtime/content-language.d.ts +32 -0
  55. package/dist/runtime/content-language.js +46 -0
  56. package/dist/runtime/content-region.d.ts +13 -0
  57. package/dist/runtime/content-region.js +29 -0
  58. package/dist/runtime/content-registration.d.ts +13 -0
  59. package/dist/runtime/content-registration.js +25 -0
  60. package/dist/runtime/core/SectionEngineCore.d.ts +2 -4
  61. package/dist/runtime/core/SectionEngineCore.js +2 -6
  62. package/dist/runtime/core/cohort.d.ts +1 -6
  63. package/dist/runtime/core/cohort.js +1 -10
  64. package/dist/runtime/core/engine-input.d.ts +18 -30
  65. package/dist/runtime/core/engine-input.js +11 -12
  66. package/dist/runtime/core/engine-output.d.ts +17 -40
  67. package/dist/runtime/core/engine-output.js +9 -21
  68. package/dist/runtime/core/engine-readiness.d.ts +5 -37
  69. package/dist/runtime/core/engine-readiness.js +8 -41
  70. package/dist/runtime/core/engine-resolver.d.ts +46 -74
  71. package/dist/runtime/core/engine-resolver.js +18 -43
  72. package/dist/runtime/core/engine-state.d.ts +28 -36
  73. package/dist/runtime/core/engine-state.js +18 -21
  74. package/dist/runtime/core/engine-transition.d.ts +14 -19
  75. package/dist/runtime/core/engine-transition.js +74 -53
  76. package/dist/runtime/engine.d.ts +16 -19
  77. package/dist/runtime/engine.js +12 -19
  78. package/dist/runtime/page-warnings.d.ts +13 -0
  79. package/dist/runtime/page-warnings.js +22 -0
  80. package/dist/runtime/policy-input-forwarding.d.ts +25 -0
  81. package/dist/runtime/policy-input-forwarding.js +25 -0
  82. package/dist/runtime/registration-events.d.ts +22 -14
  83. package/dist/runtime/runtime-event-claim.d.ts +8 -0
  84. package/dist/runtime/runtime-event-claim.js +15 -0
  85. package/dist/runtime/runtime-id.d.ts +1 -1
  86. package/dist/runtime/runtime-id.js +1 -1
  87. package/dist/runtime/shell-event-bridge.d.ts +45 -0
  88. package/dist/runtime/shell-event-bridge.js +127 -0
  89. package/dist/runtime/shell-registration.d.ts +73 -0
  90. package/dist/runtime/shell-registration.js +95 -0
  91. package/dist/runtime/shell-scope.d.ts +68 -0
  92. package/dist/runtime/shell-scope.js +194 -0
  93. package/dist/runtime/tool-host-contract.d.ts +8 -14
  94. package/dist/runtime/tool-host-contract.js +12 -12
  95. package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
  96. package/dist/runtime/unclaimed-registration-watch.js +23 -0
  97. package/dist/services/AccessibilityCatalogResolver.d.ts +21 -31
  98. package/dist/services/AccessibilityCatalogResolver.js +29 -42
  99. package/dist/services/ElementToolStateStore.d.ts +42 -67
  100. package/dist/services/ElementToolStateStore.js +65 -71
  101. package/dist/services/HighlightCoordinator.d.ts +28 -40
  102. package/dist/services/HighlightCoordinator.js +459 -381
  103. package/dist/services/RangeSerializer.d.ts +11 -33
  104. package/dist/services/RangeSerializer.js +88 -59
  105. package/dist/services/SSMLExtractor.d.ts +8 -31
  106. package/dist/services/SSMLExtractor.js +18 -40
  107. package/dist/services/TTSService.d.ts +159 -119
  108. package/dist/services/TTSService.js +937 -822
  109. package/dist/services/ToolCoordinator.d.ts +42 -102
  110. package/dist/services/ToolCoordinator.js +178 -256
  111. package/dist/services/ToolRegistry.d.ts +152 -133
  112. package/dist/services/ToolRegistry.js +192 -178
  113. package/dist/services/ToolkitCoordinator.d.ts +233 -237
  114. package/dist/services/ToolkitCoordinator.js +615 -557
  115. package/dist/services/audio-handoff.d.ts +6 -7
  116. package/dist/services/audio-handoff.js +20 -14
  117. package/dist/services/catalog-owner.d.ts +0 -1
  118. package/dist/services/framework-error-bus.d.ts +3 -6
  119. package/dist/services/framework-error-bus.js +3 -6
  120. package/dist/services/framework-error.d.ts +51 -13
  121. package/dist/services/framework-error.js +19 -20
  122. package/dist/services/interfaces.d.ts +189 -184
  123. package/dist/services/section-controller-types.d.ts +7 -7
  124. package/dist/services/section-session-assignment.d.ts +21 -0
  125. package/dist/services/section-session-assignment.js +80 -0
  126. package/dist/services/spoken-audio-cards.d.ts +2 -6
  127. package/dist/services/spoken-audio-cards.js +2 -6
  128. package/dist/services/tool-config-defaults.d.ts +3 -3
  129. package/dist/services/tool-config-defaults.js +3 -3
  130. package/dist/services/tool-config-validation.d.ts +9 -12
  131. package/dist/services/tool-config-validation.js +47 -87
  132. package/dist/services/tool-context.d.ts +7 -31
  133. package/dist/services/tool-context.js +99 -65
  134. package/dist/services/tool-failure.d.ts +30 -0
  135. package/dist/services/tool-failure.js +11 -0
  136. package/dist/services/tool-icons.d.ts +5 -5
  137. package/dist/services/tool-icons.js +6 -5
  138. package/dist/services/tool-instance-id.d.ts +8 -7
  139. package/dist/services/tool-instance-id.js +12 -24
  140. package/dist/services/tool-providers/TTSToolProvider.d.ts +20 -58
  141. package/dist/services/tool-providers/TTSToolProvider.js +32 -85
  142. package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -11
  143. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +29 -60
  144. package/dist/services/tool-providers/ToolProviderRegistry.js +128 -144
  145. package/dist/services/tool-request.d.ts +31 -12
  146. package/dist/services/tool-request.js +31 -16
  147. package/dist/services/toolbar-decided-tools.d.ts +16 -0
  148. package/dist/services/toolbar-decided-tools.js +29 -0
  149. package/dist/services/toolbar-items.js +2 -5
  150. package/dist/services/tools-config-normalizer.d.ts +33 -29
  151. package/dist/services/tools-config-normalizer.js +26 -28
  152. package/dist/services/tts/browser-provider.d.ts +39 -6
  153. package/dist/services/tts/browser-provider.js +280 -160
  154. package/dist/services/tts/catalog-span-alignment.js +14 -7
  155. package/dist/services/tts/flat-tree.d.ts +64 -0
  156. package/dist/services/tts/flat-tree.js +269 -0
  157. package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
  158. package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
  159. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
  160. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
  161. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +2 -2
  162. package/dist/services/tts/generated-speech/index.d.ts +1 -1
  163. package/dist/services/tts/generated-speech/index.js +1 -1
  164. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
  165. package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
  166. package/dist/services/tts/generated-speech/types.d.ts +1 -1
  167. package/dist/services/tts/generated-speech/types.js +1 -1
  168. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +22 -16
  169. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
  170. package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
  171. package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
  172. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
  173. package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
  174. package/dist/services/tts/highlight-target-resolver.d.ts +12 -0
  175. package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
  176. package/dist/services/tts/math-aware-text-processing.js +42 -47
  177. package/dist/services/tts/math-control-names.d.ts +21 -0
  178. package/dist/services/tts/math-control-names.js +142 -0
  179. package/dist/services/tts/math-speech.d.ts +12 -1
  180. package/dist/services/tts/math-speech.js +54 -18
  181. package/dist/services/tts/mathml-sanitization.d.ts +7 -0
  182. package/dist/services/tts/mathml-sanitization.js +9 -1
  183. package/dist/services/tts/provider-options.d.ts +52 -0
  184. package/dist/services/tts/provider-options.js +1 -0
  185. package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
  186. package/dist/services/tts/ssml/spoken-text.js +29 -0
  187. package/dist/services/tts/start-failure.d.ts +4 -0
  188. package/dist/services/tts/start-failure.js +19 -0
  189. package/dist/services/tts/text-processing.d.ts +9 -0
  190. package/dist/services/tts/text-processing.js +46 -33
  191. package/dist/services/tts/text-segmentation.d.ts +12 -0
  192. package/dist/services/tts/text-segmentation.js +60 -10
  193. package/dist/services/tts-runtime-config.d.ts +54 -26
  194. package/dist/services/tts-runtime-config.js +27 -112
  195. package/dist/tools/content-capability-resolution.d.ts +9 -11
  196. package/dist/tools/content-capability-resolution.js +8 -45
  197. package/dist/tools/registration.d.ts +59 -0
  198. package/dist/tools/registration.js +55 -0
  199. package/dist/tools/registrations/toolbar-registration-helpers.d.ts +6 -0
  200. package/dist/tools/registrations/toolbar-registration-helpers.js +8 -2
  201. package/dist/tools/tool-surface-host.d.ts +6 -10
  202. package/dist/tools/tool-surface-host.js +45 -50
  203. package/dist/tools/tool-tag-map.d.ts +5 -15
  204. package/dist/tools/tool-tag-map.js +6 -20
  205. package/dist/utils/structural-equality.d.ts +11 -0
  206. package/dist/utils/structural-equality.js +47 -0
  207. package/package.json +15 -29
  208. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
  209. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
  210. package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
  211. package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
  212. package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
  213. package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
  214. package/dist/context/runtime-context-consumer.d.ts +0 -13
  215. package/dist/context/runtime-context-consumer.js +0 -18
  216. package/dist/item-loader.d.ts +0 -44
  217. package/dist/item-loader.js +0 -61
  218. package/dist/policy/core/engine-context.d.ts +0 -29
  219. package/dist/policy/core/engine-context.js +0 -27
  220. package/dist/policy/core/policy-source-tag.d.ts +0 -31
  221. package/dist/policy/core/policy-source-tag.js +0 -28
  222. package/dist/policy/internal.d.ts +0 -25
  223. package/dist/policy/internal.js +0 -25
  224. package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
  225. package/dist/runtime/adapter/coordinator-bridge.js +0 -126
  226. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
  227. package/dist/runtime/adapter/framework-error-bridge.js +0 -46
  228. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
  229. package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
  230. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
  231. package/dist/runtime/core/engine-stage-derivation.js +0 -29
  232. package/dist/runtime/engine-context.d.ts +0 -39
  233. package/dist/runtime/engine-context.js +0 -31
  234. package/dist/runtime/internal.d.ts +0 -43
  235. package/dist/runtime/internal.js +0 -45
  236. package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
  237. package/dist/runtime/section-runtime-engine-host-context.js +0 -57
  238. package/dist/runtime/stage-emit-gate.d.ts +0 -92
  239. package/dist/runtime/stage-emit-gate.js +0 -82
  240. package/dist/services/ContextVariableStore.d.ts +0 -113
  241. package/dist/services/ContextVariableStore.js +0 -206
  242. package/dist/services/I18nService.d.ts +0 -47
  243. package/dist/services/I18nService.js +0 -84
  244. package/dist/services/ThemeProvider.d.ts +0 -95
  245. package/dist/services/ThemeProvider.js +0 -334
  246. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
  247. package/dist/services/defaultPersonalNeedsProfile.js +0 -26
  248. package/dist/services/pnp-standard-features.d.ts +0 -238
  249. package/dist/services/pnp-standard-features.js +0 -338
  250. package/dist/services/tts-control-events.d.ts +0 -13
  251. package/dist/services/tts-control-events.js +0 -7
  252. package/dist/tools/client.d.ts +0 -10
  253. package/dist/tools/client.js +0 -10
  254. package/dist/tools/internal.d.ts +0 -44
  255. package/dist/tools/internal.js +0 -39
  256. package/dist/utils/logger.d.ts +0 -6
  257. package/dist/utils/logger.js +0 -10
@@ -2,64 +2,58 @@
2
2
  * PNP/profile policy input detection. See
3
3
  * `docs/tools-and-accomodations/architecture.md`.
4
4
  *
5
- * Pure helpers that decide whether the inputs the engine has been given
6
- * actually carry PNP/profile policy material. Used by:
7
- *
8
- * - {@link ToolPolicyEngine}'s constructor as the auto-default for
9
- * `pnpEnforcement` when the host did not pass an explicit `"on"` /
10
- * `"off"`.
11
- * - {@link ToolkitCoordinator.resolveEffectivePnpEnforcement} as the
12
- * auto-mode rule. PR 2 used a coarse "any non-null assessment → on"
13
- * placeholder; PR 4 narrows it to "assessment OR currentItemRef
14
- * carries profile policy material."
5
+ * Pure helpers that decide whether policy inputs carry PNP/profile policy
6
+ * material, which is what auto-mode `pnpEnforcement` turns on for. The engine
7
+ * resolves auto-mode per decision: the bound assessment for every decision, and
8
+ * for a decision scoped to an item, that item's settings as well.
15
9
  *
16
10
  * The rule is intentionally narrow. Hosts that bind a bare assessment
17
11
  * record (only `id` / `name`, no PNP and no settings) do not engage
18
12
  * PNP/profile gates. The flip happens the moment the assessment carries any of:
19
13
  *
20
- * - `personalNeedsProfile` (any `supports`, `prohibitedSupports`, or
21
- * `activateAtInit`),
22
- * - `settings.districtPolicy` (any `blockedTools`, `requiredTools`,
23
- * or `policies`),
24
- * - `settings.testAdministration` (any populated key — `mode`,
25
- * `toolOverrides`, etc.),
26
- * - or the bound `currentItemRef.settings` carries
27
- * `requiredTools` / `restrictedTools` / `toolParameters`.
14
+ * - `personalNeedsProfile` (any `supports` or `prohibitedSupports`),
15
+ * - `settings.districtPolicy` (any `blockedTools` or `requiredTools`),
16
+ * - `settings.testAdministration` (any `toolOverrides` entry),
17
+ * - or, for an item-scoped decision, the item's settings carry
18
+ * `requiredTools` / `restrictedTools`.
19
+ *
20
+ * Tool parameters are no policy material: the engine resolves them whatever the
21
+ * enforcement state.
28
22
  *
29
23
  * Hosts opt out of the auto-on behavior by passing
30
24
  * `pnpEnforcement: "off"` explicitly (engine input or
31
25
  * `ToolkitCoordinator.setPnpEnforcement("off")`).
32
26
  */
33
- import type { AssessmentEntity, AssessmentItemRef } from "@pie-players/pie-players-shared/types";
27
+ import type { AssessmentEntity, ItemSettings } from "@pie-players/pie-players-shared/types";
34
28
  /**
35
29
  * Return `true` when the assessment carries any PNP/profile policy
36
30
  * material that the engine's `PnpPolicySource` would consume.
37
31
  *
38
- * The check is structural — the presence of a non-empty PNP, district
39
- * policy, or test administration block is enough; the content does not
40
- * have to validate against any specific rule.
32
+ * The check is structural — a non-empty field the source reads is enough;
33
+ * the content does not have to validate against any specific rule.
41
34
  */
42
35
  export declare function assessmentHasPnpPolicyInputs(assessment: AssessmentEntity | null | undefined): boolean;
43
36
  /**
44
- * Return `true` when the bound item reference carries item-level profile policy
45
- * material (`requiredTools`, `restrictedTools`, or `toolParameters`).
37
+ * Return `true` when an item's settings carry item-level profile policy
38
+ * material (`requiredTools` or `restrictedTools`).
46
39
  *
47
- * Used in addition to {@link assessmentHasPnpPolicyInputs} so a host that
48
- * navigates to an item with profile-relevant settings — without a parent
49
- * assessment carrying its own PNP/district/test-admin block — still
50
- * gets PNP/profile gates engaged for that item.
40
+ * Used in addition to {@link assessmentHasPnpPolicyInputs} for a decision scoped
41
+ * to the item, so an item with profile-relevant settings engages PNP/profile
42
+ * gates on its own toolbar without a parent assessment carrying a
43
+ * PNP/district/test-admin block.
51
44
  */
52
- export declare function itemRefHasPnpPolicyInputs(itemRef: AssessmentItemRef | null | undefined): boolean;
45
+ export declare function itemSettingsHavePnpPolicyInputs(settings: ItemSettings | null | undefined): boolean;
53
46
  /**
54
- * Resolve the default `pnpEnforcement` mode given the bound inputs.
47
+ * Resolve the auto-mode `pnpEnforcement` for one decision.
55
48
  *
56
- * Returns `"on"` when {@link assessmentHasPnpPolicyInputs} or
57
- * {@link itemRefHasPnpPolicyInputs} reports any profile policy material; otherwise
58
- * `"off"`. Hosts override the default by passing an explicit
59
- * `pnpEnforcement` value (engine input) or by calling
49
+ * Returns `"on"` when {@link assessmentHasPnpPolicyInputs} reports profile
50
+ * policy material, or when `itemSettings` (the settings of the item a decision
51
+ * is scoped to) do per {@link itemSettingsHavePnpPolicyInputs}; otherwise
52
+ * `"off"`. Hosts override the default by passing an explicit `pnpEnforcement`
53
+ * value (engine input) or by calling
60
54
  * `ToolkitCoordinator.setPnpEnforcement("on" | "off")`.
61
55
  */
62
56
  export declare function resolveDefaultPnpEnforcement(args: {
63
57
  assessment?: AssessmentEntity | null;
64
- currentItemRef?: AssessmentItemRef | null;
58
+ itemSettings?: ItemSettings | null;
65
59
  }): "on" | "off";
@@ -2,135 +2,87 @@
2
2
  * PNP/profile policy input detection. See
3
3
  * `docs/tools-and-accomodations/architecture.md`.
4
4
  *
5
- * Pure helpers that decide whether the inputs the engine has been given
6
- * actually carry PNP/profile policy material. Used by:
7
- *
8
- * - {@link ToolPolicyEngine}'s constructor as the auto-default for
9
- * `pnpEnforcement` when the host did not pass an explicit `"on"` /
10
- * `"off"`.
11
- * - {@link ToolkitCoordinator.resolveEffectivePnpEnforcement} as the
12
- * auto-mode rule. PR 2 used a coarse "any non-null assessment → on"
13
- * placeholder; PR 4 narrows it to "assessment OR currentItemRef
14
- * carries profile policy material."
5
+ * Pure helpers that decide whether policy inputs carry PNP/profile policy
6
+ * material, which is what auto-mode `pnpEnforcement` turns on for. The engine
7
+ * resolves auto-mode per decision: the bound assessment for every decision, and
8
+ * for a decision scoped to an item, that item's settings as well.
15
9
  *
16
10
  * The rule is intentionally narrow. Hosts that bind a bare assessment
17
11
  * record (only `id` / `name`, no PNP and no settings) do not engage
18
12
  * PNP/profile gates. The flip happens the moment the assessment carries any of:
19
13
  *
20
- * - `personalNeedsProfile` (any `supports`, `prohibitedSupports`, or
21
- * `activateAtInit`),
22
- * - `settings.districtPolicy` (any `blockedTools`, `requiredTools`,
23
- * or `policies`),
24
- * - `settings.testAdministration` (any populated key — `mode`,
25
- * `toolOverrides`, etc.),
26
- * - or the bound `currentItemRef.settings` carries
27
- * `requiredTools` / `restrictedTools` / `toolParameters`.
14
+ * - `personalNeedsProfile` (any `supports` or `prohibitedSupports`),
15
+ * - `settings.districtPolicy` (any `blockedTools` or `requiredTools`),
16
+ * - `settings.testAdministration` (any `toolOverrides` entry),
17
+ * - or, for an item-scoped decision, the item's settings carry
18
+ * `requiredTools` / `restrictedTools`.
19
+ *
20
+ * Tool parameters are no policy material: the engine resolves them whatever the
21
+ * enforcement state.
28
22
  *
29
23
  * Hosts opt out of the auto-on behavior by passing
30
24
  * `pnpEnforcement: "off"` explicitly (engine input or
31
25
  * `ToolkitCoordinator.setPnpEnforcement("off")`).
32
26
  */
27
+ const hasEntries = (value) => Array.isArray(value) && value.length > 0;
33
28
  /**
34
29
  * Return `true` when the assessment carries any PNP/profile policy
35
30
  * material that the engine's `PnpPolicySource` would consume.
36
31
  *
37
- * The check is structural — the presence of a non-empty PNP, district
38
- * policy, or test administration block is enough; the content does not
39
- * have to validate against any specific rule.
32
+ * The check is structural — a non-empty field the source reads is enough;
33
+ * the content does not have to validate against any specific rule.
40
34
  */
41
35
  export function assessmentHasPnpPolicyInputs(assessment) {
42
36
  if (!assessment)
43
37
  return false;
44
38
  const pnp = assessment.personalNeedsProfile;
45
- if (pnp) {
46
- if (Array.isArray(pnp.supports) && pnp.supports.length > 0)
47
- return true;
48
- if (Array.isArray(pnp.prohibitedSupports) &&
49
- pnp.prohibitedSupports.length > 0) {
50
- return true;
51
- }
52
- if (Array.isArray(pnp.activateAtInit) && pnp.activateAtInit.length > 0) {
53
- return true;
54
- }
39
+ if (pnp && (hasEntries(pnp.supports) || hasEntries(pnp.prohibitedSupports))) {
40
+ return true;
55
41
  }
56
42
  const settings = assessment.settings;
57
43
  if (settings) {
58
44
  const district = settings.districtPolicy;
59
- if (district) {
60
- if (Array.isArray(district.blockedTools) &&
61
- district.blockedTools.length > 0) {
62
- return true;
63
- }
64
- if (Array.isArray(district.requiredTools) &&
65
- district.requiredTools.length > 0) {
66
- return true;
67
- }
68
- if (district.policies &&
69
- typeof district.policies === "object" &&
70
- Object.keys(district.policies).length > 0) {
71
- return true;
72
- }
45
+ if (district &&
46
+ (hasEntries(district.blockedTools) || hasEntries(district.requiredTools))) {
47
+ return true;
73
48
  }
74
- const admin = settings.testAdministration;
75
- if (admin && typeof admin === "object") {
76
- for (const key of Object.keys(admin)) {
77
- const value = admin[key];
78
- if (value === undefined || value === null)
79
- continue;
80
- if (Array.isArray(value) && value.length === 0)
81
- continue;
82
- if (typeof value === "object" &&
83
- !Array.isArray(value) &&
84
- Object.keys(value).length === 0) {
85
- continue;
86
- }
87
- return true;
88
- }
49
+ const overrides = settings.testAdministration?.toolOverrides;
50
+ if (overrides &&
51
+ typeof overrides === "object" &&
52
+ Object.keys(overrides).length > 0) {
53
+ return true;
89
54
  }
90
55
  }
91
56
  return false;
92
57
  }
93
58
  /**
94
- * Return `true` when the bound item reference carries item-level profile policy
95
- * material (`requiredTools`, `restrictedTools`, or `toolParameters`).
59
+ * Return `true` when an item's settings carry item-level profile policy
60
+ * material (`requiredTools` or `restrictedTools`).
96
61
  *
97
- * Used in addition to {@link assessmentHasPnpPolicyInputs} so a host that
98
- * navigates to an item with profile-relevant settings — without a parent
99
- * assessment carrying its own PNP/district/test-admin block — still
100
- * gets PNP/profile gates engaged for that item.
62
+ * Used in addition to {@link assessmentHasPnpPolicyInputs} for a decision scoped
63
+ * to the item, so an item with profile-relevant settings engages PNP/profile
64
+ * gates on its own toolbar without a parent assessment carrying a
65
+ * PNP/district/test-admin block.
101
66
  */
102
- export function itemRefHasPnpPolicyInputs(itemRef) {
103
- const settings = itemRef?.settings;
67
+ export function itemSettingsHavePnpPolicyInputs(settings) {
104
68
  if (!settings)
105
69
  return false;
106
- if (Array.isArray(settings.requiredTools) &&
107
- settings.requiredTools.length > 0) {
108
- return true;
109
- }
110
- if (Array.isArray(settings.restrictedTools) &&
111
- settings.restrictedTools.length > 0) {
112
- return true;
113
- }
114
- if (settings.toolParameters &&
115
- typeof settings.toolParameters === "object" &&
116
- Object.keys(settings.toolParameters).length > 0) {
117
- return true;
118
- }
119
- return false;
70
+ return hasEntries(settings.requiredTools) || hasEntries(settings.restrictedTools);
120
71
  }
121
72
  /**
122
- * Resolve the default `pnpEnforcement` mode given the bound inputs.
73
+ * Resolve the auto-mode `pnpEnforcement` for one decision.
123
74
  *
124
- * Returns `"on"` when {@link assessmentHasPnpPolicyInputs} or
125
- * {@link itemRefHasPnpPolicyInputs} reports any profile policy material; otherwise
126
- * `"off"`. Hosts override the default by passing an explicit
127
- * `pnpEnforcement` value (engine input) or by calling
75
+ * Returns `"on"` when {@link assessmentHasPnpPolicyInputs} reports profile
76
+ * policy material, or when `itemSettings` (the settings of the item a decision
77
+ * is scoped to) do per {@link itemSettingsHavePnpPolicyInputs}; otherwise
78
+ * `"off"`. Hosts override the default by passing an explicit `pnpEnforcement`
79
+ * value (engine input) or by calling
128
80
  * `ToolkitCoordinator.setPnpEnforcement("on" | "off")`.
129
81
  */
130
82
  export function resolveDefaultPnpEnforcement(args) {
131
83
  if (assessmentHasPnpPolicyInputs(args.assessment))
132
84
  return "on";
133
- if (itemRefHasPnpPolicyInputs(args.currentItemRef))
85
+ if (itemSettingsHavePnpPolicyInputs(args.itemSettings))
134
86
  return "on";
135
87
  return "off";
136
88
  }
@@ -7,17 +7,22 @@
7
7
  * Tracks decisions from every Pass-1 contributor: host placement, host policy,
8
8
  * provider veto, PNP/profile gates, and custom policy sources.
9
9
  */
10
+ /** The PNP/profile precedence rules, highest first. */
11
+ export type PnpPolicySourceRule = "district-block" | "test-admin-override" | "item-restriction" | "pnp-prohibited" | "item-requirement" | "district-requirement" | "pnp-support";
10
12
  /**
11
- * Rule vocabulary for tool policy decisions.
13
+ * Rule vocabulary for tool policy decisions. A decision's `rule` is the one
14
+ * attribution channel: it names the step that enabled, blocked or advised on a
15
+ * tool.
12
16
  */
13
- export type ToolPolicyDecisionRule = "district-block" | "test-admin-override" | "item-restriction" | "item-requirement" | "district-requirement" | "pnp-support" | "pnp-prohibited" | "placement-membership" | "provider-disabled" | "host-allowlist" | "host-blocked" | "required-tool-blocked" | "custom-source" | "system-default";
17
+ export type ToolPolicyDecisionRule = PnpPolicySourceRule | "placement-membership" | "provider-disabled" | "host-allowlist" | "host-blocked" | "required-tool-blocked" | "custom-source" | "system-default";
14
18
  export type ToolPolicySourceType = "organization" | "assessment" | "section" | "item" | "student" | "system" | "host" | "custom";
15
19
  export interface ToolPolicyResolutionDecision {
16
20
  /** Monotonic step index across the entire decision log. */
17
21
  step: number;
18
22
  /**
19
- * Precedence level. PNP/profile rules use `1`–`6`; non-profile rules use `0`
20
- * for host-side gates or `7+` for custom sources that run after PNP/profile policy.
23
+ * Precedence level. PNP/profile rules use `1`–`8`; non-profile rules use `0`
24
+ * for host-side gates, and a custom source, which runs after PNP/profile
25
+ * policy, uses its own or `100 + i` for the `i`th registered source.
21
26
  */
22
27
  precedence: number;
23
28
  rule: ToolPolicyDecisionRule;
@@ -120,7 +125,7 @@ export declare class ToolPolicyProvenanceBuilder {
120
125
  * walks every feature trail and rewrites `finalState` from the
121
126
  * final candidate set so callers can rely on
122
127
  * `provenance.features.get(toolId)?.finalState` as the canonical
123
- * "is this tool visible right now?" answer (M8 design § 4 + § 12).
128
+ * "is this tool visible right now?" answer.
124
129
  *
125
130
  * Semantics applied per feature:
126
131
  * - In `survivingIds` → "enabled"
@@ -124,7 +124,7 @@ export class ToolPolicyProvenanceBuilder {
124
124
  * walks every feature trail and rewrites `finalState` from the
125
125
  * final candidate set so callers can rely on
126
126
  * `provenance.features.get(toolId)?.finalState` as the canonical
127
- * "is this tool visible right now?" answer (M8 design § 4 + § 12).
127
+ * "is this tool visible right now?" answer.
128
128
  *
129
129
  * Semantics applied per feature:
130
130
  * - In `survivingIds` → "enabled"
@@ -0,0 +1,11 @@
1
+ import type { AssessmentEntity, ToolParametersFor } from "@pie-players/pie-players-shared/types";
2
+ import type { PnpPolicyItem } from "../sources/PnpPolicySource.js";
3
+ /**
4
+ * A tool's parameters for one decision: the item's `toolParameters` entry, else
5
+ * the assessment's `toolParameters` entry.
6
+ *
7
+ * Parameters say how a tool behaves wherever policy shows it, so they reach a
8
+ * tool placed without a grant, and PNP enforcement does not gate them. `item` is
9
+ * set only for a decision about the item's own toolbar or content.
10
+ */
11
+ export declare function resolveToolParameters<K extends string>(toolId: K, assessment: AssessmentEntity | null | undefined, item: PnpPolicyItem | undefined): ToolParametersFor<K> | undefined;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * A tool's parameters for one decision: the item's `toolParameters` entry, else
3
+ * the assessment's `toolParameters` entry.
4
+ *
5
+ * Parameters say how a tool behaves wherever policy shows it, so they reach a
6
+ * tool placed without a grant, and PNP enforcement does not gate them. `item` is
7
+ * set only for a decision about the item's own toolbar or content.
8
+ */
9
+ export function resolveToolParameters(toolId, assessment, item) {
10
+ const settings = assessment?.settings;
11
+ const value = item?.settings.toolParameters?.[toolId] ?? settings?.toolParameters?.[toolId];
12
+ return value && typeof value === "object" && !Array.isArray(value)
13
+ ? value
14
+ : undefined;
15
+ }
@@ -1,28 +1,12 @@
1
1
  /**
2
- * Tool policy engine — facade entry point (M8 PR 1).
3
- *
4
- * Narrow, stable public surface for hosts that want to instantiate or
5
- * consume a `ToolPolicyEngine`. Re-exports the engine class, its
6
- * decision request/response types, the Svelte context key, and the
7
- * minimal `PolicySource` extension contract.
8
- *
9
- * **Pairing.** This module is the stable counterpart to
10
- * `./policy/internal`, which exposes the wider, evolving surface
11
- * (composition pipeline, provenance builder, PNP policy source). Consumers
12
- * that only need to drive an engine and read its decisions should
13
- * import from here. Consumers that need to reach past the facade
14
- * (e.g. test the composition pipeline directly, build a custom PNP policy
15
- * source variant, instrument provenance generation) should import
16
- * from `./policy/internal` and accept the documented stability
17
- * disclaimer there.
18
- *
19
- * Mirrors the M7 `runtime/engine` / `runtime/internal` split — see
20
- * the same rationale in `src/runtime/engine.ts`.
2
+ * Tool policy engine — the stable entry for a host that instantiates or
3
+ * consumes a `ToolPolicyEngine`: the engine class, its decision request and
4
+ * response types, and the `PolicySource` extension contract. The composition
5
+ * pipeline, the PNP policy source and the provenance builder stay behind the
6
+ * engine, with no entry of their own.
21
7
  */
22
8
  export { ToolPolicyEngine, type PnpEnforcementMode, type ResolvedEngineInputs, type ToolPolicyChangeEvent, type ToolPolicyChangeListener, type ToolPolicyEngineArgs, type ToolPolicyEngineInputs, } from "./core/ToolPolicyEngine.js";
23
- export { TOOL_POLICY_ENGINE_KEY, type ToolPolicyEngineContext, } from "./core/engine-context.js";
24
9
  export { isHostDeniedFeature, type FeaturePolicyDecision, type FeaturePolicyRule, } from "./core/feature-decision.js";
25
- export type { RequiredToolBlockedDetails, ToolPolicyDecision, ToolPolicyDecisionRequest, ToolPolicyDiagnostic, ToolPolicyDiagnosticCode, ToolPolicyEntry, ToolPolicyHostGate, ToolScope, } from "./core/decision-types.js";
10
+ export type { ItemSettingNotAppliedDetails, OverrideBlockedDetails, PlacementMissingDetails, RequiredToolBlockedDetails, ToolPolicyDecision, ToolPolicyDecisionRequest, ToolPolicyDiagnostic, ToolPolicyDiagnosticCode, ToolPolicyDiagnosticDetails, ToolPolicyEntry, ToolPolicyHostGate, ToolScope, UnknownSupportIdDetails, } from "./core/decision-types.js";
26
11
  export type { PolicySource, PolicySourceDecisionContext, PolicySourceProvenanceEntry, PolicySourceResult, } from "./core/PolicySource.js";
27
- export type { PolicySourceTag, PnpPolicySourceRule, PnpPolicySourceTag, CustomPolicySourceTag, } from "./core/policy-source-tag.js";
28
- export type { ToolPolicyDecisionRule, ToolPolicyFeatureTrail, ToolPolicyProvenance, ToolPolicyResolutionDecision, ToolPolicySourceType, } from "./core/provenance.js";
12
+ export type { PnpPolicySourceRule, ToolPolicyDecisionRule, ToolPolicyFeatureTrail, ToolPolicyProvenance, ToolPolicyResolutionDecision, ToolPolicySourceType, } from "./core/provenance.js";
@@ -1,24 +1,9 @@
1
1
  /**
2
- * Tool policy engine — facade entry point (M8 PR 1).
3
- *
4
- * Narrow, stable public surface for hosts that want to instantiate or
5
- * consume a `ToolPolicyEngine`. Re-exports the engine class, its
6
- * decision request/response types, the Svelte context key, and the
7
- * minimal `PolicySource` extension contract.
8
- *
9
- * **Pairing.** This module is the stable counterpart to
10
- * `./policy/internal`, which exposes the wider, evolving surface
11
- * (composition pipeline, provenance builder, PNP policy source). Consumers
12
- * that only need to drive an engine and read its decisions should
13
- * import from here. Consumers that need to reach past the facade
14
- * (e.g. test the composition pipeline directly, build a custom PNP policy
15
- * source variant, instrument provenance generation) should import
16
- * from `./policy/internal` and accept the documented stability
17
- * disclaimer there.
18
- *
19
- * Mirrors the M7 `runtime/engine` / `runtime/internal` split — see
20
- * the same rationale in `src/runtime/engine.ts`.
2
+ * Tool policy engine — the stable entry for a host that instantiates or
3
+ * consumes a `ToolPolicyEngine`: the engine class, its decision request and
4
+ * response types, and the `PolicySource` extension contract. The composition
5
+ * pipeline, the PNP policy source and the provenance builder stay behind the
6
+ * engine, with no entry of their own.
21
7
  */
22
8
  export { ToolPolicyEngine, } from "./core/ToolPolicyEngine.js";
23
- export { TOOL_POLICY_ENGINE_KEY, } from "./core/engine-context.js";
24
9
  export { isHostDeniedFeature, } from "./core/feature-decision.js";
@@ -1,6 +1,5 @@
1
1
  /**
2
- * PNP Policy Source for the M8 engine
3
- * (see
2
+ * PNP Policy Source: step 5 of `composeDecision` (see
4
3
  * `docs/tools-and-accomodations/architecture.md`).
5
4
  *
6
5
  * This source applies the PNP/profile precedence rules as a `(candidates, pnpPolicyInputs) →
@@ -8,26 +7,35 @@
8
7
  * engine can call once per `decide(...)` request, with all results
9
8
  * routed through the unified `ToolPolicyProvenanceBuilder`.
10
9
  */
11
- import type { AssessmentEntity, AssessmentItemRef } from "@pie-players/pie-players-shared/types";
10
+ import type { AssessmentEntity, ItemSettings } from "@pie-players/pie-players-shared/types";
12
11
  import type { ToolRegistry } from "../../services/ToolRegistry.js";
13
- import type { ToolPolicyResolutionDecision, ToolPolicySourceType } from "../core/provenance.js";
14
- import type { PnpPolicySourceRule } from "../core/policy-source-tag.js";
12
+ import type { PnpPolicySourceRule, ToolPolicyResolutionDecision, ToolPolicySourceType } from "../core/provenance.js";
13
+ import type { OverrideBlockedDetails } from "../core/decision-types.js";
15
14
  /** Per-tool flags PNP/profile policy may attach to a surviving entry. */
16
15
  export interface PnpPolicyToolFlags {
17
16
  /** PNP/profile policy mandates this tool (item or district `requiredTools`). */
18
17
  required: boolean;
19
- /** Student PNP marks this tool as a support (host UI cannot toggle off). */
18
+ /**
19
+ * The student's profile supports this tool, or a test-administration
20
+ * override grants it (host UI cannot toggle off).
21
+ */
20
22
  alwaysAvailable: boolean;
21
- /** Tool-specific settings derived from item / assessment settings. */
22
- settings?: unknown;
23
23
  /** Which PNP/profile rule contributed the surviving verdict. */
24
24
  rule: PnpPolicySourceRule;
25
- /** Source attribution for `ToolPolicyEntry.sources`. */
26
- sourceType: ToolPolicySourceType;
25
+ }
26
+ /** An item a decision is scoped to, by its canonical id, with its settings. */
27
+ export interface PnpPolicyItem {
28
+ id: string;
29
+ settings: ItemSettings;
27
30
  }
28
31
  export interface PnpPolicyApplyArgs {
29
32
  assessment?: AssessmentEntity | null;
30
- currentItemRef?: AssessmentItemRef;
33
+ /**
34
+ * The item the decision is scoped to. Only a decision for the item's own
35
+ * toolbar or content carries one: an item's settings do not reach a section-
36
+ * or assessment-level decision.
37
+ */
38
+ item?: PnpPolicyItem;
31
39
  }
32
40
  /**
33
41
  * Provenance event the engine should append to its builder. Pre-baked
@@ -35,8 +43,8 @@ export interface PnpPolicyApplyArgs {
35
43
  * keeps a uniform `addDecision(...)` shape.
36
44
  */
37
45
  export interface PnpPolicyDecisionEvent {
38
- precedence: 1 | 2 | 3 | 4 | 5 | 6;
39
- rule: "district-block" | "test-admin-override" | "item-restriction" | "item-requirement" | "district-requirement" | "pnp-support" | "pnp-prohibited";
46
+ precedence: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
47
+ rule: PnpPolicySourceRule;
40
48
  featureId: string;
41
49
  action: ToolPolicyResolutionDecision["action"];
42
50
  sourceType: ToolPolicySourceType;
@@ -50,11 +58,12 @@ export interface PnpPolicyResult {
50
58
  */
51
59
  blockedToolIds: Set<string>;
52
60
  /**
53
- * Tool IDs PNP/profile policy mandates (item or district `requiredTools`). Used
54
- * by the engine to detect `tool-policy.requiredToolBlocked`
61
+ * Tools PNP/profile policy mandates (item or district `requiredTools`), each
62
+ * with the requirement that names it, including a tool a `true` override
63
+ * decides. Used by the engine to detect `tool-policy.requiredToolBlocked`
55
64
  * diagnostics for tools removed by host policy.
56
65
  */
57
- mandatedToolIds: Set<string>;
66
+ mandates: Map<string, "item-requirement" | "district-requirement">;
58
67
  /**
59
68
  * Per-tool flags merged into surviving `ToolPolicyEntry`s.
60
69
  */
@@ -66,13 +75,11 @@ export interface PnpPolicyResult {
66
75
  */
67
76
  decisions: PnpPolicyDecisionEvent[];
68
77
  /**
69
- * Support ids named by a profile, district policy or item that no
70
- * registration claims. Nothing resolves them, so they are silently inert —
71
- * the engine turns each into a `tool-policy.unknownSupportId` diagnostic so a
72
- * host sending its own label instead of an AfA/QTI feature id learns of it
73
- * rather than seeing a tool quietly fail to appear.
78
+ * Ids a `true` test-administration override names that an item restriction
79
+ * or a PNP prohibition withdrew instead, each with the rule that did. The
80
+ * engine turns each into a `tool-policy.overrideBlocked` diagnostic.
74
81
  */
75
- unmappedSupportIds: Set<string>;
82
+ blockedOverrides: Map<string, OverrideBlockedDetails["rule"]>;
76
83
  /** Configuration sources the engine should attach to its provenance. */
77
84
  sources: {
78
85
  assessment?: {
@@ -98,20 +105,18 @@ export declare class PnpPolicySource {
98
105
  constructor(toolRegistry: ToolRegistry);
99
106
  apply(args: PnpPolicyApplyArgs): PnpPolicyResult;
100
107
  /**
101
- * Evaluate exactly one PNP support id through the same six-level
108
+ * Evaluate exactly one PNP support id through the same eight-level
102
109
  * precedence `apply(...)` uses.
103
110
  *
104
111
  * This exists for **policy-addressable capabilities that are not toolbar
105
112
  * tools** — a signed alternate rendered as its own region, for example.
106
113
  * `apply(...)` only evaluates support ids that appear somewhere in the
107
- * bound policy inputs, and it keys its result maps by *mapped tool id*, so
108
- * a caller asking about one feature would have to re-derive that mapping
109
- * and could collide with another support id that maps to the same tool.
110
- * Evaluating one id in isolation avoids both problems: the returned result
111
- * carries exactly one decision, and `decisions[0].action` is the verdict.
114
+ * bound policy inputs. Evaluating one id in isolation answers for any
115
+ * feature: the returned result carries exactly one decision, and
116
+ * `decisions[0].action` is the verdict.
112
117
  *
113
- * Reusing `resolveSupport(...)` rather than re-walking the precedence rules
114
- * is the point — a second copy of the six levels would drift.
118
+ * It reuses `resolveSupport(...)` because a second copy of the eight levels
119
+ * would drift.
115
120
  */
116
121
  resolveFeature(featureId: string, args: PnpPolicyApplyArgs): PnpPolicyResult;
117
122
  /**
@@ -121,43 +126,13 @@ export declare class PnpPolicySource {
121
126
  private prepare;
122
127
  private resolveSupport;
123
128
  /**
124
- * Map a QTI / PNP support id (e.g. `"calculator-basic"`) to the
125
- * registered tool id that owns it.
126
- *
127
- * Resolution rules:
128
- *
129
- * 1. **Multiple tools register the same support id:** the
130
- * *first-registered* tool wins. `ToolRegistry.getToolsByPNPSupport`
131
- * returns a `Set<string>` whose iteration order matches the
132
- * `register(...)` insertion order, so the result is
133
- * deterministic across runs but order-sensitive at registration
134
- * time. This is an unusual configuration — the typical case is
135
- * one tool per support id — but it is reachable when an
136
- * integrator overrides a default tool with a replacement that
137
- * claims the same `pnpSupportIds`. In that case the integrator
138
- * should `unregister(...)` the default before registering the
139
- * replacement.
140
- *
141
- * 2. **No tool registers the support id:** the support id is
142
- * returned verbatim as the `featureId` for provenance trails
143
- * and `ToolPolicyEntry.toolId`. This lets hosts use raw QTI
144
- * strings for tools the registry does not (yet) carry without
145
- * losing them in policy evaluation.
146
- *
147
- * `tests/policy/PnpPolicySource.test.ts` locks this mapping behavior.
148
- */
149
- private mapSupportToToolId;
150
- /**
151
- * Resolve tool-specific settings, item-level taking precedence over
152
- * assessment-level.
153
- *
154
- * Uses `??` (nullish-coalescing). Tool config values are typed `unknown` and may legitimately
155
- * be `false` / `0` / `""` (e.g. a tool whose canonical "off" config
156
- * is the literal `false`). `||` would treat those as missing and
157
- * silently fall through to the assessment-level config, masking the
158
- * authored item-level intent. `??` only falls through on
159
- * `null`/`undefined`, which matches the semantic of "value not
160
- * provided."
129
+ * Ids the assessment or any of `items` names that no tool is registered
130
+ * under, each with the rules whose lists name it, in precedence order. A
131
+ * support id is the id of the tool it grants, so an id missing from the
132
+ * registry matches nothing placed; the engine turns each into a
133
+ * `tool-policy.unknownSupportId` diagnostic. Empty when the registry is: an
134
+ * empty registry has nothing to check an id against, and
135
+ * `tool-config-validation` already reports it once.
161
136
  */
162
- private resolveToolSettings;
137
+ unknownSupportIds(assessment: AssessmentEntity | null | undefined, items: readonly ItemSettings[]): Map<string, PnpPolicySourceRule[]>;
163
138
  }