@qlm/learnlog-mcp 0.0.0-stage → 0.2.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 (74) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +43 -0
  4. package/README.md +121 -2
  5. package/data/SOURCE_CORRECTION.md +9 -0
  6. package/data/THIRD_PARTY_DATA.md +148 -0
  7. package/data/examples/chain-tpc-clinical-v2-tampered.json +97 -0
  8. package/data/examples/chain-tpc-clinical-v2.json +95 -0
  9. package/data/examples/evidence-record-clean.json +173 -0
  10. package/data/examples/evidence-record-tampered.json +173 -0
  11. package/data/labpath-worlds.json +107 -0
  12. package/data/preprint/README.md +10 -0
  13. package/data/preprint/assistments_episodes.json +37 -0
  14. package/data/preprint/cross_source_episodes.json +63 -0
  15. package/data/provenance/oatutor.json +84 -0
  16. package/data/provenance/source-audit.json +69 -0
  17. package/data/provenance/tutormoments.json +91 -0
  18. package/data/samples/explicit-assistance-SYNTHETIC.jsonl +5 -0
  19. package/data/samples/oatutor-real-participants.csv +13 -0
  20. package/data/samples/sandpiper-SYNTHETIC.csv +9 -0
  21. package/data/samples/tutormoments-real-transcripts.jsonl +2 -0
  22. package/dist/bin.js +6 -0
  23. package/dist/episodes.js +488 -0
  24. package/dist/files.js +39 -0
  25. package/dist/package.js +14 -0
  26. package/dist/py.js +419 -0
  27. package/dist/respond.js +47 -0
  28. package/dist/sealed.js +31 -0
  29. package/dist/server.js +115 -0
  30. package/dist/stages.js +35 -0
  31. package/dist/tools/formats.js +35 -0
  32. package/dist/tools/inventory.js +179 -0
  33. package/dist/tools/learning-graph.js +111 -0
  34. package/dist/tools/misconceptions.js +54 -0
  35. package/dist/tools/preprint.js +241 -0
  36. package/dist/tools/qlm-datasets.js +56 -0
  37. package/dist/tools/transform.js +61 -0
  38. package/dist/tools/validate.js +47 -0
  39. package/dist/tools/verify-credential.js +155 -0
  40. package/dist/tools/verify-evidence.js +51 -0
  41. package/dist/tools/worlds.js +24 -0
  42. package/dist/transformer.js +92 -0
  43. package/dist/zip.js +71 -0
  44. package/package.json +66 -4
  45. package/skills/learnlog-assistments/SKILL.md +38 -0
  46. package/skills/learnlog-export-to-action/SKILL.md +31 -0
  47. package/skills/learnlog-learning-commons/SKILL.md +33 -0
  48. package/skills/learnlog-nto-sandpiper/SKILL.md +31 -0
  49. package/vendor/PROVENANCE.json +259 -0
  50. package/vendor/qlm-measure/checks.d.ts +24 -0
  51. package/vendor/qlm-measure/checks.js +42 -0
  52. package/vendor/qlm-measure/cli.d.ts +17 -0
  53. package/vendor/qlm-measure/cli.js +461 -0
  54. package/vendor/qlm-measure/rules.d.ts +10 -0
  55. package/vendor/qlm-measure/rules.js +46 -0
  56. package/vendor/qlm-measure/schemes/index.d.ts +133 -0
  57. package/vendor/qlm-measure/schemes/index.js +339 -0
  58. package/vendor/qlm-measure/schemes/node.d.ts +8 -0
  59. package/vendor/qlm-measure/schemes/node.js +15 -0
  60. package/vendor/qlm-measure/schemes/products.d.ts +38 -0
  61. package/vendor/qlm-measure/schemes/products.js +725 -0
  62. package/vendor/qlm-measure/verifier/hash.d.ts +27 -0
  63. package/vendor/qlm-measure/verifier/hash.js +163 -0
  64. package/vendor/qlm-measure/verifier/verify-record.d.ts +22 -0
  65. package/vendor/qlm-measure/verifier/verify-record.js +123 -0
  66. package/vendor/qlm-measure/verifier-v03.d.ts +24 -0
  67. package/vendor/qlm-measure/verifier-v03.js +205 -0
  68. package/vendor/transformer/LICENSE.txt +29 -0
  69. package/vendor/transformer/cross-source-analysis-data.d.mts +3 -0
  70. package/vendor/transformer/cross-source-analysis-data.mjs +238 -0
  71. package/vendor/transformer/engine.d.mts +61 -0
  72. package/vendor/transformer/engine.mjs +445 -0
  73. package/vendor/transformer/profiles.mjs +309 -0
  74. package/vendor/transformer/research-semantics.mjs +1453 -0
