@pie-players/pie-assessment-toolkit 0.3.44 → 0.3.45

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 (191) hide show
  1. package/README.md +54 -69
  2. package/dist/components/ItemToolBar.custom-element.js +479 -324
  3. package/dist/components/PieAssessmentToolkit.custom-element.js +19498 -3590
  4. package/dist/components/SectionToolBar.custom-element.js +761 -507
  5. package/dist/index.d.ts +3 -6
  6. package/dist/index.js +1 -3
  7. package/dist/index.js.map +1 -1
  8. package/dist/policy/core/ToolPolicyEngine.d.ts +1 -2
  9. package/dist/policy/core/ToolPolicyEngine.js +1 -2
  10. package/dist/policy/core/ToolPolicyEngine.js.map +1 -1
  11. package/dist/policy/core/pnp-policy-inputs.d.ts +2 -3
  12. package/dist/policy/core/pnp-policy-inputs.js +2 -3
  13. package/dist/policy/core/pnp-policy-inputs.js.map +1 -1
  14. package/dist/policy/core/provenance.d.ts +7 -34
  15. package/dist/policy/core/provenance.js +4 -21
  16. package/dist/policy/core/provenance.js.map +1 -1
  17. package/dist/policy/sources/PnpPolicySource.d.ts +6 -19
  18. package/dist/policy/sources/PnpPolicySource.js +6 -19
  19. package/dist/policy/sources/PnpPolicySource.js.map +1 -1
  20. package/dist/runtime/SectionRuntimeEngine.d.ts +13 -28
  21. package/dist/runtime/SectionRuntimeEngine.js +13 -27
  22. package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
  23. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +2 -2
  24. package/dist/runtime/adapter/SectionEngineAdapter.js +2 -2
  25. package/dist/runtime/adapter/SectionEngineAdapter.js.map +1 -1
  26. package/dist/runtime/adapter/coordinator-bridge.d.ts +1 -1
  27. package/dist/runtime/adapter/coordinator-bridge.js +1 -1
  28. package/dist/runtime/adapter/coordinator-bridge.js.map +1 -1
  29. package/dist/runtime/adapter/dom-event-bridge.d.ts +1 -1
  30. package/dist/runtime/adapter/dom-event-bridge.js +1 -1
  31. package/dist/runtime/adapter/dom-event-bridge.js.map +1 -1
  32. package/dist/runtime/catalog-registration.d.ts +12 -0
  33. package/dist/runtime/catalog-registration.js +54 -0
  34. package/dist/runtime/catalog-registration.js.map +1 -0
  35. package/dist/runtime/core/engine-output.d.ts +2 -2
  36. package/dist/runtime/core/engine-output.js +2 -2
  37. package/dist/runtime/core/engine-output.js.map +1 -1
  38. package/dist/runtime/core/engine-readiness.d.ts +2 -2
  39. package/dist/runtime/core/engine-readiness.js.map +1 -1
  40. package/dist/runtime/core/engine-resolver.d.ts +13 -41
  41. package/dist/runtime/core/engine-resolver.js +19 -76
  42. package/dist/runtime/core/engine-resolver.js.map +1 -1
  43. package/dist/runtime/core/engine-transition.d.ts +1 -1
  44. package/dist/runtime/core/engine-transition.js +1 -1
  45. package/dist/runtime/core/engine-transition.js.map +1 -1
  46. package/dist/runtime/engine.d.ts +1 -1
  47. package/dist/runtime/engine.js.map +1 -1
  48. package/dist/runtime/section-runtime-engine-host-context.d.ts +22 -15
  49. package/dist/runtime/section-runtime-engine-host-context.js +10 -10
  50. package/dist/runtime/section-runtime-engine-host-context.js.map +1 -1
  51. package/dist/runtime/stage-emit-gate.d.ts +4 -4
  52. package/dist/runtime/stage-emit-gate.js +3 -3
  53. package/dist/runtime/stage-emit-gate.js.map +1 -1
  54. package/dist/services/AccessibilityCatalogResolver.d.ts +23 -0
  55. package/dist/services/AccessibilityCatalogResolver.js +200 -28
  56. package/dist/services/AccessibilityCatalogResolver.js.map +1 -1
  57. package/dist/services/HighlightCoordinator.d.ts +25 -0
  58. package/dist/services/HighlightCoordinator.js +125 -0
  59. package/dist/services/HighlightCoordinator.js.map +1 -1
  60. package/dist/services/SSMLExtractor.d.ts +16 -1
  61. package/dist/services/SSMLExtractor.js +86 -7
  62. package/dist/services/SSMLExtractor.js.map +1 -1
  63. package/dist/services/TTSService.d.ts +30 -4
  64. package/dist/services/TTSService.js +652 -159
  65. package/dist/services/TTSService.js.map +1 -1
  66. package/dist/services/ToolRegistry.d.ts +2 -2
  67. package/dist/services/ToolRegistry.js +1 -1
  68. package/dist/services/ToolRegistry.js.map +1 -1
  69. package/dist/services/ToolkitCoordinator.d.ts +16 -73
  70. package/dist/services/ToolkitCoordinator.js +34 -113
  71. package/dist/services/ToolkitCoordinator.js.map +1 -1
  72. package/dist/services/interfaces.d.ts +20 -2
  73. package/dist/services/interfaces.js.map +1 -1
  74. package/dist/services/tool-config-validation.d.ts +1 -1
  75. package/dist/services/tool-config-validation.js +7 -7
  76. package/dist/services/tool-config-validation.js.map +1 -1
  77. package/dist/services/tool-instance-id.js +1 -1
  78. package/dist/services/tool-instance-id.js.map +1 -1
  79. package/dist/services/tool-providers/TTSToolProvider.js +3 -3
  80. package/dist/services/tool-providers/TTSToolProvider.js.map +1 -1
  81. package/dist/services/tools-config-normalizer.d.ts +13 -2
  82. package/dist/services/tools-config-normalizer.js +3 -8
  83. package/dist/services/tools-config-normalizer.js.map +1 -1
  84. package/dist/services/tts/browser-provider.js +2 -0
  85. package/dist/services/tts/browser-provider.js.map +1 -1
  86. package/dist/services/tts/catalog-span-alignment.d.ts +39 -0
  87. package/dist/services/tts/catalog-span-alignment.js +300 -0
  88. package/dist/services/tts/catalog-span-alignment.js.map +1 -0
  89. package/dist/services/tts/generated-speech/assemble-plan.d.ts +33 -0
  90. package/dist/services/tts/generated-speech/assemble-plan.js +111 -0
  91. package/dist/services/tts/generated-speech/assemble-plan.js.map +1 -0
  92. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +25 -0
  93. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +66 -0
  94. package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +1 -0
  95. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +19 -0
  96. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +71 -0
  97. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +1 -0
  98. package/dist/services/tts/generated-speech/dom/types.d.ts +37 -0
  99. package/dist/services/tts/generated-speech/dom/types.js +2 -0
  100. package/dist/services/tts/generated-speech/dom/types.js.map +1 -0
  101. package/dist/services/tts/generated-speech/index.d.ts +17 -0
  102. package/dist/services/tts/generated-speech/index.js +17 -0
  103. package/dist/services/tts/generated-speech/index.js.map +1 -0
  104. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +15 -0
  105. package/dist/services/tts/generated-speech/math-speech-cache.js +78 -0
  106. package/dist/services/tts/generated-speech/math-speech-cache.js.map +1 -0
  107. package/dist/services/tts/generated-speech/types.d.ts +104 -0
  108. package/dist/services/tts/generated-speech/types.js +18 -0
  109. package/dist/services/tts/generated-speech/types.js.map +1 -0
  110. package/dist/services/tts/highlight-pipeline/boundary-normalizer.d.ts +2 -0
  111. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +106 -0
  112. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +1 -0
  113. package/dist/services/tts/highlight-pipeline/create-highlight-plan.d.ts +6 -0
  114. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +217 -0
  115. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +1 -0
  116. package/dist/services/tts/highlight-pipeline/highlight-policy.d.ts +2 -0
  117. package/dist/services/tts/highlight-pipeline/highlight-policy.js +32 -0
  118. package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +1 -0
  119. package/dist/services/tts/highlight-pipeline/index.d.ts +9 -0
  120. package/dist/services/tts/highlight-pipeline/index.js +9 -0
  121. package/dist/services/tts/highlight-pipeline/index.js.map +1 -0
  122. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.d.ts +3 -0
  123. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +32 -0
  124. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +1 -0
  125. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.d.ts +29 -0
  126. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +44 -0
  127. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +1 -0
  128. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.d.ts +2 -0
  129. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +20 -0
  130. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +1 -0
  131. package/dist/services/tts/highlight-pipeline/readable-region.d.ts +1 -0
  132. package/dist/services/tts/highlight-pipeline/readable-region.js +19 -0
  133. package/dist/services/tts/highlight-pipeline/readable-region.js.map +1 -0
  134. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.d.ts +6 -0
  135. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +198 -0
  136. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +1 -0
  137. package/dist/services/tts/highlight-pipeline/types.d.ts +72 -0
  138. package/dist/services/tts/highlight-pipeline/types.js +2 -0
  139. package/dist/services/tts/highlight-pipeline/types.js.map +1 -0
  140. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +2 -0
  141. package/dist/services/tts/highlight-pipeline/visible-map-range.js +22 -0
  142. package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +1 -0
  143. package/dist/services/tts/math-alignment/index.d.ts +30 -0
  144. package/dist/services/tts/math-alignment/index.js +66 -0
  145. package/dist/services/tts/math-alignment/index.js.map +1 -0
  146. package/dist/services/tts/math-alignment/mathml-tokenizer.d.ts +2 -0
  147. package/dist/services/tts/math-alignment/mathml-tokenizer.js +317 -0
  148. package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +1 -0
  149. package/dist/services/tts/math-alignment/range-resolver.d.ts +10 -0
  150. package/dist/services/tts/math-alignment/range-resolver.js +13 -0
  151. package/dist/services/tts/math-alignment/range-resolver.js.map +1 -0
  152. package/dist/services/tts/math-alignment/sequence-aligner.d.ts +7 -0
  153. package/dist/services/tts/math-alignment/sequence-aligner.js +119 -0
  154. package/dist/services/tts/math-alignment/sequence-aligner.js.map +1 -0
  155. package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +31 -0
  156. package/dist/services/tts/math-alignment/speech-tokenizer.js +143 -0
  157. package/dist/services/tts/math-alignment/speech-tokenizer.js.map +1 -0
  158. package/dist/services/tts/math-alignment/types.d.ts +58 -0
  159. package/dist/services/tts/math-alignment/types.js +2 -0
  160. package/dist/services/tts/math-alignment/types.js.map +1 -0
  161. package/dist/services/tts/math-aware-text-processing.d.ts +18 -0
  162. package/dist/services/tts/math-aware-text-processing.js +348 -0
  163. package/dist/services/tts/math-aware-text-processing.js.map +1 -0
  164. package/dist/services/tts/math-speech.d.ts +44 -0
  165. package/dist/services/tts/math-speech.js +147 -0
  166. package/dist/services/tts/math-speech.js.map +1 -0
  167. package/dist/services/tts/mathml-sanitization.d.ts +1 -0
  168. package/dist/services/tts/mathml-sanitization.js +170 -0
  169. package/dist/services/tts/mathml-sanitization.js.map +1 -0
  170. package/dist/services/tts/ssml/spoken-text.d.ts +33 -0
  171. package/dist/services/tts/ssml/spoken-text.js +159 -0
  172. package/dist/services/tts/ssml/spoken-text.js.map +1 -0
  173. package/dist/services/tts/text-processing.d.ts +3 -0
  174. package/dist/services/tts/text-processing.js +30 -4
  175. package/dist/services/tts/text-processing.js.map +1 -1
  176. package/dist/services/tts-runtime-config.d.ts +17 -1
  177. package/dist/services/tts-runtime-config.js +11 -1
  178. package/dist/services/tts-runtime-config.js.map +1 -1
  179. package/dist/tools/registrations/accessibility-tools.js +0 -1
  180. package/dist/tools/registrations/accessibility-tools.js.map +1 -1
  181. package/dist/tools/registrations/calculator.js +41 -2
  182. package/dist/tools/registrations/calculator.js.map +1 -1
  183. package/dist/tools/tool-tag-map.js +0 -1
  184. package/dist/tools/tool-tag-map.js.map +1 -1
  185. package/package.json +8 -11
  186. package/dist/services/PNPToolResolver.d.ts +0 -280
  187. package/dist/services/PNPToolResolver.js +0 -514
  188. package/dist/services/PNPToolResolver.js.map +0 -1
  189. package/dist/services/pnp-provenance.d.ts +0 -166
  190. package/dist/services/pnp-provenance.js +0 -231
  191. package/dist/services/pnp-provenance.js.map +0 -1
