@happyvertical/smrt-education 0.57.40 → 0.57.42

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 (54) hide show
  1. package/AGENTS.md +12 -0
  2. package/README.md +10 -0
  3. package/agents/enrollment-notes.md +42 -0
  4. package/agents/group-sharing.md +278 -0
  5. package/agents/learner-plan.md +66 -0
  6. package/agents/submission-service.md +16 -1
  7. package/agents/workbook-service.md +39 -3
  8. package/dist/chunks/{workbook-photo-service-DCmqeWLd.js → workbook-photo-service-C6mbUUp6.js} +721 -280
  9. package/dist/chunks/workbook-photo-service-C6mbUUp6.js.map +1 -0
  10. package/dist/enrollment-note-service.d.ts +24 -0
  11. package/dist/enrollment-note-service.d.ts.map +1 -0
  12. package/dist/enrollment-notes.d.ts +22 -0
  13. package/dist/enrollment-notes.d.ts.map +1 -0
  14. package/dist/group-sharing-authorization.d.ts +67 -0
  15. package/dist/group-sharing-authorization.d.ts.map +1 -0
  16. package/dist/group-sharing-comments.d.ts +30 -0
  17. package/dist/group-sharing-comments.d.ts.map +1 -0
  18. package/dist/group-sharing-contracts.d.ts +201 -0
  19. package/dist/group-sharing-contracts.d.ts.map +1 -0
  20. package/dist/group-sharing-hash.d.ts +3 -0
  21. package/dist/group-sharing-hash.d.ts.map +1 -0
  22. package/dist/group-sharing-models.d.ts +106 -0
  23. package/dist/group-sharing-models.d.ts.map +1 -0
  24. package/dist/group-sharing-policy.d.ts +19 -0
  25. package/dist/group-sharing-policy.d.ts.map +1 -0
  26. package/dist/group-sharing-reader.d.ts +45 -0
  27. package/dist/group-sharing-reader.d.ts.map +1 -0
  28. package/dist/group-sharing-service.d.ts +108 -0
  29. package/dist/group-sharing-service.d.ts.map +1 -0
  30. package/dist/group-sharing-workspace.d.ts +10 -0
  31. package/dist/group-sharing-workspace.d.ts.map +1 -0
  32. package/dist/index.d.ts +13 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +2266 -56
  35. package/dist/index.js.map +1 -1
  36. package/dist/ingestion.js +1 -1
  37. package/dist/learner-plan-service.d.ts +46 -0
  38. package/dist/learner-plan-service.d.ts.map +1 -0
  39. package/dist/learner-service.d.ts +1 -1
  40. package/dist/learner-service.d.ts.map +1 -1
  41. package/dist/manifest.json +5610 -1922
  42. package/dist/recipes.d.ts +24 -0
  43. package/dist/recipes.d.ts.map +1 -1
  44. package/dist/smrt-knowledge.json +1455 -48
  45. package/dist/submission-service.d.ts +3 -0
  46. package/dist/submission-service.d.ts.map +1 -1
  47. package/dist/workbook-completion.d.ts +38 -0
  48. package/dist/workbook-completion.d.ts.map +1 -0
  49. package/dist/workbook-contracts.d.ts +33 -0
  50. package/dist/workbook-contracts.d.ts.map +1 -1
  51. package/dist/workbook-service.d.ts +6 -1
  52. package/dist/workbook-service.d.ts.map +1 -1
  53. package/package.json +9 -9
  54. package/dist/chunks/workbook-photo-service-DCmqeWLd.js.map +0 -1
@@ -4,8 +4,8 @@ import { AssetCollection, createAssetRuntime, withPrivateAssetAccess } from "@ha
4
4
  import { TenantCollection } from "@happyvertical/smrt-users";
5
5
  //#region \0rolldown/runtime.js
6
6
  var __create = Object.create;
7
- var __defProp$7 = Object.defineProperty;
8
- var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
7
+ var __defProp$8 = Object.defineProperty;
8
+ var __getOwnPropDesc$8 = Object.getOwnPropertyDescriptor;
9
9
  var __getOwnPropNames = Object.getOwnPropertyNames;
10
10
  var __getProtoOf = Object.getPrototypeOf;
11
11
  var __hasOwnProp = Object.prototype.hasOwnProperty;