@@ -0,0 +1,1453 @@
1
+ // Versioned application profile. JSON companion must remain byte-semantically equal (tested).
2
+ export const ASSISTANCE_CONTEXT_PROFILE = {
3
+ "$id": "https://transformer.quantumlearningmachines.com/schemas/assistance-context-v0.1.0.json",
4
+ "profile_id": "qlm:assistance-context:0.1.0",
5
+ "version": "0.1.0",
6
+ "status": "proposed_application_profile_not_ietf_standard",
7
+ "context_fields": {
8
+ "phase": {
9
+ "values": [
10
+ "practice",
11
+ "assessment",
12
+ "other",
13
+ "unknown"
14
+ ],
15
+ "default": "unknown",
16
+ "definition": "Declared activity phase; not inferred from task content or scores."
17
+ },
18
+ "permission": {
19
+ "values": [
20
+ "permitted",
21
+ "prohibited",
22
+ "restricted",
23
+ "unknown"
24
+ ],
25
+ "default": "unknown",
26
+ "definition": "Declared assistance policy; permission is not observation of use."
27
+ },
28
+ "observed_use": {
29
+ "values": [
30
+ "recorded",
31
+ "not_recorded",
32
+ "unknown"
33
+ ],
34
+ "default": "unknown",
35
+ "definition": "Source assertion about recording assistance use within its capture boundary; not_recorded is not proof of absence."
36
+ },
37
+ "capture": {
38
+ "values": [
39
+ "complete_within_boundary",
40
+ "partial",
41
+ "unknown"
42
+ ],
43
+ "default": "unknown",
44
+ "definition": "Declared instrument coverage within an explicit boundary; outside help remains unobserved."
45
+ },
46
+ "capture_boundary": {
47
+ "type": "nullable_string",
48
+ "default": null,
49
+ "definition": "Source-declared system, event classes and period within which coverage is asserted.",
50
+ "max_length": 2000
51
+ },
52
+ "product_version": {
53
+ "type": "nullable_string",
54
+ "default": null,
55
+ "definition": "Version reported by source; absence remains unknown.",
56
+ "max_length": 2000
57
+ },
58
+ "model_version": {
59
+ "type": "nullable_string",
60
+ "default": null,
61
+ "definition": "AI model version reported by source; absence remains unknown.",
62
+ "max_length": 2000
63
+ }
64
+ },
65
+ "event_types": {
66
+ "help_requested": "Source explicitly records a request; no delivery is inferred.",
67
+ "help_generated": "Source explicitly records creation of assistance; transmission is not inferred.",
68
+ "help_transmitted": "Source explicitly records transmission; display is not inferred.",
69
+ "help_displayed": "Source explicitly records display; reading, understanding and learning are not inferred.",
70
+ "learner_response_observed": "Source explicitly records a subsequent learner action; causal benefit and understanding are not inferred."
71
+ },
72
+ "semantics": {
73
+ "tutormoments": {
74
+ "contract_version": "0.1.0",
75
+ "review": {
76
+ "status": "implementation_checked_not_independently_reviewed",
77
+ "scope": "Executable mapping checks do not establish semantic validity or educational effectiveness."
78
+ },
79
+ "original_event_time": "Preserve source timestamp or relative offsets separately. Import entry time is the importer observation time. Missing absolute source timestamps remain null.",
80
+ "source_unit": "transcript_session",
81
+ "target_unit": "dialogue_turn",
82
+ "measurement_profile": null,
83
+ "denominator": null,
84
+ "coverage": {
85
+ "status": "partial",
86
+ "boundary": "Selected released transcript text and offsets; no original recording or outside-help capture."
87
+ },
88
+ "coding_method": "source_speaker_and_text_only_no_help_classification",
89
+ "definition": "One released transcript turn is imported as one observed dialogue turn. Source Tutor role is not a help-delivery label.",
90
+ "transformations": [
91
+ "HMAC-scope known learner identifier",
92
+ "Preserve source turn order, speaker, text and relative offsets",
93
+ "Record importer actor and import time separately from historical speaker and unknown source clock"
94
+ ],
95
+ "information_loss": [
96
+ "Known original identity columns are excluded from event data",
97
+ "Model-generated enrichments are not imported as observations",
98
+ "Audio fidelity and external assistance cannot be checked from released text"
99
+ ],
100
+ "pooling_limit": "Dialogue-turn counts cannot be combined with participant summaries or assistance-event counts. Published companion interval coding is a separate measurement profile."
101
+ },
102
+ "oatutor": {
103
+ "contract_version": "0.1.0",
104
+ "review": {
105
+ "status": "implementation_checked_not_independently_reviewed",
106
+ "scope": "Executable mapping checks do not establish semantic validity or educational effectiveness."
107
+ },
108
+ "original_event_time": "Preserve source timestamp or relative offsets separately. Import entry time is the importer observation time. Missing absolute source timestamps remain null.",
109
+ "source_unit": "participant_summary",
110
+ "target_unit": "participant_summary",
111
+ "measurement_profile": null,
112
+ "denominator": null,
113
+ "coverage": {
114
+ "status": "unknown",
115
+ "boundary": "Participant summary table; no individual assistance timeline."
116
+ },
117
+ "coding_method": "source_assigned_condition_and_reported_metric_strings",
118
+ "definition": "One participant table row is imported as one observed summary. Assigned hint condition is not observed use.",
119
+ "transformations": [
120
+ "HMAC-scope known learner identifier",
121
+ "Retain original reported metric strings and missing values",
122
+ "Record importer observation time; original event clock is unknown"
123
+ ],
124
+ "information_loss": [
125
+ "Known participant identifier is excluded from event data",
126
+ "No help requests, generated hints, transmissions or displays are recoverable from summary rows"
127
+ ],
128
+ "pooling_limit": "Assigned conditions and reported outcomes do not enter assistance-event or coded-interval denominators."
129
+ },
130
+ "explicit-assistance-events": {
131
+ "contract_version": "0.1.0",
132
+ "review": {
133
+ "status": "implementation_checked_not_independently_reviewed",
134
+ "scope": "Executable mapping checks do not establish semantic validity or educational effectiveness."
135
+ },
136
+ "original_event_time": "Preserve source timestamp or relative offsets separately. Import entry time is the importer observation time. Missing absolute source timestamps remain null.",
137
+ "source_unit": "explicit_source_event",
138
+ "target_unit": "assistance_lifecycle_event",
139
+ "measurement_profile": "qlm:explicit-assistance-lifecycle-counts:0.1.0",
140
+ "denominator": "Distinct native event identities within one declared capture boundary and active source revision; counts stratified by event_type.",
141
+ "coverage": {
142
+ "status": "source_declared",
143
+ "boundary": null
144
+ },
145
+ "coding_method": "explicit_source_event_type_no_text_classifier",
146
+ "definition": "One explicit source lifecycle assertion is imported as one observation. Lifecycle states remain separate; no state implies a later one.",
147
+ "transformations": [
148
+ "Scope learner pseudonym to adapter, source_id, tenant and key",
149
+ "Preserve source event_id, revision, lifecycle type, assistance/request links and optional timestamp",
150
+ "Normalize declared context with unknown defaults; no text-based inference"
151
+ ],
152
+ "information_loss": [
153
+ "Known learner source identifier is not copied into event data",
154
+ "Unknown source fields are excluded",
155
+ "A matching input shape establishes neither source authenticity nor measurement validity"
156
+ ],
157
+ "pooling_limit": "Requires independently reviewed compatible source mappings, lifecycle definitions, units, denominators, coding and capture coverage. Synthetic fixtures cannot support empirical product claims."
158
+ }
159
+ },
160
+ "pooling": "Compatibility is a conservative descriptive screen, not a transportability proof, independence assumption or causal inference license."
161
+ };
162
+ export const IMPORT_EVENT_SCHEMA = {
163
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
164
+ "$id": "https://transformer.quantumlearningmachines.com/schemas/research-import-v2.json",
165
+ "title": "QLM research-import event extension, version 2",
166
+ "description": "Importer observations of historical source records. Actor is the importer; historical speaker stays in dialogue. Source absolute time is null when unreported. Integrity bindings do not establish semantic validity.",
167
+ "type": "object",
168
+ "required": [
169
+ "name",
170
+ "salt",
171
+ "data",
172
+ "activity",
173
+ "actor"
174
+ ],
175
+ "properties": {
176
+ "name": {
177
+ "enum": [
178
+ "qlm_import:dialogue_turn_observed",
179
+ "qlm_import:participant_summary_observed"
180
+ ]
181
+ },
182
+ "salt": {
183
+ "type": "string",
184
+ "minLength": 1
185
+ },
186
+ "actor": {
187
+ "type": "object",
188
+ "required": [
189
+ "role",
190
+ "kind",
191
+ "id"
192
+ ],
193
+ "properties": {
194
+ "role": {
195
+ "const": "system"
196
+ },
197
+ "kind": {
198
+ "const": "software"
199
+ },
200
+ "id": {
201
+ "const": "QLM-transformer"
202
+ }
203
+ },
204
+ "additionalProperties": false
205
+ },
206
+ "activity": {
207
+ "type": "object",
208
+ "required": [
209
+ "id"
210
+ ],
211
+ "properties": {
212
+ "id": {
213
+ "type": "string",
214
+ "minLength": 1
215
+ }
216
+ }
217
+ },
218
+ "data": {
219
+ "type": "object",
220
+ "required": [
221
+ "provenance",
222
+ "time_basis",
223
+ "raw_source_fields",
224
+ "reported_metrics",
225
+ "missing_metrics",
226
+ "assistance",
227
+ "original_source_timestamp",
228
+ "assistance_context"
229
+ ],
230
+ "properties": {
231
+ "provenance": {
232
+ "type": "object",
233
+ "required": [
234
+ "platform",
235
+ "source_sha256",
236
+ "source_row",
237
+ "imported_at",
238
+ "mapping_sha256"
239
+ ],
240
+ "properties": {
241
+ "platform": {
242
+ "enum": [
243
+ "tutormoments",
244
+ "oatutor"
245
+ ]
246
+ },
247
+ "source_sha256": {
248
+ "$ref": "#/$defs/hash"
249
+ },
250
+ "mapping_sha256": {
251
+ "$ref": "#/$defs/hash"
252
+ },
253
+ "source_record_sha256": {
254
+ "$ref": "#/$defs/hash"
255
+ },
256
+ "source_file_sha256": {
257
+ "$ref": "#/$defs/hash"
258
+ },
259
+ "source_row": {
260
+ "type": "integer",
261
+ "minimum": 1
262
+ },
263
+ "source_file_row": {
264
+ "type": "integer",
265
+ "minimum": 1
266
+ },
267
+ "imported_at": {
268
+ "type": "string",
269
+ "format": "date-time"
270
+ },
271
+ "source_field_path": {
272
+ "type": "string"
273
+ },
274
+ "source_kind": {
275
+ "enum": [
276
+ "verified_public_sample",
277
+ "unverified_user_input"
278
+ ]
279
+ }
280
+ }
281
+ },
282
+ "time_basis": {
283
+ "const": "import_time"
284
+ },
285
+ "source_time_basis": {
286
+ "const": "relative_offsets_seconds"
287
+ },
288
+ "raw_source_fields": {
289
+ "type": "object"
290
+ },
291
+ "reported_metrics": {
292
+ "type": "object"
293
+ },
294
+ "missing_metrics": {
295
+ "type": "array",
296
+ "items": {
297
+ "type": "string"
298
+ },
299
+ "uniqueItems": true
300
+ },
301
+ "assistance": {
302
+ "enum": [
303
+ "tutor_utterance_recorded",
304
+ "student_utterance_recorded",
305
+ "assigned_condition_only"
306
+ ]
307
+ },
308
+ "dialogue": {
309
+ "type": "object",
310
+ "required": [
311
+ "role",
312
+ "text",
313
+ "turn_number",
314
+ "start_seconds",
315
+ "end_seconds"
316
+ ],
317
+ "additionalProperties": false,
318
+ "properties": {
319
+ "role": {
320
+ "enum": [
321
+ "Student",
322
+ "Tutor"
323
+ ]
324
+ },
325
+ "text": {
326
+ "type": "string"
327
+ },
328
+ "turn_number": {
329
+ "type": "integer",
330
+ "minimum": 1
331
+ },
332
+ "start_seconds": {
333
+ "type": "number",
334
+ "minimum": 0
335
+ },
336
+ "end_seconds": {
337
+ "type": "number",
338
+ "minimum": 0
339
+ }
340
+ }
341
+ },
342
+ "help_evidence": {
343
+ "type": "object",
344
+ "required": [
345
+ "condition",
346
+ "actual_help_use"
347
+ ],
348
+ "properties": {
349
+ "condition": {
350
+ "enum": [
351
+ "ChatGPT",
352
+ "Human tutor",
353
+ "No-hint control"
354
+ ]
355
+ },
356
+ "actual_help_use": {
357
+ "enum": [
358
+ "not_reported_in_participant_table",
359
+ "not_established_by_this_adapter"
360
+ ]
361
+ }
362
+ }
363
+ },
364
+ "original_source_timestamp": {
365
+ "type": "null"
366
+ },
367
+ "assistance_context": {
368
+ "type": "object",
369
+ "required": [
370
+ "phase",
371
+ "permission",
372
+ "observed_use",
373
+ "capture",
374
+ "capture_boundary",
375
+ "product_version",
376
+ "model_version"
377
+ ],
378
+ "additionalProperties": false,
379
+ "properties": {
380
+ "phase": {
381
+ "enum": [
382
+ "practice",
383
+ "assessment",
384
+ "other",
385
+ "unknown"
386
+ ]
387
+ },
388
+ "permission": {
389
+ "enum": [
390
+ "permitted",
391
+ "prohibited",
392
+ "restricted",
393
+ "unknown"
394
+ ]
395
+ },
396
+ "observed_use": {
397
+ "enum": [
398
+ "recorded",
399
+ "not_recorded",
400
+ "unknown"
401
+ ]
402
+ },
403
+ "capture": {
404
+ "enum": [
405
+ "complete_within_boundary",
406
+ "partial",
407
+ "unknown"
408
+ ]
409
+ },
410
+ "capture_boundary": {
411
+ "type": [
412
+ "string",
413
+ "null"
414
+ ],
415
+ "minLength": 1,
416
+ "maxLength": 2000
417
+ },
418
+ "product_version": {
419
+ "type": [
420
+ "string",
421
+ "null"
422
+ ],
423
+ "minLength": 1,
424
+ "maxLength": 2000
425
+ },
426
+ "model_version": {
427
+ "type": [
428
+ "string",
429
+ "null"
430
+ ],
431
+ "minLength": 1,
432
+ "maxLength": 2000
433
+ }
434
+ },
435
+ "allOf": [
436
+ {
437
+ "if": {
438
+ "properties": {
439
+ "capture": {
440
+ "const": "complete_within_boundary"
441
+ }
442
+ }
443
+ },
444
+ "then": {
445
+ "properties": {
446
+ "capture_boundary": {
447
+ "type": "string",
448
+ "minLength": 1
449
+ }
450
+ }
451
+ }
452
+ }
453
+ ]
454
+ }
455
+ }
456
+ }
457
+ },
458
+ "$defs": {
459
+ "hash": {
460
+ "type": "string",
461
+ "pattern": "^[0-9a-f]{64}$"
462
+ }
463
+ },
464
+ "allOf": [
465
+ {
466
+ "if": {
467
+ "properties": {
468
+ "name": {
469
+ "const": "qlm_import:dialogue_turn_observed"
470
+ }
471
+ }
472
+ },
473
+ "then": {
474
+ "properties": {
475
+ "data": {
476
+ "required": [
477
+ "dialogue",
478
+ "source_time_basis"
479
+ ],
480
+ "properties": {
481
+ "assistance": {
482
+ "enum": [
483
+ "tutor_utterance_recorded",
484
+ "student_utterance_recorded"
485
+ ]
486
+ },
487
+ "reported_metrics": {
488
+ "maxProperties": 0
489
+ },
490
+ "missing_metrics": {
491
+ "maxItems": 0
492
+ }
493
+ }
494
+ }
495
+ }
496
+ }
497
+ },
498
+ {
499
+ "if": {
500
+ "properties": {
501
+ "name": {
502
+ "const": "qlm_import:participant_summary_observed"
503
+ }
504
+ }
505
+ },
506
+ "then": {
507
+ "properties": {
508
+ "data": {
509
+ "required": [
510
+ "help_evidence"
511
+ ],
512
+ "properties": {
513
+ "assistance": {
514
+ "enum": [
515
+ "assigned_condition_only"
516
+ ]
517
+ }
518
+ }
519
+ }
520
+ }
521
+ }
522
+ }
523
+ ]
524
+ };
525
+ export const LIFECYCLE_EVENT_SCHEMA = {
526
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
527
+ "$id": "https://transformer.quantumlearningmachines.com/schemas/assistance-lifecycle-v1.json",
528
+ "title": "Explicit assistance lifecycle import, version 1",
529
+ "description": "Importer observation of an explicit source lifecycle assertion. Display is not reading or learning. Fixtures are not research data.",
530
+ "type": "object",
531
+ "required": [
532
+ "name",
533
+ "salt",
534
+ "actor",
535
+ "activity",
536
+ "data"
537
+ ],
538
+ "properties": {
539
+ "name": {
540
+ "const": "qlm_import:assistance_lifecycle_observed"
541
+ },
542
+ "salt": {
543
+ "type": "string",
544
+ "minLength": 1
545
+ },
546
+ "actor": {
547
+ "type": "object",
548
+ "required": [
549
+ "role",
550
+ "kind",
551
+ "id"
552
+ ],
553
+ "properties": {
554
+ "role": {
555
+ "const": "system"
556
+ },
557
+ "kind": {
558
+ "const": "software"
559
+ },
560
+ "id": {
561
+ "const": "QLM-transformer"
562
+ }
563
+ },
564
+ "additionalProperties": false
565
+ },
566
+ "activity": {
567
+ "type": "object",
568
+ "required": [
569
+ "id"
570
+ ],
571
+ "properties": {
572
+ "id": {
573
+ "type": "string",
574
+ "minLength": 1
575
+ }
576
+ }
577
+ },
578
+ "data": {
579
+ "type": "object",
580
+ "required": [
581
+ "provenance",
582
+ "time_basis",
583
+ "original_source_timestamp",
584
+ "raw_source_fields",
585
+ "reported_metrics",
586
+ "missing_metrics",
587
+ "assistance",
588
+ "lifecycle",
589
+ "assistance_context"
590
+ ],
591
+ "properties": {
592
+ "time_basis": {
593
+ "const": "import_time"
594
+ },
595
+ "original_source_timestamp": {
596
+ "type": [
597
+ "string",
598
+ "null"
599
+ ],
600
+ "format": "date-time"
601
+ },
602
+ "assistance": {
603
+ "enum": [
604
+ "help_requested",
605
+ "help_generated",
606
+ "help_transmitted",
607
+ "help_displayed",
608
+ "learner_response_observed"
609
+ ]
610
+ },
611
+ "lifecycle": {
612
+ "type": "object",
613
+ "required": [
614
+ "source_id",
615
+ "event_id",
616
+ "source_revision",
617
+ "event_type",
618
+ "source_timestamp",
619
+ "assistance_id",
620
+ "request_event_id",
621
+ "request_origin",
622
+ "native_hint_depth",
623
+ "delivery_order"
624
+ ],
625
+ "properties": {
626
+ "source_id": {
627
+ "type": "string",
628
+ "minLength": 1
629
+ },
630
+ "event_id": {
631
+ "type": "string",
632
+ "minLength": 1
633
+ },
634
+ "source_revision": {
635
+ "type": "integer",
636
+ "minimum": 0
637
+ },
638
+ "event_type": {
639
+ "enum": [
640
+ "help_requested",
641
+ "help_generated",
642
+ "help_transmitted",
643
+ "help_displayed",
644
+ "learner_response_observed"
645
+ ]
646
+ },
647
+ "source_timestamp": {
648
+ "type": [
649
+ "string",
650
+ "null"
651
+ ],
652
+ "format": "date-time"
653
+ },
654
+ "assistance_id": {
655
+ "type": [
656
+ "string",
657
+ "null"
658
+ ]
659
+ },
660
+ "request_event_id": {
661
+ "type": [
662
+ "string",
663
+ "null"
664
+ ]
665
+ },
666
+ "content": {
667
+ "type": "string"
668
+ },
669
+ "request_origin": {
670
+ "enum": [
671
+ "requested",
672
+ "unsolicited",
673
+ "unknown"
674
+ ],
675
+ "description": "Explicit declared origin; absent request link does not establish unsolicited help."
676
+ },
677
+ "native_hint_depth": {
678
+ "type": [
679
+ "string",
680
+ "integer",
681
+ "null"
682
+ ],
683
+ "minimum": 0,
684
+ "description": "Source depth or label; never mapped to Learnlog delivery order."
685
+ },
686
+ "delivery_order": {
687
+ "type": [
688
+ "integer",
689
+ "null"
690
+ ],
691
+ "minimum": 1,
692
+ "description": "Source-declared delivery sequence; not inferred from native hint depth."
693
+ }
694
+ },
695
+ "additionalProperties": false
696
+ },
697
+ "assistance_context": {
698
+ "type": "object",
699
+ "required": [
700
+ "phase",
701
+ "permission",
702
+ "observed_use",
703
+ "capture",
704
+ "capture_boundary",
705
+ "product_version",
706
+ "model_version"
707
+ ],
708
+ "additionalProperties": false,
709
+ "properties": {
710
+ "phase": {
711
+ "enum": [
712
+ "practice",
713
+ "assessment",
714
+ "other",
715
+ "unknown"
716
+ ]
717
+ },
718
+ "permission": {
719
+ "enum": [
720
+ "permitted",
721
+ "prohibited",
722
+ "restricted",
723
+ "unknown"
724
+ ]
725
+ },
726
+ "observed_use": {
727
+ "enum": [
728
+ "recorded",
729
+ "not_recorded",
730
+ "unknown"
731
+ ]
732
+ },
733
+ "capture": {
734
+ "enum": [
735
+ "complete_within_boundary",
736
+ "partial",
737
+ "unknown"
738
+ ]
739
+ },
740
+ "capture_boundary": {
741
+ "type": [
742
+ "string",
743
+ "null"
744
+ ],
745
+ "minLength": 1,
746
+ "maxLength": 2000
747
+ },
748
+ "product_version": {
749
+ "type": [
750
+ "string",
751
+ "null"
752
+ ],
753
+ "minLength": 1,
754
+ "maxLength": 2000
755
+ },
756
+ "model_version": {
757
+ "type": [
758
+ "string",
759
+ "null"
760
+ ],
761
+ "minLength": 1,
762
+ "maxLength": 2000
763
+ }
764
+ },
765
+ "allOf": [
766
+ {
767
+ "if": {
768
+ "properties": {
769
+ "capture": {
770
+ "const": "complete_within_boundary"
771
+ }
772
+ }
773
+ },
774
+ "then": {
775
+ "properties": {
776
+ "capture_boundary": {
777
+ "type": "string",
778
+ "minLength": 1
779
+ }
780
+ }
781
+ }
782
+ }
783
+ ]
784
+ },
785
+ "provenance": {
786
+ "type": "object"
787
+ },
788
+ "reported_metrics": {
789
+ "type": "object",
790
+ "maxProperties": 0
791
+ },
792
+ "missing_metrics": {
793
+ "type": "array",
794
+ "maxItems": 0
795
+ },
796
+ "raw_source_fields": {
797
+ "type": "object"
798
+ }
799
+ }
800
+ }
801
+ }
802
+ };
803
+ export const ASSISTANCE_CONTEXT_PROFILE_V0_2 = {
804
+ "$id": "https://transformer.quantumlearningmachines.com/schemas/assistance-context-v0.2.0.json",
805
+ "profile_id": "qlm:assistance-context:0.2.0",
806
+ "version": "0.2.0",
807
+ "status": "proposed_application_profile_not_ietf_standard",
808
+ "context_fields": {
809
+ "phase": {
810
+ "values": [
811
+ "practice",
812
+ "assessment",
813
+ "other",
814
+ "unknown"
815
+ ],
816
+ "default": "unknown",
817
+ "definition": "Declared activity phase; not inferred from task content or scores."
818
+ },
819
+ "permission": {
820
+ "values": [
821
+ "permitted",
822
+ "prohibited",
823
+ "restricted",
824
+ "unknown"
825
+ ],
826
+ "default": "unknown",
827
+ "definition": "Declared assistance policy; permission is not observation of use."
828
+ },
829
+ "observed_use": {
830
+ "values": [
831
+ "recorded",
832
+ "not_recorded",
833
+ "unknown"
834
+ ],
835
+ "default": "unknown",
836
+ "definition": "Source assertion about recording assistance use within its capture boundary; not_recorded is not proof of absence."
837
+ },
838
+ "capture": {
839
+ "values": [
840
+ "complete_within_boundary",
841
+ "partial",
842
+ "unknown"
843
+ ],
844
+ "default": "unknown",
845
+ "definition": "Declared instrument coverage within an explicit boundary; outside help remains unobserved."
846
+ },
847
+ "capture_boundary": {
848
+ "type": "nullable_string",
849
+ "default": null,
850
+ "definition": "Source-declared system, event classes and period within which coverage is asserted.",
851
+ "max_length": 2000
852
+ },
853
+ "product_version": {
854
+ "type": "nullable_string",
855
+ "default": null,
856
+ "definition": "Version reported by source; absence remains unknown.",
857
+ "max_length": 2000
858
+ },
859
+ "model_version": {
860
+ "type": "nullable_string",
861
+ "default": null,
862
+ "definition": "AI model version reported by source; absence remains unknown.",
863
+ "max_length": 2000
864
+ }
865
+ },
866
+ "event_types": {
867
+ "help_requested": "Source explicitly records a request; no delivery is inferred.",
868
+ "help_generated": "Source explicitly records creation of assistance; transmission is not inferred.",
869
+ "help_transmitted": "Source explicitly records transmission; display is not inferred.",
870
+ "help_displayed": "Source explicitly records display; reading, understanding and learning are not inferred.",
871
+ "learner_response_observed": "Source explicitly records a subsequent learner action; causal benefit and understanding are not inferred."
872
+ },
873
+ "semantics": {
874
+ "tutormoments": {
875
+ "contract_version": "0.1.0",
876
+ "review": {
877
+ "status": "implementation_checked_not_independently_reviewed",
878
+ "scope": "Executable mapping checks do not establish semantic validity or educational effectiveness."
879
+ },
880
+ "original_event_time": "Preserve source timestamp or relative offsets separately. Import entry time is the importer observation time. Missing absolute source timestamps remain null.",
881
+ "source_unit": "transcript_session",
882
+ "target_unit": "dialogue_turn",
883
+ "measurement_profile": null,
884
+ "denominator": null,
885
+ "coverage": {
886
+ "status": "partial",
887
+ "boundary": "Selected released transcript text and offsets; no original recording or outside-help capture."
888
+ },
889
+ "coding_method": "source_speaker_and_text_only_no_help_classification",
890
+ "definition": "One released transcript turn is imported as one observed dialogue turn. Source Tutor role is not a help-delivery label.",
891
+ "transformations": [
892
+ "HMAC-scope known learner identifier",
893
+ "Preserve source turn order, speaker, text and relative offsets",
894
+ "Record importer actor and import time separately from historical speaker and unknown source clock"
895
+ ],
896
+ "information_loss": [
897
+ "Known original identity columns are excluded from event data",
898
+ "Model-generated enrichments are not imported as observations",
899
+ "Audio fidelity and external assistance cannot be checked from released text"
900
+ ],
901
+ "pooling_limit": "Dialogue-turn counts cannot be combined with participant summaries or assistance-event counts. Published companion interval coding is a separate measurement profile."
902
+ },
903
+ "oatutor": {
904
+ "contract_version": "0.1.0",
905
+ "review": {
906
+ "status": "implementation_checked_not_independently_reviewed",
907
+ "scope": "Executable mapping checks do not establish semantic validity or educational effectiveness."
908
+ },
909
+ "original_event_time": "Preserve source timestamp or relative offsets separately. Import entry time is the importer observation time. Missing absolute source timestamps remain null.",
910
+ "source_unit": "participant_summary",
911
+ "target_unit": "participant_summary",
912
+ "measurement_profile": null,
913
+ "denominator": null,
914
+ "coverage": {
915
+ "status": "unknown",
916
+ "boundary": "Participant summary table; no individual assistance timeline."
917
+ },
918
+ "coding_method": "source_assigned_condition_and_reported_metric_strings",
919
+ "definition": "One participant table row is imported as one observed summary. Assigned hint condition is not observed use.",
920
+ "transformations": [
921
+ "HMAC-scope known learner identifier",
922
+ "Retain original reported metric strings and missing values",
923
+ "Record importer observation time; original event clock is unknown"
924
+ ],
925
+ "information_loss": [
926
+ "Known participant identifier is excluded from event data",
927
+ "No help requests, generated hints, transmissions or displays are recoverable from summary rows"
928
+ ],
929
+ "pooling_limit": "Assigned conditions and reported outcomes do not enter assistance-event or coded-interval denominators."
930
+ },
931
+ "explicit-assistance-events": {
932
+ "contract_version": "0.1.0",
933
+ "review": {
934
+ "status": "implementation_checked_not_independently_reviewed",
935
+ "scope": "Executable mapping checks do not establish semantic validity or educational effectiveness."
936
+ },
937
+ "original_event_time": "Preserve source timestamp or relative offsets separately. Import entry time is the importer observation time. Missing absolute source timestamps remain null.",
938
+ "source_unit": "explicit_source_event",
939
+ "target_unit": "assistance_lifecycle_event",
940
+ "measurement_profile": "qlm:explicit-assistance-lifecycle-counts:0.1.0",
941
+ "denominator": "Distinct native event identities within one declared capture boundary and active source revision; counts stratified by event_type.",
942
+ "coverage": {
943
+ "status": "source_declared",
944
+ "boundary": null
945
+ },
946
+ "coding_method": "explicit_source_event_type_no_text_classifier",
947
+ "definition": "One explicit source lifecycle assertion is imported as one observation. Lifecycle states remain separate; no state implies a later one.",
948
+ "transformations": [
949
+ "Scope learner pseudonym to adapter, source_id, tenant and key",
950
+ "Preserve source event_id, revision, lifecycle type, assistance/request links and optional timestamp",
951
+ "Normalize declared context with unknown defaults; no text-based inference"
952
+ ],
953
+ "information_loss": [
954
+ "Known learner source identifier is not copied into event data",
955
+ "Unknown source fields are excluded",
956
+ "A matching input shape establishes neither source authenticity nor measurement validity"
957
+ ],
958
+ "pooling_limit": "Requires independently reviewed compatible source mappings, lifecycle definitions, units, denominators, coding and capture coverage. Synthetic fixtures cannot support empirical product claims."
959
+ },
960
+ "sandpiper": {
961
+ "contract_version": "0.1.0",
962
+ "review": {
963
+ "status": "implementation_checked_not_independently_reviewed",
964
+ "scope": "Executable mapping checks against a SYNTHETIC fixture built from Sandpiper's documentation examples. No real Sandpiper export or National Tutoring Observatory data was examined. Checks do not establish semantic validity, annotation accuracy or educational effectiveness."
965
+ },
966
+ "original_event_time": "Keep Sandpiper start_time and end_time as the source's unparsed strings, or null when the cell is empty. Import entry time is the importer observation time. No absolute source timestamp is derived.",
967
+ "source_unit": "sandpiper_session",
968
+ "target_unit": "dialogue_turn",
969
+ "measurement_profile": null,
970
+ "denominator": null,
971
+ "coverage": {
972
+ "status": "partial",
973
+ "boundary": "Utterance rows and utterance-level annotation columns of one Sandpiper PER_UTTERANCE utterances CSV. No recording, no session-level annotations, no sessions or meta CSV, no prompt or model details, and no capture of help inside or outside the session."
974
+ },
975
+ "coding_method": "source_speaker_and_text_with_annotation_codes_kept_as_inferred_assertions_no_help_classification",
976
+ "definition": "One Sandpiper utterance row is imported as one observed dialogue turn, grouped into one record per session_id and ordered by the numeric value of sequence_id. Annotation codes on the row are kept as inferred assertions by their named annotator. No code sets a help stage, and all six assistance stages are not recorded.",
977
+ "assistance_stages": {
978
+ "requested": "not_recorded",
979
+ "offered": "not_recorded",
980
+ "generated": "not_recorded",
981
+ "transmitted": "not_recorded",
982
+ "displayed": "not_recorded",
983
+ "accepted": "not_recorded"
984
+ },
985
+ "transformations": [
986
+ "HMAC-scope session_id as the record subject; Sandpiper exports carry no learner identifier, so a subject is a session pseudonym, not a person",
987
+ "Group rows by session_id and order entries by the numeric value of sequence_id; sequence_id must be a positive integer string, unique within a session",
988
+ "Keep _id, session_id, sequence_id, role and content as source text; keep start_time and end_time as unparsed strings, with an empty cell kept as null",
989
+ "Read each annotator[<name>][<index>]<field> column into an inferred assertion with its annotator name, index, field values and identifiedBy when a column carries it; blank cells are not codes",
990
+ "Record importer actor and import time separately from the historical speaker and the unparsed source times"
991
+ ],
992
+ "information_loss": [
993
+ "Blank annotation cells are not imported as codes; the full source row stays in raw_source_fields",
994
+ "Session-level annotations, the sessions and meta CSVs, prompts and model details are not imported",
995
+ "Whether an annotator was a model or a person is not established unless an identifiedBy column says so; the annotator name is kept as given",
996
+ "Audio fidelity, transcript accuracy and help outside the transcript cannot be checked from exported text"
997
+ ],
998
+ "pooling_limit": "Utterance counts and annotation-code counts cannot be combined with participant summaries, assistance-event counts or other sources' turns. Inferred codes are a separate measurement that needs its own reviewed codebook and agreement evidence."
999
+ }
1000
+ },
1001
+ "pooling": "Compatibility is a conservative descriptive screen, not a transportability proof, independence assumption or causal inference license."
1002
+ };
1003
+ export const SANDPIPER_EVENT_SCHEMA = {
1004
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1005
+ "$id": "https://transformer.quantumlearningmachines.com/schemas/sandpiper-utterance-v1.json",
1006
+ "title": "QLM Sandpiper utterance import event, version 1",
1007
+ "description": "An importer's observation of one utterance row from a Sandpiper (National Tutoring Observatory) PER_UTTERANCE utterances CSV. The utterance is an observed dialogue turn. Annotation codes are inferred assertions by a named annotator, never observations of help, and every assistance stage is not_recorded. Actor is the importer; the historical speaker stays in utterance.role. Source times stay unparsed strings. Integrity bindings do not establish semantic validity.",
1008
+ "type": "object",
1009
+ "required": [
1010
+ "name",
1011
+ "salt",
1012
+ "data",
1013
+ "activity",
1014
+ "actor"
1015
+ ],
1016
+ "additionalProperties": false,
1017
+ "properties": {
1018
+ "name": {
1019
+ "const": "qlm_import:transcript_utterance_observed"
1020
+ },
1021
+ "salt": {
1022
+ "type": "string",
1023
+ "minLength": 1
1024
+ },
1025
+ "actor": {
1026
+ "type": "object",
1027
+ "required": [
1028
+ "role",
1029
+ "kind",
1030
+ "id"
1031
+ ],
1032
+ "properties": {
1033
+ "role": {
1034
+ "const": "system"
1035
+ },
1036
+ "kind": {
1037
+ "const": "software"
1038
+ },
1039
+ "id": {
1040
+ "const": "QLM-transformer"
1041
+ }
1042
+ },
1043
+ "additionalProperties": false
1044
+ },
1045
+ "activity": {
1046
+ "type": "object",
1047
+ "required": [
1048
+ "id"
1049
+ ],
1050
+ "additionalProperties": false,
1051
+ "properties": {
1052
+ "id": {
1053
+ "type": "string",
1054
+ "minLength": 1
1055
+ }
1056
+ }
1057
+ },
1058
+ "data": {
1059
+ "type": "object",
1060
+ "required": [
1061
+ "provenance",
1062
+ "time_basis",
1063
+ "source_time_basis",
1064
+ "original_source_timestamp",
1065
+ "utterance",
1066
+ "inferred_annotations",
1067
+ "assistance",
1068
+ "assistance_stages",
1069
+ "raw_source_fields",
1070
+ "reported_metrics",
1071
+ "missing_metrics",
1072
+ "assistance_context"
1073
+ ],
1074
+ "additionalProperties": false,
1075
+ "properties": {
1076
+ "provenance": {
1077
+ "type": "object",
1078
+ "required": [
1079
+ "platform",
1080
+ "source_sha256",
1081
+ "source_row",
1082
+ "source_record_sha256",
1083
+ "imported_at",
1084
+ "mapping_sha256",
1085
+ "source_kind"
1086
+ ],
1087
+ "additionalProperties": false,
1088
+ "properties": {
1089
+ "platform": {
1090
+ "const": "sandpiper"
1091
+ },
1092
+ "source_sha256": {
1093
+ "$ref": "#/$defs/hash"
1094
+ },
1095
+ "source_record_sha256": {
1096
+ "$ref": "#/$defs/hash"
1097
+ },
1098
+ "mapping_sha256": {
1099
+ "$ref": "#/$defs/hash"
1100
+ },
1101
+ "source_row": {
1102
+ "type": "integer",
1103
+ "minimum": 1
1104
+ },
1105
+ "imported_at": {
1106
+ "type": "string",
1107
+ "format": "date-time"
1108
+ },
1109
+ "source_kind": {
1110
+ "enum": [
1111
+ "synthetic_fixture",
1112
+ "unverified_user_input"
1113
+ ]
1114
+ }
1115
+ }
1116
+ },
1117
+ "time_basis": {
1118
+ "const": "import_time"
1119
+ },
1120
+ "source_time_basis": {
1121
+ "const": "source_strings_unparsed"
1122
+ },
1123
+ "original_source_timestamp": {
1124
+ "type": "null"
1125
+ },
1126
+ "utterance": {
1127
+ "type": "object",
1128
+ "required": [
1129
+ "_id",
1130
+ "session_id",
1131
+ "sequence_id",
1132
+ "role",
1133
+ "content",
1134
+ "start_time",
1135
+ "end_time"
1136
+ ],
1137
+ "additionalProperties": false,
1138
+ "properties": {
1139
+ "_id": {
1140
+ "type": "string"
1141
+ },
1142
+ "session_id": {
1143
+ "type": "string",
1144
+ "minLength": 1
1145
+ },
1146
+ "sequence_id": {
1147
+ "type": "string",
1148
+ "pattern": "^[1-9][0-9]*$"
1149
+ },
1150
+ "role": {
1151
+ "type": "string",
1152
+ "minLength": 1
1153
+ },
1154
+ "content": {
1155
+ "type": "string"
1156
+ },
1157
+ "start_time": {
1158
+ "type": [
1159
+ "string",
1160
+ "null"
1161
+ ],
1162
+ "minLength": 1
1163
+ },
1164
+ "end_time": {
1165
+ "type": [
1166
+ "string",
1167
+ "null"
1168
+ ],
1169
+ "minLength": 1
1170
+ }
1171
+ }
1172
+ },
1173
+ "inferred_annotations": {
1174
+ "type": "array",
1175
+ "items": {
1176
+ "$ref": "#/$defs/inferred_annotation"
1177
+ }
1178
+ },
1179
+ "assistance": {
1180
+ "const": "utterance_recorded_no_help_stage"
1181
+ },
1182
+ "assistance_stages": {
1183
+ "type": "object",
1184
+ "required": [
1185
+ "requested",
1186
+ "offered",
1187
+ "generated",
1188
+ "transmitted",
1189
+ "displayed",
1190
+ "accepted"
1191
+ ],
1192
+ "additionalProperties": false,
1193
+ "properties": {
1194
+ "requested": {
1195
+ "const": "not_recorded"
1196
+ },
1197
+ "offered": {
1198
+ "const": "not_recorded"
1199
+ },
1200
+ "generated": {
1201
+ "const": "not_recorded"
1202
+ },
1203
+ "transmitted": {
1204
+ "const": "not_recorded"
1205
+ },
1206
+ "displayed": {
1207
+ "const": "not_recorded"
1208
+ },
1209
+ "accepted": {
1210
+ "const": "not_recorded"
1211
+ }
1212
+ }
1213
+ },
1214
+ "raw_source_fields": {
1215
+ "type": "object",
1216
+ "additionalProperties": {
1217
+ "type": "string"
1218
+ }
1219
+ },
1220
+ "reported_metrics": {
1221
+ "type": "object",
1222
+ "maxProperties": 0
1223
+ },
1224
+ "missing_metrics": {
1225
+ "type": "array",
1226
+ "maxItems": 0
1227
+ },
1228
+ "assistance_context": {
1229
+ "type": "object",
1230
+ "required": [
1231
+ "phase",
1232
+ "permission",
1233
+ "observed_use",
1234
+ "capture",
1235
+ "capture_boundary",
1236
+ "product_version",
1237
+ "model_version"
1238
+ ],
1239
+ "additionalProperties": false,
1240
+ "properties": {
1241
+ "phase": {
1242
+ "enum": [
1243
+ "practice",
1244
+ "assessment",
1245
+ "other",
1246
+ "unknown"
1247
+ ]
1248
+ },
1249
+ "permission": {
1250
+ "enum": [
1251
+ "permitted",
1252
+ "prohibited",
1253
+ "restricted",
1254
+ "unknown"
1255
+ ]
1256
+ },
1257
+ "observed_use": {
1258
+ "enum": [
1259
+ "recorded",
1260
+ "not_recorded",
1261
+ "unknown"
1262
+ ]
1263
+ },
1264
+ "capture": {
1265
+ "enum": [
1266
+ "complete_within_boundary",
1267
+ "partial",
1268
+ "unknown"
1269
+ ]
1270
+ },
1271
+ "capture_boundary": {
1272
+ "type": [
1273
+ "string",
1274
+ "null"
1275
+ ],
1276
+ "minLength": 1,
1277
+ "maxLength": 2000
1278
+ },
1279
+ "product_version": {
1280
+ "type": [
1281
+ "string",
1282
+ "null"
1283
+ ],
1284
+ "minLength": 1,
1285
+ "maxLength": 2000
1286
+ },
1287
+ "model_version": {
1288
+ "type": [
1289
+ "string",
1290
+ "null"
1291
+ ],
1292
+ "minLength": 1,
1293
+ "maxLength": 2000
1294
+ }
1295
+ },
1296
+ "allOf": [
1297
+ {
1298
+ "if": {
1299
+ "properties": {
1300
+ "capture": {
1301
+ "const": "complete_within_boundary"
1302
+ }
1303
+ }
1304
+ },
1305
+ "then": {
1306
+ "properties": {
1307
+ "capture_boundary": {
1308
+ "type": "string",
1309
+ "minLength": 1
1310
+ }
1311
+ }
1312
+ }
1313
+ }
1314
+ ]
1315
+ }
1316
+ }
1317
+ }
1318
+ },
1319
+ "$defs": {
1320
+ "hash": {
1321
+ "type": "string",
1322
+ "pattern": "^[0-9a-f]{64}$"
1323
+ },
1324
+ "inferred_annotation": {
1325
+ "type": "object",
1326
+ "required": [
1327
+ "basis",
1328
+ "annotator",
1329
+ "index",
1330
+ "fields"
1331
+ ],
1332
+ "additionalProperties": false,
1333
+ "description": "A code an annotator (a model or a person; not established unless identifiedBy says so) assigned to the utterance. Derived, not observed; it sets no help stage.",
1334
+ "properties": {
1335
+ "basis": {
1336
+ "const": "inferred"
1337
+ },
1338
+ "annotator": {
1339
+ "type": "string",
1340
+ "minLength": 1
1341
+ },
1342
+ "index": {
1343
+ "type": "integer",
1344
+ "minimum": 0,
1345
+ "maximum": 9007199254740991
1346
+ },
1347
+ "fields": {
1348
+ "type": "object",
1349
+ "propertyNames": {
1350
+ "minLength": 1,
1351
+ "not": {
1352
+ "const": "identifiedBy"
1353
+ }
1354
+ },
1355
+ "additionalProperties": {
1356
+ "type": "string",
1357
+ "minLength": 1
1358
+ }
1359
+ },
1360
+ "identifiedBy": {
1361
+ "type": "string",
1362
+ "minLength": 1
1363
+ }
1364
+ }
1365
+ }
1366
+ }
1367
+ };
1368
+
1369
+
1370
+ function deepFreeze(value){if(value&&typeof value==='object'){for(const child of Object.values(value))deepFreeze(child);Object.freeze(value);}return value;}
1371
+ deepFreeze(ASSISTANCE_CONTEXT_PROFILE);deepFreeze(IMPORT_EVENT_SCHEMA);deepFreeze(LIFECYCLE_EVENT_SCHEMA);deepFreeze(ASSISTANCE_CONTEXT_PROFILE_V0_2);deepFreeze(SANDPIPER_EVENT_SCHEMA);
1372
+ const object=value=>value!==null&&typeof value==='object'&&!Array.isArray(value);
1373
+ const copy=value=>JSON.parse(JSON.stringify(value));
1374
+ export const SEMANTIC_PROFILE_ID=ASSISTANCE_CONTEXT_PROFILE.profile_id;
1375
+ export const SEMANTIC_PROFILE_VERSION=ASSISTANCE_CONTEXT_PROFILE.version;
1376
+ export const ASSISTANCE_EVENT_TYPES=Object.keys(ASSISTANCE_CONTEXT_PROFILE.event_types);
1377
+ /** Normalize declared context only. No inference from assignment, wording or scores. */
1378
+ export function normalizeContext(input={}) {
1379
+ if(!object(input))throw new Error('Assistance context must be an object.');
1380
+ const output={};
1381
+ for(const key of Object.keys(input))if(!Object.hasOwn(ASSISTANCE_CONTEXT_PROFILE.context_fields,key))throw new Error('Unsupported assistance context field: '+key+'.');
1382
+ for(const [key,definition]of Object.entries(ASSISTANCE_CONTEXT_PROFILE.context_fields)){
1383
+ const value=Object.hasOwn(input,key)?input[key]:definition.default;
1384
+ if(definition.values){if(!definition.values.includes(value))throw new Error('Unsupported assistance context '+key+'.');}
1385
+ else if(value!==null&&(typeof value!=='string'||!value.trim()||Array.from(value).length>2000))throw new Error('Assistance context '+key+' must be nonempty text or null.');
1386
+ output[key]=value;
1387
+ }
1388
+ if(output.capture==='complete_within_boundary'&&!output.capture_boundary)throw new Error('Complete capture requires an explicit capture_boundary.');
1389
+ return output;
1390
+ }
1391
+ // Profile versions in order. A later version only adds platform contracts and never changes an
1392
+ // earlier one. Each platform's documents bind the version that introduced its contract, so
1393
+ // documents written before a platform was added keep their exact binding and still verify.
1394
+ export const SEMANTIC_PROFILES=Object.freeze([ASSISTANCE_CONTEXT_PROFILE,ASSISTANCE_CONTEXT_PROFILE_V0_2]);
1395
+ /** The profile version that introduced this adapter's mapping contract. */
1396
+ export function semanticProfileFor(profileId) {
1397
+ const profile=SEMANTIC_PROFILES.find(item=>typeof profileId==='string'&&Object.hasOwn(item.semantics,profileId));
1398
+ if(!profile)throw new Error('No versioned semantic mapping for this adapter.');
1399
+ return profile;
1400
+ }
1401
+ export function getSemanticContract(profileId) {
1402
+ return copy(semanticProfileFor(profileId).semantics[profileId]);
1403
+ }
1404
+ function poolingInputs(items){
1405
+ return items.map((item,index)=>{
1406
+ if(!object(item))return {source_id:null,invalid_descriptor:true};
1407
+ if(item.profile){
1408
+ const contexts=(Array.isArray(item.records)?item.records:[]).flatMap(record=>(Array.isArray(record.entries)?record.entries:[]).map(entry=>entry.event?.data?.assistance_context));
1409
+ const first=contexts[0]||normalizeContext();
1410
+ const consistent=contexts.every(context=>JSON.stringify(context)===JSON.stringify(first));
1411
+ return {source_id:item.sourceNamespace||item.profile.id,source_kind:item.sourceKind,mapping:item.semanticContract||(SEMANTIC_PROFILES.some(profile=>Object.hasOwn(profile.semantics,item.profile.id))?getSemanticContract(item.profile.id):{}),context:first,context_consistent:consistent};
1412
+ }
1413
+ return {...item,source_id:item.source_id??null};
1414
+ });
1415
+ }
1416
+ /** A conservative, explicit screen. It cannot establish causal comparability. */
1417
+ export function assessPoolingCompatibility(items) {
1418
+ if(!Array.isArray(items))throw new Error('Pooling inputs must be an array.');
1419
+ const sources=poolingInputs(items),reasons=[],strata=[];
1420
+ const fields=['measurement_profile','target_unit','denominator','coding_method','definition'];
1421
+ let reference=null;
1422
+ for(const source of sources){
1423
+ const mapping=object(source.mapping)?source.mapping:{},local=[];
1424
+ if(source.invalid_descriptor||!object(source.mapping))local.push({code:'invalid_descriptor',detail:'A structured mapping descriptor is required.'});
1425
+ if(typeof source.source_id!=='string'||!source.source_id.trim())local.push({code:'source_id_unknown',detail:'A nonempty source stratum identity is required.'});
1426
+ if(source.source_kind==='synthetic_fixture')local.push({code:'synthetic_fixture',detail:'Synthetic contract fixtures cannot enter empirical pooled measurement.'});
1427
+ if(source.context_consistent===false)local.push({code:'context_mixed',detail:'Input contexts vary; stratify before considering pooling.'});
1428
+ if(!object(mapping.review)||mapping.review.status!=='independently_reviewed')local.push({code:'mapping_unreviewed',detail:'Mapping has not been independently reviewed for semantic validity.'});
1429
+ else if(!['reviewer','scope','reference'].every(key=>typeof mapping.review[key]==='string'&&mapping.review[key].trim())||!/^https:\/\//.test(mapping.review.reference))local.push({code:'review_documentation_missing',detail:'An independent review declaration requires reviewer, scope and an HTTPS review-record reference.'});
1430
+ for(const key of ['source_unit'])if(typeof mapping[key]!=='string'||!mapping[key].trim())local.push({code:key+'_unknown',detail:'Mapping '+key+' must be defined explicitly.'});
1431
+ for(const key of ['transformations','information_loss'])if(!Array.isArray(mapping[key])||(key==='transformations'&&!mapping[key].length)||mapping[key].some(value=>typeof value!=='string'||!value.trim()))local.push({code:key+'_unknown',detail:'Mapping '+key+' must be an explicit list of nonempty descriptions.'});
1432
+ for(const field of fields)if(typeof mapping[field]!=='string'||!mapping[field].trim())local.push({code:field+'_unknown',detail:'A shared '+field+' is not established.'});
1433
+ if(!object(mapping.coverage)||mapping.coverage.status!=='complete_within_boundary'||typeof mapping.coverage.boundary!=='string'||!mapping.coverage.boundary.trim())local.push({code:'coverage_not_established',detail:'Comparable capture coverage within an explicit boundary is not established.'});
1434
+ const signature=Object.fromEntries(fields.map(field=>[field,mapping[field]??null]));
1435
+ signature.coverage=object(mapping.coverage)?{status:mapping.coverage.status??null,boundary:mapping.coverage.boundary??null}:null;
1436
+ if(source.context){
1437
+ try{const context=normalizeContext(source.context);
1438
+ signature.context={phase:context.phase,permission:context.permission,observed_use:context.observed_use,capture:context.capture,capture_boundary:context.capture_boundary};
1439
+ if(context.phase==='unknown'||context.permission==='unknown'||context.observed_use==='unknown'||context.capture!=='complete_within_boundary')local.push({code:'context_unknown',detail:'Phase, permitted assistance, observed-use scope and bounded capture must be established or explicitly stratified.'});
1440
+ }catch{local.push({code:'context_invalid',detail:'Declared assistance context is malformed.'});}
1441
+ }else local.push({code:'context_unknown',detail:'No activity and assistance context was supplied.'});
1442
+ if(reference){
1443
+ for(const field of fields)if(signature[field]!==reference[field])local.push({code:field+'_incompatible',detail:field+' differs from the first selected source.'});
1444
+ if(JSON.stringify(signature.coverage)!==JSON.stringify(reference.coverage))local.push({code:'coverage_incompatible',detail:'Capture boundaries or coverage declarations differ.'});
1445
+ if(JSON.stringify(signature.context)!==JSON.stringify(reference.context))local.push({code:'context_incompatible',detail:'Activity or assistance contexts differ; preserve source/context strata.'});
1446
+ }else reference=signature;
1447
+ const stratum={source_id:source.source_id,source_kind:source.source_kind||'unverified_user_input',mapping_review:mapping.review?.status||'unknown',signature,reasons:local};
1448
+ strata.push(stratum);for(const reason of local)reasons.push({source_id:source.source_id,...reason});
1449
+ }
1450
+ if(new Set(sources.map(source=>source.source_id).filter(value=>typeof value==='string'&&value.trim())).size<2)reasons.push({source_id:null,code:'multiple_sources_required',detail:'At least two distinct source strata are required for a pooling decision.'});
1451
+ const eligible=sources.length>=2&&reasons.length===0;
1452
+ return {profile_id:'qlm:pooling-compatibility:0.1.0',eligible,status:eligible?'eligible_for_descriptive_pooling':'blocked',source_strata:strata,reasons,limitations:['This screen checks declared compatibility; it does not validate declarations or mappings.','Eligibility does not establish independent observations, equivalent learning constructs, transportability or causal effects.','Do not add heterogeneous counts or treat missing assistance data as zero.']};
1453
+ }