package/README.md CHANGED
@@ -44,8 +44,8 @@ const toolkitCoordinator = new ToolkitCoordinator({
44
44
  }
45
45
  });
46
46
 
47
- // Pass single coordinator to player
48
- player.toolkitCoordinator = toolkitCoordinator;
47
+ // Pass single coordinator to section-player through runtime
48
+ player.runtime = { ...(player.runtime ?? {}), coordinator: toolkitCoordinator };
49
49
  ```
50
50
 
51
51
  ## What Does It Solve?
@@ -102,7 +102,7 @@ dedupe is a safety net, not a substitute for clear ownership.
102
102
 
103
103
  ## Architecture Overview
104
104
 
105
- See [ToolkitCoordinator Architecture](../../docs/architecture/TOOLKIT_COORDINATOR.md) for complete design documentation.
105
+ See the [ToolkitCoordinator section in the architecture overview](../../docs/architecture/architecture.md#toolkitcoordinator-centralized-service-management) for the current design documentation.
106
106
 
107
107
  ### Core Principles
108
108
 
@@ -149,30 +149,24 @@ tier; the choice is about ergonomics, not capability.
149
149
  placement: { item: ["calculator", "textToSpeech"] },
150
150
  },
151
151
  });
152
- el.coordinator = coordinator;
152
+ el.runtime = { ...(el.runtime ?? {}), coordinator };
153
153
  ```