@@ -13,34 +13,34 @@ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).expor
13
13
  var __copyProps = (to, from, except, desc) => {
14
14
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
15
15
  key = keys[i];
16
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp$7(to, key, {
16
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp$8(to, key, {
17
17
  get: ((k) => from[k]).bind(null, key),
18
- enumerable: !(desc = __getOwnPropDesc$7(from, key)) || desc.enumerable
18
+ enumerable: !(desc = __getOwnPropDesc$8(from, key)) || desc.enumerable
19
19
  });
20
20
  }
21
21
  return to;
22
22
  };
23
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp$7(target, "default", {
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp$8(target, "default", {
24
24
  value: mod,
25
25
  enumerable: true
26
26
  }) : target, mod));
27
27
  //#endregion
28
28
  //#region src/catalog.ts
29
- var __defProp$6 = Object.defineProperty;
30
- var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
31
- var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, {
29
+ var __defProp$7 = Object.defineProperty;
30
+ var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
31
+ var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, {
32
32
  enumerable: true,
33
33
  configurable: true,
34
34
  writable: true,
35
35
  value
36
36
  }) : obj[key] = value;
37
- var __decorateClass$6 = (decorators, target, key, kind) => {
38
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
37
+ var __decorateClass$7 = (decorators, target, key, kind) => {
38
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
39
39
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
40
- if (kind && result) __defProp$6(target, key, result);
40
+ if (kind && result) __defProp$7(target, key, result);
41
41
  return result;
42
42
  };
43
- var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
43
+ var __publicField$7 = (obj, key, value) => __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
44
44
  var closed$5 = {
45
45
  api: false,
46
46
  mcp: false,
@@ -100,22 +100,22 @@ var Course = class extends SmrtObject {
100
100
  return await super.save(options);
101
101
  }
102
102
  };
103
- __publicField$6(Course, "__smrtPackage__", "@happyvertical/smrt-education");
104
- __decorateClass$6([tenantId({ nullable: true })], Course.prototype, "tenantId", 2);
105
- __decorateClass$6([field({ required: true })], Course.prototype, "title", 2);
106
- Course = __decorateClass$6([TenantScoped({ mode: "optional" }), smrt({
103
+ __publicField$7(Course, "__smrtPackage__", "@happyvertical/smrt-education");
104
+ __decorateClass$7([tenantId({ nullable: true })], Course.prototype, "tenantId", 2);
105
+ __decorateClass$7([field({ required: true })], Course.prototype, "title", 2);
106
+ Course = __decorateClass$7([TenantScoped({ mode: "optional" }), smrt({
107
107
  tableStrategy: "sti",
108
108
  ...closed$5
109
109
  })], Course);
110
110
  var ExternalCourse = class extends Course {};
111
- __publicField$6(ExternalCourse, "__smrtPackage__", "@happyvertical/smrt-education");
112
- ExternalCourse = __decorateClass$6([smrt({
111
+ __publicField$7(ExternalCourse, "__smrtPackage__", "@happyvertical/smrt-education");
112
+ ExternalCourse = __decorateClass$7([smrt({
113
113
  tableName: "courses",
114
114
  ...closed$5
115
115
  })], ExternalCourse);
116
116
  var AuthoredCourse = class extends Course {};
117
- __publicField$6(AuthoredCourse, "__smrtPackage__", "@happyvertical/smrt-education");
118
- AuthoredCourse = __decorateClass$6([smrt({
117
+ __publicField$7(AuthoredCourse, "__smrtPackage__", "@happyvertical/smrt-education");
118
+ AuthoredCourse = __decorateClass$7([smrt({
119
119
  tableName: "courses",
120
120
  ...closed$5
121
121
  })], AuthoredCourse);
@@ -138,11 +138,11 @@ var CourseSection = class extends SmrtObject {
138
138
  return await super.save(options);
139
139
  }
140
140
  };
141
- __publicField$6(CourseSection, "__smrtPackage__", "@happyvertical/smrt-education");
142
- __decorateClass$6([tenantId({ nullable: true })], CourseSection.prototype, "tenantId", 2);
143
- __decorateClass$6([foreignKey("Course", { required: true })], CourseSection.prototype, "courseId", 2);
144
- __decorateClass$6([field({ required: true })], CourseSection.prototype, "title", 2);
145
- CourseSection = __decorateClass$6([TenantScoped({ mode: "optional" }), smrt({ ...closed$5 })], CourseSection);
141
+ __publicField$7(CourseSection, "__smrtPackage__", "@happyvertical/smrt-education");
142
+ __decorateClass$7([tenantId({ nullable: true })], CourseSection.prototype, "tenantId", 2);
143
+ __decorateClass$7([foreignKey("Course", { required: true })], CourseSection.prototype, "courseId", 2);
144
+ __decorateClass$7([field({ required: true })], CourseSection.prototype, "title", 2);
145
+ CourseSection = __decorateClass$7([TenantScoped({ mode: "optional" }), smrt({ ...closed$5 })], CourseSection);
146
146
  var Lesson = class extends SmrtObject {
147
147
  tenantId = null;
148
148
  sectionId = "";
@@ -169,17 +169,17 @@ var Lesson = class extends SmrtObject {
169
169
  return await super.save(options);
170
170
  }
171
171
  };
172
- __publicField$6(Lesson, "__smrtPackage__", "@happyvertical/smrt-education");
173
- __decorateClass$6([tenantId({ nullable: true })], Lesson.prototype, "tenantId", 2);
174
- __decorateClass$6([foreignKey("CourseSection", { required: true })], Lesson.prototype, "sectionId", 2);
175
- __decorateClass$6([field({ required: true })], Lesson.prototype, "title", 2);
176
- Lesson = __decorateClass$6([TenantScoped({ mode: "optional" }), smrt({
172
+ __publicField$7(Lesson, "__smrtPackage__", "@happyvertical/smrt-education");
173
+ __decorateClass$7([tenantId({ nullable: true })], Lesson.prototype, "tenantId", 2);
174
+ __decorateClass$7([foreignKey("CourseSection", { required: true })], Lesson.prototype, "sectionId", 2);
175
+ __decorateClass$7([field({ required: true })], Lesson.prototype, "title", 2);
176
+ Lesson = __decorateClass$7([TenantScoped({ mode: "optional" }), smrt({
177
177
  tableStrategy: "sti",
178
178
  ...closed$5
179
179
  })], Lesson);
180
180
  var Reading = class extends Lesson {};
181
- __publicField$6(Reading, "__smrtPackage__", "@happyvertical/smrt-education");
182
- Reading = __decorateClass$6([smrt({
181
+ __publicField$7(Reading, "__smrtPackage__", "@happyvertical/smrt-education");
182
+ Reading = __decorateClass$7([smrt({
183
183
  tableName: "lessons",
184
184
  ...closed$5
185
185
  })], Reading);
@@ -190,8 +190,8 @@ var Video = class extends Lesson {
190
190
  applyModelOptions(this, options);
191
191
  }
192
192
  };
193
- __publicField$6(Video, "__smrtPackage__", "@happyvertical/smrt-education");
194
- Video = __decorateClass$6([smrt({
193
+ __publicField$7(Video, "__smrtPackage__", "@happyvertical/smrt-education");
194
+ Video = __decorateClass$7([smrt({
195
195
  tableName: "lessons",
196
196
  ...closed$5
197
197
  })], Video);
@@ -205,14 +205,14 @@ var Worksheet = class extends Lesson {
205
205
  applyModelOptions(this, options);
206
206
  }
207
207
  };
208
- __publicField$6(Worksheet, "__smrtPackage__", "@happyvertical/smrt-education");
209
- Worksheet = __decorateClass$6([smrt({
208
+ __publicField$7(Worksheet, "__smrtPackage__", "@happyvertical/smrt-education");
209
+ Worksheet = __decorateClass$7([smrt({
210
210
  tableName: "lessons",
211
211
  ...closed$5
212
212
  })], Worksheet);
213
213
  var Exercise = class extends Lesson {};
214
- __publicField$6(Exercise, "__smrtPackage__", "@happyvertical/smrt-education");
215
- Exercise = __decorateClass$6([smrt({
214
+ __publicField$7(Exercise, "__smrtPackage__", "@happyvertical/smrt-education");
215
+ Exercise = __decorateClass$7([smrt({
216
216
  tableName: "lessons",
217
217
  ...closed$5
218
218
  })], Exercise);
@@ -246,21 +246,21 @@ GlobalInterceptors.register({
246
246
  });
247
247
  //#endregion
248
248
  //#region src/learners.ts
249
- var __defProp$5 = Object.defineProperty;
250
- var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
251
- var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, {
249
+ var __defProp$6 = Object.defineProperty;
250
+ var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
251
+ var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, {
252
252
  enumerable: true,
253
253
  configurable: true,
254
254
  writable: true,
255
255
  value
256
256
  }) : obj[key] = value;
257
- var __decorateClass$5 = (decorators, target, key, kind) => {
258
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
257
+ var __decorateClass$6 = (decorators, target, key, kind) => {
258
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
259
259
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
260
- if (kind && result) __defProp$5(target, key, result);
260
+ if (kind && result) __defProp$6(target, key, result);
261
261
  return result;
262
262
  };
263
- var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
263
+ var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
264
264
  var closed$4 = {
265
265
  api: false,
266
266
  mcp: false,
@@ -326,11 +326,11 @@ var Learner = class extends SmrtObject {
326
326
  return super.save(options);
327
327
  }
328
328
  };
329
- __publicField$5(Learner, "__smrtPackage__", "@happyvertical/smrt-education");
330
- __decorateClass$5([tenantId()], Learner.prototype, "tenantId", 2);
331
- __decorateClass$5([crossPackageRef("@happyvertical/smrt-users:User", { required: true })], Learner.prototype, "userId", 2);
332
- __decorateClass$5([crossPackageRef("@happyvertical/smrt-profiles:Profile", { required: true })], Learner.prototype, "profileId", 2);
333
- Learner = __decorateClass$5([TenantScoped({ mode: "required" }), smrt({ ...closed$4 })], Learner);
329
+ __publicField$6(Learner, "__smrtPackage__", "@happyvertical/smrt-education");
330
+ __decorateClass$6([tenantId()], Learner.prototype, "tenantId", 2);
331
+ __decorateClass$6([crossPackageRef("@happyvertical/smrt-users:User", { required: true })], Learner.prototype, "userId", 2);
332
+ __decorateClass$6([crossPackageRef("@happyvertical/smrt-profiles:Profile", { required: true })], Learner.prototype, "profileId", 2);
333
+ Learner = __decorateClass$6([TenantScoped({ mode: "required" }), smrt({ ...closed$4 })], Learner);
334
334
  var Curriculum = class extends SmrtObject {
335
335
  tenantId = "";
336
336
  learnerId = "";
@@ -357,11 +357,11 @@ var Curriculum = class extends SmrtObject {
357
357
  return super.save(options);
358
358
  }
359
359
  };
360
- __publicField$5(Curriculum, "__smrtPackage__", "@happyvertical/smrt-education");
361
- __decorateClass$5([tenantId()], Curriculum.prototype, "tenantId", 2);
362
- __decorateClass$5([foreignKey("Learner", { required: true })], Curriculum.prototype, "learnerId", 2);
363
- __decorateClass$5([field({ required: true })], Curriculum.prototype, "title", 2);
364
- Curriculum = __decorateClass$5([TenantScoped({ mode: "required" }), smrt({
360
+ __publicField$6(Curriculum, "__smrtPackage__", "@happyvertical/smrt-education");
361
+ __decorateClass$6([tenantId()], Curriculum.prototype, "tenantId", 2);
362
+ __decorateClass$6([foreignKey("Learner", { required: true })], Curriculum.prototype, "learnerId", 2);
363
+ __decorateClass$6([field({ required: true })], Curriculum.prototype, "title", 2);
364
+ Curriculum = __decorateClass$6([TenantScoped({ mode: "required" }), smrt({
365
365
  ...closed$4,
366
366
  tableName: "curricula"
367
367
  })], Curriculum);
@@ -401,12 +401,12 @@ var Enrollment = class extends SmrtObject {
401
401
  return super.save(options);
402
402
  }
403
403
  };
404
- __publicField$5(Enrollment, "__smrtPackage__", "@happyvertical/smrt-education");
405
- __decorateClass$5([tenantId()], Enrollment.prototype, "tenantId", 2);
406
- __decorateClass$5([foreignKey("Curriculum", { required: true })], Enrollment.prototype, "curriculumId", 2);
407
- __decorateClass$5([foreignKey("Learner", { required: true })], Enrollment.prototype, "learnerId", 2);
408
- __decorateClass$5([foreignKey("Course", { required: true })], Enrollment.prototype, "courseId", 2);
409
- Enrollment = __decorateClass$5([TenantScoped({ mode: "required" }), smrt({
404
+ __publicField$6(Enrollment, "__smrtPackage__", "@happyvertical/smrt-education");
405
+ __decorateClass$6([tenantId()], Enrollment.prototype, "tenantId", 2);
406
+ __decorateClass$6([foreignKey("Curriculum", { required: true })], Enrollment.prototype, "curriculumId", 2);
407
+ __decorateClass$6([foreignKey("Learner", { required: true })], Enrollment.prototype, "learnerId", 2);
408
+ __decorateClass$6([foreignKey("Course", { required: true })], Enrollment.prototype, "courseId", 2);
409
+ Enrollment = __decorateClass$6([TenantScoped({ mode: "required" }), smrt({
410
410
  ...closed$4,
411
411
  conflictColumns: [
412
412
  "tenant_id",
@@ -458,16 +458,16 @@ var LessonProgress = class extends SmrtObject {
458
458
  return super.save(options);
459
459
  }
460
460
  };
461
- __publicField$5(LessonProgress, "__smrtPackage__", "@happyvertical/smrt-education");
462
- __decorateClass$5([tenantId()], LessonProgress.prototype, "tenantId", 2);
463
- __decorateClass$5([foreignKey("Enrollment", { required: true })], LessonProgress.prototype, "enrollmentId", 2);
464
- __decorateClass$5([foreignKey("Learner", { required: true })], LessonProgress.prototype, "learnerId", 2);
465
- __decorateClass$5([foreignKey("Lesson", { required: true })], LessonProgress.prototype, "lessonId", 2);
466
- __decorateClass$5([field({
461
+ __publicField$6(LessonProgress, "__smrtPackage__", "@happyvertical/smrt-education");
462
+ __decorateClass$6([tenantId()], LessonProgress.prototype, "tenantId", 2);
463
+ __decorateClass$6([foreignKey("Enrollment", { required: true })], LessonProgress.prototype, "enrollmentId", 2);
464
+ __decorateClass$6([foreignKey("Learner", { required: true })], LessonProgress.prototype, "learnerId", 2);
465
+ __decorateClass$6([foreignKey("Lesson", { required: true })], LessonProgress.prototype, "lessonId", 2);
466
+ __decorateClass$6([field({
467
467
  type: "decimal",
468
468
  nullable: true
469
469
  })], LessonProgress.prototype, "bestScore", 2);
470
- LessonProgress = __decorateClass$5([TenantScoped({ mode: "required" }), smrt({
470
+ LessonProgress = __decorateClass$6([TenantScoped({ mode: "required" }), smrt({
471
471
  ...closed$4,
472
472
  tableName: "lesson_progress",
473
473
  conflictColumns: [
@@ -478,21 +478,21 @@ LessonProgress = __decorateClass$5([TenantScoped({ mode: "required" }), smrt({
478
478
  })], LessonProgress);
479
479
  //#endregion
480
480
  //#region src/lesson-assets.ts
481
- var __defProp$4 = Object.defineProperty;
482
- var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
483
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, {
481
+ var __defProp$5 = Object.defineProperty;
482
+ var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
483
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, {
484
484
  enumerable: true,
485
485
  configurable: true,
486
486
  writable: true,
487
487
  value
488
488
  }) : obj[key] = value;
489
- var __decorateClass$4 = (decorators, target, key, kind) => {
490
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
489
+ var __decorateClass$5 = (decorators, target, key, kind) => {
490
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
491
491
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
492
- if (kind && result) __defProp$4(target, key, result);
492
+ if (kind && result) __defProp$5(target, key, result);
493
493
  return result;
494
494
  };
495
- var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
495
+ var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
496
496
  var LessonAsset = class extends SmrtObject {
497
497
  tenantId = null;
498
498
  lessonId = "";
@@ -526,12 +526,12 @@ var LessonAsset = class extends SmrtObject {
526
526
  return await super.save(options);
527
527
  }
528
528
  };
529
- __publicField$4(LessonAsset, "__smrtPackage__", "@happyvertical/smrt-education");
530
- __decorateClass$4([tenantId({ nullable: true })], LessonAsset.prototype, "tenantId", 2);
531
- __decorateClass$4([foreignKey("Lesson", { required: true })], LessonAsset.prototype, "lessonId", 2);
532
- __decorateClass$4([crossPackageRef("@happyvertical/smrt-assets:Asset", { required: true })], LessonAsset.prototype, "assetId", 2);
533
- __decorateClass$4([field({ required: true })], LessonAsset.prototype, "relationship", 2);
534
- LessonAsset = __decorateClass$4([TenantScoped({ mode: "optional" }), smrt({
529
+ __publicField$5(LessonAsset, "__smrtPackage__", "@happyvertical/smrt-education");
530
+ __decorateClass$5([tenantId({ nullable: true })], LessonAsset.prototype, "tenantId", 2);
531
+ __decorateClass$5([foreignKey("Lesson", { required: true })], LessonAsset.prototype, "lessonId", 2);
532
+ __decorateClass$5([crossPackageRef("@happyvertical/smrt-assets:Asset", { required: true })], LessonAsset.prototype, "assetId", 2);
533
+ __decorateClass$5([field({ required: true })], LessonAsset.prototype, "relationship", 2);
534
+ LessonAsset = __decorateClass$5([TenantScoped({ mode: "optional" }), smrt({
535
535
  tableName: "lesson_assets",
536
536
  conflictColumns: [
537
537
  "tenant_id",
@@ -545,21 +545,21 @@ LessonAsset = __decorateClass$4([TenantScoped({ mode: "optional" }), smrt({
545
545
  })], LessonAsset);
546
546
  //#endregion
547
547
  //#region src/submissions.ts
548
- var __defProp$3 = Object.defineProperty;
549
- var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
550
- var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, {
548
+ var __defProp$4 = Object.defineProperty;
549
+ var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
550
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, {
551
551
  enumerable: true,
552
552
  configurable: true,
553
553
  writable: true,
554
554
  value
555
555
  }) : obj[key] = value;
556
- var __decorateClass$3 = (decorators, target, key, kind) => {
557
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
556
+ var __decorateClass$4 = (decorators, target, key, kind) => {
557
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
558
558
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
559
- if (kind && result) __defProp$3(target, key, result);
559
+ if (kind && result) __defProp$4(target, key, result);
560
560
  return result;
561
561
  };
562
- var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
562
+ var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
563
563
  var closed$3 = {
564
564
  api: false,
565
565
  mcp: false,
@@ -650,18 +650,18 @@ var Submission = class extends SmrtObject {
650
650
  return super.save(options);
651
651
  }
652
652
  };
653
- __publicField$3(Submission, "__smrtPackage__", "@happyvertical/smrt-education");
654
- __decorateClass$3([tenantId()], Submission.prototype, "tenantId", 2);
655
- __decorateClass$3([foreignKey("Learner", { required: true })], Submission.prototype, "learnerId", 2);
656
- __decorateClass$3([foreignKey("Lesson", { required: true })], Submission.prototype, "lessonId", 2);
657
- __decorateClass$3([foreignKey("LessonProgress", { required: true })], Submission.prototype, "progressId", 2);
658
- __decorateClass$3([field({ required: true })], Submission.prototype, "kind", 2);
659
- __decorateClass$3([foreignKey("Submission", { nullable: true })], Submission.prototype, "previousSubmissionId", 2);
660
- __decorateClass$3([field({
653
+ __publicField$4(Submission, "__smrtPackage__", "@happyvertical/smrt-education");
654
+ __decorateClass$4([tenantId()], Submission.prototype, "tenantId", 2);
655
+ __decorateClass$4([foreignKey("Learner", { required: true })], Submission.prototype, "learnerId", 2);
656
+ __decorateClass$4([foreignKey("Lesson", { required: true })], Submission.prototype, "lessonId", 2);
657
+ __decorateClass$4([foreignKey("LessonProgress", { required: true })], Submission.prototype, "progressId", 2);
658
+ __decorateClass$4([field({ required: true })], Submission.prototype, "kind", 2);
659
+ __decorateClass$4([foreignKey("Submission", { nullable: true })], Submission.prototype, "previousSubmissionId", 2);
660
+ __decorateClass$4([field({
661
661
  type: "text",
662
662
  nullable: true
663
663
  })], Submission.prototype, "intakeItemId", 2);
664
- Submission = __decorateClass$3([TenantScoped({ mode: "required" }), smrt({
664
+ Submission = __decorateClass$4([TenantScoped({ mode: "required" }), smrt({
665
665
  tableStrategy: "sti",
666
666
  ...closed$3
667
667
  })], Submission);
@@ -684,14 +684,14 @@ var SubmissionIntakeBinding = class extends SmrtObject {
684
684
  return super.save(options);
685
685
  }
686
686
  };
687
- __publicField$3(SubmissionIntakeBinding, "__smrtPackage__", "@happyvertical/smrt-education");
688
- __decorateClass$3([tenantId()], SubmissionIntakeBinding.prototype, "tenantId", 2);
689
- __decorateClass$3([field({
687
+ __publicField$4(SubmissionIntakeBinding, "__smrtPackage__", "@happyvertical/smrt-education");
688
+ __decorateClass$4([tenantId()], SubmissionIntakeBinding.prototype, "tenantId", 2);
689
+ __decorateClass$4([field({
690
690
  required: true,
691
691
  type: "text"
692
692
  })], SubmissionIntakeBinding.prototype, "intakeItemId", 2);
693
- __decorateClass$3([foreignKey("Submission", { required: true })], SubmissionIntakeBinding.prototype, "submissionId", 2);
694
- SubmissionIntakeBinding = __decorateClass$3([TenantScoped({ mode: "required" }), smrt({
693
+ __decorateClass$4([foreignKey("Submission", { required: true })], SubmissionIntakeBinding.prototype, "submissionId", 2);
694
+ SubmissionIntakeBinding = __decorateClass$4([TenantScoped({ mode: "required" }), smrt({
695
695
  tableName: "submission_intake_bindings",
696
696
  ...closed$3,
697
697
  conflictColumns: ["tenant_id", "intake_item_id"]
@@ -715,8 +715,8 @@ var ScannedSubmission = class extends Submission {
715
715
  return super.save(options);
716
716
  }
717
717
  };
718
- __publicField$3(ScannedSubmission, "__smrtPackage__", "@happyvertical/smrt-education");
719
- ScannedSubmission = __decorateClass$3([smrt({
718
+ __publicField$4(ScannedSubmission, "__smrtPackage__", "@happyvertical/smrt-education");
719
+ ScannedSubmission = __decorateClass$4([smrt({
720
720
  tableName: "submissions",
721
721
  ...closed$3
722
722
  })], ScannedSubmission);
@@ -738,15 +738,15 @@ var ExternalCompletion = class extends Submission {
738
738
  return super.save(options);
739
739
  }
740
740
  };
741
- __publicField$3(ExternalCompletion, "__smrtPackage__", "@happyvertical/smrt-education");
742
- __decorateClass$3([field({ type: "decimal" })], ExternalCompletion.prototype, "reportedScore", 2);
743
- ExternalCompletion = __decorateClass$3([smrt({
741
+ __publicField$4(ExternalCompletion, "__smrtPackage__", "@happyvertical/smrt-education");
742
+ __decorateClass$4([field({ type: "decimal" })], ExternalCompletion.prototype, "reportedScore", 2);
743
+ ExternalCompletion = __decorateClass$4([smrt({
744
744
  tableName: "submissions",
745
745
  ...closed$3
746
746
  })], ExternalCompletion);
747
747
  var Attestation = class extends Submission {};
748
- __publicField$3(Attestation, "__smrtPackage__", "@happyvertical/smrt-education");
749
- Attestation = __decorateClass$3([smrt({
748
+ __publicField$4(Attestation, "__smrtPackage__", "@happyvertical/smrt-education");
749
+ Attestation = __decorateClass$4([smrt({
750
750
  tableName: "submissions",
751
751
  ...closed$3
752
752
  })], Attestation);
@@ -785,12 +785,12 @@ var SubmissionAsset = class extends SmrtObject {
785
785
  return super.save(options);
786
786
  }
787
787
  };
788
- __publicField$3(SubmissionAsset, "__smrtPackage__", "@happyvertical/smrt-education");
789
- __decorateClass$3([tenantId()], SubmissionAsset.prototype, "tenantId", 2);
790
- __decorateClass$3([foreignKey("Submission", { required: true })], SubmissionAsset.prototype, "submissionId", 2);
791
- __decorateClass$3([crossPackageRef("@happyvertical/smrt-assets:Asset", { required: true })], SubmissionAsset.prototype, "assetId", 2);
792
- __decorateClass$3([field({ required: true })], SubmissionAsset.prototype, "relationship", 2);
793
- SubmissionAsset = __decorateClass$3([TenantScoped({ mode: "required" }), smrt({
788
+ __publicField$4(SubmissionAsset, "__smrtPackage__", "@happyvertical/smrt-education");
789
+ __decorateClass$4([tenantId()], SubmissionAsset.prototype, "tenantId", 2);
790
+ __decorateClass$4([foreignKey("Submission", { required: true })], SubmissionAsset.prototype, "submissionId", 2);
791
+ __decorateClass$4([crossPackageRef("@happyvertical/smrt-assets:Asset", { required: true })], SubmissionAsset.prototype, "assetId", 2);
792
+ __decorateClass$4([field({ required: true })], SubmissionAsset.prototype, "relationship", 2);
793
+ SubmissionAsset = __decorateClass$4([TenantScoped({ mode: "required" }), smrt({
794
794
  tableName: "submission_assets",
795
795
  conflictColumns: [
796
796
  "tenant_id",
@@ -839,24 +839,24 @@ var Grade = class extends SmrtObject {
839
839
  return super.save(options);
840
840
  }
841
841
  };
842
- __publicField$3(Grade, "__smrtPackage__", "@happyvertical/smrt-education");
843
- __decorateClass$3([tenantId()], Grade.prototype, "tenantId", 2);
844
- __decorateClass$3([foreignKey("Submission", { required: true })], Grade.prototype, "submissionId", 2);
845
- __decorateClass$3([field({ required: true })], Grade.prototype, "graderKind", 2);
846
- __decorateClass$3([crossPackageRef("@happyvertical/smrt-profiles:Profile", { nullable: true })], Grade.prototype, "graderProfileId", 2);
847
- __decorateClass$3([field({
842
+ __publicField$4(Grade, "__smrtPackage__", "@happyvertical/smrt-education");
843
+ __decorateClass$4([tenantId()], Grade.prototype, "tenantId", 2);
844
+ __decorateClass$4([foreignKey("Submission", { required: true })], Grade.prototype, "submissionId", 2);
845
+ __decorateClass$4([field({ required: true })], Grade.prototype, "graderKind", 2);
846
+ __decorateClass$4([crossPackageRef("@happyvertical/smrt-profiles:Profile", { nullable: true })], Grade.prototype, "graderProfileId", 2);
847
+ __decorateClass$4([field({
848
848
  type: "decimal",
849
849
  nullable: true
850
850
  })], Grade.prototype, "score", 2);
851
- __decorateClass$3([field({
851
+ __decorateClass$4([field({
852
852
  type: "integer",
853
853
  nullable: true
854
854
  })], Grade.prototype, "pointsEarned", 2);
855
- __decorateClass$3([field({
855
+ __decorateClass$4([field({
856
856
  type: "integer",
857
857
  nullable: true
858
858
  })], Grade.prototype, "pointsPossible", 2);
859
- Grade = __decorateClass$3([TenantScoped({ mode: "required" }), smrt({ ...closed$3 })], Grade);
859
+ Grade = __decorateClass$4([TenantScoped({ mode: "required" }), smrt({ ...closed$3 })], Grade);
860
860
  //#endregion
861
861
  //#region src/collections.ts
862
862
  var CourseCollection = class extends SmrtCollection {
@@ -1059,21 +1059,21 @@ function familyIntakeScopeId(tenantId) {
1059
1059
  }
1060
1060
  //#endregion
1061
1061
  //#region src/family-intake-models.ts
1062
- var __defProp$2 = Object.defineProperty;
1063
- var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
1064
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, {
1062
+ var __defProp$3 = Object.defineProperty;
1063
+ var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
1064
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, {
1065
1065
  enumerable: true,
1066
1066
  configurable: true,
1067
1067
  writable: true,
1068
1068
  value
1069
1069
  }) : obj[key] = value;
1070
- var __decorateClass$2 = (decorators, target, key, kind) => {
1071
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
1070
+ var __decorateClass$3 = (decorators, target, key, kind) => {
1071
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
1072
1072
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1073
- if (kind && result) __defProp$2(target, key, result);
1073
+ if (kind && result) __defProp$3(target, key, result);
1074
1074
  return result;
1075
1075
  };
1076
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1076
+ var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
1077
1077
  var closed$2 = {
1078
1078
  api: false,
1079
1079
  mcp: false,
@@ -1104,11 +1104,11 @@ var FamilyIntake = class extends SmrtObject {
1104
1104
  noDelete();
1105
1105
  }
1106
1106
  };
1107
- __publicField$2(FamilyIntake, "__smrtPackage__", "@happyvertical/smrt-education");
1108
- __decorateClass$2([tenantId()], FamilyIntake.prototype, "tenantId", 2);
1109
- __decorateClass$2([crossPackageRef("@happyvertical/smrt-profiles:Profile")], FamilyIntake.prototype, "ownerProfileId", 2);
1110
- __decorateClass$2([field({ type: "text" })], FamilyIntake.prototype, "sourceJson", 2);
1111
- FamilyIntake = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1107
+ __publicField$3(FamilyIntake, "__smrtPackage__", "@happyvertical/smrt-education");
1108
+ __decorateClass$3([tenantId()], FamilyIntake.prototype, "tenantId", 2);
1109
+ __decorateClass$3([crossPackageRef("@happyvertical/smrt-profiles:Profile")], FamilyIntake.prototype, "ownerProfileId", 2);
1110
+ __decorateClass$3([field({ type: "text" })], FamilyIntake.prototype, "sourceJson", 2);
1111
+ FamilyIntake = __decorateClass$3([TenantScoped({ mode: "required" }), smrt({
1112
1112
  tableName: "family_intakes",
1113
1113
  conflictColumns: ["tenant_id", "item_id"],
1114
1114
  ...closed$2
@@ -1146,11 +1146,11 @@ var FamilyIntakeAsset = class extends SmrtObject {
1146
1146
  noDelete();
1147
1147
  }
1148
1148
  };
1149
- __publicField$2(FamilyIntakeAsset, "__smrtPackage__", "@happyvertical/smrt-education");
1150
- __decorateClass$2([tenantId()], FamilyIntakeAsset.prototype, "tenantId", 2);
1151
- __decorateClass$2([foreignKey("FamilyIntake", { required: true })], FamilyIntakeAsset.prototype, "intakeId", 2);
1152
- __decorateClass$2([crossPackageRef("@happyvertical/smrt-assets:Asset")], FamilyIntakeAsset.prototype, "assetId", 2);
1153
- FamilyIntakeAsset = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1149
+ __publicField$3(FamilyIntakeAsset, "__smrtPackage__", "@happyvertical/smrt-education");
1150
+ __decorateClass$3([tenantId()], FamilyIntakeAsset.prototype, "tenantId", 2);
1151
+ __decorateClass$3([foreignKey("FamilyIntake", { required: true })], FamilyIntakeAsset.prototype, "intakeId", 2);
1152
+ __decorateClass$3([crossPackageRef("@happyvertical/smrt-assets:Asset")], FamilyIntakeAsset.prototype, "assetId", 2);
1153
+ FamilyIntakeAsset = __decorateClass$3([TenantScoped({ mode: "required" }), smrt({
1154
1154
  tableName: "family_intake_assets",
1155
1155
  conflictColumns: [
1156
1156
  "tenant_id",
@@ -1185,11 +1185,11 @@ var FamilyIntakeDecision = class extends SmrtObject {
1185
1185
  noDelete();
1186
1186
  }
1187
1187
  };
1188
- __publicField$2(FamilyIntakeDecision, "__smrtPackage__", "@happyvertical/smrt-education");
1189
- __decorateClass$2([tenantId()], FamilyIntakeDecision.prototype, "tenantId", 2);
1190
- __decorateClass$2([foreignKey("FamilyIntake", { required: true })], FamilyIntakeDecision.prototype, "intakeId", 2);
1191
- __decorateClass$2([field({ type: "text" })], FamilyIntakeDecision.prototype, "bodyJson", 2);
1192
- FamilyIntakeDecision = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1188
+ __publicField$3(FamilyIntakeDecision, "__smrtPackage__", "@happyvertical/smrt-education");
1189
+ __decorateClass$3([tenantId()], FamilyIntakeDecision.prototype, "tenantId", 2);
1190
+ __decorateClass$3([foreignKey("FamilyIntake", { required: true })], FamilyIntakeDecision.prototype, "intakeId", 2);
1191
+ __decorateClass$3([field({ type: "text" })], FamilyIntakeDecision.prototype, "bodyJson", 2);
1192
+ FamilyIntakeDecision = __decorateClass$3([TenantScoped({ mode: "required" }), smrt({
1193
1193
  tableName: "family_intake_decisions",
1194
1194
  conflictColumns: [
1195
1195
  "tenant_id",
@@ -1216,6 +1216,160 @@ async function familyAdoptionOwner(db, photo) {
1216
1216
  return String(family.owner_profile_id);
1217
1217
  }
1218
1218
  //#endregion
1219
+ //#region src/group-sharing-contracts.ts
1220
+ var GroupSharingAuthorizationError = class extends Error {
1221
+ constructor(message = "Group sharing authorization denied") {
1222
+ super(message);
1223
+ this.name = "GroupSharingAuthorizationError";
1224
+ }
1225
+ };
1226
+ var GroupSharingConflictError = class extends Error {
1227
+ constructor(message = "Group sharing state conflict") {
1228
+ super(message);
1229
+ this.name = "GroupSharingConflictError";
1230
+ }
1231
+ };
1232
+ var GroupSharingPolicyValidationError = class extends Error {
1233
+ constructor(message = "Invalid group sharing policy") {
1234
+ super(message);
1235
+ this.name = "GroupSharingPolicyValidationError";
1236
+ }
1237
+ };
1238
+ function record$1(value) {
1239
+ return !!value && typeof value === "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
1240
+ }
1241
+ function keys(value, expected) {
1242
+ const actual = Object.keys(value);
1243
+ return actual.length === expected.length && actual.every((key) => expected.includes(key));
1244
+ }
1245
+ function selector(value) {
1246
+ if (!record$1(value)) return false;
1247
+ if (value.mode === "all") return keys(value, ["mode"]);
1248
+ return value.mode === "include" && keys(value, ["mode", "values"]) && Array.isArray(value.values) && value.values.length <= 1e3 && value.values.every((v) => typeof v === "string" && v.length > 0 && v.length <= 256 && v.trim() === v) && new Set(value.values).size === value.values.length;
1249
+ }
1250
+ function validateGroupSharingPolicy(value) {
1251
+ const booleans = [
1252
+ "shareWork",
1253
+ "shareMedia",
1254
+ "shareFeedback",
1255
+ "shareProgress",
1256
+ "parentReviewedOnly"
1257
+ ];
1258
+ if (!record$1(value) || !keys(value, [
1259
+ "schemaVersion",
1260
+ ...booleans,
1261
+ "commentVisibility",
1262
+ "historyFrom",
1263
+ "subjects",
1264
+ "courseIds"
1265
+ ]) || value.schemaVersion !== 1 || !["parent-only", "learner-visible"].includes(value.commentVisibility) || !booleans.every((key) => typeof value[key] === "boolean") || typeof value.historyFrom !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value.historyFrom) || !Number.isFinite(Date.parse(value.historyFrom)) || new Date(value.historyFrom).toISOString() !== value.historyFrom || !selector(value.subjects) || !selector(value.courseIds)) throw new GroupSharingPolicyValidationError();
1266
+ }
1267
+ function canonicalGroupSharingPolicy(value) {
1268
+ validateGroupSharingPolicy(value);
1269
+ const ordered = (s) => s.mode === "all" ? { mode: "all" } : {
1270
+ mode: "include",
1271
+ values: [...s.values].sort()
1272
+ };
1273
+ return JSON.stringify({
1274
+ schemaVersion: 1,
1275
+ shareWork: value.shareWork,
1276
+ shareMedia: value.shareMedia,
1277
+ shareFeedback: value.shareFeedback,
1278
+ shareProgress: value.shareProgress,
1279
+ parentReviewedOnly: value.parentReviewedOnly,
1280
+ commentVisibility: value.commentVisibility,
1281
+ historyFrom: value.historyFrom,
1282
+ subjects: ordered(value.subjects),
1283
+ courseIds: ordered(value.courseIds)
1284
+ });
1285
+ }
1286
+ //#endregion
1287
+ //#region src/group-sharing-policy.ts
1288
+ function evaluateGroupSharingPolicy(policy, facts) {
1289
+ validateGroupSharingPolicy(policy);
1290
+ const reasons = [];
1291
+ const captured = Date.parse(facts.capturedAt);
1292
+ if (!Number.isFinite(captured) || !Number.isFinite(Date.parse(policy.historyFrom)) || captured < Date.parse(policy.historyFrom)) reasons.push("history");
1293
+ if (policy.courseIds.mode === "include" && (!facts.courseId || !policy.courseIds.values.includes(facts.courseId))) reasons.push("course");
1294
+ if (policy.subjects.mode === "include" && (!facts.subject || !policy.subjects.values.includes(facts.subject))) reasons.push("subject");
1295
+ if (policy.parentReviewedOnly && !facts.parentReviewed) reasons.push("parent-review");
1296
+ const eligible = reasons.length === 0;
1297
+ return {
1298
+ eligible,
1299
+ work: eligible && policy.shareWork,
1300
+ media: eligible && policy.shareWork && policy.shareMedia,
1301
+ feedback: eligible && policy.shareFeedback,
1302
+ progress: eligible && policy.shareProgress,
1303
+ reasons
1304
+ };
1305
+ }
1306
+ function summarizeWorkbookEvidence(evidence) {
1307
+ const summaries = /* @__PURE__ */ new Map();
1308
+ const independentSeen = /* @__PURE__ */ new Set();
1309
+ for (const { attempt, revisionId, assessment, confirmed } of evidence) for (const q of assessment.questions) for (const conceptId of q.conceptIds) {
1310
+ const key = `${assessment.vocabulary.namespace}:${assessment.vocabulary.version}:${conceptId}`;
1311
+ const concept = assessment.vocabulary.concepts.find((c) => c.id === conceptId);
1312
+ if (!concept) throw new Error("Unknown workbook concept");
1313
+ let summary = summaries.get(key);
1314
+ if (!summary) {
1315
+ summary = {
1316
+ namespace: assessment.vocabulary.namespace,
1317
+ version: assessment.vocabulary.version,
1318
+ conceptId,
1319
+ label: concept.label,
1320
+ confirmed: {
1321
+ independent: 0,
1322
+ assisted: 0,
1323
+ unknown: 0,
1324
+ correct: 0,
1325
+ needsCorrection: 0,
1326
+ unassessed: 0
1327
+ },
1328
+ provisional: 0,
1329
+ episodes: [],
1330
+ revisionIds: [],
1331
+ cues: [],
1332
+ observations: []
1333
+ };
1334
+ summaries.set(key, summary);
1335
+ }
1336
+ if (!summary.episodes.includes(attempt.episodeId)) summary.episodes.push(attempt.episodeId);
1337
+ if (!summary.revisionIds.includes(revisionId)) summary.revisionIds.push(revisionId);
1338
+ const assistance = attempt.previousAttemptId && attempt.assistance === "independent" ? "unknown" : attempt.assistance;
1339
+ summary.observations.push({
1340
+ submissionId: attempt.submissionId,
1341
+ revisionId,
1342
+ questionId: q.id,
1343
+ episodeId: attempt.episodeId,
1344
+ result: q.result,
1345
+ assistance,
1346
+ isCorrection: Boolean(attempt.previousAttemptId),
1347
+ capturedAt: attempt.capturedAt,
1348
+ confirmed
1349
+ });
1350
+ if (!confirmed) {
1351
+ summary.provisional++;
1352
+ continue;
1353
+ }
1354
+ if (q.result === "unassessed") {
1355
+ summary.confirmed.unassessed++;
1356
+ continue;
1357
+ }
1358
+ const episodeQuestion = `${key}:${attempt.episodeId}:${q.id}`;
1359
+ if (assistance !== "independent" || !independentSeen.has(episodeQuestion)) summary.confirmed[assistance]++;
1360
+ if (assistance === "independent") independentSeen.add(episodeQuestion);
1361
+ if (q.result === "correct") summary.confirmed.correct++;
1362
+ else summary.confirmed.needsCorrection++;
1363
+ if (attempt.previousAttemptId && q.result === "correct" && !summary.cues.includes("improving on this item")) summary.cues.push("improving on this item");
1364
+ }
1365
+ for (const summary of summaries.values()) {
1366
+ if (!summary.confirmed.independent) summary.cues.push("needs a fresh independent check");
1367
+ if (summary.confirmed.needsCorrection) summary.cues.push("needs practice");
1368
+ if (!summary.confirmed.correct && !summary.confirmed.needsCorrection) summary.cues.push("not enough evidence");
1369
+ }
1370
+ return [...summaries.values()];
1371
+ }
1372
+ //#endregion
1219
1373
  //#region src/workbook-contracts.ts
1220
1374
  var WorkbookAssessmentValidationError = class extends Error {
1221
1375
  constructor(message, reason = "invalid_assessment") {
@@ -1251,6 +1405,14 @@ function aggregateWorkbookQuestions(questions) {
1251
1405
  function workbookAggregateFeedback(aggregate) {
1252
1406
  return `${aggregate.correct} correct; ${aggregate.incorrect} incorrect; ${aggregate.partiallyCorrect} partially correct; ${aggregate.unassessed} unassessed.`;
1253
1407
  }
1408
+ var WorkbookCompletionConflictError = class extends Error {
1409
+ constructor(code) {
1410
+ super(`Workbook completion conflict: ${code}`);
1411
+ this.code = code;
1412
+ this.name = "WorkbookCompletionConflictError";
1413
+ }
1414
+ code;
1415
+ };
1254
1416
  function text(value, label, max = 1e4) {
1255
1417
  if (typeof value !== "string" || value.length > max) throw new WorkbookAssessmentValidationError(`Invalid ${label}`);
1256
1418
  }
@@ -1363,21 +1525,21 @@ var WORKBOOK_MATH_VOCABULARY = {
1363
1525
  };
1364
1526
  //#endregion
1365
1527
  //#region src/workbook-models.ts
1366
- var __defProp$1 = Object.defineProperty;
1367
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1368
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, {
1528
+ var __defProp$2 = Object.defineProperty;
1529
+ var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
1530
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, {
1369
1531
  enumerable: true,
1370
1532
  configurable: true,
1371
1533
  writable: true,
1372
1534
  value
1373
1535
  }) : obj[key] = value;
1374
- var __decorateClass$1 = (decorators, target, key, kind) => {
1375
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
1536
+ var __decorateClass$2 = (decorators, target, key, kind) => {
1537
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
1376
1538
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1377
- if (kind && result) __defProp$1(target, key, result);
1539
+ if (kind && result) __defProp$2(target, key, result);
1378
1540
  return result;
1379
1541
  };
1380
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
1542
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
1381
1543
  var closed$1 = {
1382
1544
  api: false,
1383
1545
  mcp: false,
@@ -1416,10 +1578,10 @@ var Workbook = class extends SmrtObject {
1416
1578
  return super.save(options);
1417
1579
  }
1418
1580
  };
1419
- __publicField$1(Workbook, "__smrtPackage__", "@happyvertical/smrt-education");
1420
- __decorateClass$1([tenantId()], Workbook.prototype, "tenantId", 2);
1421
- __decorateClass$1([foreignKey("Learner", { required: true })], Workbook.prototype, "learnerId", 2);
1422
- Workbook = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
1581
+ __publicField$2(Workbook, "__smrtPackage__", "@happyvertical/smrt-education");
1582
+ __decorateClass$2([tenantId()], Workbook.prototype, "tenantId", 2);
1583
+ __decorateClass$2([foreignKey("Learner", { required: true })], Workbook.prototype, "learnerId", 2);
1584
+ Workbook = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1423
1585
  tableName: "workbooks",
1424
1586
  conflictColumns: [
1425
1587
  "tenant_id",
@@ -1497,19 +1659,19 @@ var WorkbookActivity = class extends SmrtObject {
1497
1659
  return super.save(options);
1498
1660
  }
1499
1661
  };
1500
- __publicField$1(WorkbookActivity, "__smrtPackage__", "@happyvertical/smrt-education");
1501
- __decorateClass$1([tenantId()], WorkbookActivity.prototype, "tenantId", 2);
1502
- __decorateClass$1([foreignKey("Learner", { required: true })], WorkbookActivity.prototype, "learnerId", 2);
1503
- __decorateClass$1([foreignKey("Workbook", { nullable: true })], WorkbookActivity.prototype, "workbookId", 2);
1504
- __decorateClass$1([field({
1662
+ __publicField$2(WorkbookActivity, "__smrtPackage__", "@happyvertical/smrt-education");
1663
+ __decorateClass$2([tenantId()], WorkbookActivity.prototype, "tenantId", 2);
1664
+ __decorateClass$2([foreignKey("Learner", { required: true })], WorkbookActivity.prototype, "learnerId", 2);
1665
+ __decorateClass$2([foreignKey("Workbook", { nullable: true })], WorkbookActivity.prototype, "workbookId", 2);
1666
+ __decorateClass$2([field({
1505
1667
  type: "text",
1506
1668
  required: true,
1507
1669
  default: "identified"
1508
1670
  })], WorkbookActivity.prototype, "captureKind", 2);
1509
- __decorateClass$1([foreignKey("WorkbookPhotoIntake", { nullable: true })], WorkbookActivity.prototype, "photoIntakeId", 2);
1510
- __decorateClass$1([foreignKey("Enrollment", { required: true })], WorkbookActivity.prototype, "enrollmentId", 2);
1511
- __decorateClass$1([foreignKey("Lesson", { required: true })], WorkbookActivity.prototype, "lessonId", 2);
1512
- WorkbookActivity = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
1671
+ __decorateClass$2([foreignKey("WorkbookPhotoIntake", { nullable: true })], WorkbookActivity.prototype, "photoIntakeId", 2);
1672
+ __decorateClass$2([foreignKey("Enrollment", { required: true })], WorkbookActivity.prototype, "enrollmentId", 2);
1673
+ __decorateClass$2([foreignKey("Lesson", { required: true })], WorkbookActivity.prototype, "lessonId", 2);
1674
+ WorkbookActivity = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1513
1675
  tableName: "workbook_activities",
1514
1676
  conflictColumns: [
1515
1677
  "tenant_id",
@@ -1573,14 +1735,14 @@ var WorkbookWorkFiling = class extends SmrtObject {
1573
1735
  return super.save(options);
1574
1736
  }
1575
1737
  };
1576
- __publicField$1(WorkbookWorkFiling, "__smrtPackage__", "@happyvertical/smrt-education");
1577
- __decorateClass$1([tenantId()], WorkbookWorkFiling.prototype, "tenantId", 2);
1578
- __decorateClass$1([foreignKey("Learner", { required: true })], WorkbookWorkFiling.prototype, "learnerId", 2);
1579
- __decorateClass$1([foreignKey("WorkbookActivity", { required: true })], WorkbookWorkFiling.prototype, "sourceActivityId", 2);
1580
- __decorateClass$1([foreignKey("Workbook", { required: true })], WorkbookWorkFiling.prototype, "workbookId", 2);
1581
- __decorateClass$1([foreignKey("WorkbookActivity", { nullable: true })], WorkbookWorkFiling.prototype, "canonicalActivityId", 2);
1582
- __decorateClass$1([foreignKey("WorkbookWorkFiling", { nullable: true })], WorkbookWorkFiling.prototype, "previousRevisionId", 2);
1583
- WorkbookWorkFiling = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
1738
+ __publicField$2(WorkbookWorkFiling, "__smrtPackage__", "@happyvertical/smrt-education");
1739
+ __decorateClass$2([tenantId()], WorkbookWorkFiling.prototype, "tenantId", 2);
1740
+ __decorateClass$2([foreignKey("Learner", { required: true })], WorkbookWorkFiling.prototype, "learnerId", 2);
1741
+ __decorateClass$2([foreignKey("WorkbookActivity", { required: true })], WorkbookWorkFiling.prototype, "sourceActivityId", 2);
1742
+ __decorateClass$2([foreignKey("Workbook", { required: true })], WorkbookWorkFiling.prototype, "workbookId", 2);
1743
+ __decorateClass$2([foreignKey("WorkbookActivity", { nullable: true })], WorkbookWorkFiling.prototype, "canonicalActivityId", 2);
1744
+ __decorateClass$2([foreignKey("WorkbookWorkFiling", { nullable: true })], WorkbookWorkFiling.prototype, "previousRevisionId", 2);
1745
+ WorkbookWorkFiling = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1584
1746
  tableName: "workbook_work_filings",
1585
1747
  conflictColumns: [
1586
1748
  "tenant_id",
@@ -1647,14 +1809,14 @@ var WorkbookAttempt = class extends SmrtObject {
1647
1809
  return super.save(options);
1648
1810
  }
1649
1811
  };
1650
- __publicField$1(WorkbookAttempt, "__smrtPackage__", "@happyvertical/smrt-education");
1651
- __decorateClass$1([tenantId()], WorkbookAttempt.prototype, "tenantId", 2);
1652
- __decorateClass$1([foreignKey("Learner", { required: true })], WorkbookAttempt.prototype, "learnerId", 2);
1653
- __decorateClass$1([foreignKey("WorkbookActivity", { required: true })], WorkbookAttempt.prototype, "activityId", 2);
1654
- __decorateClass$1([foreignKey("Submission", { required: true })], WorkbookAttempt.prototype, "submissionId", 2);
1655
- __decorateClass$1([foreignKey("WorkbookAttempt", { nullable: true })], WorkbookAttempt.prototype, "previousAttemptId", 2);
1656
- __decorateClass$1([field({ type: "text" })], WorkbookAttempt.prototype, "sourceHashes", 2);
1657
- WorkbookAttempt = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
1812
+ __publicField$2(WorkbookAttempt, "__smrtPackage__", "@happyvertical/smrt-education");
1813
+ __decorateClass$2([tenantId()], WorkbookAttempt.prototype, "tenantId", 2);
1814
+ __decorateClass$2([foreignKey("Learner", { required: true })], WorkbookAttempt.prototype, "learnerId", 2);
1815
+ __decorateClass$2([foreignKey("WorkbookActivity", { required: true })], WorkbookAttempt.prototype, "activityId", 2);
1816
+ __decorateClass$2([foreignKey("Submission", { required: true })], WorkbookAttempt.prototype, "submissionId", 2);
1817
+ __decorateClass$2([foreignKey("WorkbookAttempt", { nullable: true })], WorkbookAttempt.prototype, "previousAttemptId", 2);
1818
+ __decorateClass$2([field({ type: "text" })], WorkbookAttempt.prototype, "sourceHashes", 2);
1819
+ WorkbookAttempt = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1658
1820
  tableName: "workbook_attempts",
1659
1821
  conflictColumns: ["tenant_id", "submission_id"],
1660
1822
  ...closed$1
@@ -1701,14 +1863,14 @@ var WorkbookAssessmentRevision = class extends SmrtObject {
1701
1863
  return super.save(options);
1702
1864
  }
1703
1865
  };
1704
- __publicField$1(WorkbookAssessmentRevision, "__smrtPackage__", "@happyvertical/smrt-education");
1705
- __decorateClass$1([tenantId()], WorkbookAssessmentRevision.prototype, "tenantId", 2);
1706
- __decorateClass$1([foreignKey("Learner", { required: true })], WorkbookAssessmentRevision.prototype, "learnerId", 2);
1707
- __decorateClass$1([foreignKey("WorkbookAttempt", { required: true })], WorkbookAssessmentRevision.prototype, "attemptId", 2);
1708
- __decorateClass$1([foreignKey("WorkbookAssessmentRevision", { nullable: true })], WorkbookAssessmentRevision.prototype, "sourceRevisionId", 2);
1709
- __decorateClass$1([foreignKey("WorkbookAssessmentRevision", { nullable: true })], WorkbookAssessmentRevision.prototype, "supersedesId", 2);
1710
- __decorateClass$1([field({ type: "text" })], WorkbookAssessmentRevision.prototype, "assessmentJson", 2);
1711
- WorkbookAssessmentRevision = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
1866
+ __publicField$2(WorkbookAssessmentRevision, "__smrtPackage__", "@happyvertical/smrt-education");
1867
+ __decorateClass$2([tenantId()], WorkbookAssessmentRevision.prototype, "tenantId", 2);
1868
+ __decorateClass$2([foreignKey("Learner", { required: true })], WorkbookAssessmentRevision.prototype, "learnerId", 2);
1869
+ __decorateClass$2([foreignKey("WorkbookAttempt", { required: true })], WorkbookAssessmentRevision.prototype, "attemptId", 2);
1870
+ __decorateClass$2([foreignKey("WorkbookAssessmentRevision", { nullable: true })], WorkbookAssessmentRevision.prototype, "sourceRevisionId", 2);
1871
+ __decorateClass$2([foreignKey("WorkbookAssessmentRevision", { nullable: true })], WorkbookAssessmentRevision.prototype, "supersedesId", 2);
1872
+ __decorateClass$2([field({ type: "text" })], WorkbookAssessmentRevision.prototype, "assessmentJson", 2);
1873
+ WorkbookAssessmentRevision = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1712
1874
  tableName: "workbook_assessment_revisions",
1713
1875
  conflictColumns: [
1714
1876
  "tenant_id",
@@ -1753,13 +1915,13 @@ var WorkbookFeedbackRelease = class extends SmrtObject {
1753
1915
  return super.save(options);
1754
1916
  }
1755
1917
  };
1756
- __publicField$1(WorkbookFeedbackRelease, "__smrtPackage__", "@happyvertical/smrt-education");
1757
- __decorateClass$1([tenantId()], WorkbookFeedbackRelease.prototype, "tenantId", 2);
1758
- __decorateClass$1([foreignKey("Learner", { required: true })], WorkbookFeedbackRelease.prototype, "learnerId", 2);
1759
- __decorateClass$1([foreignKey("WorkbookAttempt", { required: true })], WorkbookFeedbackRelease.prototype, "attemptId", 2);
1760
- __decorateClass$1([foreignKey("WorkbookAssessmentRevision", { required: true })], WorkbookFeedbackRelease.prototype, "revisionId", 2);
1761
- __decorateClass$1([foreignKey("WorkbookAssessmentRevision", { nullable: true })], WorkbookFeedbackRelease.prototype, "expectedHeadId", 2);
1762
- WorkbookFeedbackRelease = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
1918
+ __publicField$2(WorkbookFeedbackRelease, "__smrtPackage__", "@happyvertical/smrt-education");
1919
+ __decorateClass$2([tenantId()], WorkbookFeedbackRelease.prototype, "tenantId", 2);
1920
+ __decorateClass$2([foreignKey("Learner", { required: true })], WorkbookFeedbackRelease.prototype, "learnerId", 2);
1921
+ __decorateClass$2([foreignKey("WorkbookAttempt", { required: true })], WorkbookFeedbackRelease.prototype, "attemptId", 2);
1922
+ __decorateClass$2([foreignKey("WorkbookAssessmentRevision", { required: true })], WorkbookFeedbackRelease.prototype, "revisionId", 2);
1923
+ __decorateClass$2([foreignKey("WorkbookAssessmentRevision", { nullable: true })], WorkbookFeedbackRelease.prototype, "expectedHeadId", 2);
1924
+ WorkbookFeedbackRelease = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1763
1925
  tableName: "workbook_feedback_releases",
1764
1926
  conflictColumns: [
1765
1927
  "tenant_id",
@@ -1802,12 +1964,12 @@ var WorkbookFeedbackDelivery = class extends SmrtObject {
1802
1964
  throw new Error("Workbook delivery history is immutable");
1803
1965
  }
1804
1966
  };
1805
- __publicField$1(WorkbookFeedbackDelivery, "__smrtPackage__", "@happyvertical/smrt-education");
1806
- __decorateClass$1([tenantId()], WorkbookFeedbackDelivery.prototype, "tenantId", 2);
1807
- __decorateClass$1([foreignKey("Learner", { required: true })], WorkbookFeedbackDelivery.prototype, "learnerId", 2);
1808
- __decorateClass$1([foreignKey("WorkbookAttempt", { required: true })], WorkbookFeedbackDelivery.prototype, "attemptId", 2);
1809
- __decorateClass$1([foreignKey("WorkbookAssessmentRevision", { required: true })], WorkbookFeedbackDelivery.prototype, "revisionId", 2);
1810
- WorkbookFeedbackDelivery = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
1967
+ __publicField$2(WorkbookFeedbackDelivery, "__smrtPackage__", "@happyvertical/smrt-education");
1968
+ __decorateClass$2([tenantId()], WorkbookFeedbackDelivery.prototype, "tenantId", 2);
1969
+ __decorateClass$2([foreignKey("Learner", { required: true })], WorkbookFeedbackDelivery.prototype, "learnerId", 2);
1970
+ __decorateClass$2([foreignKey("WorkbookAttempt", { required: true })], WorkbookFeedbackDelivery.prototype, "attemptId", 2);
1971
+ __decorateClass$2([foreignKey("WorkbookAssessmentRevision", { required: true })], WorkbookFeedbackDelivery.prototype, "revisionId", 2);
1972
+ WorkbookFeedbackDelivery = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
1811
1973
  tableName: "workbook_feedback_deliveries",
1812
1974
  conflictColumns: [
1813
1975
  "tenant_id",
@@ -1821,6 +1983,176 @@ var WorkbookFeedbackDeliveryCollection = class extends SmrtCollection {
1821
1983
  static _itemClass = WorkbookFeedbackDelivery;
1822
1984
  };
1823
1985
  //#endregion
1986
+ //#region src/workbook-completion.ts
1987
+ var __defProp$1 = Object.defineProperty;
1988
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1989
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, {
1990
+ enumerable: true,
1991
+ configurable: true,
1992
+ writable: true,
1993
+ value
1994
+ }) : obj[key] = value;
1995
+ var __decorateClass$1 = (decorators, target, key, kind) => {
1996
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
1997
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1998
+ if (kind && result) __defProp$1(target, key, result);
1999
+ return result;
2000
+ };
2001
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
2002
+ var WorkbookCompletionDecision = class extends SmrtObject {
2003
+ tenantId = "";
2004
+ learnerId = "";
2005
+ activityId = "";
2006
+ attemptId = "";
2007
+ submissionId = "";
2008
+ feedbackHeadId = null;
2009
+ actorUserId = "";
2010
+ requestKey = "";
2011
+ recordedAt = /* @__PURE__ */ new Date();
2012
+ constructor(options = {}) {
2013
+ super(options);
2014
+ for (const [key, value] of Object.entries(options)) if (Object.hasOwn(this, key)) this[key] = value;
2015
+ }
2016
+ async delete() {
2017
+ throw new Error("Workbook completion history is immutable");
2018
+ }
2019
+ async save(options) {
2020
+ if (this.id && await this.db.get(this.tableName, { id: this.id })) throw new Error("Workbook completion history is immutable");
2021
+ if (!this.tenantId.trim() || !this.actorUserId || typeof this.requestKey !== "string" || !this.requestKey.trim() || this.requestKey.length > 200 || Array.from(this.requestKey).some((character) => {
2022
+ const code = character.charCodeAt(0);
2023
+ return code < 32 || code === 127;
2024
+ })) throw new Error("Workbook completion requires tenant, actor and request key");
2025
+ const owner = {
2026
+ tenant_id: this.tenantId,
2027
+ learner_id: this.learnerId
2028
+ };
2029
+ const activity = await this.db.get("workbook_activities", {
2030
+ ...owner,
2031
+ id: this.activityId
2032
+ });
2033
+ const submission = await this.db.get("submissions", {
2034
+ ...owner,
2035
+ id: this.submissionId
2036
+ });
2037
+ if (!activity || !submission || !await this.db.get("learners", {
2038
+ id: this.learnerId,
2039
+ tenant_id: this.tenantId
2040
+ }) || !await this.db.get("workbook_attempts", {
2041
+ ...owner,
2042
+ id: this.attemptId,
2043
+ activity_id: this.activityId,
2044
+ submission_id: this.submissionId
2045
+ }) || submission.lesson_id !== activity.lesson_id || !await this.db.get("lesson_progress", {
2046
+ ...owner,
2047
+ id: submission.progress_id,
2048
+ enrollment_id: activity.enrollment_id,
2049
+ lesson_id: activity.lesson_id
2050
+ }) || !await this.db.get("memberships", {
2051
+ tenant_id: this.tenantId,
2052
+ user_id: this.actorUserId,
2053
+ status: "active"
2054
+ })) throw new Error("Workbook completion ownership mismatch");
2055
+ if (this.feedbackHeadId !== null && !await this.db.get("workbook_assessment_revisions", {
2056
+ ...owner,
2057
+ id: this.feedbackHeadId,
2058
+ attempt_id: this.attemptId
2059
+ })) throw new Error("Workbook completion feedback head mismatch");
2060
+ this.recordedAt = /* @__PURE__ */ new Date();
2061
+ this.requireInsertOnSave();
2062
+ return super.save(options);
2063
+ }
2064
+ };
2065
+ __publicField$1(WorkbookCompletionDecision, "__smrtPackage__", "@happyvertical/smrt-education");
2066
+ __decorateClass$1([tenantId()], WorkbookCompletionDecision.prototype, "tenantId", 2);
2067
+ __decorateClass$1([foreignKey("Learner", { required: true })], WorkbookCompletionDecision.prototype, "learnerId", 2);
2068
+ __decorateClass$1([foreignKey("WorkbookActivity", { required: true })], WorkbookCompletionDecision.prototype, "activityId", 2);
2069
+ __decorateClass$1([foreignKey("WorkbookAttempt", { required: true })], WorkbookCompletionDecision.prototype, "attemptId", 2);
2070
+ __decorateClass$1([foreignKey("Submission", { required: true })], WorkbookCompletionDecision.prototype, "submissionId", 2);
2071
+ __decorateClass$1([foreignKey("WorkbookAssessmentRevision", { nullable: true })], WorkbookCompletionDecision.prototype, "feedbackHeadId", 2);
2072
+ __decorateClass$1([crossPackageRef("@happyvertical/smrt-users:User", { required: true })], WorkbookCompletionDecision.prototype, "actorUserId", 2);
2073
+ WorkbookCompletionDecision = __decorateClass$1([TenantScoped({ mode: "required" }), smrt({
2074
+ api: false,
2075
+ mcp: false,
2076
+ cli: false,
2077
+ tableName: "workbook_completion_decisions",
2078
+ conflictColumns: [
2079
+ "tenant_id",
2080
+ "activity_id",
2081
+ "request_key"
2082
+ ],
2083
+ indexes: [{
2084
+ name: "workbook_completion_decisions_attempt_unique",
2085
+ columns: [
2086
+ "tenantId",
2087
+ "activityId",
2088
+ "attemptId"
2089
+ ],
2090
+ unique: true
2091
+ }]
2092
+ })], WorkbookCompletionDecision);
2093
+ var WorkbookCompletionDecisionCollection = class extends SmrtCollection {
2094
+ static _itemClass = WorkbookCompletionDecision;
2095
+ };
2096
+ async function readWorkbookCompletion(db, scope) {
2097
+ if (!scope.tenantId || !scope.learnerId || !scope.activityId) throw new Error("Workbook completion requires exact scope");
2098
+ const attempts = await db.tableExists("workbook_attempts") ? await (await WorkbookAttemptCollection.create({ db })).list({ where: scope }) : [];
2099
+ const byId = new Map(attempts.map((attempt) => [String(attempt.id), attempt]));
2100
+ const episodes = /* @__PURE__ */ new Map();
2101
+ for (const attempt of attempts) {
2102
+ const episode = episodes.get(attempt.episodeId) ?? [];
2103
+ episode.push(attempt);
2104
+ episodes.set(attempt.episodeId, episode);
2105
+ }
2106
+ const leaves = [];
2107
+ for (const episode of episodes.values()) {
2108
+ const predecessors = new Set(episode.flatMap((attempt) => attempt.previousAttemptId ? [attempt.previousAttemptId] : []));
2109
+ const episodeLeaves = episode.filter((attempt) => !predecessors.has(String(attempt.id)));
2110
+ if (episodeLeaves.length !== 1) throw new Error("Workbook current attempt is ambiguous");
2111
+ const leaf = episodeLeaves[0];
2112
+ const visited = /* @__PURE__ */ new Set();
2113
+ let ancestor = leaf;
2114
+ while (ancestor) {
2115
+ const id = String(ancestor.id);
2116
+ if (visited.has(id)) throw new Error("Workbook attempt ancestry is cyclic");
2117
+ if (ancestor.episodeId !== leaf.episodeId) throw new Error("Workbook attempt ancestry crosses practice episodes");
2118
+ visited.add(id);
2119
+ const previous = ancestor.previousAttemptId;
2120
+ ancestor = previous ? byId.get(previous) ?? null : null;
2121
+ if (previous && !ancestor) throw new Error("Workbook attempt ancestry is incomplete");
2122
+ }
2123
+ if (visited.size !== episode.length) throw new Error("Workbook attempt ancestry is disconnected");
2124
+ leaves.push(leaf);
2125
+ }
2126
+ leaves.sort((a, b) => new Date(b.capturedAt).getTime() - new Date(a.capturedAt).getTime() || String(b.id).localeCompare(String(a.id)));
2127
+ const currentAttempt = leaves[0] ?? null;
2128
+ const revisions = currentAttempt ? await (await WorkbookAssessmentRevisionCollection.create({ db })).list({ where: {
2129
+ tenantId: scope.tenantId,
2130
+ learnerId: scope.learnerId,
2131
+ attemptId: String(currentAttempt.id)
2132
+ } }) : [];
2133
+ revisions.sort((a, b) => new Date(b.recordedAt).getTime() - new Date(a.recordedAt).getTime() || String(b.id).localeCompare(String(a.id)));
2134
+ const head = revisions.find((revision) => revision.kind === "guardian") ?? revisions.find((revision) => revision.kind === "ai");
2135
+ const decisions = await db.tableExists("workbook_completion_decisions") ? await (await WorkbookCompletionDecisionCollection.create({ db })).list({ where: scope }) : [];
2136
+ const currentDecisions = decisions.filter((decision2) => decision2.attemptId === currentAttempt?.id);
2137
+ if (currentDecisions.length > 1) throw new Error("Workbook current completion decision is ambiguous");
2138
+ const decision = currentDecisions[0] ?? null;
2139
+ if (decision && decision.submissionId !== currentAttempt?.submissionId) throw new Error("Workbook completion submission mismatch");
2140
+ return {
2141
+ currentAttempt,
2142
+ feedbackHeadId: head ? String(head.id) : null,
2143
+ decision,
2144
+ decisions,
2145
+ completion: decision ? {
2146
+ status: "complete",
2147
+ completedAt: new Date(decision.recordedAt)
2148
+ } : {
2149
+ status: "doing",
2150
+ completedAt: null
2151
+ },
2152
+ attempts
2153
+ };
2154
+ }
2155
+ //#endregion
1824
2156
  //#region src/learner-service.ts
1825
2157
  var LearnerAuthorizationError = class extends Error {
1826
2158
  constructor() {
@@ -2019,7 +2351,10 @@ var LearnerService = class LearnerService {
2019
2351
  if (own && [
2020
2352
  "education.learner.plan",
2021
2353
  "education.learner.grade",
2022
- "education.learner.packet"
2354
+ "education.learner.packet",
2355
+ "education.learner.notes.read",
2356
+ "education.learner.notes.add",
2357
+ "education.learner.complete"
2023
2358
  ].includes(action)) throw new LearnerAuthorizationError();
2024
2359
  if (await this.authorize({
2025
2360
  ...request,
@@ -2255,15 +2590,25 @@ var LearnerService = class LearnerService {
2255
2590
  /** Compatibility progress never transports a workbook learner's feedback. */
2256
2591
  async projectProgress(request, row) {
2257
2592
  const view = progressView(row);
2258
- if ((await this.db.get("learners", {
2593
+ const activity = await this.findWorkbookActivity(request, {
2594
+ enrollmentId: row.enrollmentId,
2595
+ lessonId: row.lessonId
2596
+ });
2597
+ if (!activity) return view;
2598
+ const state = await readWorkbookCompletion(this.db, {
2599
+ tenantId: request.tenantId,
2600
+ learnerId: request.learnerId,
2601
+ activityId: String(activity.id)
2602
+ });
2603
+ const learner = await this.db.get("learners", {
2259
2604
  id: request.learnerId,
2260
2605
  tenant_id: request.tenantId
2261
- }))?.user_id !== request.actorUserId || !await this.findWorkbookActivity(request, { lessonId: row.lessonId })) return view;
2606
+ });
2262
2607
  return {
2263
2608
  ...view,
2264
- bestScore: null,
2265
- completedAt: null,
2266
- status: view.status === "complete" ? "submitted" : view.status
2609
+ status: state.completion.status === "complete" ? "complete" : ["complete", "skipped"].includes(view.status) ? "in_progress" : view.status,
2610
+ completedAt: state.completion.completedAt,
2611
+ ...learner?.user_id === request.actorUserId ? { bestScore: null } : {}
2267
2612
  };
2268
2613
  }
2269
2614
  async listProgress(request) {
@@ -25107,9 +25452,13 @@ var SubmissionService = class SubmissionService extends LearnerService {
25107
25452
  "application/pdf"
25108
25453
  ].includes(page.mimeType) || !Buffer.isBuffer(page.data) || !page.data.length || page.data.length > 25 * 1024 * 1024) throw new Error("Invalid capture page");
25109
25454
  }
25455
+ async lockProgressOwner(request) {
25456
+ if (await this.findWorkbookActivity(request, { enrollmentId: request.enrollmentId })) await this.db.query("UPDATE learners SET id = id WHERE id = ? AND tenant_id = ?", [request.learnerId, request.tenantId]);
25457
+ await this.lock("enrollments", request.enrollmentId, request.tenantId);
25458
+ }
25110
25459
  async progress(request, action) {
25111
25460
  const enrollment = await this.requireEnrollment(request, action);
25112
- await this.lock("enrollments", request.enrollmentId, request.tenantId);
25461
+ await this.lockProgressOwner(request);
25113
25462
  if (!(await this.catalog(request, enrollment.courseId)).sections.some((section) => section.lessons.some(({ lesson }) => lesson.id === request.lessonId))) throw new LearnerAuthorizationError();
25114
25463
  const collection = await LessonProgressCollection.create({ db: this.db });
25115
25464
  let progress = await collection.get({
@@ -25446,6 +25795,38 @@ var SubmissionService = class SubmissionService extends LearnerService {
25446
25795
  async getSubmission(request) {
25447
25796
  return this.view((await this.requireSubmission(request, "education.learner.read")).row, request.actorUserId);
25448
25797
  }
25798
+ /** Latest retained completed-work evidence; lesson completion is independent. */
25799
+ async getLatestLessonSubmission(request) {
25800
+ const enrollment = await this.requireEnrollment(request, "education.learner.read");
25801
+ if (!(await this.catalog(request, enrollment.courseId)).sections.some((section) => section.lessons.some(({ lesson }) => lesson.id === request.lessonId))) throw new LearnerAuthorizationError();
25802
+ const progress = await (await LessonProgressCollection.create({ db: this.db })).get({
25803
+ enrollmentId: request.enrollmentId,
25804
+ learnerId: request.learnerId,
25805
+ lessonId: request.lessonId,
25806
+ tenantId: request.tenantId
25807
+ }, { cache: false });
25808
+ if (!progress) return null;
25809
+ const [row] = await (await SubmissionCollection.create({ db: this.db })).list({
25810
+ where: {
25811
+ tenantId: request.tenantId,
25812
+ learnerId: request.learnerId,
25813
+ lessonId: request.lessonId,
25814
+ progressId: String(progress.id),
25815
+ kind: "completed_work",
25816
+ "status in": [
25817
+ "captured",
25818
+ "needs_review",
25819
+ "graded"
25820
+ ]
25821
+ },
25822
+ orderBy: ["submittedAt DESC", "id DESC"],
25823
+ limit: 1
25824
+ });
25825
+ return row ? this.getSubmission({
25826
+ ...request,
25827
+ submissionId: String(row.id)
25828
+ }) : null;
25829
+ }
25449
25830
  async grade(request, row, progress, score, feedback) {
25450
25831
  if (typeof feedback !== "string") throw new Error("Feedback must be text");
25451
25832
  if (score !== null && (typeof score !== "number" || !Number.isFinite(score) || score < 0 || score > 1)) throw new Error("Score must be null or a number from zero to one");
@@ -25469,8 +25850,17 @@ var SubmissionService = class SubmissionService extends LearnerService {
25469
25850
  });
25470
25851
  row.status = "graded";
25471
25852
  await row.save();
25472
- progress.status = "complete";
25473
- progress.completedAt = /* @__PURE__ */ new Date();
25853
+ const workbookActivity = await this.findWorkbookActivity(request, {
25854
+ enrollmentId: progress.enrollmentId,
25855
+ lessonId: row.lessonId
25856
+ });
25857
+ const completion = workbookActivity ? await readWorkbookCompletion(this.db, {
25858
+ tenantId: request.tenantId,
25859
+ learnerId: request.learnerId,
25860
+ activityId: String(workbookActivity.id)
25861
+ }) : null;
25862
+ progress.status = completion ? completion.completion.status === "complete" ? "complete" : "in_progress" : "complete";
25863
+ progress.completedAt = completion ? completion.completion.completedAt : /* @__PURE__ */ new Date();
25474
25864
  progress.bestScore = score;
25475
25865
  await progress.save();
25476
25866
  return this.view(row, request.actorUserId);
@@ -25478,7 +25868,7 @@ var SubmissionService = class SubmissionService extends LearnerService {
25478
25868
  async reviewSubmission(request) {
25479
25869
  return this.mutate(async (service) => {
25480
25870
  await service.requireEnrollment(request, "education.learner.grade");
25481
- await service.lock("enrollments", request.enrollmentId, request.tenantId);
25871
+ await service.lockProgressOwner(request);
25482
25872
  const { row, progress } = await service.requireSubmission(request, "education.learner.grade");
25483
25873
  if (row.kind !== "completed_work") throw new Error("Blank workbook references cannot be graded");
25484
25874
  if ((await (await SubmissionCollection.create({ db: service.db })).list({ where: {
@@ -26018,6 +26408,17 @@ function revisionView(row) {
26018
26408
  assessment: JSON.parse(row.assessmentJson)
26019
26409
  };
26020
26410
  }
26411
+ function completionDecisionView(row) {
26412
+ return {
26413
+ id: String(row.id),
26414
+ activityId: row.activityId,
26415
+ attemptId: row.attemptId,
26416
+ submissionId: row.submissionId,
26417
+ feedbackHeadId: row.feedbackHeadId,
26418
+ actorUserId: row.actorUserId,
26419
+ completedAt: new Date(row.recordedAt)
26420
+ };
26421
+ }
26021
26422
  var WorkbookService = class WorkbookService extends SubmissionService {
26022
26423
  constructor(db, authorize, authorizeCatalog, workbookOptions) {
26023
26424
  super(db, authorize, authorizeCatalog, workbookOptions);
@@ -26086,7 +26487,15 @@ var WorkbookService = class WorkbookService extends SubmissionService {
26086
26487
  workbookId: head.workbookId
26087
26488
  }, { cache: false }) : null;
26088
26489
  if (head && (!projectedBook || head.canonicalActivityId && !target)) throw new LearnerAuthorizationError();
26490
+ const completion = await readWorkbookCompletion(this.db, {
26491
+ tenantId: row.tenantId,
26492
+ learnerId: row.learnerId,
26493
+ activityId: String(row.id)
26494
+ });
26089
26495
  return {
26496
+ currentAttemptId: completion.currentAttempt ? String(completion.currentAttempt.id) : null,
26497
+ latestSubmissionId: completion.currentAttempt?.submissionId ?? null,
26498
+ completion: completion.completion,
26090
26499
  id: String(row.id),
26091
26500
  learnerId: row.learnerId,
26092
26501
  workbookKey: projectedBook?.workbookKey ?? "",
@@ -26521,6 +26930,21 @@ var WorkbookService = class WorkbookService extends SubmissionService {
26521
26930
  previous = predecessor.attempt;
26522
26931
  if (predecessor.learner.userId === request.actorUserId && !await service.questionIdentityVisible(request, previous, await service.identityRevision(request, previous))) throw new LearnerAuthorizationError();
26523
26932
  }
26933
+ if (request.previousSubmissionId) {
26934
+ const current = await readWorkbookCompletion(service.db, {
26935
+ tenantId: request.tenantId,
26936
+ learnerId: request.learnerId,
26937
+ activityId: request.activityId
26938
+ });
26939
+ if (current.currentAttempt?.submissionId !== request.previousSubmissionId) {
26940
+ if (!(await (await SubmissionCollection.create({ db: service.db })).list({ where: {
26941
+ tenantId: request.tenantId,
26942
+ learnerId: request.learnerId,
26943
+ lessonId: row.lessonId,
26944
+ captureKey: request.requestKey
26945
+ } })).some((submission2) => current.attempts.some((attempt2) => attempt2.submissionId === submission2.id))) throw new WorkbookCompletionConflictError("stale_attempt");
26946
+ }
26947
+ }
26524
26948
  const capture = {
26525
26949
  ...request,
26526
26950
  enrollmentId: row.enrollmentId,
@@ -26563,7 +26987,11 @@ var WorkbookService = class WorkbookService extends SubmissionService {
26563
26987
  policyVersion: workbook?.policyVersion ?? 1,
26564
26988
  assistance,
26565
26989
  actorUserId: request.actorUserId,
26566
- capturedAt: submission.submittedAt,
26990
+ capturedAt: new Date(Math.max(new Date(submission.submittedAt).getTime(), ...(await attempts.list({ where: {
26991
+ tenantId: request.tenantId,
26992
+ learnerId: request.learnerId,
26993
+ activityId: request.activityId
26994
+ } })).map((prior2) => new Date(prior2.capturedAt).getTime() + 1))),
26567
26995
  sourceHashes: JSON.stringify(sourceHashes)
26568
26996
  });
26569
26997
  return service.attemptView(request, row, attempt);
@@ -26875,6 +27303,57 @@ var WorkbookService = class WorkbookService extends SubmissionService {
26875
27303
  return revisionView(result.row);
26876
27304
  });
26877
27305
  }
27306
+ async completeWorkbookActivity(request) {
27307
+ return this.composeMutation(async (service) => {
27308
+ await service.requireActor(request, "education.learner.complete");
27309
+ await service.serialize(request);
27310
+ const { row } = await service.activity(request, "education.learner.complete");
27311
+ nonempty(request.requestKey, "completion request key");
27312
+ nonempty(request.expectedAttemptId, "completion attempt");
27313
+ nonempty(request.submissionId, "completion submission");
27314
+ if (request.expectedHeadId !== null) nonempty(request.expectedHeadId, "completion feedback head");
27315
+ const decisions = await WorkbookCompletionDecisionCollection.create({ db: service.db });
27316
+ const prior = await decisions.get({
27317
+ tenantId: request.tenantId,
27318
+ activityId: request.activityId,
27319
+ requestKey: request.requestKey
27320
+ }, { cache: false });
27321
+ if (prior) {
27322
+ if (prior.learnerId !== request.learnerId || prior.actorUserId !== request.actorUserId || prior.attemptId !== request.expectedAttemptId || prior.submissionId !== request.submissionId || prior.feedbackHeadId !== request.expectedHeadId) throw new WorkbookCompletionConflictError("changed_retry");
27323
+ return completionDecisionView(prior);
27324
+ }
27325
+ const current = await readWorkbookCompletion(service.db, {
27326
+ tenantId: request.tenantId,
27327
+ learnerId: request.learnerId,
27328
+ activityId: request.activityId
27329
+ });
27330
+ if (!current.currentAttempt || current.currentAttempt.id !== request.expectedAttemptId || current.currentAttempt.submissionId !== request.submissionId) throw new WorkbookCompletionConflictError("stale_attempt");
27331
+ if (current.feedbackHeadId !== request.expectedHeadId) throw new WorkbookCompletionConflictError("stale_head");
27332
+ if (current.decision) throw new WorkbookCompletionConflictError("already_complete");
27333
+ const decision = await decisions.create({
27334
+ tenantId: request.tenantId,
27335
+ learnerId: request.learnerId,
27336
+ activityId: request.activityId,
27337
+ attemptId: request.expectedAttemptId,
27338
+ submissionId: request.submissionId,
27339
+ feedbackHeadId: request.expectedHeadId,
27340
+ actorUserId: request.actorUserId,
27341
+ requestKey: request.requestKey,
27342
+ _insertOnly: true
27343
+ });
27344
+ const progress = await (await LessonProgressCollection.create({ db: service.db })).get({
27345
+ tenantId: request.tenantId,
27346
+ learnerId: request.learnerId,
27347
+ enrollmentId: row.enrollmentId,
27348
+ lessonId: row.lessonId
27349
+ }, { cache: false });
27350
+ if (!progress) throw new LearnerAuthorizationError();
27351
+ progress.status = "complete";
27352
+ progress.completedAt = new Date(decision.recordedAt);
27353
+ await progress.save();
27354
+ return completionDecisionView(decision);
27355
+ });
27356
+ }
26878
27357
  async releaseWorkbookFeedback(request) {
26879
27358
  return this.composeMutation(async (service) => {
26880
27359
  await service.requireActor(request, "education.learner.grade");
@@ -26958,6 +27437,11 @@ var WorkbookService = class WorkbookService extends SubmissionService {
26958
27437
  async getWorkbookTimeline(request) {
26959
27438
  const { row, workbook, learner } = await this.activity(request);
26960
27439
  const own = learner.userId === request.actorUserId;
27440
+ const completion = await readWorkbookCompletion(this.db, {
27441
+ tenantId: request.tenantId,
27442
+ learnerId: request.learnerId,
27443
+ activityId: request.activityId
27444
+ });
26961
27445
  const limit = request.limit ?? 25;
26962
27446
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw new Error("Invalid timeline limit");
26963
27447
  const all = await (await WorkbookAttemptCollection.create({ db: this.db })).list({
@@ -27014,10 +27498,22 @@ var WorkbookService = class WorkbookService extends SubmissionService {
27014
27498
  actorUserId: d.actorUserId,
27015
27499
  createdAt: d.recordedAt
27016
27500
  })),
27017
- headId: own ? null : headId ? String(headId) : null
27501
+ headId: own ? null : headId ? String(headId) : null,
27502
+ completionDecision: own ? null : (() => {
27503
+ const decision = completion.decisions.find((value) => value.attemptId === attempt.id);
27504
+ return decision ? completionDecisionView(decision) : null;
27505
+ })()
27018
27506
  });
27019
27507
  }
27020
27508
  return {
27509
+ currentAttemptId: completion.currentAttempt ? String(completion.currentAttempt.id) : null,
27510
+ latestSubmissionId: completion.currentAttempt?.submissionId ?? null,
27511
+ completion: completion.completion,
27512
+ completionContext: own || !completion.currentAttempt ? null : {
27513
+ attemptId: String(completion.currentAttempt.id),
27514
+ submissionId: completion.currentAttempt.submissionId,
27515
+ expectedHeadId: completion.feedbackHeadId
27516
+ },
27021
27517
  activity: await this.activityView(row, workbook),
27022
27518
  attempts,
27023
27519
  nextCursor: start + limit < all.length ? String(all[start + limit - 1].id) : null
@@ -27031,8 +27527,7 @@ var WorkbookService = class WorkbookService extends SubmissionService {
27031
27527
  learnerId: request.learnerId,
27032
27528
  activityId: request.activityId
27033
27529
  } });
27034
- const summaries = /* @__PURE__ */ new Map();
27035
- const independentSeen = /* @__PURE__ */ new Set();
27530
+ const evidence = [];
27036
27531
  const ordered = [...attempts].sort((a, b) => Number(Boolean(a.previousAttemptId)) - Number(Boolean(b.previousAttemptId)) || String(a.id).localeCompare(String(b.id)));
27037
27532
  for (const attempt of ordered) {
27038
27533
  const revisions = await this.revisions(request, String(attempt.id));
@@ -27040,70 +27535,16 @@ var WorkbookService = class WorkbookService extends SubmissionService {
27040
27535
  const effective = confirmed ?? revisions.filter((r) => r.kind === "ai").at(-1);
27041
27536
  if (!effective) continue;
27042
27537
  const assessment = revisionView(effective).assessment;
27043
- for (const q of assessment.questions) for (const conceptId of q.conceptIds) {
27044
- const key = `${assessment.vocabulary.namespace}:${assessment.vocabulary.version}:${conceptId}`;
27045
- let summary = summaries.get(key);
27046
- if (!summary) {
27047
- summary = {
27048
- namespace: assessment.vocabulary.namespace,
27049
- version: assessment.vocabulary.version,
27050
- conceptId,
27051
- label: assessment.vocabulary.concepts.find((c) => c.id === conceptId).label,
27052
- confirmed: {
27053
- independent: 0,
27054
- assisted: 0,
27055
- unknown: 0,
27056
- correct: 0,
27057
- needsCorrection: 0,
27058
- unassessed: 0
27059
- },
27060
- provisional: 0,
27061
- episodes: [],
27062
- revisionIds: [],
27063
- cues: [],
27064
- observations: []
27065
- };
27066
- summaries.set(key, summary);
27067
- }
27068
- if (!summary.episodes.includes(attempt.episodeId)) summary.episodes.push(attempt.episodeId);
27069
- if (!summary.revisionIds.includes(String(effective.id))) summary.revisionIds.push(String(effective.id));
27070
- const observationAssistance = attempt.previousAttemptId && attempt.assistance === "independent" ? "unknown" : attempt.assistance;
27071
- summary.observations.push({
27072
- submissionId: attempt.submissionId,
27073
- revisionId: String(effective.id),
27074
- questionId: q.id,
27075
- episodeId: attempt.episodeId,
27076
- result: q.result,
27077
- assistance: observationAssistance,
27078
- isCorrection: Boolean(attempt.previousAttemptId),
27079
- capturedAt: attempt.capturedAt,
27080
- confirmed: Boolean(confirmed)
27081
- });
27082
- if (!confirmed) {
27083
- summary.provisional++;
27084
- continue;
27085
- }
27086
- if (q.result === "unassessed") {
27087
- summary.confirmed.unassessed++;
27088
- continue;
27089
- }
27090
- const episodeQuestion = `${key}:${attempt.episodeId}:${q.id}`;
27091
- const assistance = attempt.previousAttemptId && attempt.assistance === "independent" ? "unknown" : attempt.assistance;
27092
- if (assistance !== "independent" || !independentSeen.has(episodeQuestion)) summary.confirmed[assistance]++;
27093
- if (assistance === "independent") independentSeen.add(episodeQuestion);
27094
- if (q.result === "correct") summary.confirmed.correct++;
27095
- else summary.confirmed.needsCorrection++;
27096
- if (attempt.previousAttemptId && q.result === "correct" && !summary.cues.includes("improving on this item")) summary.cues.push("improving on this item");
27097
- }
27098
- }
27099
- for (const summary of summaries.values()) {
27100
- if (!summary.confirmed.independent) summary.cues.push("needs a fresh independent check");
27101
- if (summary.confirmed.needsCorrection) summary.cues.push("needs practice");
27102
- if (!summary.confirmed.correct && !summary.confirmed.needsCorrection) summary.cues.push("not enough evidence");
27538
+ evidence.push({
27539
+ attempt,
27540
+ revisionId: String(effective.id),
27541
+ assessment,
27542
+ confirmed: Boolean(confirmed)
27543
+ });
27103
27544
  }
27104
27545
  return {
27105
27546
  algorithmVersion: "workbook-descriptive-v1",
27106
- concepts: [...summaries.values()]
27547
+ concepts: summarizeWorkbookEvidence(evidence)
27107
27548
  };
27108
27549
  }
27109
27550
  };
@@ -27600,6 +28041,6 @@ var WorkbookPhotoService = class WorkbookPhotoService extends WorkbookService {
27600
28041
  }
27601
28042
  };
27602
28043
  //#endregion
27603
- export { EnrollmentCollection as $, WorkbookFeedbackRelease as A, Exercise as At, FamilyIntakeAsset as B, WorkbookAssessmentRevision as C, Curriculum as Ct, WorkbookCollection as D, AuthoredCourse as Dt, WorkbookAttemptCollection as E, LessonProgress as Et, WorkbookAssessmentValidationError as F, Worksheet as Ft, familyIntakeScopeId as G, FamilyIntakeCollection as H, aggregateWorkbookQuestions as I, AttestationCollection as J, CatalogAuthorizationError as K, validateWorkbookAssessment as L, WorkbookWorkFiling as M, Lesson as Mt, WorkbookWorkFilingCollection as N, Reading as Nt, WorkbookFeedbackDelivery as O, Course as Ot, WORKBOOK_MATH_VOCABULARY as P, Video as Pt, CurriculumCollection as Q, workbookAggregateFeedback as R, WorkbookActivityCollection as S, LessonAsset as St, WorkbookAttempt as T, Learner as Tt, FamilyIntakeDecision as U, FamilyIntakeAssetCollection as V, FamilyIntakeDecisionCollection as W, CourseCollection as X, AuthoredCourseCollection as Y, CourseSectionCollection as Z, WorkbookWorkflowRequiredError as _, Grade as _t, WorkbookContextRevisionCollection as a, LessonAssetCollection as at, Workbook as b, SubmissionAsset as bt, WorkbookPhotoDecision as c, ReadingCollection as ct, WorkbookPhotoIntakeCollection as d, SubmissionCollection as dt, ExerciseCollection as et, validateWorkbookPhotoIdentification as f, SubmissionIntakeBindingCollection as ft, SubmissionStorageCleanupError as g, ExternalCompletion as gt, SubmissionService as h, Attestation as ht, WorkbookContextRevision as i, LearnerCollection as it, WorkbookFeedbackReleaseCollection as j, ExternalCourse as jt, WorkbookFeedbackDeliveryCollection as k, CourseSection as kt, WorkbookPhotoDecisionCollection as l, ScannedSubmissionCollection as lt, normalizeWorkbookAnswerRegions as m, WorksheetCollection as mt, preservePhotoProposal as n, ExternalCourseCollection as nt, WorkbookPhotoAsset as o, LessonCollection as ot, normalizeWorkbookObservedLayout as p, VideoCollection as pt, CatalogService as q, WorkbookService as r, GradeCollection as rt, WorkbookPhotoAssetCollection as s, LessonProgressCollection as st, WorkbookPhotoService as t, ExternalCompletionCollection as tt, WorkbookPhotoIntake as u, SubmissionAssetCollection as ut, LearnerAuthorizationError as v, ScannedSubmission as vt, WorkbookAssessmentRevisionCollection as w, Enrollment as wt, WorkbookActivity as x, SubmissionIntakeBinding as xt, LearnerService as y, Submission as yt, FamilyIntake as z };
28044
+ export { FamilyIntakeDecision as $, WorkbookFeedbackDelivery as A, SubmissionAsset as At, validateWorkbookAssessment as B, Exercise as Bt, WorkbookActivity as C, VideoCollection as Ct, WorkbookAttempt as D, Grade as Dt, WorkbookAssessmentRevisionCollection as E, ExternalCompletion as Et, WorkbookWorkFilingCollection as F, Learner as Ft, GroupSharingConflictError as G, Worksheet as Gt, evaluateGroupSharingPolicy as H, Lesson as Ht, WORKBOOK_MATH_VOCABULARY as I, LessonProgress as It, validateGroupSharingPolicy as J, GroupSharingPolicyValidationError as K, WorkbookAssessmentValidationError as L, AuthoredCourse as Lt, WorkbookFeedbackRelease as M, LessonAsset as Mt, WorkbookFeedbackReleaseCollection as N, Curriculum as Nt, WorkbookAttemptCollection as O, ScannedSubmission as Ot, WorkbookWorkFiling as P, Enrollment as Pt, FamilyIntakeCollection as Q, WorkbookCompletionConflictError as R, Course as Rt, Workbook as S, SubmissionIntakeBindingCollection as St, WorkbookAssessmentRevision as T, Attestation as Tt, summarizeWorkbookEvidence as U, Reading as Ut, workbookAggregateFeedback as V, ExternalCourse as Vt, GroupSharingAuthorizationError as W, Video as Wt, FamilyIntakeAsset as X, FamilyIntake as Y, FamilyIntakeAssetCollection as Z, WorkbookWorkflowRequiredError as _, LessonProgressCollection as _t, WorkbookContextRevisionCollection as a, AuthoredCourseCollection as at, WorkbookCompletionDecision as b, SubmissionAssetCollection as bt, WorkbookPhotoDecision as c, CurriculumCollection as ct, WorkbookPhotoIntakeCollection as d, ExternalCompletionCollection as dt, FamilyIntakeDecisionCollection as et, validateWorkbookPhotoIdentification as f, ExternalCourseCollection as ft, SubmissionStorageCleanupError as g, LessonCollection as gt, SubmissionService as h, LessonAssetCollection as ht, WorkbookContextRevision as i, AttestationCollection as it, WorkbookFeedbackDeliveryCollection as j, SubmissionIntakeBinding as jt, WorkbookCollection as k, Submission as kt, WorkbookPhotoDecisionCollection as l, EnrollmentCollection as lt, normalizeWorkbookAnswerRegions as m, LearnerCollection as mt, preservePhotoProposal as n, CatalogAuthorizationError as nt, WorkbookPhotoAsset as o, CourseCollection as ot, normalizeWorkbookObservedLayout as p, GradeCollection as pt, canonicalGroupSharingPolicy as q, WorkbookService as r, CatalogService as rt, WorkbookPhotoAssetCollection as s, CourseSectionCollection as st, WorkbookPhotoService as t, familyIntakeScopeId as tt, WorkbookPhotoIntake as u, ExerciseCollection as ut, LearnerAuthorizationError as v, ReadingCollection as vt, WorkbookActivityCollection as w, WorksheetCollection as wt, WorkbookCompletionDecisionCollection as x, SubmissionCollection as xt, LearnerService as y, ScannedSubmissionCollection as yt, aggregateWorkbookQuestions as z, CourseSection as zt };
27604
28045
 
27605
- //# sourceMappingURL=workbook-photo-service-DCmqeWLd.js.map
28046
+ //# sourceMappingURL=workbook-photo-service-C6mbUUp6.js.map