@happyvertical/smrt-education 0.57.33 → 0.57.34
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.
- package/AGENTS.md +8 -1
- package/README.md +117 -13
- package/agents/catalog-sync.md +46 -0
- package/agents/photo-intake.md +249 -0
- package/agents/workbook-service.md +355 -0
- package/agents/workbook-test-matrix.md +74 -0
- package/dist/catalog-acquisition-service.d.ts.map +1 -1
- package/dist/catalog-capability.d.ts +21 -0
- package/dist/catalog-capability.d.ts.map +1 -0
- package/dist/catalog-contracts.d.ts +40 -0
- package/dist/catalog-contracts.d.ts.map +1 -1
- package/dist/catalog-database-sync-store.d.ts.map +1 -1
- package/dist/catalog-models.d.ts +5 -1
- package/dist/catalog-models.d.ts.map +1 -1
- package/dist/catalog-read-service.d.ts +2 -1
- package/dist/catalog-read-service.d.ts.map +1 -1
- package/dist/catalog-service.d.ts +5 -0
- package/dist/catalog-service.d.ts.map +1 -1
- package/dist/chunks/workbook-photo-service-1wcnf-Xf.js +27359 -0
- package/dist/chunks/workbook-photo-service-1wcnf-Xf.js.map +1 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +339 -24864
- package/dist/index.js.map +1 -1
- package/dist/ingestion.d.ts +37 -0
- package/dist/ingestion.d.ts.map +1 -1
- package/dist/ingestion.js +829 -20
- package/dist/ingestion.js.map +1 -1
- package/dist/learner-service.d.ts +15 -0
- package/dist/learner-service.d.ts.map +1 -1
- package/dist/manifest.json +4735 -6
- package/dist/smrt-knowledge.json +1760 -26
- package/dist/sources/public-illustrative-mathematics-adapter.d.ts.map +1 -1
- package/dist/sources/public-illustrative-mathematics-parser.d.ts.map +1 -1
- package/dist/submission-service.d.ts +38 -11
- package/dist/submission-service.d.ts.map +1 -1
- package/dist/workbook-answer-regions.d.ts +11 -0
- package/dist/workbook-answer-regions.d.ts.map +1 -0
- package/dist/workbook-contracts.d.ts +224 -0
- package/dist/workbook-contracts.d.ts.map +1 -0
- package/dist/workbook-layout.d.ts +13 -0
- package/dist/workbook-layout.d.ts.map +1 -0
- package/dist/workbook-models.d.ts +129 -0
- package/dist/workbook-models.d.ts.map +1 -0
- package/dist/workbook-photo-contracts.d.ts +56 -0
- package/dist/workbook-photo-contracts.d.ts.map +1 -0
- package/dist/workbook-photo-ingestion.d.ts +70 -0
- package/dist/workbook-photo-ingestion.d.ts.map +1 -0
- package/dist/workbook-photo-models.d.ts +63 -0
- package/dist/workbook-photo-models.d.ts.map +1 -0
- package/dist/workbook-photo-service.d.ts +72 -0
- package/dist/workbook-photo-service.d.ts.map +1 -0
- package/dist/workbook-service.d.ts +158 -0
- package/dist/workbook-service.d.ts.map +1 -0
- package/package.json +8 -8
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-education",
|
|
6
|
-
"packageVersion": "0.57.
|
|
6
|
+
"packageVersion": "0.57.34",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "fab474c85273e7d0af8fe3160f1145e753b8a7a38ade60a3d79da8b6bc5886ca",
|
|
11
|
+
"packageJson": "721869383199a708785024d36689b876f97148e902395644bf5192cceb3a6f82",
|
|
12
|
+
"agents": "bf8b00b31d2dfc9394ab1766421bbeb2dcc5326db3ea60ecc9922b6800f21f48",
|
|
13
13
|
"moduleDoc:agents/learner-service.md": "7da70d214547eb78fbf058e997599cc80ab86f5bb55748bd158a9a4b30070d3b",
|
|
14
14
|
"moduleDoc:agents/submission-service.md": "8e63b83ac84628d5e5ddacbb6a47077c0e65eee3da87518d9bf6e30fb8348336",
|
|
15
|
+
"moduleDoc:agents/workbook-service.md": "3ca3fb60d39daf76b94ec668fc19dc67aa7e090b7f44412bed5c9d92e8efaf5a",
|
|
16
|
+
"moduleDoc:agents/photo-intake.md": "3b7057201ee30b2361aad23ea52e8a55c8ed5354e7a7e4a8193b7de17ca2f0db",
|
|
15
17
|
"moduleDoc:agents/source-import.md": "30ee823589d655442bcf1abcd86a2132172065b4bc5e3c4d89b1e8b7afcc66bc"
|
|
16
18
|
},
|
|
17
19
|
"exports": [
|
|
@@ -57,7 +59,7 @@
|
|
|
57
59
|
"smrt"
|
|
58
60
|
],
|
|
59
61
|
"risks": [
|
|
60
|
-
"cross-package-refs:
|
|
62
|
+
"cross-package-refs:6",
|
|
61
63
|
"sensitive-fields-excluded",
|
|
62
64
|
"sti-inheritance"
|
|
63
65
|
],
|
|
@@ -274,6 +276,12 @@
|
|
|
274
276
|
"required": false,
|
|
275
277
|
"columnType": "TEXT"
|
|
276
278
|
},
|
|
279
|
+
{
|
|
280
|
+
"name": "representation",
|
|
281
|
+
"type": "text",
|
|
282
|
+
"required": false,
|
|
283
|
+
"columnType": "TEXT"
|
|
284
|
+
},
|
|
277
285
|
{
|
|
278
286
|
"name": "licenseEvidence",
|
|
279
287
|
"type": "text",
|
|
@@ -308,8 +316,40 @@
|
|
|
308
316
|
}
|
|
309
317
|
],
|
|
310
318
|
"relationships": [],
|
|
311
|
-
"methods": [
|
|
319
|
+
"methods": [
|
|
320
|
+
"getRepresentation",
|
|
321
|
+
"setRepresentation"
|
|
322
|
+
],
|
|
323
|
+
"methodSignatures": [
|
|
324
|
+
{
|
|
325
|
+
"name": "getRepresentation",
|
|
326
|
+
"returns": "CatalogResourceRepresentation | undefined"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"name": "setRepresentation",
|
|
330
|
+
"params": [
|
|
331
|
+
"value: CatalogResourceRepresentation | undefined"
|
|
332
|
+
],
|
|
333
|
+
"returns": "void"
|
|
334
|
+
}
|
|
335
|
+
],
|
|
312
336
|
"surfaces": [],
|
|
337
|
+
"withheldSurfaces": [
|
|
338
|
+
{
|
|
339
|
+
"kind": "api",
|
|
340
|
+
"operation": "getRepresentation",
|
|
341
|
+
"code": "api-disabled",
|
|
342
|
+
"reason": "api is disabled",
|
|
343
|
+
"objectName": "@happyvertical/smrt-education:CatalogResource"
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"kind": "api",
|
|
347
|
+
"operation": "setRepresentation",
|
|
348
|
+
"code": "api-disabled",
|
|
349
|
+
"reason": "api is disabled",
|
|
350
|
+
"objectName": "@happyvertical/smrt-education:CatalogResource"
|
|
351
|
+
}
|
|
352
|
+
],
|
|
313
353
|
"relationshipFeatures": [
|
|
314
354
|
"uuidColumns"
|
|
315
355
|
],
|
|
@@ -4156,29 +4196,1723 @@
|
|
|
4156
4196
|
],
|
|
4157
4197
|
"tags": [],
|
|
4158
4198
|
"risks": []
|
|
4159
|
-
}
|
|
4160
|
-
],
|
|
4161
|
-
"surfaces": [],
|
|
4162
|
-
"prompts": [],
|
|
4163
|
-
"relationshipsV2": {
|
|
4164
|
-
"foreignKeyFields": 36,
|
|
4165
|
-
"crossPackageRefFields": 5,
|
|
4166
|
-
"junctionCollections": 2,
|
|
4167
|
-
"hierarchicalObjects": 0,
|
|
4168
|
-
"polymorphicAssociations": 0,
|
|
4169
|
-
"uuidColumns": 184
|
|
4170
|
-
},
|
|
4171
|
-
"agentDoc": "# smrt-education\n\nCatalog objects are optional-tenant scoped and have no generated REST, CLI, MCP,\nor AI surface. Applications use `CatalogService`, supplying their actor-bound\nauthorization decision and the trusted `TenantCollection.withTenantCatalogReadScope`\nadapter from smrt-users. That read scope may widen only an allowlisted catalog\ncollection's `list` to self, ancestors, and globals; it never authorizes a get,\nwrite, raw query, sibling, or descendant read.\n\n`personal-use-copy` courses and all `answer-key` lesson assets are private\nfamily material. Do not publish them, promote them, or expose them through a\nlearner-facing result. `LessonAsset` is the sole owner join for lesson files;\ndo not add asset-id fields to lesson subclasses.\n\nLearner objects require exact tenant ownership and close generated surfaces.\n`LearnerService` owns actor/resource authorization and transactional planning and\nstart-progress; see [learner contract](agents/learner-service.md). Guardian\nrelationships require a host decision about their type and effective terms.\nNever expose direct collection writes as learner handlers, reset progress on a\nretry, or treat an unscored nullable decimal as zero.\n\nSubmission capture, guardian grading and private PDF packet splitting use\n`SubmissionService`; see [submission contract](agents/submission-service.md).\nKeep asset metadata on the same transaction and compensate only newly planned\nfiles after rollback; never expose source URIs or accept learner grade patches.\n\nValidation: `pnpm --filter @happyvertical/smrt-education test`,\n`test:postgres`, `typecheck`, and `build`.\n\nSource import: [host boundary and transaction contract](agents/source-import.md).\n`SourceImportService` requires host opt-in, matching active actor/tenant and\nexplicit licensing authorization; never add a system/global write bypass.\n",
|
|
4172
|
-
"moduleDocs": [
|
|
4199
|
+
},
|
|
4173
4200
|
{
|
|
4174
|
-
"
|
|
4175
|
-
"
|
|
4176
|
-
"
|
|
4201
|
+
"name": "Workbook",
|
|
4202
|
+
"qualifiedName": "@happyvertical/smrt-education:Workbook",
|
|
4203
|
+
"displayLabelField": "title",
|
|
4204
|
+
"collection": "workbooks",
|
|
4205
|
+
"description": "Education-owned private workbook record. Mutations go through WorkbookService.",
|
|
4206
|
+
"tableName": "workbooks",
|
|
4207
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4208
|
+
"extends": "SmrtObject",
|
|
4209
|
+
"fields": [
|
|
4210
|
+
{
|
|
4211
|
+
"name": "tenantId",
|
|
4212
|
+
"type": "text",
|
|
4213
|
+
"required": true,
|
|
4214
|
+
"columnType": "UUID"
|
|
4215
|
+
},
|
|
4216
|
+
{
|
|
4217
|
+
"name": "learnerId",
|
|
4218
|
+
"type": "foreignKey",
|
|
4219
|
+
"required": true,
|
|
4220
|
+
"related": "Learner",
|
|
4221
|
+
"columnType": "UUID"
|
|
4222
|
+
},
|
|
4223
|
+
{
|
|
4224
|
+
"name": "workbookKey",
|
|
4225
|
+
"type": "text",
|
|
4226
|
+
"required": false,
|
|
4227
|
+
"columnType": "TEXT"
|
|
4228
|
+
},
|
|
4229
|
+
{
|
|
4230
|
+
"name": "title",
|
|
4231
|
+
"type": "text",
|
|
4232
|
+
"required": false,
|
|
4233
|
+
"columnType": "TEXT"
|
|
4234
|
+
},
|
|
4235
|
+
{
|
|
4236
|
+
"name": "edition",
|
|
4237
|
+
"type": "text",
|
|
4238
|
+
"required": false,
|
|
4239
|
+
"columnType": "TEXT"
|
|
4240
|
+
},
|
|
4241
|
+
{
|
|
4242
|
+
"name": "reviewRequired",
|
|
4243
|
+
"type": "boolean",
|
|
4244
|
+
"required": true,
|
|
4245
|
+
"columnType": "BOOLEAN",
|
|
4246
|
+
"default": true
|
|
4247
|
+
},
|
|
4248
|
+
{
|
|
4249
|
+
"name": "policyVersion",
|
|
4250
|
+
"type": "integer",
|
|
4251
|
+
"required": true,
|
|
4252
|
+
"columnType": "INTEGER",
|
|
4253
|
+
"default": 1
|
|
4254
|
+
}
|
|
4255
|
+
],
|
|
4256
|
+
"relationships": [
|
|
4257
|
+
{
|
|
4258
|
+
"name": "learnerId",
|
|
4259
|
+
"type": "foreignKey",
|
|
4260
|
+
"required": true,
|
|
4261
|
+
"related": "Learner",
|
|
4262
|
+
"columnType": "UUID"
|
|
4263
|
+
}
|
|
4264
|
+
],
|
|
4265
|
+
"methods": [
|
|
4266
|
+
"save"
|
|
4267
|
+
],
|
|
4268
|
+
"methodSignatures": [
|
|
4269
|
+
{
|
|
4270
|
+
"name": "save",
|
|
4271
|
+
"async": true,
|
|
4272
|
+
"returns": "Promise<this>"
|
|
4273
|
+
}
|
|
4274
|
+
],
|
|
4275
|
+
"tenant": {
|
|
4276
|
+
"scoped": true,
|
|
4277
|
+
"mode": "required",
|
|
4278
|
+
"field": "tenantId"
|
|
4279
|
+
},
|
|
4280
|
+
"conflictColumns": [
|
|
4281
|
+
"tenant_id",
|
|
4282
|
+
"learner_id",
|
|
4283
|
+
"workbook_key"
|
|
4284
|
+
],
|
|
4285
|
+
"surfaces": [],
|
|
4286
|
+
"withheldSurfaces": [
|
|
4287
|
+
{
|
|
4288
|
+
"kind": "api",
|
|
4289
|
+
"operation": "save",
|
|
4290
|
+
"code": "api-disabled",
|
|
4291
|
+
"reason": "api is disabled",
|
|
4292
|
+
"objectName": "@happyvertical/smrt-education:Workbook"
|
|
4293
|
+
}
|
|
4294
|
+
],
|
|
4295
|
+
"relationshipFeatures": [
|
|
4296
|
+
"foreignKey",
|
|
4297
|
+
"uuidColumns"
|
|
4298
|
+
],
|
|
4299
|
+
"tags": [],
|
|
4300
|
+
"risks": []
|
|
4177
4301
|
},
|
|
4178
4302
|
{
|
|
4179
|
-
"
|
|
4180
|
-
"
|
|
4181
|
-
"
|
|
4303
|
+
"name": "WorkbookCollection",
|
|
4304
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookCollection",
|
|
4305
|
+
"collection": "workbooks",
|
|
4306
|
+
"tableName": "workbooks",
|
|
4307
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4308
|
+
"extends": "SmrtCollection",
|
|
4309
|
+
"fields": [],
|
|
4310
|
+
"relationships": [],
|
|
4311
|
+
"methods": [],
|
|
4312
|
+
"surfaces": [],
|
|
4313
|
+
"relationshipFeatures": [
|
|
4314
|
+
"uuidColumns"
|
|
4315
|
+
],
|
|
4316
|
+
"tags": [],
|
|
4317
|
+
"risks": []
|
|
4318
|
+
},
|
|
4319
|
+
{
|
|
4320
|
+
"name": "WorkbookActivity",
|
|
4321
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookActivity",
|
|
4322
|
+
"collection": "workbookactivities",
|
|
4323
|
+
"description": "Education-owned private workbook record. Mutations go through WorkbookService.",
|
|
4324
|
+
"tableName": "workbook_activities",
|
|
4325
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4326
|
+
"extends": "SmrtObject",
|
|
4327
|
+
"fields": [
|
|
4328
|
+
{
|
|
4329
|
+
"name": "tenantId",
|
|
4330
|
+
"type": "text",
|
|
4331
|
+
"required": true,
|
|
4332
|
+
"columnType": "UUID"
|
|
4333
|
+
},
|
|
4334
|
+
{
|
|
4335
|
+
"name": "learnerId",
|
|
4336
|
+
"type": "foreignKey",
|
|
4337
|
+
"required": true,
|
|
4338
|
+
"related": "Learner",
|
|
4339
|
+
"columnType": "UUID"
|
|
4340
|
+
},
|
|
4341
|
+
{
|
|
4342
|
+
"name": "workbookId",
|
|
4343
|
+
"type": "foreignKey",
|
|
4344
|
+
"required": false,
|
|
4345
|
+
"related": "Workbook",
|
|
4346
|
+
"columnType": "UUID"
|
|
4347
|
+
},
|
|
4348
|
+
{
|
|
4349
|
+
"name": "captureKind",
|
|
4350
|
+
"type": "text",
|
|
4351
|
+
"required": true,
|
|
4352
|
+
"columnType": "TEXT",
|
|
4353
|
+
"default": "identified"
|
|
4354
|
+
},
|
|
4355
|
+
{
|
|
4356
|
+
"name": "photoIntakeId",
|
|
4357
|
+
"type": "foreignKey",
|
|
4358
|
+
"required": false,
|
|
4359
|
+
"related": "WorkbookPhotoIntake",
|
|
4360
|
+
"columnType": "UUID"
|
|
4361
|
+
},
|
|
4362
|
+
{
|
|
4363
|
+
"name": "enrollmentId",
|
|
4364
|
+
"type": "foreignKey",
|
|
4365
|
+
"required": true,
|
|
4366
|
+
"related": "Enrollment",
|
|
4367
|
+
"columnType": "UUID"
|
|
4368
|
+
},
|
|
4369
|
+
{
|
|
4370
|
+
"name": "lessonId",
|
|
4371
|
+
"type": "foreignKey",
|
|
4372
|
+
"required": true,
|
|
4373
|
+
"related": "Lesson",
|
|
4374
|
+
"columnType": "UUID"
|
|
4375
|
+
},
|
|
4376
|
+
{
|
|
4377
|
+
"name": "pageKey",
|
|
4378
|
+
"type": "text",
|
|
4379
|
+
"required": false,
|
|
4380
|
+
"columnType": "TEXT"
|
|
4381
|
+
},
|
|
4382
|
+
{
|
|
4383
|
+
"name": "pageLabel",
|
|
4384
|
+
"type": "text",
|
|
4385
|
+
"required": false,
|
|
4386
|
+
"columnType": "TEXT"
|
|
4387
|
+
}
|
|
4388
|
+
],
|
|
4389
|
+
"relationships": [
|
|
4390
|
+
{
|
|
4391
|
+
"name": "learnerId",
|
|
4392
|
+
"type": "foreignKey",
|
|
4393
|
+
"required": true,
|
|
4394
|
+
"related": "Learner",
|
|
4395
|
+
"columnType": "UUID"
|
|
4396
|
+
},
|
|
4397
|
+
{
|
|
4398
|
+
"name": "workbookId",
|
|
4399
|
+
"type": "foreignKey",
|
|
4400
|
+
"required": false,
|
|
4401
|
+
"related": "Workbook",
|
|
4402
|
+
"columnType": "UUID"
|
|
4403
|
+
},
|
|
4404
|
+
{
|
|
4405
|
+
"name": "photoIntakeId",
|
|
4406
|
+
"type": "foreignKey",
|
|
4407
|
+
"required": false,
|
|
4408
|
+
"related": "WorkbookPhotoIntake",
|
|
4409
|
+
"columnType": "UUID"
|
|
4410
|
+
},
|
|
4411
|
+
{
|
|
4412
|
+
"name": "enrollmentId",
|
|
4413
|
+
"type": "foreignKey",
|
|
4414
|
+
"required": true,
|
|
4415
|
+
"related": "Enrollment",
|
|
4416
|
+
"columnType": "UUID"
|
|
4417
|
+
},
|
|
4418
|
+
{
|
|
4419
|
+
"name": "lessonId",
|
|
4420
|
+
"type": "foreignKey",
|
|
4421
|
+
"required": true,
|
|
4422
|
+
"related": "Lesson",
|
|
4423
|
+
"columnType": "UUID"
|
|
4424
|
+
}
|
|
4425
|
+
],
|
|
4426
|
+
"methods": [
|
|
4427
|
+
"delete",
|
|
4428
|
+
"save"
|
|
4429
|
+
],
|
|
4430
|
+
"methodSignatures": [
|
|
4431
|
+
{
|
|
4432
|
+
"name": "delete",
|
|
4433
|
+
"async": true,
|
|
4434
|
+
"returns": "Promise<void>"
|
|
4435
|
+
},
|
|
4436
|
+
{
|
|
4437
|
+
"name": "save",
|
|
4438
|
+
"async": true,
|
|
4439
|
+
"returns": "Promise<this>"
|
|
4440
|
+
}
|
|
4441
|
+
],
|
|
4442
|
+
"tenant": {
|
|
4443
|
+
"scoped": true,
|
|
4444
|
+
"mode": "required",
|
|
4445
|
+
"field": "tenantId"
|
|
4446
|
+
},
|
|
4447
|
+
"conflictColumns": [
|
|
4448
|
+
"tenant_id",
|
|
4449
|
+
"workbook_id",
|
|
4450
|
+
"page_key"
|
|
4451
|
+
],
|
|
4452
|
+
"surfaces": [],
|
|
4453
|
+
"withheldSurfaces": [
|
|
4454
|
+
{
|
|
4455
|
+
"kind": "api",
|
|
4456
|
+
"operation": "delete",
|
|
4457
|
+
"code": "api-disabled",
|
|
4458
|
+
"reason": "api is disabled",
|
|
4459
|
+
"objectName": "@happyvertical/smrt-education:WorkbookActivity"
|
|
4460
|
+
},
|
|
4461
|
+
{
|
|
4462
|
+
"kind": "api",
|
|
4463
|
+
"operation": "save",
|
|
4464
|
+
"code": "api-disabled",
|
|
4465
|
+
"reason": "api is disabled",
|
|
4466
|
+
"objectName": "@happyvertical/smrt-education:WorkbookActivity"
|
|
4467
|
+
}
|
|
4468
|
+
],
|
|
4469
|
+
"relationshipFeatures": [
|
|
4470
|
+
"foreignKey",
|
|
4471
|
+
"uuidColumns"
|
|
4472
|
+
],
|
|
4473
|
+
"tags": [],
|
|
4474
|
+
"risks": []
|
|
4475
|
+
},
|
|
4476
|
+
{
|
|
4477
|
+
"name": "WorkbookActivityCollection",
|
|
4478
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookActivityCollection",
|
|
4479
|
+
"collection": "workbookactivities",
|
|
4480
|
+
"tableName": "workbook_activities",
|
|
4481
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4482
|
+
"extends": "SmrtCollection",
|
|
4483
|
+
"fields": [],
|
|
4484
|
+
"relationships": [],
|
|
4485
|
+
"methods": [],
|
|
4486
|
+
"surfaces": [],
|
|
4487
|
+
"relationshipFeatures": [
|
|
4488
|
+
"uuidColumns"
|
|
4489
|
+
],
|
|
4490
|
+
"tags": [],
|
|
4491
|
+
"risks": []
|
|
4492
|
+
},
|
|
4493
|
+
{
|
|
4494
|
+
"name": "WorkbookWorkFiling",
|
|
4495
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookWorkFiling",
|
|
4496
|
+
"collection": "workbookworkfilings",
|
|
4497
|
+
"description": "Append-only association; the source work unit and its routing never move.",
|
|
4498
|
+
"tableName": "workbook_work_filings",
|
|
4499
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4500
|
+
"extends": "SmrtObject",
|
|
4501
|
+
"fields": [
|
|
4502
|
+
{
|
|
4503
|
+
"name": "tenantId",
|
|
4504
|
+
"type": "text",
|
|
4505
|
+
"required": true,
|
|
4506
|
+
"columnType": "UUID"
|
|
4507
|
+
},
|
|
4508
|
+
{
|
|
4509
|
+
"name": "learnerId",
|
|
4510
|
+
"type": "foreignKey",
|
|
4511
|
+
"required": true,
|
|
4512
|
+
"related": "Learner",
|
|
4513
|
+
"columnType": "UUID"
|
|
4514
|
+
},
|
|
4515
|
+
{
|
|
4516
|
+
"name": "sourceActivityId",
|
|
4517
|
+
"type": "foreignKey",
|
|
4518
|
+
"required": true,
|
|
4519
|
+
"related": "WorkbookActivity",
|
|
4520
|
+
"columnType": "UUID"
|
|
4521
|
+
},
|
|
4522
|
+
{
|
|
4523
|
+
"name": "workbookId",
|
|
4524
|
+
"type": "foreignKey",
|
|
4525
|
+
"required": true,
|
|
4526
|
+
"related": "Workbook",
|
|
4527
|
+
"columnType": "UUID"
|
|
4528
|
+
},
|
|
4529
|
+
{
|
|
4530
|
+
"name": "canonicalActivityId",
|
|
4531
|
+
"type": "foreignKey",
|
|
4532
|
+
"required": false,
|
|
4533
|
+
"related": "WorkbookActivity",
|
|
4534
|
+
"columnType": "UUID"
|
|
4535
|
+
},
|
|
4536
|
+
{
|
|
4537
|
+
"name": "previousRevisionId",
|
|
4538
|
+
"type": "foreignKey",
|
|
4539
|
+
"required": false,
|
|
4540
|
+
"related": "WorkbookWorkFiling",
|
|
4541
|
+
"columnType": "UUID"
|
|
4542
|
+
},
|
|
4543
|
+
{
|
|
4544
|
+
"name": "actorUserId",
|
|
4545
|
+
"type": "text",
|
|
4546
|
+
"required": false,
|
|
4547
|
+
"columnType": "TEXT"
|
|
4548
|
+
},
|
|
4549
|
+
{
|
|
4550
|
+
"name": "requestKey",
|
|
4551
|
+
"type": "text",
|
|
4552
|
+
"required": false,
|
|
4553
|
+
"columnType": "TEXT"
|
|
4554
|
+
},
|
|
4555
|
+
{
|
|
4556
|
+
"name": "reason",
|
|
4557
|
+
"type": "text",
|
|
4558
|
+
"required": false,
|
|
4559
|
+
"columnType": "TEXT"
|
|
4560
|
+
},
|
|
4561
|
+
{
|
|
4562
|
+
"name": "digest",
|
|
4563
|
+
"type": "text",
|
|
4564
|
+
"required": false,
|
|
4565
|
+
"columnType": "TEXT"
|
|
4566
|
+
}
|
|
4567
|
+
],
|
|
4568
|
+
"relationships": [
|
|
4569
|
+
{
|
|
4570
|
+
"name": "learnerId",
|
|
4571
|
+
"type": "foreignKey",
|
|
4572
|
+
"required": true,
|
|
4573
|
+
"related": "Learner",
|
|
4574
|
+
"columnType": "UUID"
|
|
4575
|
+
},
|
|
4576
|
+
{
|
|
4577
|
+
"name": "sourceActivityId",
|
|
4578
|
+
"type": "foreignKey",
|
|
4579
|
+
"required": true,
|
|
4580
|
+
"related": "WorkbookActivity",
|
|
4581
|
+
"columnType": "UUID"
|
|
4582
|
+
},
|
|
4583
|
+
{
|
|
4584
|
+
"name": "workbookId",
|
|
4585
|
+
"type": "foreignKey",
|
|
4586
|
+
"required": true,
|
|
4587
|
+
"related": "Workbook",
|
|
4588
|
+
"columnType": "UUID"
|
|
4589
|
+
},
|
|
4590
|
+
{
|
|
4591
|
+
"name": "canonicalActivityId",
|
|
4592
|
+
"type": "foreignKey",
|
|
4593
|
+
"required": false,
|
|
4594
|
+
"related": "WorkbookActivity",
|
|
4595
|
+
"columnType": "UUID"
|
|
4596
|
+
},
|
|
4597
|
+
{
|
|
4598
|
+
"name": "previousRevisionId",
|
|
4599
|
+
"type": "foreignKey",
|
|
4600
|
+
"required": false,
|
|
4601
|
+
"related": "WorkbookWorkFiling",
|
|
4602
|
+
"columnType": "UUID"
|
|
4603
|
+
}
|
|
4604
|
+
],
|
|
4605
|
+
"methods": [
|
|
4606
|
+
"delete",
|
|
4607
|
+
"save"
|
|
4608
|
+
],
|
|
4609
|
+
"methodSignatures": [
|
|
4610
|
+
{
|
|
4611
|
+
"name": "delete",
|
|
4612
|
+
"async": true,
|
|
4613
|
+
"returns": "Promise<void>"
|
|
4614
|
+
},
|
|
4615
|
+
{
|
|
4616
|
+
"name": "save",
|
|
4617
|
+
"async": true,
|
|
4618
|
+
"returns": "Promise<this>"
|
|
4619
|
+
}
|
|
4620
|
+
],
|
|
4621
|
+
"tenant": {
|
|
4622
|
+
"scoped": true,
|
|
4623
|
+
"mode": "required",
|
|
4624
|
+
"field": "tenantId"
|
|
4625
|
+
},
|
|
4626
|
+
"conflictColumns": [
|
|
4627
|
+
"tenant_id",
|
|
4628
|
+
"source_activity_id",
|
|
4629
|
+
"request_key"
|
|
4630
|
+
],
|
|
4631
|
+
"surfaces": [],
|
|
4632
|
+
"withheldSurfaces": [
|
|
4633
|
+
{
|
|
4634
|
+
"kind": "api",
|
|
4635
|
+
"operation": "delete",
|
|
4636
|
+
"code": "api-disabled",
|
|
4637
|
+
"reason": "api is disabled",
|
|
4638
|
+
"objectName": "@happyvertical/smrt-education:WorkbookWorkFiling"
|
|
4639
|
+
},
|
|
4640
|
+
{
|
|
4641
|
+
"kind": "api",
|
|
4642
|
+
"operation": "save",
|
|
4643
|
+
"code": "api-disabled",
|
|
4644
|
+
"reason": "api is disabled",
|
|
4645
|
+
"objectName": "@happyvertical/smrt-education:WorkbookWorkFiling"
|
|
4646
|
+
}
|
|
4647
|
+
],
|
|
4648
|
+
"relationshipFeatures": [
|
|
4649
|
+
"foreignKey",
|
|
4650
|
+
"uuidColumns"
|
|
4651
|
+
],
|
|
4652
|
+
"tags": [],
|
|
4653
|
+
"risks": []
|
|
4654
|
+
},
|
|
4655
|
+
{
|
|
4656
|
+
"name": "WorkbookWorkFilingCollection",
|
|
4657
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookWorkFilingCollection",
|
|
4658
|
+
"collection": "workbookworkfilings",
|
|
4659
|
+
"tableName": "workbook_work_filings",
|
|
4660
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4661
|
+
"extends": "SmrtCollection",
|
|
4662
|
+
"fields": [],
|
|
4663
|
+
"relationships": [],
|
|
4664
|
+
"methods": [],
|
|
4665
|
+
"surfaces": [],
|
|
4666
|
+
"relationshipFeatures": [
|
|
4667
|
+
"uuidColumns"
|
|
4668
|
+
],
|
|
4669
|
+
"tags": [],
|
|
4670
|
+
"risks": []
|
|
4671
|
+
},
|
|
4672
|
+
{
|
|
4673
|
+
"name": "WorkbookAttempt",
|
|
4674
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookAttempt",
|
|
4675
|
+
"collection": "workbookattempts",
|
|
4676
|
+
"description": "Education-owned private workbook record. Mutations go through WorkbookService.",
|
|
4677
|
+
"tableName": "workbook_attempts",
|
|
4678
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4679
|
+
"extends": "SmrtObject",
|
|
4680
|
+
"fields": [
|
|
4681
|
+
{
|
|
4682
|
+
"name": "tenantId",
|
|
4683
|
+
"type": "text",
|
|
4684
|
+
"required": true,
|
|
4685
|
+
"columnType": "UUID"
|
|
4686
|
+
},
|
|
4687
|
+
{
|
|
4688
|
+
"name": "learnerId",
|
|
4689
|
+
"type": "foreignKey",
|
|
4690
|
+
"required": true,
|
|
4691
|
+
"related": "Learner",
|
|
4692
|
+
"columnType": "UUID"
|
|
4693
|
+
},
|
|
4694
|
+
{
|
|
4695
|
+
"name": "activityId",
|
|
4696
|
+
"type": "foreignKey",
|
|
4697
|
+
"required": true,
|
|
4698
|
+
"related": "WorkbookActivity",
|
|
4699
|
+
"columnType": "UUID"
|
|
4700
|
+
},
|
|
4701
|
+
{
|
|
4702
|
+
"name": "submissionId",
|
|
4703
|
+
"type": "foreignKey",
|
|
4704
|
+
"required": true,
|
|
4705
|
+
"related": "Submission",
|
|
4706
|
+
"columnType": "UUID"
|
|
4707
|
+
},
|
|
4708
|
+
{
|
|
4709
|
+
"name": "previousAttemptId",
|
|
4710
|
+
"type": "foreignKey",
|
|
4711
|
+
"required": false,
|
|
4712
|
+
"related": "WorkbookAttempt",
|
|
4713
|
+
"columnType": "UUID"
|
|
4714
|
+
},
|
|
4715
|
+
{
|
|
4716
|
+
"name": "capturedAt",
|
|
4717
|
+
"type": "text",
|
|
4718
|
+
"required": false,
|
|
4719
|
+
"columnType": "TEXT"
|
|
4720
|
+
},
|
|
4721
|
+
{
|
|
4722
|
+
"name": "episodeId",
|
|
4723
|
+
"type": "text",
|
|
4724
|
+
"required": false,
|
|
4725
|
+
"columnType": "TEXT"
|
|
4726
|
+
},
|
|
4727
|
+
{
|
|
4728
|
+
"name": "reviewRequired",
|
|
4729
|
+
"type": "boolean",
|
|
4730
|
+
"required": true,
|
|
4731
|
+
"columnType": "BOOLEAN",
|
|
4732
|
+
"default": true
|
|
4733
|
+
},
|
|
4734
|
+
{
|
|
4735
|
+
"name": "policyVersion",
|
|
4736
|
+
"type": "integer",
|
|
4737
|
+
"required": true,
|
|
4738
|
+
"columnType": "INTEGER",
|
|
4739
|
+
"default": 1
|
|
4740
|
+
},
|
|
4741
|
+
{
|
|
4742
|
+
"name": "assistance",
|
|
4743
|
+
"type": "text",
|
|
4744
|
+
"required": false,
|
|
4745
|
+
"columnType": "TEXT"
|
|
4746
|
+
},
|
|
4747
|
+
{
|
|
4748
|
+
"name": "actorUserId",
|
|
4749
|
+
"type": "text",
|
|
4750
|
+
"required": false,
|
|
4751
|
+
"columnType": "TEXT"
|
|
4752
|
+
},
|
|
4753
|
+
{
|
|
4754
|
+
"name": "sourceHashes",
|
|
4755
|
+
"type": "text",
|
|
4756
|
+
"required": false,
|
|
4757
|
+
"columnType": "TEXT"
|
|
4758
|
+
}
|
|
4759
|
+
],
|
|
4760
|
+
"relationships": [
|
|
4761
|
+
{
|
|
4762
|
+
"name": "learnerId",
|
|
4763
|
+
"type": "foreignKey",
|
|
4764
|
+
"required": true,
|
|
4765
|
+
"related": "Learner",
|
|
4766
|
+
"columnType": "UUID"
|
|
4767
|
+
},
|
|
4768
|
+
{
|
|
4769
|
+
"name": "activityId",
|
|
4770
|
+
"type": "foreignKey",
|
|
4771
|
+
"required": true,
|
|
4772
|
+
"related": "WorkbookActivity",
|
|
4773
|
+
"columnType": "UUID"
|
|
4774
|
+
},
|
|
4775
|
+
{
|
|
4776
|
+
"name": "submissionId",
|
|
4777
|
+
"type": "foreignKey",
|
|
4778
|
+
"required": true,
|
|
4779
|
+
"related": "Submission",
|
|
4780
|
+
"columnType": "UUID"
|
|
4781
|
+
},
|
|
4782
|
+
{
|
|
4783
|
+
"name": "previousAttemptId",
|
|
4784
|
+
"type": "foreignKey",
|
|
4785
|
+
"required": false,
|
|
4786
|
+
"related": "WorkbookAttempt",
|
|
4787
|
+
"columnType": "UUID"
|
|
4788
|
+
}
|
|
4789
|
+
],
|
|
4790
|
+
"methods": [
|
|
4791
|
+
"delete",
|
|
4792
|
+
"save"
|
|
4793
|
+
],
|
|
4794
|
+
"methodSignatures": [
|
|
4795
|
+
{
|
|
4796
|
+
"name": "delete",
|
|
4797
|
+
"async": true,
|
|
4798
|
+
"returns": "Promise<void>"
|
|
4799
|
+
},
|
|
4800
|
+
{
|
|
4801
|
+
"name": "save",
|
|
4802
|
+
"async": true,
|
|
4803
|
+
"returns": "Promise<this>"
|
|
4804
|
+
}
|
|
4805
|
+
],
|
|
4806
|
+
"tenant": {
|
|
4807
|
+
"scoped": true,
|
|
4808
|
+
"mode": "required",
|
|
4809
|
+
"field": "tenantId"
|
|
4810
|
+
},
|
|
4811
|
+
"conflictColumns": [
|
|
4812
|
+
"tenant_id",
|
|
4813
|
+
"submission_id"
|
|
4814
|
+
],
|
|
4815
|
+
"surfaces": [],
|
|
4816
|
+
"withheldSurfaces": [
|
|
4817
|
+
{
|
|
4818
|
+
"kind": "api",
|
|
4819
|
+
"operation": "delete",
|
|
4820
|
+
"code": "api-disabled",
|
|
4821
|
+
"reason": "api is disabled",
|
|
4822
|
+
"objectName": "@happyvertical/smrt-education:WorkbookAttempt"
|
|
4823
|
+
},
|
|
4824
|
+
{
|
|
4825
|
+
"kind": "api",
|
|
4826
|
+
"operation": "save",
|
|
4827
|
+
"code": "api-disabled",
|
|
4828
|
+
"reason": "api is disabled",
|
|
4829
|
+
"objectName": "@happyvertical/smrt-education:WorkbookAttempt"
|
|
4830
|
+
}
|
|
4831
|
+
],
|
|
4832
|
+
"relationshipFeatures": [
|
|
4833
|
+
"foreignKey",
|
|
4834
|
+
"uuidColumns"
|
|
4835
|
+
],
|
|
4836
|
+
"tags": [],
|
|
4837
|
+
"risks": []
|
|
4838
|
+
},
|
|
4839
|
+
{
|
|
4840
|
+
"name": "WorkbookAttemptCollection",
|
|
4841
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookAttemptCollection",
|
|
4842
|
+
"collection": "workbookattempts",
|
|
4843
|
+
"tableName": "workbook_attempts",
|
|
4844
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4845
|
+
"extends": "SmrtCollection",
|
|
4846
|
+
"fields": [],
|
|
4847
|
+
"relationships": [],
|
|
4848
|
+
"methods": [],
|
|
4849
|
+
"surfaces": [],
|
|
4850
|
+
"relationshipFeatures": [
|
|
4851
|
+
"uuidColumns"
|
|
4852
|
+
],
|
|
4853
|
+
"tags": [],
|
|
4854
|
+
"risks": []
|
|
4855
|
+
},
|
|
4856
|
+
{
|
|
4857
|
+
"name": "WorkbookAssessmentRevision",
|
|
4858
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookAssessmentRevision",
|
|
4859
|
+
"collection": "workbookassessmentrevisions",
|
|
4860
|
+
"description": "Education-owned private workbook record. Mutations go through WorkbookService.",
|
|
4861
|
+
"tableName": "workbook_assessment_revisions",
|
|
4862
|
+
"packageName": "@happyvertical/smrt-education",
|
|
4863
|
+
"extends": "SmrtObject",
|
|
4864
|
+
"fields": [
|
|
4865
|
+
{
|
|
4866
|
+
"name": "tenantId",
|
|
4867
|
+
"type": "text",
|
|
4868
|
+
"required": true,
|
|
4869
|
+
"columnType": "UUID"
|
|
4870
|
+
},
|
|
4871
|
+
{
|
|
4872
|
+
"name": "learnerId",
|
|
4873
|
+
"type": "foreignKey",
|
|
4874
|
+
"required": true,
|
|
4875
|
+
"related": "Learner",
|
|
4876
|
+
"columnType": "UUID"
|
|
4877
|
+
},
|
|
4878
|
+
{
|
|
4879
|
+
"name": "attemptId",
|
|
4880
|
+
"type": "foreignKey",
|
|
4881
|
+
"required": true,
|
|
4882
|
+
"related": "WorkbookAttempt",
|
|
4883
|
+
"columnType": "UUID"
|
|
4884
|
+
},
|
|
4885
|
+
{
|
|
4886
|
+
"name": "requestKey",
|
|
4887
|
+
"type": "text",
|
|
4888
|
+
"required": false,
|
|
4889
|
+
"columnType": "TEXT"
|
|
4890
|
+
},
|
|
4891
|
+
{
|
|
4892
|
+
"name": "requestDigest",
|
|
4893
|
+
"type": "text",
|
|
4894
|
+
"required": false,
|
|
4895
|
+
"columnType": "TEXT"
|
|
4896
|
+
},
|
|
4897
|
+
{
|
|
4898
|
+
"name": "kind",
|
|
4899
|
+
"type": "text",
|
|
4900
|
+
"required": false,
|
|
4901
|
+
"columnType": "TEXT"
|
|
4902
|
+
},
|
|
4903
|
+
{
|
|
4904
|
+
"name": "actorUserId",
|
|
4905
|
+
"type": "text",
|
|
4906
|
+
"required": false,
|
|
4907
|
+
"columnType": "TEXT"
|
|
4908
|
+
},
|
|
4909
|
+
{
|
|
4910
|
+
"name": "sourceRevisionId",
|
|
4911
|
+
"type": "foreignKey",
|
|
4912
|
+
"required": false,
|
|
4913
|
+
"related": "WorkbookAssessmentRevision",
|
|
4914
|
+
"columnType": "UUID"
|
|
4915
|
+
},
|
|
4916
|
+
{
|
|
4917
|
+
"name": "decisionReason",
|
|
4918
|
+
"type": "text",
|
|
4919
|
+
"required": false,
|
|
4920
|
+
"columnType": "TEXT"
|
|
4921
|
+
},
|
|
4922
|
+
{
|
|
4923
|
+
"name": "supersedesId",
|
|
4924
|
+
"type": "foreignKey",
|
|
4925
|
+
"required": false,
|
|
4926
|
+
"related": "WorkbookAssessmentRevision",
|
|
4927
|
+
"columnType": "UUID"
|
|
4928
|
+
},
|
|
4929
|
+
{
|
|
4930
|
+
"name": "assessmentJson",
|
|
4931
|
+
"type": "text",
|
|
4932
|
+
"required": false,
|
|
4933
|
+
"columnType": "TEXT"
|
|
4934
|
+
},
|
|
4935
|
+
{
|
|
4936
|
+
"name": "recordedAt",
|
|
4937
|
+
"type": "text",
|
|
4938
|
+
"required": false,
|
|
4939
|
+
"columnType": "TEXT"
|
|
4940
|
+
}
|
|
4941
|
+
],
|
|
4942
|
+
"relationships": [
|
|
4943
|
+
{
|
|
4944
|
+
"name": "learnerId",
|
|
4945
|
+
"type": "foreignKey",
|
|
4946
|
+
"required": true,
|
|
4947
|
+
"related": "Learner",
|
|
4948
|
+
"columnType": "UUID"
|
|
4949
|
+
},
|
|
4950
|
+
{
|
|
4951
|
+
"name": "attemptId",
|
|
4952
|
+
"type": "foreignKey",
|
|
4953
|
+
"required": true,
|
|
4954
|
+
"related": "WorkbookAttempt",
|
|
4955
|
+
"columnType": "UUID"
|
|
4956
|
+
},
|
|
4957
|
+
{
|
|
4958
|
+
"name": "sourceRevisionId",
|
|
4959
|
+
"type": "foreignKey",
|
|
4960
|
+
"required": false,
|
|
4961
|
+
"related": "WorkbookAssessmentRevision",
|
|
4962
|
+
"columnType": "UUID"
|
|
4963
|
+
},
|
|
4964
|
+
{
|
|
4965
|
+
"name": "supersedesId",
|
|
4966
|
+
"type": "foreignKey",
|
|
4967
|
+
"required": false,
|
|
4968
|
+
"related": "WorkbookAssessmentRevision",
|
|
4969
|
+
"columnType": "UUID"
|
|
4970
|
+
}
|
|
4971
|
+
],
|
|
4972
|
+
"methods": [
|
|
4973
|
+
"delete",
|
|
4974
|
+
"save"
|
|
4975
|
+
],
|
|
4976
|
+
"methodSignatures": [
|
|
4977
|
+
{
|
|
4978
|
+
"name": "delete",
|
|
4979
|
+
"async": true,
|
|
4980
|
+
"returns": "Promise<void>"
|
|
4981
|
+
},
|
|
4982
|
+
{
|
|
4983
|
+
"name": "save",
|
|
4984
|
+
"async": true,
|
|
4985
|
+
"returns": "Promise<this>"
|
|
4986
|
+
}
|
|
4987
|
+
],
|
|
4988
|
+
"tenant": {
|
|
4989
|
+
"scoped": true,
|
|
4990
|
+
"mode": "required",
|
|
4991
|
+
"field": "tenantId"
|
|
4992
|
+
},
|
|
4993
|
+
"conflictColumns": [
|
|
4994
|
+
"tenant_id",
|
|
4995
|
+
"attempt_id",
|
|
4996
|
+
"request_key"
|
|
4997
|
+
],
|
|
4998
|
+
"surfaces": [],
|
|
4999
|
+
"withheldSurfaces": [
|
|
5000
|
+
{
|
|
5001
|
+
"kind": "api",
|
|
5002
|
+
"operation": "delete",
|
|
5003
|
+
"code": "api-disabled",
|
|
5004
|
+
"reason": "api is disabled",
|
|
5005
|
+
"objectName": "@happyvertical/smrt-education:WorkbookAssessmentRevision"
|
|
5006
|
+
},
|
|
5007
|
+
{
|
|
5008
|
+
"kind": "api",
|
|
5009
|
+
"operation": "save",
|
|
5010
|
+
"code": "api-disabled",
|
|
5011
|
+
"reason": "api is disabled",
|
|
5012
|
+
"objectName": "@happyvertical/smrt-education:WorkbookAssessmentRevision"
|
|
5013
|
+
}
|
|
5014
|
+
],
|
|
5015
|
+
"relationshipFeatures": [
|
|
5016
|
+
"foreignKey",
|
|
5017
|
+
"uuidColumns"
|
|
5018
|
+
],
|
|
5019
|
+
"tags": [],
|
|
5020
|
+
"risks": []
|
|
5021
|
+
},
|
|
5022
|
+
{
|
|
5023
|
+
"name": "WorkbookAssessmentRevisionCollection",
|
|
5024
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookAssessmentRevisionCollection",
|
|
5025
|
+
"collection": "workbookassessmentrevisions",
|
|
5026
|
+
"tableName": "workbook_assessment_revisions",
|
|
5027
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5028
|
+
"extends": "SmrtCollection",
|
|
5029
|
+
"fields": [],
|
|
5030
|
+
"relationships": [],
|
|
5031
|
+
"methods": [],
|
|
5032
|
+
"surfaces": [],
|
|
5033
|
+
"relationshipFeatures": [
|
|
5034
|
+
"uuidColumns"
|
|
5035
|
+
],
|
|
5036
|
+
"tags": [],
|
|
5037
|
+
"risks": []
|
|
5038
|
+
},
|
|
5039
|
+
{
|
|
5040
|
+
"name": "WorkbookFeedbackRelease",
|
|
5041
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookFeedbackRelease",
|
|
5042
|
+
"collection": "workbookfeedbackreleases",
|
|
5043
|
+
"description": "Education-owned private workbook record. Mutations go through WorkbookService.",
|
|
5044
|
+
"tableName": "workbook_feedback_releases",
|
|
5045
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5046
|
+
"extends": "SmrtObject",
|
|
5047
|
+
"fields": [
|
|
5048
|
+
{
|
|
5049
|
+
"name": "tenantId",
|
|
5050
|
+
"type": "text",
|
|
5051
|
+
"required": true,
|
|
5052
|
+
"columnType": "UUID"
|
|
5053
|
+
},
|
|
5054
|
+
{
|
|
5055
|
+
"name": "learnerId",
|
|
5056
|
+
"type": "foreignKey",
|
|
5057
|
+
"required": true,
|
|
5058
|
+
"related": "Learner",
|
|
5059
|
+
"columnType": "UUID"
|
|
5060
|
+
},
|
|
5061
|
+
{
|
|
5062
|
+
"name": "attemptId",
|
|
5063
|
+
"type": "foreignKey",
|
|
5064
|
+
"required": true,
|
|
5065
|
+
"related": "WorkbookAttempt",
|
|
5066
|
+
"columnType": "UUID"
|
|
5067
|
+
},
|
|
5068
|
+
{
|
|
5069
|
+
"name": "revisionId",
|
|
5070
|
+
"type": "foreignKey",
|
|
5071
|
+
"required": true,
|
|
5072
|
+
"related": "WorkbookAssessmentRevision",
|
|
5073
|
+
"columnType": "UUID"
|
|
5074
|
+
},
|
|
5075
|
+
{
|
|
5076
|
+
"name": "expectedHeadId",
|
|
5077
|
+
"type": "foreignKey",
|
|
5078
|
+
"required": false,
|
|
5079
|
+
"related": "WorkbookAssessmentRevision",
|
|
5080
|
+
"columnType": "UUID"
|
|
5081
|
+
},
|
|
5082
|
+
{
|
|
5083
|
+
"name": "requestKey",
|
|
5084
|
+
"type": "text",
|
|
5085
|
+
"required": false,
|
|
5086
|
+
"columnType": "TEXT"
|
|
5087
|
+
},
|
|
5088
|
+
{
|
|
5089
|
+
"name": "actorUserId",
|
|
5090
|
+
"type": "text",
|
|
5091
|
+
"required": false,
|
|
5092
|
+
"columnType": "TEXT"
|
|
5093
|
+
},
|
|
5094
|
+
{
|
|
5095
|
+
"name": "recordedAt",
|
|
5096
|
+
"type": "text",
|
|
5097
|
+
"required": false,
|
|
5098
|
+
"columnType": "TEXT"
|
|
5099
|
+
}
|
|
5100
|
+
],
|
|
5101
|
+
"relationships": [
|
|
5102
|
+
{
|
|
5103
|
+
"name": "learnerId",
|
|
5104
|
+
"type": "foreignKey",
|
|
5105
|
+
"required": true,
|
|
5106
|
+
"related": "Learner",
|
|
5107
|
+
"columnType": "UUID"
|
|
5108
|
+
},
|
|
5109
|
+
{
|
|
5110
|
+
"name": "attemptId",
|
|
5111
|
+
"type": "foreignKey",
|
|
5112
|
+
"required": true,
|
|
5113
|
+
"related": "WorkbookAttempt",
|
|
5114
|
+
"columnType": "UUID"
|
|
5115
|
+
},
|
|
5116
|
+
{
|
|
5117
|
+
"name": "revisionId",
|
|
5118
|
+
"type": "foreignKey",
|
|
5119
|
+
"required": true,
|
|
5120
|
+
"related": "WorkbookAssessmentRevision",
|
|
5121
|
+
"columnType": "UUID"
|
|
5122
|
+
},
|
|
5123
|
+
{
|
|
5124
|
+
"name": "expectedHeadId",
|
|
5125
|
+
"type": "foreignKey",
|
|
5126
|
+
"required": false,
|
|
5127
|
+
"related": "WorkbookAssessmentRevision",
|
|
5128
|
+
"columnType": "UUID"
|
|
5129
|
+
}
|
|
5130
|
+
],
|
|
5131
|
+
"methods": [
|
|
5132
|
+
"delete",
|
|
5133
|
+
"save"
|
|
5134
|
+
],
|
|
5135
|
+
"methodSignatures": [
|
|
5136
|
+
{
|
|
5137
|
+
"name": "delete",
|
|
5138
|
+
"async": true,
|
|
5139
|
+
"returns": "Promise<void>"
|
|
5140
|
+
},
|
|
5141
|
+
{
|
|
5142
|
+
"name": "save",
|
|
5143
|
+
"async": true,
|
|
5144
|
+
"returns": "Promise<this>"
|
|
5145
|
+
}
|
|
5146
|
+
],
|
|
5147
|
+
"tenant": {
|
|
5148
|
+
"scoped": true,
|
|
5149
|
+
"mode": "required",
|
|
5150
|
+
"field": "tenantId"
|
|
5151
|
+
},
|
|
5152
|
+
"conflictColumns": [
|
|
5153
|
+
"tenant_id",
|
|
5154
|
+
"attempt_id",
|
|
5155
|
+
"request_key"
|
|
5156
|
+
],
|
|
5157
|
+
"surfaces": [],
|
|
5158
|
+
"withheldSurfaces": [
|
|
5159
|
+
{
|
|
5160
|
+
"kind": "api",
|
|
5161
|
+
"operation": "delete",
|
|
5162
|
+
"code": "api-disabled",
|
|
5163
|
+
"reason": "api is disabled",
|
|
5164
|
+
"objectName": "@happyvertical/smrt-education:WorkbookFeedbackRelease"
|
|
5165
|
+
},
|
|
5166
|
+
{
|
|
5167
|
+
"kind": "api",
|
|
5168
|
+
"operation": "save",
|
|
5169
|
+
"code": "api-disabled",
|
|
5170
|
+
"reason": "api is disabled",
|
|
5171
|
+
"objectName": "@happyvertical/smrt-education:WorkbookFeedbackRelease"
|
|
5172
|
+
}
|
|
5173
|
+
],
|
|
5174
|
+
"relationshipFeatures": [
|
|
5175
|
+
"foreignKey",
|
|
5176
|
+
"uuidColumns"
|
|
5177
|
+
],
|
|
5178
|
+
"tags": [],
|
|
5179
|
+
"risks": []
|
|
5180
|
+
},
|
|
5181
|
+
{
|
|
5182
|
+
"name": "WorkbookFeedbackReleaseCollection",
|
|
5183
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookFeedbackReleaseCollection",
|
|
5184
|
+
"collection": "workbookfeedbackreleases",
|
|
5185
|
+
"tableName": "workbook_feedback_releases",
|
|
5186
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5187
|
+
"extends": "SmrtCollection",
|
|
5188
|
+
"fields": [],
|
|
5189
|
+
"relationships": [],
|
|
5190
|
+
"methods": [],
|
|
5191
|
+
"surfaces": [],
|
|
5192
|
+
"relationshipFeatures": [
|
|
5193
|
+
"uuidColumns"
|
|
5194
|
+
],
|
|
5195
|
+
"tags": [],
|
|
5196
|
+
"risks": []
|
|
5197
|
+
},
|
|
5198
|
+
{
|
|
5199
|
+
"name": "WorkbookFeedbackDelivery",
|
|
5200
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookFeedbackDelivery",
|
|
5201
|
+
"collection": "workbookfeedbackdeliveries",
|
|
5202
|
+
"description": "Authenticated delivery acknowledgment; this does not assert comprehension.",
|
|
5203
|
+
"tableName": "workbook_feedback_deliveries",
|
|
5204
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5205
|
+
"extends": "SmrtObject",
|
|
5206
|
+
"fields": [
|
|
5207
|
+
{
|
|
5208
|
+
"name": "tenantId",
|
|
5209
|
+
"type": "text",
|
|
5210
|
+
"required": true,
|
|
5211
|
+
"columnType": "UUID"
|
|
5212
|
+
},
|
|
5213
|
+
{
|
|
5214
|
+
"name": "learnerId",
|
|
5215
|
+
"type": "foreignKey",
|
|
5216
|
+
"required": true,
|
|
5217
|
+
"related": "Learner",
|
|
5218
|
+
"columnType": "UUID"
|
|
5219
|
+
},
|
|
5220
|
+
{
|
|
5221
|
+
"name": "attemptId",
|
|
5222
|
+
"type": "foreignKey",
|
|
5223
|
+
"required": true,
|
|
5224
|
+
"related": "WorkbookAttempt",
|
|
5225
|
+
"columnType": "UUID"
|
|
5226
|
+
},
|
|
5227
|
+
{
|
|
5228
|
+
"name": "revisionId",
|
|
5229
|
+
"type": "foreignKey",
|
|
5230
|
+
"required": true,
|
|
5231
|
+
"related": "WorkbookAssessmentRevision",
|
|
5232
|
+
"columnType": "UUID"
|
|
5233
|
+
},
|
|
5234
|
+
{
|
|
5235
|
+
"name": "actorUserId",
|
|
5236
|
+
"type": "text",
|
|
5237
|
+
"required": false,
|
|
5238
|
+
"columnType": "TEXT"
|
|
5239
|
+
},
|
|
5240
|
+
{
|
|
5241
|
+
"name": "requestKey",
|
|
5242
|
+
"type": "text",
|
|
5243
|
+
"required": false,
|
|
5244
|
+
"columnType": "TEXT"
|
|
5245
|
+
},
|
|
5246
|
+
{
|
|
5247
|
+
"name": "recordedAt",
|
|
5248
|
+
"type": "text",
|
|
5249
|
+
"required": false,
|
|
5250
|
+
"columnType": "TEXT"
|
|
5251
|
+
}
|
|
5252
|
+
],
|
|
5253
|
+
"relationships": [
|
|
5254
|
+
{
|
|
5255
|
+
"name": "learnerId",
|
|
5256
|
+
"type": "foreignKey",
|
|
5257
|
+
"required": true,
|
|
5258
|
+
"related": "Learner",
|
|
5259
|
+
"columnType": "UUID"
|
|
5260
|
+
},
|
|
5261
|
+
{
|
|
5262
|
+
"name": "attemptId",
|
|
5263
|
+
"type": "foreignKey",
|
|
5264
|
+
"required": true,
|
|
5265
|
+
"related": "WorkbookAttempt",
|
|
5266
|
+
"columnType": "UUID"
|
|
5267
|
+
},
|
|
5268
|
+
{
|
|
5269
|
+
"name": "revisionId",
|
|
5270
|
+
"type": "foreignKey",
|
|
5271
|
+
"required": true,
|
|
5272
|
+
"related": "WorkbookAssessmentRevision",
|
|
5273
|
+
"columnType": "UUID"
|
|
5274
|
+
}
|
|
5275
|
+
],
|
|
5276
|
+
"methods": [
|
|
5277
|
+
"delete",
|
|
5278
|
+
"save"
|
|
5279
|
+
],
|
|
5280
|
+
"methodSignatures": [
|
|
5281
|
+
{
|
|
5282
|
+
"name": "delete",
|
|
5283
|
+
"async": true,
|
|
5284
|
+
"returns": "Promise<void>"
|
|
5285
|
+
},
|
|
5286
|
+
{
|
|
5287
|
+
"name": "save",
|
|
5288
|
+
"async": true,
|
|
5289
|
+
"returns": "Promise<this>"
|
|
5290
|
+
}
|
|
5291
|
+
],
|
|
5292
|
+
"tenant": {
|
|
5293
|
+
"scoped": true,
|
|
5294
|
+
"mode": "required",
|
|
5295
|
+
"field": "tenantId"
|
|
5296
|
+
},
|
|
5297
|
+
"conflictColumns": [
|
|
5298
|
+
"tenant_id",
|
|
5299
|
+
"attempt_id",
|
|
5300
|
+
"actor_user_id",
|
|
5301
|
+
"revision_id"
|
|
5302
|
+
],
|
|
5303
|
+
"surfaces": [],
|
|
5304
|
+
"withheldSurfaces": [
|
|
5305
|
+
{
|
|
5306
|
+
"kind": "api",
|
|
5307
|
+
"operation": "delete",
|
|
5308
|
+
"code": "api-disabled",
|
|
5309
|
+
"reason": "api is disabled",
|
|
5310
|
+
"objectName": "@happyvertical/smrt-education:WorkbookFeedbackDelivery"
|
|
5311
|
+
},
|
|
5312
|
+
{
|
|
5313
|
+
"kind": "api",
|
|
5314
|
+
"operation": "save",
|
|
5315
|
+
"code": "api-disabled",
|
|
5316
|
+
"reason": "api is disabled",
|
|
5317
|
+
"objectName": "@happyvertical/smrt-education:WorkbookFeedbackDelivery"
|
|
5318
|
+
}
|
|
5319
|
+
],
|
|
5320
|
+
"relationshipFeatures": [
|
|
5321
|
+
"foreignKey",
|
|
5322
|
+
"uuidColumns"
|
|
5323
|
+
],
|
|
5324
|
+
"tags": [],
|
|
5325
|
+
"risks": []
|
|
5326
|
+
},
|
|
5327
|
+
{
|
|
5328
|
+
"name": "WorkbookFeedbackDeliveryCollection",
|
|
5329
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookFeedbackDeliveryCollection",
|
|
5330
|
+
"collection": "workbookfeedbackdeliveries",
|
|
5331
|
+
"tableName": "workbook_feedback_deliveries",
|
|
5332
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5333
|
+
"extends": "SmrtCollection",
|
|
5334
|
+
"fields": [],
|
|
5335
|
+
"relationships": [],
|
|
5336
|
+
"methods": [],
|
|
5337
|
+
"surfaces": [],
|
|
5338
|
+
"relationshipFeatures": [
|
|
5339
|
+
"uuidColumns"
|
|
5340
|
+
],
|
|
5341
|
+
"tags": [],
|
|
5342
|
+
"risks": []
|
|
5343
|
+
},
|
|
5344
|
+
{
|
|
5345
|
+
"name": "WorkbookPhotoIntake",
|
|
5346
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookPhotoIntake",
|
|
5347
|
+
"collection": "workbookphotointakes",
|
|
5348
|
+
"description": "Durable receipt before any course, enrollment or activity exists.",
|
|
5349
|
+
"tableName": "workbook_photo_intakes",
|
|
5350
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5351
|
+
"extends": "SmrtObject",
|
|
5352
|
+
"fields": [
|
|
5353
|
+
{
|
|
5354
|
+
"name": "tenantId",
|
|
5355
|
+
"type": "text",
|
|
5356
|
+
"required": true,
|
|
5357
|
+
"columnType": "UUID"
|
|
5358
|
+
},
|
|
5359
|
+
{
|
|
5360
|
+
"name": "learnerId",
|
|
5361
|
+
"type": "foreignKey",
|
|
5362
|
+
"required": true,
|
|
5363
|
+
"related": "Learner",
|
|
5364
|
+
"columnType": "UUID"
|
|
5365
|
+
},
|
|
5366
|
+
{
|
|
5367
|
+
"name": "itemId",
|
|
5368
|
+
"type": "text",
|
|
5369
|
+
"required": false,
|
|
5370
|
+
"columnType": "TEXT"
|
|
5371
|
+
},
|
|
5372
|
+
{
|
|
5373
|
+
"name": "actorUserId",
|
|
5374
|
+
"type": "text",
|
|
5375
|
+
"required": false,
|
|
5376
|
+
"columnType": "TEXT"
|
|
5377
|
+
},
|
|
5378
|
+
{
|
|
5379
|
+
"name": "contentDigest",
|
|
5380
|
+
"type": "text",
|
|
5381
|
+
"required": false,
|
|
5382
|
+
"columnType": "TEXT"
|
|
5383
|
+
}
|
|
5384
|
+
],
|
|
5385
|
+
"relationships": [
|
|
5386
|
+
{
|
|
5387
|
+
"name": "learnerId",
|
|
5388
|
+
"type": "foreignKey",
|
|
5389
|
+
"required": true,
|
|
5390
|
+
"related": "Learner",
|
|
5391
|
+
"columnType": "UUID"
|
|
5392
|
+
}
|
|
5393
|
+
],
|
|
5394
|
+
"methods": [
|
|
5395
|
+
"delete",
|
|
5396
|
+
"save"
|
|
5397
|
+
],
|
|
5398
|
+
"methodSignatures": [
|
|
5399
|
+
{
|
|
5400
|
+
"name": "delete",
|
|
5401
|
+
"async": true,
|
|
5402
|
+
"returns": "Promise<void>"
|
|
5403
|
+
},
|
|
5404
|
+
{
|
|
5405
|
+
"name": "save",
|
|
5406
|
+
"async": true,
|
|
5407
|
+
"returns": "Promise<this>"
|
|
5408
|
+
}
|
|
5409
|
+
],
|
|
5410
|
+
"tenant": {
|
|
5411
|
+
"scoped": true,
|
|
5412
|
+
"mode": "required",
|
|
5413
|
+
"field": "tenantId"
|
|
5414
|
+
},
|
|
5415
|
+
"conflictColumns": [
|
|
5416
|
+
"tenant_id",
|
|
5417
|
+
"item_id"
|
|
5418
|
+
],
|
|
5419
|
+
"surfaces": [],
|
|
5420
|
+
"withheldSurfaces": [
|
|
5421
|
+
{
|
|
5422
|
+
"kind": "api",
|
|
5423
|
+
"operation": "delete",
|
|
5424
|
+
"code": "api-disabled",
|
|
5425
|
+
"reason": "api is disabled",
|
|
5426
|
+
"objectName": "@happyvertical/smrt-education:WorkbookPhotoIntake"
|
|
5427
|
+
},
|
|
5428
|
+
{
|
|
5429
|
+
"kind": "api",
|
|
5430
|
+
"operation": "save",
|
|
5431
|
+
"code": "api-disabled",
|
|
5432
|
+
"reason": "api is disabled",
|
|
5433
|
+
"objectName": "@happyvertical/smrt-education:WorkbookPhotoIntake"
|
|
5434
|
+
}
|
|
5435
|
+
],
|
|
5436
|
+
"relationshipFeatures": [
|
|
5437
|
+
"foreignKey",
|
|
5438
|
+
"uuidColumns"
|
|
5439
|
+
],
|
|
5440
|
+
"tags": [],
|
|
5441
|
+
"risks": []
|
|
5442
|
+
},
|
|
5443
|
+
{
|
|
5444
|
+
"name": "WorkbookPhotoIntakeCollection",
|
|
5445
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookPhotoIntakeCollection",
|
|
5446
|
+
"collection": "workbookphotointakes",
|
|
5447
|
+
"tableName": "workbook_photo_intakes",
|
|
5448
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5449
|
+
"extends": "SmrtCollection",
|
|
5450
|
+
"fields": [],
|
|
5451
|
+
"relationships": [],
|
|
5452
|
+
"methods": [],
|
|
5453
|
+
"surfaces": [],
|
|
5454
|
+
"relationshipFeatures": [
|
|
5455
|
+
"uuidColumns"
|
|
5456
|
+
],
|
|
5457
|
+
"tags": [],
|
|
5458
|
+
"risks": []
|
|
5459
|
+
},
|
|
5460
|
+
{
|
|
5461
|
+
"name": "WorkbookPhotoAsset",
|
|
5462
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookPhotoAsset",
|
|
5463
|
+
"displayLabelField": "name",
|
|
5464
|
+
"collection": "workbookphotoassets",
|
|
5465
|
+
"description": "Sole owner join for original photo intake files.",
|
|
5466
|
+
"tableName": "workbook_photo_assets",
|
|
5467
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5468
|
+
"extends": "SmrtObject",
|
|
5469
|
+
"fields": [
|
|
5470
|
+
{
|
|
5471
|
+
"name": "tenantId",
|
|
5472
|
+
"type": "text",
|
|
5473
|
+
"required": true,
|
|
5474
|
+
"columnType": "UUID"
|
|
5475
|
+
},
|
|
5476
|
+
{
|
|
5477
|
+
"name": "learnerId",
|
|
5478
|
+
"type": "foreignKey",
|
|
5479
|
+
"required": true,
|
|
5480
|
+
"related": "Learner",
|
|
5481
|
+
"columnType": "UUID"
|
|
5482
|
+
},
|
|
5483
|
+
{
|
|
5484
|
+
"name": "intakeId",
|
|
5485
|
+
"type": "foreignKey",
|
|
5486
|
+
"required": true,
|
|
5487
|
+
"related": "WorkbookPhotoIntake",
|
|
5488
|
+
"columnType": "UUID"
|
|
5489
|
+
},
|
|
5490
|
+
{
|
|
5491
|
+
"name": "assetId",
|
|
5492
|
+
"type": "crossPackageRef",
|
|
5493
|
+
"required": false,
|
|
5494
|
+
"related": "@happyvertical/smrt-assets:Asset",
|
|
5495
|
+
"columnType": "UUID"
|
|
5496
|
+
},
|
|
5497
|
+
{
|
|
5498
|
+
"name": "name",
|
|
5499
|
+
"type": "text",
|
|
5500
|
+
"required": false,
|
|
5501
|
+
"columnType": "TEXT"
|
|
5502
|
+
},
|
|
5503
|
+
{
|
|
5504
|
+
"name": "mimeType",
|
|
5505
|
+
"type": "text",
|
|
5506
|
+
"required": false,
|
|
5507
|
+
"columnType": "TEXT"
|
|
5508
|
+
},
|
|
5509
|
+
{
|
|
5510
|
+
"name": "sha256",
|
|
5511
|
+
"type": "text",
|
|
5512
|
+
"required": false,
|
|
5513
|
+
"columnType": "TEXT"
|
|
5514
|
+
},
|
|
5515
|
+
{
|
|
5516
|
+
"name": "position",
|
|
5517
|
+
"type": "integer",
|
|
5518
|
+
"required": true,
|
|
5519
|
+
"columnType": "INTEGER",
|
|
5520
|
+
"default": 0
|
|
5521
|
+
}
|
|
5522
|
+
],
|
|
5523
|
+
"relationships": [
|
|
5524
|
+
{
|
|
5525
|
+
"name": "learnerId",
|
|
5526
|
+
"type": "foreignKey",
|
|
5527
|
+
"required": true,
|
|
5528
|
+
"related": "Learner",
|
|
5529
|
+
"columnType": "UUID"
|
|
5530
|
+
},
|
|
5531
|
+
{
|
|
5532
|
+
"name": "intakeId",
|
|
5533
|
+
"type": "foreignKey",
|
|
5534
|
+
"required": true,
|
|
5535
|
+
"related": "WorkbookPhotoIntake",
|
|
5536
|
+
"columnType": "UUID"
|
|
5537
|
+
},
|
|
5538
|
+
{
|
|
5539
|
+
"name": "assetId",
|
|
5540
|
+
"type": "crossPackageRef",
|
|
5541
|
+
"required": false,
|
|
5542
|
+
"related": "@happyvertical/smrt-assets:Asset",
|
|
5543
|
+
"columnType": "UUID"
|
|
5544
|
+
}
|
|
5545
|
+
],
|
|
5546
|
+
"methods": [
|
|
5547
|
+
"delete",
|
|
5548
|
+
"save"
|
|
5549
|
+
],
|
|
5550
|
+
"methodSignatures": [
|
|
5551
|
+
{
|
|
5552
|
+
"name": "delete",
|
|
5553
|
+
"async": true,
|
|
5554
|
+
"returns": "Promise<void>"
|
|
5555
|
+
},
|
|
5556
|
+
{
|
|
5557
|
+
"name": "save",
|
|
5558
|
+
"async": true,
|
|
5559
|
+
"returns": "Promise<this>"
|
|
5560
|
+
}
|
|
5561
|
+
],
|
|
5562
|
+
"tenant": {
|
|
5563
|
+
"scoped": true,
|
|
5564
|
+
"mode": "required",
|
|
5565
|
+
"field": "tenantId"
|
|
5566
|
+
},
|
|
5567
|
+
"conflictColumns": [
|
|
5568
|
+
"tenant_id",
|
|
5569
|
+
"intake_id",
|
|
5570
|
+
"asset_id"
|
|
5571
|
+
],
|
|
5572
|
+
"surfaces": [],
|
|
5573
|
+
"withheldSurfaces": [
|
|
5574
|
+
{
|
|
5575
|
+
"kind": "api",
|
|
5576
|
+
"operation": "delete",
|
|
5577
|
+
"code": "api-disabled",
|
|
5578
|
+
"reason": "api is disabled",
|
|
5579
|
+
"objectName": "@happyvertical/smrt-education:WorkbookPhotoAsset"
|
|
5580
|
+
},
|
|
5581
|
+
{
|
|
5582
|
+
"kind": "api",
|
|
5583
|
+
"operation": "save",
|
|
5584
|
+
"code": "api-disabled",
|
|
5585
|
+
"reason": "api is disabled",
|
|
5586
|
+
"objectName": "@happyvertical/smrt-education:WorkbookPhotoAsset"
|
|
5587
|
+
}
|
|
5588
|
+
],
|
|
5589
|
+
"relationshipFeatures": [
|
|
5590
|
+
"crossPackageRef",
|
|
5591
|
+
"foreignKey",
|
|
5592
|
+
"uuidColumns"
|
|
5593
|
+
],
|
|
5594
|
+
"tags": [],
|
|
5595
|
+
"risks": []
|
|
5596
|
+
},
|
|
5597
|
+
{
|
|
5598
|
+
"name": "WorkbookPhotoAssetCollection",
|
|
5599
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookPhotoAssetCollection",
|
|
5600
|
+
"collection": "workbookphotoassets",
|
|
5601
|
+
"tableName": "workbook_photo_assets",
|
|
5602
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5603
|
+
"extends": "SmrtCollection",
|
|
5604
|
+
"fields": [],
|
|
5605
|
+
"relationships": [],
|
|
5606
|
+
"methods": [],
|
|
5607
|
+
"surfaces": [],
|
|
5608
|
+
"relationshipFeatures": [
|
|
5609
|
+
"uuidColumns"
|
|
5610
|
+
],
|
|
5611
|
+
"tags": [],
|
|
5612
|
+
"risks": []
|
|
5613
|
+
},
|
|
5614
|
+
{
|
|
5615
|
+
"name": "WorkbookPhotoDecision",
|
|
5616
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookPhotoDecision",
|
|
5617
|
+
"collection": "workbookphotodecisions",
|
|
5618
|
+
"description": "Immutable provider/guardian/capture decision, retaining provenance and retry key.",
|
|
5619
|
+
"tableName": "workbook_photo_decisions",
|
|
5620
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5621
|
+
"extends": "SmrtObject",
|
|
5622
|
+
"fields": [
|
|
5623
|
+
{
|
|
5624
|
+
"name": "tenantId",
|
|
5625
|
+
"type": "text",
|
|
5626
|
+
"required": true,
|
|
5627
|
+
"columnType": "UUID"
|
|
5628
|
+
},
|
|
5629
|
+
{
|
|
5630
|
+
"name": "learnerId",
|
|
5631
|
+
"type": "foreignKey",
|
|
5632
|
+
"required": true,
|
|
5633
|
+
"related": "Learner",
|
|
5634
|
+
"columnType": "UUID"
|
|
5635
|
+
},
|
|
5636
|
+
{
|
|
5637
|
+
"name": "intakeId",
|
|
5638
|
+
"type": "foreignKey",
|
|
5639
|
+
"required": true,
|
|
5640
|
+
"related": "WorkbookPhotoIntake",
|
|
5641
|
+
"columnType": "UUID"
|
|
5642
|
+
},
|
|
5643
|
+
{
|
|
5644
|
+
"name": "requestKey",
|
|
5645
|
+
"type": "text",
|
|
5646
|
+
"required": false,
|
|
5647
|
+
"columnType": "TEXT"
|
|
5648
|
+
},
|
|
5649
|
+
{
|
|
5650
|
+
"name": "sequence",
|
|
5651
|
+
"type": "integer",
|
|
5652
|
+
"required": true,
|
|
5653
|
+
"columnType": "INTEGER",
|
|
5654
|
+
"default": 0
|
|
5655
|
+
},
|
|
5656
|
+
{
|
|
5657
|
+
"name": "actorUserId",
|
|
5658
|
+
"type": "text",
|
|
5659
|
+
"required": false,
|
|
5660
|
+
"columnType": "TEXT"
|
|
5661
|
+
},
|
|
5662
|
+
{
|
|
5663
|
+
"name": "bodyJson",
|
|
5664
|
+
"type": "text",
|
|
5665
|
+
"required": false,
|
|
5666
|
+
"columnType": "TEXT"
|
|
5667
|
+
}
|
|
5668
|
+
],
|
|
5669
|
+
"relationships": [
|
|
5670
|
+
{
|
|
5671
|
+
"name": "learnerId",
|
|
5672
|
+
"type": "foreignKey",
|
|
5673
|
+
"required": true,
|
|
5674
|
+
"related": "Learner",
|
|
5675
|
+
"columnType": "UUID"
|
|
5676
|
+
},
|
|
5677
|
+
{
|
|
5678
|
+
"name": "intakeId",
|
|
5679
|
+
"type": "foreignKey",
|
|
5680
|
+
"required": true,
|
|
5681
|
+
"related": "WorkbookPhotoIntake",
|
|
5682
|
+
"columnType": "UUID"
|
|
5683
|
+
}
|
|
5684
|
+
],
|
|
5685
|
+
"methods": [
|
|
5686
|
+
"delete",
|
|
5687
|
+
"save"
|
|
5688
|
+
],
|
|
5689
|
+
"methodSignatures": [
|
|
5690
|
+
{
|
|
5691
|
+
"name": "delete",
|
|
5692
|
+
"async": true,
|
|
5693
|
+
"returns": "Promise<void>"
|
|
5694
|
+
},
|
|
5695
|
+
{
|
|
5696
|
+
"name": "save",
|
|
5697
|
+
"async": true,
|
|
5698
|
+
"returns": "Promise<this>"
|
|
5699
|
+
}
|
|
5700
|
+
],
|
|
5701
|
+
"tenant": {
|
|
5702
|
+
"scoped": true,
|
|
5703
|
+
"mode": "required",
|
|
5704
|
+
"field": "tenantId"
|
|
5705
|
+
},
|
|
5706
|
+
"conflictColumns": [
|
|
5707
|
+
"tenant_id",
|
|
5708
|
+
"intake_id",
|
|
5709
|
+
"request_key"
|
|
5710
|
+
],
|
|
5711
|
+
"surfaces": [],
|
|
5712
|
+
"withheldSurfaces": [
|
|
5713
|
+
{
|
|
5714
|
+
"kind": "api",
|
|
5715
|
+
"operation": "delete",
|
|
5716
|
+
"code": "api-disabled",
|
|
5717
|
+
"reason": "api is disabled",
|
|
5718
|
+
"objectName": "@happyvertical/smrt-education:WorkbookPhotoDecision"
|
|
5719
|
+
},
|
|
5720
|
+
{
|
|
5721
|
+
"kind": "api",
|
|
5722
|
+
"operation": "save",
|
|
5723
|
+
"code": "api-disabled",
|
|
5724
|
+
"reason": "api is disabled",
|
|
5725
|
+
"objectName": "@happyvertical/smrt-education:WorkbookPhotoDecision"
|
|
5726
|
+
}
|
|
5727
|
+
],
|
|
5728
|
+
"relationshipFeatures": [
|
|
5729
|
+
"foreignKey",
|
|
5730
|
+
"uuidColumns"
|
|
5731
|
+
],
|
|
5732
|
+
"tags": [],
|
|
5733
|
+
"risks": []
|
|
5734
|
+
},
|
|
5735
|
+
{
|
|
5736
|
+
"name": "WorkbookPhotoDecisionCollection",
|
|
5737
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookPhotoDecisionCollection",
|
|
5738
|
+
"collection": "workbookphotodecisions",
|
|
5739
|
+
"tableName": "workbook_photo_decisions",
|
|
5740
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5741
|
+
"extends": "SmrtCollection",
|
|
5742
|
+
"fields": [],
|
|
5743
|
+
"relationships": [],
|
|
5744
|
+
"methods": [],
|
|
5745
|
+
"surfaces": [],
|
|
5746
|
+
"relationshipFeatures": [
|
|
5747
|
+
"uuidColumns"
|
|
5748
|
+
],
|
|
5749
|
+
"tags": [],
|
|
5750
|
+
"risks": []
|
|
5751
|
+
},
|
|
5752
|
+
{
|
|
5753
|
+
"name": "WorkbookContextRevision",
|
|
5754
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookContextRevision",
|
|
5755
|
+
"collection": "workbookcontextrevisions",
|
|
5756
|
+
"description": "Versioned contextual metadata; never edits Workbook identity or learning evidence.",
|
|
5757
|
+
"tableName": "workbook_context_revisions",
|
|
5758
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5759
|
+
"extends": "SmrtObject",
|
|
5760
|
+
"fields": [
|
|
5761
|
+
{
|
|
5762
|
+
"name": "tenantId",
|
|
5763
|
+
"type": "text",
|
|
5764
|
+
"required": true,
|
|
5765
|
+
"columnType": "UUID"
|
|
5766
|
+
},
|
|
5767
|
+
{
|
|
5768
|
+
"name": "learnerId",
|
|
5769
|
+
"type": "foreignKey",
|
|
5770
|
+
"required": true,
|
|
5771
|
+
"related": "Learner",
|
|
5772
|
+
"columnType": "UUID"
|
|
5773
|
+
},
|
|
5774
|
+
{
|
|
5775
|
+
"name": "workbookId",
|
|
5776
|
+
"type": "foreignKey",
|
|
5777
|
+
"required": true,
|
|
5778
|
+
"related": "Workbook",
|
|
5779
|
+
"columnType": "UUID"
|
|
5780
|
+
},
|
|
5781
|
+
{
|
|
5782
|
+
"name": "decisionId",
|
|
5783
|
+
"type": "foreignKey",
|
|
5784
|
+
"required": true,
|
|
5785
|
+
"related": "WorkbookPhotoDecision",
|
|
5786
|
+
"columnType": "UUID"
|
|
5787
|
+
},
|
|
5788
|
+
{
|
|
5789
|
+
"name": "metadataJson",
|
|
5790
|
+
"type": "text",
|
|
5791
|
+
"required": false,
|
|
5792
|
+
"columnType": "TEXT"
|
|
5793
|
+
}
|
|
5794
|
+
],
|
|
5795
|
+
"relationships": [
|
|
5796
|
+
{
|
|
5797
|
+
"name": "learnerId",
|
|
5798
|
+
"type": "foreignKey",
|
|
5799
|
+
"required": true,
|
|
5800
|
+
"related": "Learner",
|
|
5801
|
+
"columnType": "UUID"
|
|
5802
|
+
},
|
|
5803
|
+
{
|
|
5804
|
+
"name": "workbookId",
|
|
5805
|
+
"type": "foreignKey",
|
|
5806
|
+
"required": true,
|
|
5807
|
+
"related": "Workbook",
|
|
5808
|
+
"columnType": "UUID"
|
|
5809
|
+
},
|
|
5810
|
+
{
|
|
5811
|
+
"name": "decisionId",
|
|
5812
|
+
"type": "foreignKey",
|
|
5813
|
+
"required": true,
|
|
5814
|
+
"related": "WorkbookPhotoDecision",
|
|
5815
|
+
"columnType": "UUID"
|
|
5816
|
+
}
|
|
5817
|
+
],
|
|
5818
|
+
"methods": [
|
|
5819
|
+
"delete",
|
|
5820
|
+
"save"
|
|
5821
|
+
],
|
|
5822
|
+
"methodSignatures": [
|
|
5823
|
+
{
|
|
5824
|
+
"name": "delete",
|
|
5825
|
+
"async": true,
|
|
5826
|
+
"returns": "Promise<void>"
|
|
5827
|
+
},
|
|
5828
|
+
{
|
|
5829
|
+
"name": "save",
|
|
5830
|
+
"async": true,
|
|
5831
|
+
"returns": "Promise<this>"
|
|
5832
|
+
}
|
|
5833
|
+
],
|
|
5834
|
+
"tenant": {
|
|
5835
|
+
"scoped": true,
|
|
5836
|
+
"mode": "required",
|
|
5837
|
+
"field": "tenantId"
|
|
5838
|
+
},
|
|
5839
|
+
"conflictColumns": [
|
|
5840
|
+
"tenant_id",
|
|
5841
|
+
"decision_id"
|
|
5842
|
+
],
|
|
5843
|
+
"surfaces": [],
|
|
5844
|
+
"withheldSurfaces": [
|
|
5845
|
+
{
|
|
5846
|
+
"kind": "api",
|
|
5847
|
+
"operation": "delete",
|
|
5848
|
+
"code": "api-disabled",
|
|
5849
|
+
"reason": "api is disabled",
|
|
5850
|
+
"objectName": "@happyvertical/smrt-education:WorkbookContextRevision"
|
|
5851
|
+
},
|
|
5852
|
+
{
|
|
5853
|
+
"kind": "api",
|
|
5854
|
+
"operation": "save",
|
|
5855
|
+
"code": "api-disabled",
|
|
5856
|
+
"reason": "api is disabled",
|
|
5857
|
+
"objectName": "@happyvertical/smrt-education:WorkbookContextRevision"
|
|
5858
|
+
}
|
|
5859
|
+
],
|
|
5860
|
+
"relationshipFeatures": [
|
|
5861
|
+
"foreignKey",
|
|
5862
|
+
"uuidColumns"
|
|
5863
|
+
],
|
|
5864
|
+
"tags": [],
|
|
5865
|
+
"risks": []
|
|
5866
|
+
},
|
|
5867
|
+
{
|
|
5868
|
+
"name": "WorkbookContextRevisionCollection",
|
|
5869
|
+
"qualifiedName": "@happyvertical/smrt-education:WorkbookContextRevisionCollection",
|
|
5870
|
+
"collection": "workbookcontextrevisions",
|
|
5871
|
+
"tableName": "workbook_context_revisions",
|
|
5872
|
+
"packageName": "@happyvertical/smrt-education",
|
|
5873
|
+
"extends": "SmrtCollection",
|
|
5874
|
+
"fields": [],
|
|
5875
|
+
"relationships": [],
|
|
5876
|
+
"methods": [],
|
|
5877
|
+
"surfaces": [],
|
|
5878
|
+
"relationshipFeatures": [
|
|
5879
|
+
"uuidColumns"
|
|
5880
|
+
],
|
|
5881
|
+
"tags": [],
|
|
5882
|
+
"risks": []
|
|
5883
|
+
}
|
|
5884
|
+
],
|
|
5885
|
+
"surfaces": [],
|
|
5886
|
+
"prompts": [],
|
|
5887
|
+
"relationshipsV2": {
|
|
5888
|
+
"foreignKeyFields": 70,
|
|
5889
|
+
"crossPackageRefFields": 6,
|
|
5890
|
+
"junctionCollections": 2,
|
|
5891
|
+
"hierarchicalObjects": 0,
|
|
5892
|
+
"polymorphicAssociations": 0,
|
|
5893
|
+
"uuidColumns": 298
|
|
5894
|
+
},
|
|
5895
|
+
"agentDoc": "# smrt-education\n\nCatalog objects are optional-tenant scoped and have no generated REST, CLI, MCP,\nor AI surface. Applications use `CatalogService`, supplying their actor-bound\nauthorization decision and the trusted `TenantCollection.withTenantCatalogReadScope`\nadapter from smrt-users. Catalog authorization receives the exact shared collection\nexecutor; live session/permission checks must use that executor. That read scope may widen only an allowlisted catalog\ncollection's `list` to self, ancestors, and globals; it never authorizes a get,\nwrite, raw query, sibling, or descendant read.\n\n`personal-use-copy` courses and all `answer-key` lesson assets are private\nfamily material. Do not publish them, promote them, or expose them through a\nlearner-facing result. `LessonAsset` is the sole owner join for lesson files;\ndo not add asset-id fields to lesson subclasses.\n\nLearner objects require exact tenant ownership and close generated surfaces.\n`LearnerService` owns actor/resource authorization and transactional planning and\nstart-progress; see [learner contract](agents/learner-service.md). Guardian\nrelationships require a host decision about their type and effective terms.\nNever expose direct collection writes as learner handlers, reset progress on a\nretry, or treat an unscored nullable decimal as zero.\n\nSubmission capture, guardian grading and private PDF packet splitting use\n`SubmissionService`; see [submission contract](agents/submission-service.md).\nKeep asset metadata on the same transaction and compensate only newly planned\nfiles after rollback; never expose source URIs or accept learner grade patches.\n\nValidation: `pnpm --filter @happyvertical/smrt-education test`,\n`test:postgres`, `typecheck`, and `build`.\n\nPhysical workbook capture, immutable assessment/feedback history and descriptive\nevidence use [WorkbookService](agents/workbook-service.md). Keep provisional\nsharing separate from guardian Grade authority. Photo-first receipt/context\ncapture uses [WorkbookPhotoService](agents/photo-intake.md); covers/references\nnever create learning evidence, and blank exercises need explicit work intent.\n\nSource import: [host boundary and transaction contract](agents/source-import.md).\n`SourceImportService` requires host opt-in, matching active actor/tenant and\nexplicit licensing authorization; never add a system/global write bypass.\n",
|
|
5896
|
+
"moduleDocs": [
|
|
5897
|
+
{
|
|
5898
|
+
"path": "agents/learner-service.md",
|
|
5899
|
+
"module": "learner-service",
|
|
5900
|
+
"content": "# Learner service boundary\n\n`Learner`, `Curriculum`, `Enrollment`, and `LessonProgress` require an exact\nowning tenant. Their generated API, CLI, MCP, and AI operations are closed.\nThe corresponding collections and model saves are trusted server persistence\nAPIs, not request handlers. Hosts must not wrap them in generic learner CRUD.\n\n## Identity and authority\n\nProvision a Person Profile, a linked User, and an active Membership before\ncreating a Learner with explicit `tenantId`, `userId`, and `profileId`. User\nemail is optional. Learner identity cannot be reassigned after creation.\nGuardianship is a directed `ProfileRelationship`, not a Learner `parentId`.\nThe shared Tenant has no family discriminator: “family” means a tenant that\nthe host has provisioned and authorized for this purpose.\n\nConstruct `LearnerService(db, authorizeLearner, authorizeCatalog)` with one\nroot database executor and trusted server-side authorizers. Enable tenancy\nand establish `withTenant({ tenantId, userId }, callback)` from authenticated\ncredentials. Every request must match that active actor and tenant exactly;\nmissing context, spoofed identifiers, and super-admin bypass contexts fail.\nBoth the actor and learner must hold active memberships in that exact tenant.\nA learner's User must still link to the learner's Person Profile.\n\nFor a different actor, supply `guardianRelationshipId`. The service checks\nthat this relationship is owned by the active tenant and directed from the\nactor's User Profile to the learner's Profile. `authorizeLearner` receives\n`own`, `action`, and the verified `relationshipTypeId`; the host must check\nthat the type represents guardianship, its terms are effective, and that\nits permission policy allows the action. An arbitrary relationship is not a\nguardian grant. Missing relationships and host denials/exceptions fail closed.\nThe authorizer also receives `executor`, the service's active database\nexecutor. Host policy reads, including relationship and revocation checks, must\nuse that executor so they share a `withTransaction` or `withExecutor` boundary.\nSame-tenant membership alone never grants sibling access.\n\n## Supported workflow\n\n- `getLearnerSetupStatus({ tenantId, actorUserId, learnerUserId })` is a\n host-authorized onboarding probe for an active tenant member. It returns only\n `{ needsSetup }`; it neither returns learner or relationship data nor grants\n relationships. The host authorizer receives `education.learner.setup`, the\n target user id, and the active executor. `provisionLearner` remains the write\n path and still requires a directed guardian relationship.\n- `getLearner` returns authorized learner metadata and parsed settings.\n- `createCurriculum` creates a **planned** curriculum for an authorized guardian.\n A learner cannot plan their own curriculum. Title is required.\n- `enroll` requires that guardian authority and a matching tenant curriculum.\n It returns the existing enrollment for the same curriculum/course unchanged.\n- `startLesson` allows the learner or authorized guardian to create their own\n enrollment's lesson progress in `in_progress`. It derives learner identity\n from the validated enrollment. Repeated starts return existing state without\n resetting counters, scores, completion, or timestamps.\n- `listProgress` reads only the validated learner/enrollment and lessons still\n visible in that enrollment's course. Inconsistent persisted links fail\n closed or are omitted from the list.\n\nService results are data-only views, with no persistence or database methods.\nExtra request fields are never model patches: a learner cannot set score,\nattempt count, or completion status through this API. Curriculum activation,\ncompletion transitions and dashboard aggregation are not supported by this\nbase service. SubmissionService extends its owning transaction/auth boundary for\ncapture and guardian grading; see [submission contract](submission-service.md).\nDirect model writes require trusted authority.\n\nCatalog visibility and child ownership reuse `CatalogService` on the same\nexecutor. Learner operations request private-inclusive catalog access, so the\nhost catalog authorizer must permit both `education.catalog.read` and\n`education.catalog.read-private` for the supported enrollment workflow.\nPrivate copies remain restricted to the exact owner. The service constructs a\nwhitelisted catalog request; caller options and answer-key flags are discarded.\nAnswer keys are never requested or returned. A catalog grant does not grant learner authorization.\n\n## Transactions and history\n\n`withTransaction(async transaction => ...)` composes service operations on the\ncallback's transaction-bound service. Use that service for every operation in\nthe unit. Throwing rolls back its curriculum, enrollment, and progress writes.\nThe callback's service closes when the transaction ends and rejects later calls.\nA failed mutation poisons the unit even if the callback catches its rejection;\nqueued mutations are cancelled. Callback failure stops pending work and waits for\nalready-running mutations to settle before rollback releases the executor. On\nsuccess, already-enqueued mutations finish before commit. Do not retain the\ncallback's service beyond the callback. The constructor\naccepts a root database; callers composing work use this method instead of\npassing an unmanaged transaction handle.\n\nPostgreSQL locks the parent curriculum/enrollment row before find-or-create.\nEmbedded writes use the core transaction queue. Concurrent mutations inside\none callback transaction are ordered on that connection, so duplicate starts\nwithin a `Promise.all` also return the same row. Database natural-key constraints\narbitrate enrollment `(tenant_id, curriculum_id, course_id)` and progress\n`(tenant_id, enrollment_id, lesson_id)` identities. Explicit storage conflict\nkeys include the tenant prefix because core does not add it to explicit keys.\nThe logical keys required by #3968 remain curriculum/course and enrollment/lesson:\nimmutable parent ownership and parent-tenant checks reject any second tenant\nfor the same parent. The storage prefix cannot create a second valid logical row. Ordinary repeated and concurrent calls\nreturn one row; an aborted workflow can be retried. No in-memory process lock\nis used for PostgreSQL concurrency.\n\nA new year is a new curriculum and enrollment, producing independent progress.\nUnscored `bestScore` persists as null; score zero is a real score. Scores are\nnullable decimals in [0,1]; counters are nonnegative safe integers. Settings\nare stored as JSON text and accessed through guarded `getSettings`/`setSettings`.\n\nThe shared executable workflows in `src/__tests__/learner-workflows.ts` run\nagainst SQLite and PostgreSQL, including actor/resource/context denial,\nconcurrent creation, rollback, history preservation, and zero/null scores.\nFull DuckDB schemas remain blocked by existing upstream DDL restrictions;\nno full learner workflow support on DuckDB is claimed.\n"
|
|
5901
|
+
},
|
|
5902
|
+
{
|
|
5903
|
+
"path": "agents/submission-service.md",
|
|
5904
|
+
"module": "submission-service",
|
|
5905
|
+
"content": "# Submission and paper workflow\n\nConstruct `SubmissionService(db, authorizeLearner, authorizeCatalog, {storage,\nstoreOptions?, signAsset?})` from trusted server configuration. It extends\n`LearnerService`: the same active actor/tenant, memberships, linked person,\ndirected guardian relationship and host permission checks apply. The host must\nallow `education.learner.submit`, `education.learner.grade` or\n`education.learner.packet` as appropriate. Grade and packet actions always reject\nthe learner's own identity, even if the host authorizer returns true.\n\nThe protected transaction/auth hooks are internal owning-service extension\npoints. They are not handler APIs. All public service results remain data-only.\nUse the callback's service in `withTransaction`; escaped services are closed.\nSubmission operations share its serial queue, poison-on-failure behavior and\nPostgreSQL enrollment lock. No runtime schema creation occurs.\n\n## Capture and grading\n\n`captureSubmission` takes the learner request, `enrollmentId`, `lessonId`, a\nnonempty stable `requestKey`, and `pages` containing `name`, `mimeType` and Buffer\n`data`. Each upload becomes a fresh Asset owned by the learner's profile and\nexact tenant, joined solely through `SubmissionAsset`. Up to 100 uploads of\n25 MiB each are accepted (JPEG, PNG, WebP, or PDF). It atomically creates the\nsubmission/asset metadata and moves only that enrollment's progress to\n`submitted`, incrementing attempts once. Caller status, score, grader, asset ID,\nand answer-key flags are never patches. Equal request-key/content retries return\nthe existing submission; altered retries fail. Submitted/review/complete/skipped\nprogress rejects new captures. Content includes ordered filenames, MIME types,\nbytes and notes. Hosts should impose tighter request/body limits as needed.\n\n`captureBlankReference` records a workbook-page scan as immutable\n`blank_reference` evidence without advancing completion or creating a grade.\nTrusted ingestion scopes each receipt to the exact `enrollmentId` and calls\n`assertLessonAssignment` before reading retained evidence; it proves the active\ntenant, actor, learner, enrollment, and lesson without creating progress. A\nstable `intakeItemId` is claimed once in the durable tenant/item binding table,\nthen bound to the tenant, learner, lesson, progress, kind and predecessor; an\naltered retry fails. The binding is database-unique across enrollments while\nordinary uploads remain unbound. `resubmitSubmission` requires the current completed-work predecessor and\ncreates a new immutable `completed_work` attempt. Old pages, grades and feedback\nremain history; stale predecessors cannot be graded or replaced. A correction may\nreopen submitted, review or complete progress, while an AI proposal remains a\nnon-authoritative Grade until guardian review.\n\n`reviewSubmission` accepts an existing authorized submission and optional score\nand feedback. `attestLesson` creates/reuses an Attestation for a matching progress\nitem without requiring uploads. Both require a verified guardian and create a\nnew guardian Grade, set submission to `graded` and progress to `complete`.\nOmitted/null score is unscored; zero is a real score. The latest guardian grade\nwins over auto/AI/instructor proposals, and its score replaces progress's score.\nReview history is retained. ExternalCompletion is available for trusted server\npersistence; phase-zero learner handlers cannot forge reported scores. Online\nsubmissions and automatic grading are not implemented.\n\n`getSubmission` returns only submission metadata, page asset IDs and the selected\ngrade. It does not include catalog answers, extracted answers, storage paths,\nmodel objects, or database handles.\n\n## Storage and signed routes\n\nThe service creates an AssetRuntime bound to the operation's transaction. It\nrequires a resolved URI whose filename retains the fresh asset UUID and MIME\nextension; custom resolvers may choose its provider/directory but must route\nthat URI to the corresponding unique file. Shared existing locators are\nrejected before preservation or cleanup registration. It plans and persists\nthat unique asset URI before writing, registering that new\nasset for cleanup before the provider can partially write. After rollback,\nfiles are removed through the runtime store. Before deletion, the root database\nmust confirm that no asset owner committed: uncertain commit/read failures never\ncause committed bytes to be removed. Only this operation's newly created assets\nare candidates; pre-existing files are never adopted into its cleanup ledger.\n\nDatabase metadata is atomic; external storage uses compensation, not a database\ntransaction. If cleanup cannot complete, `SubmissionStorageCleanupError` contains\nfresh asset IDs and the original cause. Operators must reconcile those IDs in\nthe configured `education-page/<id>.<extension>` storage namespace (custom\nresolvers must retain their routing audit). A successful retry uses fresh IDs\nand cannot delete or reuse orphaned bytes. Process termination may also leave\nunreferenced files; hosts need storage orphan reconciliation. Do not suppress\ncleanup failures or claim crash-safe distributed atomicity.\n\n`signSubmissionAsset` verifies the submission/enrollment/learner ownership and\nits noun join, then verifies the Asset's tenant and learner profile before\ncalling the configured trusted signer. Claims contain actor, tenant, learner,\nenrollment, submission, asset and a five-minute expiry; no static source URI is\nreturned. The host signs every claim and its serving endpoint must verify the\nsignature, expiry, current actor/tenant and object authorization before serving.\nA bearer URL must never become a cross-family or sibling access bypass.\n\n`signLessonPrintable` signs only the exact authorized `printable` LessonAsset\njoin for an enrolled lesson. It never accepts an answer-key join and carries the\nsame tenant, actor, learner and enrollment scope as a submission claim.\n\n## Personal worksheet packets\n\n`splitWorksheetPacket` takes the guardian learner request, exact-owned\n`personal-use-copy` `courseId`, PDF bytes and ranges `{lessonId, firstPage,\nlastPage}`. Page bounds are inclusive and one-based. Ranges must be in bounds,\ndisjoint and target distinct Worksheet lessons in that private course. The\n50 MiB / 100-range limits are enforced before PDF parsing. `pdf-lib` extracts\nactual pages into independent PDFs and records private printable LessonAssets.\nAll resulting metadata commits together; storage failure compensates new PDFs.\nThe source packet is not retained. No shared/global course is accepted and no\nanswer-key join is created. A successful repeated packet upload intentionally\nadds another printable version; a failed upload can be retried safely.\n\nShared integration workflows run on SQLite and real PostgreSQL. Full DuckDB\nworkflow support remains outside the existing supported schema boundary.\n\n## Private asset boundary\n\nCaptured pages and split packet pages use `Asset.accessMode: 'private'` from their\ninitial insert. Ordinary generated Asset REST/MCP operations cannot list, hydrate,\nupdate or delete them, including through the parent STI model. The service opens\nan exact tenant/actor/learner-owner/asset-ID capability only after its normal learner\nrelationship checks. A host's signed serving route must repeat authorization and\nopen that exact capability before loading the asset. See the owning\n[private asset contract](../../assets/agents/private-assets.md).\n\nStorage must be private (including provider ACLs). Public HTTP(S) source locators\nare rejected; a hidden URL alone cannot protect publicly accessible bytes. Apply\nthe manifest migration adding `assets.access_mode` before deploying this runtime;\nlegacy ordinary assets retain the public default. Runtime creates no schema.\n\nPrivate lesson files must already be private Assets in the same tenant. Saving\nan answer-key join, or any join under a personal-use-copy course, requires the\nhost's exact actor/owner/asset capability; a public asset is never automatically\npromoted. Packet splitting retains this capability while creating each join.\nPersisted course tenant/license and section/lesson parent identity cannot change;\ncreate new structure and explicitly re-author its joins instead. Mandatory\nwrite predicates enforce this even for natural-key adoption and concurrent writes.\nOrdinary content and metadata refresh remains supported. This\nprevents existing public material being reclassified by moving its parent.\n\nFor pre-existing invalid joins, an authorized host must inventory private-material\njoins and remove invalid associations, recreate the bytes in private storage as a\nnew privately classified Asset, then re-author the join under its exact capability.\nDo not merely hide the URI, mutate access_mode with raw SQL, or delete shared\npublic bytes. This repair requires explicit storage/ownership authorization;\nnormal runtime does not migrate existing records.\n"
|
|
5906
|
+
},
|
|
5907
|
+
{
|
|
5908
|
+
"path": "agents/workbook-service.md",
|
|
5909
|
+
"module": "workbook-service",
|
|
5910
|
+
"content": "# Physical workbook contract\n\n`WorkbookService` extends `SubmissionService` with the same actor-bound authorization,\nprivate asset runtime, transaction executor and guardian Grade boundary. Both\nLearnerAuthorizer and CatalogAuthorizer receive the active executor. Host session\nheartbeats and current permission checks must use that supplied executor, including\nnested guardian review; a captured outer pool can wait on its own transaction.\nCatalogService obtains the executor through the public collection.db getter and\nfails closed when its four catalog collections do not share the same handle. It is an\nadditive API; existing Submission/Grade consumers retain their contracts. The six\nclosed `Workbook*` models are package-manifest models. Apply normal application\nschema migrations before using them; the service never creates runtime schema.\n\nA guardian calls `resolveWorkbookActivity` with an explicit learner, workbook key,\ntitle, edition (empty means unknown), page key and printed page label. Keys identify\nthe physical copy/edition and page occurrence, including section/side where needed.\nNames and photo hashes do not establish identity. A conflicting reuse fails. One\nprivate personal-use-copy course/enrollment per learner/workbook supplies internal\nsubmission scaffolding. The public page API needs no course-building workflow.\n\n`captureWorkbookWork` owns original files through SubmissionAsset and creates an\nimmutable attempt with source SHA-256 hashes, actual page bounds, actor, capture\ntime, assistance, correction predecessor/episode, policy version and effective\nreview-required flag. Assistance defaults to `unknown`. Capture retries reuse the\nsame Submission/attempt and reject changed bytes or help claims. A correction\nrequires the same learner/activity/current predecessor and stays in the same\npractice episode. Analysis/reanalysis adds revisions, never practice attempts.\nExisting LessonProgress.attempts remains a compatibility capture counter.\n\nThe workbook review-required policy defaults on and is shared by its pages.\n`setWorkbookFeedbackPolicy` uses expected policy version; existing capture snapshots\nremain unchanged. Policy changes cannot erase prior disclosure. Guarded capture,\npolicy and decision writes serialize on the learner within the active transaction.\nIngestion target resolution acquires that learner lock before the enrollment lock,\nmatching capture/review order. Guardian apply follows authorized timeline cursors\nwhen locating a preserved assessment on an older attempt.\nComposite mutations also use the education transaction's serialized composite\nqueue, allowing inherited queued capture/review writes without queue recursion.\nAny swallowed mutation error still aborts the complete transaction and file rollback\nuses the inherited fresh-file compensation ledger.\n\n## Structured assessment and real ingestion\n\n`WorkbookAssessment` version 1 has stable question IDs, original/corrected prompt\nand answer text, explicit result/reason, nullable points, feedback, retained source\nanchors and curated concept mappings. An anchor names an owned asset plus SHA-256,\none-based verified page (or null for a whole-file PDF source) and normalized\nx/y/width/height rectangle. Ingestion proposals never generate rectangles: the\nservice uses [0,0,1,1] and explicit source/page grounding with limitations. The provider wire contract\ncontains evidenceId only, with no page/rect selector. An image is a known single\npage; PDF questions retain a whole original-file anchor with page:null, not a\nclaim about page 1. Extraction metadata remains provenance, never a model page\nlocalization grant. Fictitious PDF\npages and foreign files fail. Missing, illegible, unsupported and absent questions\nare unassessed; unassessed points stay null. Marking certainty, mapping certainty,\nmapping review provenance and evidence limitations are distinct; absent certainty\nis explicitly persisted as unknown. Reviewed overrides append new bodies and keep\noriginal AI text inspectable.\n\nThe configurable vocabulary is trusted server configuration, not AI-created labels.\nThe default `WORKBOOK_MATH_VOCABULARY` has addition, subtraction and place value.\nOther subjects may use an explicit curated `workbookVocabulary` on WorkbookService,\nthe ingestion handler factory and preservation helper. Unknown concept IDs fail;\nzero mapped concepts is valid. AI mappings are proposed, guardian mappings reviewed.\nKnown question IDs are pinned across captures; exact prompts are pinned within a\ncapture's reanalysis only. Correction transcriptions come from the new capture,\nso earlier answer-bearing prompts cannot override corrected work. Immutable\nhistorical prompts remain unchanged. The public `getWorkbookQuestionIdentity`\nreturns IDs only: historical prompt text can itself include a learner response.\nHandlers use printed number/occurrence/subpart when present; unnumbered tasks use\nanswer-free printed-task slugs with visible-task occurrence/subpart, never output\narray position or learner answers. They\nread prompt, answer, feedback and marks exclusively from current evidence. New\nprompts should contain printed task text without the learner response. An absent\nor ambiguously matched known question is unassessed/not_present with an explicit\nlimitation. Missing, duplicate, invented or foreign IDs reject the proposal; a\nchanged question order does not change identity. The ID check does not establish\nsemantic equivalence of printed tasks; uncertain correspondence requires human\nreview rather than an invented match.\nA different page/question requires explicit new activity identity, never AI retargeting.\n\n`captureEducationIntake` accepts optional `workbookActivityId`/`assistance` when its\nservice is WorkbookService. It preserves the existing return SubmissionView and\nbinds the workbook attempt/policy before the host queues processing.\n`educationIntakeHandlers({workbookActivityId,...})` advertises a bounded JSON question\nschema to real ingestion generation. Assessed results require reason none;\nunassessed reasons require null points. Per-question outcomes must agree with\npoints. Correctness is independent of scoring: readable objectively solvable\narithmetic may be assessed without a printed rubric. When no point allocation is\nevidenced, both `points` and `possiblePoints` remain null for every result,\nincluding incorrect; never invent weights or mix null/numeric values. The\naggregate still counts correct/incorrect results while its score remains null.\nIncorrect earns zero only when the possible-point allocation is known.\nModel aggregate scores/narratives are not requested or trusted: exact\ncount summaries and points scores are computed from validated questions without\nrounding or invented partial credit. Default legacy handlers remain compatible.\n`preserveWorkbookIntakeAnalysis` reads the public immutable completed generation,\nchecks its exact submission and offered workbook schema, maps cited original\ningestion evidence by hash/order to retained education-owned files and imports one\nvalidated proposal. Provider/model/version, configuration/rubric, source analysis\npin and proposal digest are durable provenance. Pending/failed processing produces\nno assessment. Malformed/ambiguous/foreign-source proposals return typed needs_review with no\neducation revision or Grade. The host can offer retry/review without a route 500.\nAuthorization, durable-source integrity and database failures still throw. Repeated\ntransfer is idempotent by analysis attempt. Preservation must complete before host\nscratch retention cleanup; there is no automatic retention bypass in this API.\nThe ingestion handler can apply only an exact previously preserved proposal through\nguardian review; it never grants AI grade authority. No prose parsing is used. The generation configuration carries bounded source\nhash/page-support provenance from the completed extraction before it ceases to be\nthe current analysis. This uses public ingestion APIs and is pinned by its\nconfiguration digest; question anchors always use honest source-only grounding.\n\n## Authority, visibility and history\n\n`recordWorkbookAnalysis` accepts authorized submit context and writes provisional\nAI revisions only. `reviewWorkbookAssessment` requires guardian authority, exact\nsource revision, expected current guardian head and stable request key. It appends\nan immutable guardian revision recording sourceRevisionId, superseded head, actor,\ntime and decision reason. Changed retries conflict. Review of the current capture\nuses the existing guardian Grade/progress boundary. Historical annotation changes\nits evidence authority only and cannot grade or reopen its successor. Reanalysis\nafter a guardian decision remains provisional. A guardian can invalidate an outcome\nwith an unassessed replacement and a stated reason; old AI does not regain authority.\n\n`releaseWorkbookFeedback` separately releases an exact revision using expected\ncurrent guardian head. New stale releases fail; accepted retry requests replay their\noriginal result after later decisions. Release creates no Grade. With the gate off,\nAI feedback is visible as provisional while authoritative grading still requires a\nguardian. Gate-on learners receive no unreleased assessment payloads, scores or\nhints. WorkbookService's ordinary getSubmission also redacts Grade for own learners;\nhosts must route workbook reads through this service and prevent legacy routes from\nserving workbook grades. Guardian history is complete; learners see only their own\nwork and visible revisions. Answer keys/source storage URIs never enter these DTOs.\n\n`acknowledgeWorkbookFeedbackDelivery` is an authenticated own-learner acknowledgment\nfor an exact currently visible feedback revision. It deduplicates per actor/revision\nand rejects withheld, foreign, sibling and guardian acknowledgments. Immutable\ndelivery events record actor/time and mean rendered/delivered, never read or understood.\n\n`getWorkbookTimeline` uses education rows/assets without reading ingestion. It lists\nnewest captures/revisions first, includes all releases/delivery events and provides\ncursor pagination (1–100, default 25). A guardian revisionId selector locates its\ncontaining capture for evidence links. Guardian source IDs/actors/reasons remain\ninspectable. Released historical revisions remain visible after supersession.\n\n`getConceptEvidenceSummary` is guardian-only for this milestone. It recomputes from\ncurrent guardian revisions, separates provisional observations, and reports independent,\nassisted and unknown evidence. Correction captures remain one episode; they cannot\nbe extra independent demonstrations. Every observation cites capture, question,\nrevision, episode, result, assistance and time. Unassessed work is excluded from\nerror/correct counts. Descriptive cues cite actual evidence: needs practice, improving\non this item, needs a fresh independent check, not enough evidence. No mastery\npercentage, generic score comparison, hidden threshold or predictive label is used.\n\n## Preserved processing status and retention\n\n`getWorkbookPreservationState` authorizes exact actor/learner/activity/submission\nand returns only the intake ID plus retained assessment/analysis IDs and the\ninterpretation revision. It contains no questions, scores, feedback or hidden\nassessment payloads. New AI provenance records `analysisRevision` from the\ncompleted interpretation snapshot, separately from its extraction-source pin.\n\n`preserveWorkbookIntakeAnalysis` checks this education-owned state and the public\ncontent-free ingestion item revision before requesting raw completed analysis.\nAn exact already-preserved revision returns `retained` immediately. Guardian\nreview, release, corrections and receipt expiry therefore do not force a stale\nproposal reread or prevent durable education history from loading. The public\ningestion stale-snapshot guard remains unchanged and still rejects the old raw\nsnapshot after guardian grading. A genuinely new valid analysis can append a new\nprovisional revision; a novel stale analysis returns `needs_review/stale_analysis`\nand creates no mark. Legacy retained rows without interpretation revision remain\nreadable when scratch is stale/unavailable or the public\nProposalsNotConfiguredError reports removed provider read configuration. A novel\nanalysis with no provider read configuration returns unavailable without import; an authorized current snapshot whose\nattempt ID is already retained is also skipped before retry reconstruction.\n\nOnly the exact public scratch `Intake unavailable` response is translated to\nretained history, or `unavailable` for a capture with no retained assessment,\nafter independent education authorization. Missing/pending current analysis may\nreturn pending or retained. Unexpected configuration, integrity and database\nfailures still throw. This does not widen scratch access: no stale or unavailable\nraw proposal is read, imported or used as grade authority. Hosts must also render\nthe authorized education timeline independently of optional scratch processing.\n\n## Compatibility API privacy boundary\n\nWorkbook feedback is projected only from the revision/release workflow. The base\n`SubmissionService` recognizes authoritative `WorkbookActivity` lesson ownership,\nincluding calls through a plain service instance. Own-learner compatibility views\nwithhold Grade and grade-derived status; learner progress projections withhold\nbestScore/completedAt and do not let completion alter next-lesson selection.\nGuardian and ordinary non-workbook projections retain their existing behavior.\nThis applies in both feedback policy modes; direct feedback remains provisional\nand does not authorize compatibility Grade disclosure.\n\nGeneric capture/resubmission rejects workbook enrollment or lesson scaffolding,\nincluding idempotent retries. Only the protected exact-activity owning workflow\ncan capture workbook work. Hosts must call public\n`assertNonWorkbookSubmissionScope` before legacy intake receipt creation and when\nreading an old legacy binding, using authoritative enrollment, lesson and\npredecessor selectors. `WorkbookWorkflowRequiredError` is a controlled denial.\nThe browser's optional workbook flag is never authority. Authenticated original\nasset delivery remains a separate read authorization path.\n\n## Validation and limits\n\n`src/__tests__/workbook-workflows.ts` is the shared SQLite/PostgreSQL contract suite;\n`workbook-service.test.ts` runs SQLite, and `workbook.optional.test.ts` runs against\nan available isolated PostgreSQL database. Workers never provision service stacks.\nRun package test/typecheck/build; the integration owner runs test:postgres and the\nintegrated review. Synthetic provider DTO tests establish transfer integrity, not\nreal handwriting accuracy. Actual provider/sample and app restart/retention\nacceptance remain separate integration evidence. Family deletion/export/backup\npolicy and automatic lesson generation are not implemented by this milestone.\n\n## Identity-independent work\n\n[Photo intake](photo-intake.md#marking-before-filing) may own an explicit unfiled\nwork unit. Its nullable workbook FK is constrained to an exact retained receipt;\nprivate enrollment/worksheet routing is technical rather than bibliographic.\nThe same attempt/assessment/review/release and concept-evidence contracts apply.\n`filing` is optional on legacy-shaped DTOs and explicit on current projections.\nAppend-only filing changes display association only; it never moves immutable\nlearning evidence or changes captured review policy. Ordinary identified workbook\nidentity and generated-surface restrictions remain unchanged.\n\n### Unnumbered visible questions\n\nA printed question number is not required for marking. On the first valid\nassessment, an unnumbered task may use `u:<short printed-task slug>:<occurrence/subpart>`\nfrom current answer-free task text and unambiguous visible task identity. The\noccurrence distinguishes tasks in the source, never positions in a generated\nquestions array. Responses, computed solutions and changing evidence UUIDs cannot\nsupply identity. Printed IDs remain useful where present; both forms share the\nexisting nonempty/unique ID schema and 200-character bound.\n\nOnce preserved, the exact ID set remains pinned on reanalysis and correction.\nReordering output does not renumber tasks. A cropped, duplicate or illegible task\nwhose correspondence cannot be established remains `unassessed/not_present` with\na limitation on its known ID; it cannot be silently relabeled as another task.\nHistorical IDs are correspondence hints, not evidence of current responses.\nMissing IDs, collisions, changed ID sets and same-capture prompt remapping still\nfail closed. The framework never inserts omitted provider IDs or parses prose to\nrepair malformed proposals.\n\n### Semantic identity visibility\n\nQuestion IDs can contain printed task text. Own-learner identity reads and\noperation discovery therefore require release of the **selected exact revision**\non a guardian-origin source attempt. An unrelated revision or another attempt's\nrelease is insufficient. Ordinary own-origin IDs remain available for legitimate\ncurrent-work marking and corrections without exposing withheld grades/feedback.\n\nCorrection ancestry is checked completely under the same tenant, learner,\nactivity and practice episode. Each guardian-origin ancestor must have an exact\nreleased revision publishing the compatible immutable ID set before a descendant\ncan expose those semantic IDs. Matching published ID sets preserve already public\nhistorical snapshots after a later private head, without aliases or rewritten IDs.\nMissing, corrupt or cyclic ancestry fails closed; there is no depth cutoff that\nsilently skips earlier source guards. Current descendant release never grants\nrights to a hidden ancestor.\n\nThe same boundary governs child correction capture, timeline payloads and delivery\nacknowledgments, including legacy/trusted-produced descendants. Raw provisional\nrevision writes/returns cannot probe or replay guardian-origin private content;\nauthorized guardians retain processing/history access, and own-origin correction\nmarking remains supported once ancestral source identity is published. Schema,\nID collision/remapping rules and immutable stored history remain unchanged.\n\nThe offered marking schema mirrors the assessment validator: assessed results use\n`markingCertainty` clear/unknown; `uncertain` marking requires `unassessed`, an\nallowed non-none reason and both point fields null. This is not a conversion of\nprovider output: contradictory results remain refused. Mapping certainty remains\nindependent. Visible but overwritten/ambiguous responses can stay unassessed while\nother clearly visible questions are assessed in the same valid proposal.\n\n### Observed assessment layout\n\nOptional `WorkbookAssessment.layout` version 1 describes observed sections in source\norder and zero-based row/column question placements. It is advisory, with fixed\n`provenance: 'observed'`; it never establishes identity or verified geometry.\nSections reference a retained source asset and a physical one-based observed page,\nor null when unknown. A whole-file PDF anchor may support an observed page within\nits retained page count while keeping its original anchor page null. Every placed\nquestion must anchor the section's exact source. Multiple question IDs may share a\ncell. When every placement in a cell has an explicit `part`, sort by part then\noriginal question array index for ties. If any placement in that cell omits\n`part`, the whole cell follows original question array order. Partial placement is valid. Missing `readingOrder` preserves original\nquestion order for a flattened display.\n\nThe browser-safe `normalizeWorkbookObservedLayout` copies only bounded known\nfields: at most 32 sections and 200 placements, 1–8 columns, rows 0–199, parts\n0–31, section IDs up to 64 characters and labels up to 200. Duplicate section IDs\nor question placements, invalid source/page/placement references and malformed\nmetadata discard the complete optional layout and retain `layoutOmission: 'invalid'`.\nValid answers and normal assessment validation remain independent. Owning analysis\nand guardian writes both normalize against the current capture's source hashes;\na correction cannot inherit a previous capture's source layout. Default guardian\ncopies retain valid layout and safe omission markers. Bodies remain immutable JSON\ninside existing revision privacy and exact release boundaries; no migration or\nadditional learner visibility is introduced.\n\nFocused shared SQLite/PostgreSQL cases live in\n`src/__tests__/workbook-layout-workflows.ts`, with SQLite in\n`workbook-layout-service.test.ts` and isolated PostgreSQL in\n`workbook-layout.optional.test.ts`. Pure bounds/reference cases are in\n`workbook-layout.test.ts`. Integration runs the PostgreSQL wrapper under its\nexisting isolated database lifecycle.\n\nGeneration carries optional `layout` as advisory JSON so malformed presentation\ncannot make valid question answers fail the generic schema. Its described wire\nshape uses original evidence IDs; the bridge maps those IDs to retained assets\nand then calls the strict owner normalizer. Within existing transport/JSON resource\nlimits, wrong types, unknown versions and conflicting metadata are omitted with\nonly the safe marker, while answers retain their normal strict validation. Layout\nnever establishes a question ID, a source authorization or a verified region.\n\n\n### Same-capture reread context\n\n`getWorkbookQuestionContext` returns authorized canonical `{id,prompt}` only\nfrom the exact current attempt's identity revision. Generation preserves those\nstrings verbatim for the existing strict same-capture prompt invariant, while\nrereading learner responses and any observed layout from current evidence. This\ncontext is not an answer key or authority for answers. A correction without its\nown revision receives predecessor IDs only; predecessor prompts are never copied.\nExact source/revision and full correction-ancestry visibility apply to both paths,\nincluding own-learner reads. No historical rows are rewritten and absent layout\nremains valid. `getWorkbookQuestionIdentity` remains the IDs-only public API.\n\nCanonical prompts follow ordinary assessment payload visibility, including\ncaptured review policy and exact guardian revision release even for learner-owned\nsources; source/ancestry authorization alone does not publish guardian edits.\nFor ingestion callbacks only, the engine supplies current `analysisRevision` from\nits owned item row. Context selection ignores AI materialized by that exact\ncurrent-attempt revision, so preserving a proposal does not invalidate its own\ncompleted snapshot or preview. It never ignores guardian decisions, predecessor\nrevisions or another interpretation. Direct reads omit this engine pin and retain\nnormal current revision visibility; browser/upload fields cannot supply the pin.\n\n### Observed answer-region overlays\n\nOptional `answerRegions` version1/provenance observed/coordinateSpace\n`display-oriented` carries one approximate rect `[x,y,width,height]` per question,\nwith exact retained asset/page1 and observationId. Rects are normalized to the\nwhole EXIF-oriented image; they never change question IDs, grading, anchors or\nlayout, and never claim verified geometry. Images only in this version; PDFs or\nunknown positions omit regions. Direct malformed metadata is omitted with safe\n`answerRegionsOmission: invalid`; marking/layout survive independently.\n\nVision catalogs originate in the existing opt-in extraction call. Generation\nreferences exact evidence/observation IDs, never supplies coordinates. The bridge\ncopies catalog rectangles from the extraction-pinned configuration with matching\nsource hash. Guardian copies retain them; correction sources must match current\nanchors. Existing exact release and ancestry privacy apply to the complete body.\n"
|
|
5911
|
+
},
|
|
5912
|
+
{
|
|
5913
|
+
"path": "agents/photo-intake.md",
|
|
5914
|
+
"module": "photo-intake",
|
|
5915
|
+
"content": "# Photo-first workbook intake\n\n`WorkbookPhotoService` extends `WorkbookService` with the same constructor,\ntransaction executor, actor/tenant checks, private storage and rollback ledger.\nImport server integration from `@happyvertical/smrt-education/ingestion`.\nApply manifest migrations for `WorkbookPhotoIntake`, `WorkbookPhotoAsset`,\n`WorkbookPhotoDecision` and `WorkbookContextRevision` before deploying. There is\nno runtime schema creation and these models expose no generated operations. Each\nconcrete table declares its own tenant/learner fields; a custom abstract model\nbase does not supply persisted columns to independent manifest tables.\n\n## Receipt before assignment\n\nConstruct the service and `IngestionService` from one authenticated server session.\nUse `scope.confidentialScopeId = request.learnerId`, not an enrollment or model\ninference. `authorizePhotoIntake(request)` authorizes a learner's own upload or a\ncurrently authorized guardian before `receiveWorkbookPhotoIntake({ingestion,\nscope,workbooks,request,receive})` calls ingestion. Its result is `{receipt,intake}`;\n`intake:null` means no successful durable receipt. `retainWorkbookPhotoReceipt`\nrepairs an interrupted education copy from an existing authorized receipt.\n\nThe immutable education receipt retains the original bytes as private Assets\nthrough the sole `WorkbookPhotoAsset` owner join, preserving ordered SHA-256\nhashes. Copy retries reject changed bytes, filenames, media types or learner\nownership. Storage uses the inherited fresh-file compensation ledger; borrowed\nhost executors cannot perform file writes. Provider failure does not delete the\nreceipt or originals. `getWorkbookPhotoIntake`, `listWorkbookPhotoIntakes` and\n`readWorkbookPhotoOriginal` reauthorize exact ownership independently of scratch\nretention. A learner cannot list/read guardian-uploaded pending or context-only\nmaterial. Guardian-origin lesson source remains private until an explicit\nfeedback release on its exact captured attempt, regardless of automatic material\nclassification or the workbook provisional-feedback setting. Guardian reads remain\nsubject to the current relationship and grants. Child-owned uploads retain the\nnormal learner boundary.\n\n## Provider identification and human decisions\n\nConfigure `workbookPhotoIntakeHandlers({workbooks,request})` and\n`workbookPhotoIntakeTargetResolver({workbooks,request})` on ingestion's execution\ncatalog, and the normal proposal generator/read configuration. Handler\n`@happyvertical/smrt-education:identify-workbook-photo` uses\n`@happyvertical/smrt-education:WorkbookPhotoIntake:get@1` and\n`workbookphotointakes.read`. This operation classifies only; applying it never\ncreates a Grade. Keep `requireReview:true`. Candidate freshness checks use the\nsupplied executor and the exact authorized workbook/context digest.\n\nThe initial immutable captured ceiling must include all intended later stages\nand a provisioned stable execution principal; for marking, include the existing\nreview-submission/Grade operation as well. Current guardian authorization still\ncontrols every Grade. Never widen the receipt ceiling after upload or create a\nsecond receipt to evade that boundary.\n\nQueue with `queueEducationExtraction`, process extraction/interpretation with\n`processEducationAnalysis`, then call `preserveWorkbookPhotoIdentification`.\nAlternatively `processWorkbookPhotoAnalysis` combines processing, preservation\nand capture of a confidently resolved **completed** lesson. It returns\n`{processing,intake,capture,assessmentRequired}`. The provider runs outside the\neducation transaction. Preservation reads the current public immutable ingestion\nsnapshot, exact offered schema and original hashes; it retains analysis IDs,\nrevision, digests and provider/configuration provenance in append-only decisions.\nUnavailable, failed, stale, malformed and ambiguous results leave durable intake\npending. A provider needs-review outcome, alternatives or splits never authorize\nautomatic resolution; its original proposal remains inspectable. Literal page\nnumbers cannot establish lesson identity. Authorization/integrity/database failures still throw.\n\nProvider output distinguishes lesson, cover, reference (including contents and\nanswer keys), and uncertain material, plus blank/completed/unknown work state.\nAutomatic book identity requires a printed ISBN or complete printed\npublisher/title/edition quotations. Title alone never merges. An existing book may instead match exact current printed title/edition to one\nauthorized contextual record with retained publisher provenance; that publisher\nis context, never a fabricated current quotation. Conflicting or ambiguous\nmatches stay pending. Provider\ncandidates contain book metadata only, never reference text or answer keys.\n`listWorkbookPhotoWorkbooks(request)` includes cover-only books for the provider\nand guardian picker. A printed lesson identity supplies one stable activity key;\nmultiple page files may belong to that same lesson. Page number alone cannot\nautomatically establish lesson identity.\n\nA guardian may call `resolveWorkbookPhotoIntake({...request,intakeId,requestKey,\nidentification})` after upload. It requires planning authority and appends a new\ndecision. Stable request-key retries replay; changed retries conflict. Covers and\nreferences create/reuse the Workbook and append contextual metadata only: no\nCourse, Lesson, progress, attempt, Grade or concept observations. Existing\nWorkbook title/edition identity never changes; context versions retain original\nmetadata and source receipt identity. A captured intake cannot be retargeted.\n\n## Existing marking pipeline\n\n`captureWorkbookPhotoIntake({...request,intakeId,requestKey,assistance?,\npreviousSubmissionId?,completedWork?})` copies the retained originals through\n`captureWorkbookWork` and returns its immutable attempt. Blank or unknown work\nrequires explicit `completedWork:true`; no blank exercise is automatically marked\nas unanswered learner work. Corrections require the explicit current predecessor\nand preserve the existing practice episode. Existing attempts reject automatic\nreplacement; provider classification cannot silently turn a new photo into a\ncorrection.\n\nAfter capture, configure the existing `educationIntakeHandlers` with the returned\nsubmission ID, `view.activity.id`, and authoritative activity enrollment ID.\nQueue `queueEducationExtraction(ingestion,view.itemId,configuration,submissionId)`\non the **same learner-scoped receipt**, process normally, then use\n`preserveWorkbookIntakeAnalysis`. That bridge already verifies exact education\nownership and source binding independently of the receipt's confidential scope.\nGuardian Grade/release policy and immutable assessment history are unchanged.\n\n## Verification matrix\n\n`src/__tests__/photo-intake-workflows.ts` runs on SQLite and available PostgreSQL\n(`photo-intake.test.ts` / `photo-intake.optional.test.ts`).\n`photo-intake-manifest.test.ts` separately pins all concrete ownership/provenance\ncolumns and UUID/FK definitions in the generated structured manifest. It covers actor/resource\nownership and active context, private originals, sibling/foreign asset denial,\nretry conflicts, concurrent deduplication, rollback and borrowed executor denial,\ncover-only context and immutable identity, ambiguous titles, source integrity,\nblank work intent, multi-page capture, real ingestion generation for cover then\nlesson, provider failure, and guardian-only pending/reference originals. The same\ntransaction executor is used for authorization and mutations. Real provider\nhandwriting accuracy and deployment worker isolation remain host acceptance.\n\nRun package test/typecheck/build; integration owns PostgreSQL provisioning and\nfull-suite validation. Workers never start containers. This is additive feature\nwork; a base regression run is N/A because no pre-activity capture API existed.\n\n## Guardian-origin source privacy\n\nThe immutable photo receipt actor, compared with the persisted learner user,\ndetermines source origin. Classification as a lesson does not grant learner\naccess. `Submission.intakeItemId` joins the exact tenant/learner receipt; only a\n`WorkbookFeedbackRelease` on that submission's exact `WorkbookAttempt` releases\nits source. Another attempt or context-only page cannot borrow that release.\n\nThe base `SubmissionService` applies this policy too: own learners receive no\npages, notes, Grade/feedback or grade-derived status before release, and signing\na guessed source asset is denied. The DTO exposes `awaitingParentRelease:true`\nfor an own learner awaiting release, and `sourceRequiresParentRelease:true` to\nall authorized actors for guardian-origin material. The latter remains true\nafter release: every new derived revision still needs its own explicit release.\n`WorkbookAttemptView.reviewRequired` and persisted capture policy snapshots\nretain their historical workbook setting; combine that setting with\n`submission.sourceRequiresParentRelease` when describing effective visibility. Workbook timeline and\nfeedback delivery acknowledgment require exact revision release for this source,\neven when the general workbook policy permits provisional AI feedback.\n\nExisting hosts without photo schema retain ordinary submission behavior through\noptional table-existence checks; no application schema is created at runtime.\nThe shared SQLite/PostgreSQL privacy case covers guardian-origin mistaken lesson\ncapture, policy off, automatic AI and guardian assessment before release, direct\nbase submission reads/signing, exact release, later unreleased reanalysis and a\ndifferent attempt that stays private. Existing own-learner capture cases remain\nreadable, and legacy submission/workbook suites protect consumers without photo\ntables.\n\n## Existing candidate identification\n\n`workbookId` selects an exact authorized candidate; it does not replace the\nrequired `identification.workbook` contract. Copy that candidate's `workbookKey`,\n`title` and `edition` into the required object. Those are catalog identity fields,\nnot invented IDs or claims about what is printed on the new photograph.\n`identityEvidence` quotes only current visible material. A unique current\ntitle/edition may match retained publisher provenance without publisher/ISBN\nbeing reprinted. Conflicting identity, duplicate candidates and title-only\nmatching remain pending; malformed model output is never repaired automatically.\n\n## Exact receipt authorization\n\nProcessing and retries resolve `context.itemId` through the service-owned\n`getWorkbookPhotoIntakeByItem` lookup, scoped to the current tenant and learner\non the supplied executor. It delegates to the same current actor and private\nsource/release checks as the intake-ID getter. The inbox list remains capped at\n1,000 rows for display; that pagination window is never an authorization or\nlifetime receipt limit.\n\nHosts may opt into `listWorkbookPhotoIntakes(request, { newestFirst: true })`\nfor a current inbox. Default ordering remains unchanged and both orders keep the\n1,000-row display bound and the same per-receipt visibility checks. Exact selected\nreceipt reads remain independent of either display window.\n\n## Marking before filing\n\nCompleted learner work does not require a printed lesson number or workbook\nidentity. Identification `kind:'work', workState:'completed'` deliberately omits\n`workbook`, `activity` and `workbookId`. Its confidence concerns observable learner\ncompletion/material kind, independently of bibliographic identity. Printed worked\nexamples, answer keys, blank pages and uncertain material are not completed learner\nwork. `kind:'lesson'` retains the existing strict printed identity contract.\n\nConfident work preservation creates one receipt-bound **unfiled** work unit.\n`processWorkbookPhotoAnalysis` captures it and returns the existing\n`capture/assessmentRequired` result. Queue the normal assessment stage on that\nsame receipt, submission and returned activity/enrollment IDs; do not re-upload or\ncreate another receipt. Visible responses can be assessed while illegible or\nambiguous responses remain unassessed. The extraction observation contract keeps\nprinted examples separate from learner marks and reports matching endpoints only\nwhen observed. Adopt it with a new extraction configuration revision.\n\nThe internal private Course/Enrollment/Worksheet are routing records, not a claim\nabout a canonical book or lesson. `WorkbookActivity.workbookId` is null only for\nexplicit `captureKind:'unfiled'` with an exact owned `photoIntakeId`; all ordinary\nidentified activity invariants remain. No Workbook is invented. Activity DTO\n`filing.state` is `unfiled`, `filed` or `identified`; empty workbook title/key/edition\non unfiled work are intentional. Use the work label as the primary display.\n`listWorkbookActivities` includes these work units. Exact\n`getWorkbookPhotoTimeline({...request,intakeId,limit?,cursor?,revisionId?})`\nreturns existing authorized feedback/history; the normal activity timeline also\nworks. `getWorkbookActivity` refreshes exact filing presentation without loading\nhistory. The receipt view refreshes filing rather than replaying its old label.\n\nOptional guardian `fileWorkbookWork({...request,activityId,workbookId,\ncanonicalActivityId?,expectedRevisionId,requestKey,reason})` appends an association\nto a real authorized workbook and optionally a real identified activity. Initial\nexpected revision is null. Stable retries replay their original accepted result;\nchanged retries/stale heads conflict. Filing does not move or recreate activity,\nsubmission, attempt, assessment, release, source or correction episode IDs. It\ncannot change captured review policy, create a Grade, duplicate concept evidence,\nor release guardian-origin material. Future corrections continue against the\noriginal work-unit ID and explicit current predecessor. Existing identified\ncaptures cannot be retargeted through filing.\n\nApply normal manifest migrations before using this contract. Existing activities\nreceive `capture_kind='identified'` through the declared database default; the\nworkbook FK becomes nullable and photo-intake FK plus filing table are added.\nOld populated SQLite requires the explicit supported nullability-relaxation\nmigration described by #4413; no runtime DDL or manual SQL workaround is allowed.\n\nWhen no book record exists, the same optional filing operation accepts\n`newWorkbook:{title,edition?,workbookKey?}` instead of `workbookId`. Exactly one is\nrequired. Parents need only title/edition; an omitted key becomes an opaque stable\nmanual identity for this work/request, never an automatic title-based match.\nAn explicit existing key must retain its exact title/edition. New metadata cannot\nalso claim `canonicalActivityId`. Book creation and filing share the guardian's\nserialized transaction: stale/unauthorized/conflicting requests create nothing,\nand a later failure rolls both back. No cover re-upload, canonical lesson,\nsubmission replacement or second assessment is needed. New books keep the default\nreview policy; filing never changes the source attempt's captured policy.\n\n## Exact cover context for header selection\n\n`getWorkbookPhotoIntake` adds optional nullable `workbookId`, loaded from the latest\nretained resolution's exact WorkbookContextRevision under the same tenant/learner\nand decision ID. Pending/uncertain or unfiled-work resolution returns null; this is\nindependent of a later activity filing. It never guesses from title or an inbox\nlist. Existing guardian-origin receipt privacy still applies before lookup.\n\nHeader cover camera/upload uses the normal receive/process photo flow. A confident\ncover resolves book/context only, with no attempt, lesson or Grade. Read that exact\nintake, then let the guardian explicitly choose `fileWorkbookWork({workbookId,...})`.\nAn uncertain cover can use the existing guardian `resolveWorkbookPhotoIntake` with\nmanual cover metadata, followed by the same exact getter and explicit filing.\nNeither cover identification nor context alone publishes learner source/feedback.\n"
|
|
4182
5916
|
},
|
|
4183
5917
|
{
|
|
4184
5918
|
"path": "agents/source-import.md",
|