154
154
 
155
155
  ### Naming rule
156
156
 
157
- The easy-tier attribute name is the kebab-cased version of the runtime /
158
- config key. `tool-config-strictness` `toolConfigStrictness`,
159
- `assessment-id` `assessmentId`, `player-type` `playerType`. Hosts can
160
- move a knob from the easy tier to the configuration object (or back)
161
- without renaming.
157
+ Top-level attributes use kebab-case (`assessment-id`,
158
+ `tool-config-strictness`). Section-player runtime configuration is grouped
159
+ under the `runtime` object instead of duplicated as top-level props.
162
160
 
163
161
  ### Precedence rule
164
162
 
165
- The configuration object wins. When the same knob is set in both tiers,
166
- resolution is:
163
+ The configuration object owns runtime fields. Section-player layout attributes
164
+ cover identity, layout, diagnostics, and callback/event convenience:
167
165
 
168
- 1. The constructed `ToolkitCoordinator` config (or `runtime.<key>` on a
169
- consumer CE) if set
170
- 2. Top-level attribute / property if set
171
- 3. Documented default
172
-
173
- This applies symmetrically to `pie-assessment-toolkit` itself and to
174
- section-player CEs that embed it. New knobs MUST follow this precedence;
175
- do not add ad-hoc fall-throughs.
166
+ 1. Use `runtime.<key>` for player, tool, accessibility, coordinator, env,
167
+ isolation, and runtime factories.
168
+ 2. Use top-level layout attributes for section identity and layout controls.
169
+ 3. Use documented defaults when neither is provided.
176
170
 
177
171
  ### Canonical tier-1 attribute set
178
172
 
@@ -188,15 +182,9 @@ kebab-attribute ↔ camelCaseProp ↔ runtime.<sameCamelCaseKey>
188
182
  Common members include:
189
183
 
190
184
  - Identity: `assessment-id`, `section-id`, `attempt-id`
191
- - Player: `player-type`, `lazy-init`
192
- - Tools: `tools` (object property), `tool-registry` (object property),
193
- `enabled-tools` (shorthand for `tools.placement.section`)
194
- - Coordination: `coordinator` (`createSectionController` is a
195
- runtime-only key on the section-player layout CEs — see
196
- "Documented exceptions" below; `<pie-assessment-toolkit>` accepts
197
- it as a direct JS prop on its own composition surface)
198
- - Accessibility: `accessibility` (object property; the deprecated
199
- `accessibility` *attribute* mapping was removed in M5)
185
+ - Runtime config on section-player CEs: `runtime`
186
+ - Toolkit-only object properties: `tools`, `tool-registry`, `coordinator`,
187
+ `accessibility`
200
188
  - Diagnostics: `tool-config-strictness`, `debug`. Framework-error
201
189
  delivery is via the canonical `onFrameworkError` callback prop and the
202
190
  `framework-error` DOM event dispatched on the layout CE host.
@@ -210,13 +198,10 @@ Documented exceptions to the mirror rule:
210
198
  `split-pane-collapse-strategy`): layout-CE rendering concerns.
211
199
  - Per-region toolbar tool placement: hosts populate
212
200
  `tools.placement.item` / `tools.placement.passage` (object form) or
213
- `runtime.tools.placement.{item,passage}` directly. The previously
214
- deprecated `item-toolbar-tools` / `passage-toolbar-tools` attribute
215
- aliases were removed in the broad architecture review compat sweep.
201
+ `runtime.tools.placement.{item,passage}` directly.
216
202
  - Runtime-only keys on the section-player layout CEs
217
203
  (`createSectionController`, `isolation`): accepted only via
218
- `runtime.<key>`. The top-level prop aliases were removed in the
219
- broad architecture review compat sweep. `<pie-assessment-toolkit>`
204
+ `runtime.<key>`. `<pie-assessment-toolkit>`
220
205
  itself keeps `createSectionController` and `isolation` as JS-only
221
206
  props (no kebab-attribute surface): section-player layouts forward
222
207
  `runtime.isolation` and `runtime.createSectionController` to the
@@ -267,7 +252,7 @@ const coordinator = new ToolkitCoordinator({
267
252
 
268
253
  // Pass to section player
269
254
  const player = document.getElementById('player');
270
- player.toolkitCoordinator = coordinator;
255
+ player.runtime = { ...(player.runtime ?? {}), coordinator };
271
256
 
272
257
  // Access services directly if needed
273
258
  const ttsService = coordinator.ttsService;
@@ -339,7 +324,7 @@ What this means in practice for typed integrations:
339
324
  - **Watch for double-replay if you re-subscribe on every `toolkit-ready`.** Hosts that detached and re-subscribed on every `toolkit-ready` event (the correct pre-Phase D pattern, since each subscription was pinned to a `sectionId`) will now observe **two snapshot replays per navigation**: one delivered automatically when Phase D migrates the existing listener to the new active cohort, and a second when the manual re-subscribe attaches a fresh listener that replays again. Listener handlers that are not strictly idempotent will fire twice — analytics `pageAction`s, non-Set counters, side-effecting hydration. The fix is a one-line guard (`if (this.controllerUnsubscribe) return;`) so the subscribe runs only on the first `toolkit-ready`.
340
325
  - **For intentionally-pinned subscriptions to inactive sections** (e.g. a host UI that wants to keep watching section A while the user views section B), the helper API does not support that pattern by design. Use `coordinator.getSectionController({ sectionId, attemptId })` and subscribe directly on the controller handle (`controller.subscribe?.(...)`) — that binding is pinned to one controller instance and does not migrate.
341
326
 
342
- If your local types were hand-rolled structural copies of the public arg types (e.g. an Angular wrapper duplicating the shape rather than importing the package types), the legacy keys will compile but are dead code at runtime — recommend dropping them as part of the upgrade.
327
+ If your local types were hand-rolled structural copies of the public arg types (e.g. an Angular wrapper duplicating the shape rather than importing the package types), `sectionId` / `attemptId` keys will compile but are dead code at runtime — recommend dropping them as part of the upgrade.
343
328
 
344
329
  #### Pre-Phase D vs Phase D wrapper pattern
345
330
 
@@ -488,7 +473,7 @@ Tools that **float above the entire assessment** and persist across questions:
488
473
  ```typescript
489
474
  tools: {
490
475
  placement: {
491
- section: ['calculator', 'graph', 'periodicTable', 'protractor', 'lineReader', 'ruler', 'colorScheme']
476
+ section: ['calculator', 'graph', 'periodicTable', 'protractor', 'lineReader', 'ruler', 'theme']
492
477
  },
493
478
  providers: {
494
479
  calculator: {
@@ -546,7 +531,7 @@ const coordinator = new ToolkitCoordinator({
546
531
  item: ['calculator', 'textToSpeech', 'answerEliminator'],
547
532
  passage: ['textToSpeech'],
548
533
  // Section-level utilities
549
- section: ['calculator', 'graph', 'periodicTable', 'protractor', 'lineReader', 'ruler', 'colorScheme']
534
+ section: ['calculator', 'graph', 'periodicTable', 'protractor', 'lineReader', 'ruler', 'theme']
550
535
  },
551
536
  providers: {
552
537
  calculator: {
@@ -760,7 +745,7 @@ The persistence strategy works with the same `SectionControllerSessionState` sha
760
745
  - **ToolkitCoordinator**: ⭐ NEW - Centralized service orchestration
761
746
  - **ElementToolStateStore**: ⭐ NEW - Element-level ephemeral tool state management
762
747
  - **ToolRegistry**: ⭐ NEW - Registry-based tool management with QTI 3.0 PNP support
763
- - **PNPToolResolver**: ⭐ REFACTORED - QTI 3.0 Personal Needs Profile tool resolution via registry
748
+ - **ToolPolicyEngine**: QTI 3.0 Personal Needs Profile and host policy decisions via registry-backed policy sources
764
749
  - **ToolCoordinator**: Manages z-index layering and visibility for floating tools
765
750
  - **HighlightCoordinator**: Separate highlight layers for TTS (temporary) and annotations (persistent)
766
751
  - **TTSService**: Text-to-speech with QTI 3.0 catalog support
@@ -1067,7 +1052,7 @@ The section player provides automatic ToolkitCoordinator integration:
1067
1052
 
1068
1053
  // Pass to player
1069
1054
  const player = document.getElementById('player');
1070
- player.toolkitCoordinator = coordinator;
1055
+ player.runtime = { ...(player.runtime ?? {}), coordinator };
1071
1056
  player.section = mySection;
1072
1057
 
1073
1058
  // Player automatically:
@@ -1206,6 +1191,8 @@ pick the stability surface that matches their use case:
1206
1191
  `SECTION_RUNTIME_ENGINE_KEY` (Svelte context), the cross-CE host
1207
1192
  context (`sectionRuntimeEngineHostContext`), and the consumer-side
1208
1193
  helper for that bridge (`connectSectionRuntimeEngineHostContext`).
1194
+ The cross-CE host context exposes only a lifecycle handle; controller
1195
+ methods stay on `SectionRuntimeEngine`.
1209
1196
  - **Internal surface — `@pie-players/pie-assessment-toolkit/runtime/internal`.**
1210
1197
  Wider, evolving surface for advanced hosts that need to construct an
1211
1198
  engine manually, inspect FSM state, or build alternate fan-out paths.
@@ -1217,30 +1204,28 @@ pick the stability surface that matches their use case:
1217
1204
  `resolveSectionEngineRuntimeState` helpers. Symbols here may change
1218
1205
  between minor versions with a changeset note.
1219
1206
 
1220
- ### Single-engine invariant
1207
+ ### Lifecycle emit coordination
1221
1208
 
1222
1209
  When `<pie-assessment-toolkit>` is nested inside a section-player layout,
1223
- the layout kernel publishes its engine reference via
1224
- `sectionRuntimeEngineHostContext`. The toolkit detects that upstream
1225
- engine and **suppresses its own external lifecycle DOM emits and stage
1226
- tracker** in favor of the kernel's engine. From the outside, one cohort
1227
- yields one `pie-stage-change` / `pie-loading-complete` chain on the
1228
- layout CE host regardless of wrapper depth — even though, during the
1229
- 0.x line, the toolkit still constructs a local engine instance for its
1230
- controller-side surface (`register`, `handleContent*`, `initialize`).
1231
- A future release collapses the toolkit's controller-side surface onto
1232
- the upstream engine; until then the externally observable invariant —
1233
- **one cohort, one canonical event chain** — is what hosts should rely
1234
- on. A standalone `<pie-assessment-toolkit>` (no upstream context)
1235
- emits from its own engine.
1210
+ the layout kernel publishes a lifecycle handle via
1211
+ `sectionRuntimeEngineHostContext`. The toolkit detects that host
1212
+ lifecycle owner and **suppresses its own external lifecycle DOM emits
1213
+ and `onStageChange` callback** in favor of the layout CE host. From the
1214
+ outside, one cohort yields one `pie-stage-change` /
1215
+ `pie-loading-complete` chain on the layout CE host regardless of wrapper
1216
+ depth. Controller-side
1217
+ registration, content loading, session propagation, and persistence
1218
+ remain toolkit-local through its own `SectionRuntimeEngine` instance.
1219
+ A standalone `<pie-assessment-toolkit>` (no host context) emits from
1220
+ its own engine.
1236
1221
 
1237
1222
  **Detection.** If a custom layout shell emits two `pie-stage-change`
1238
1223
  events per stage transition (or two `pie-loading-complete` per cohort)
1239
1224
  on the same layout CE — typically with two distinct `detail.runtimeId`
1240
1225
  values — the shell has not published its engine via
1241
1226
  `sectionRuntimeEngineHostContext`, so the wrapped
1242
- `<pie-assessment-toolkit>` falls back to its standalone path and
1243
- constructs a second engine. Wire the bridge as shown below.
1227
+ `<pie-assessment-toolkit>` falls back to its standalone lifecycle emit
1228
+ path. Wire the bridge as shown below.
1244
1229
 
1245
1230
  ### Common-host wiring example
1246
1231
 
@@ -1272,12 +1257,17 @@ engine.attachHost({
1272
1257
  coordinator: toolkitCoordinator,
1273
1258
  });
1274
1259
 
1275
- // 2. Publish the engine reference on the layout CE host so any
1276
- // wrapped <pie-assessment-toolkit> consumes it instead of
1277
- // constructing its own (single-engine invariant).
1260
+ // 2. Publish a lifecycle handle on the layout CE host so any wrapped
1261
+ // <pie-assessment-toolkit> suppresses duplicate external lifecycle
1262
+ // emits. The toolkit still owns its controller registration/session
1263
+ // plumbing locally.
1278
1264
  const engineProvider = new ContextProvider(layoutHostElement, {
1279
1265
  context: sectionRuntimeEngineHostContext,
1280
- initialValue: { engine },
1266
+ initialValue: {
1267
+ engine: {
1268
+ getRuntimeId: () => engine.getRuntimeId(),
1269
+ },
1270
+ },
1281
1271
  });
1282
1272
  engineProvider.connect();
1283
1273
 
@@ -1330,15 +1320,10 @@ listeners on `<pie-assessment-toolkit>` itself still see the toolkit's
1330
1320
  own emit (the toolkit dispatch reaches them before the kernel listener
1331
1321
  runs). The single-emit contract is pinned by
1332
1322
  `packages/section-player/tests/section-player-framework-error-dual-emit.test.ts`.
1333
- The previous dual-emit on the layout host was removed in the broad
1334
- architecture review compat sweep.
1323
+ The layout host emits one `framework-error` DOM event per framework error.
1335
1324
 
1336
- The deprecated readiness aliases (`readiness-change`,
1337
- `interaction-ready`, `ready`) and their `legacy-event-bridge` were
1338
- removed in the broad architecture review compat sweep. Hosts that
1339
- listened for them migrate to `pie-stage-change` (with the readiness
1340
- detail also available via the kernel's `selectReadiness()`) and
1341
- `pie-loading-complete`.
1325
+ Hosts should listen to `pie-stage-change` (with the readiness detail also
1326
+ available via the kernel's `selectReadiness()`) and `pie-loading-complete`.
1342
1327
 
1343
1328
  ## State Separation: Tool State vs Session Data
1344
1329
 
@@ -1436,9 +1421,9 @@ from tool configuration. Two sanitization layers apply:
1436
1421
 
1437
1422
  - **[Tool Registry Architecture](docs/TOOL_REGISTRY.md)** - ⭐ NEW - Registry-based tool management and QTI 3.0 PNP support
1438
1423
  - **[PNP Configuration Guide](docs/PNP_CONFIGURATION.md)** - ⭐ NEW - How to configure student profiles, district policies, and governance rules
1439
- - [ToolkitCoordinator Architecture](../../docs/architecture/TOOLKIT_COORDINATOR.md) - Design decisions and patterns
1424
+ - [ToolkitCoordinator Architecture](../../docs/architecture/architecture.md#toolkitcoordinator-centralized-service-management) - Design decisions and patterns
1440
1425
  - [Section Player README](../section-player/README.md) - Section player integration
1441
- - [Section Player Architecture](../section-player/ARCHITECTURE.md#layered-runtime-engine-post-m7) - Layered runtime engine, kernel/toolkit wiring, single-engine invariant
1426
+ - [Section Player Architecture](../section-player/ARCHITECTURE.md#layered-runtime-engine-post-m7) - Layered runtime engine, kernel/toolkit wiring, lifecycle emit invariant
1442
1427
  - [Framework-Owned Error Handling](../../docs/tools-and-accomodations/framework-owned-error-handling.md) - Canonical framework error model/events and fallback behavior
1443
1428
  - [Safe Custom Tool Configuration](../../docs/tools-and-accomodations/safe-custom-tool-config.md) - Host-side config patterns and validation guidance
1444
1429
  - [Architecture Overview](../../docs/architecture/architecture.md) - Complete system architecture