@playcademy/sandbox 0.10.1-beta.1 → 0.10.1-beta.2

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/dist/cli.js CHANGED
@@ -1128,7 +1128,7 @@ var package_default;
1128
1128
  var init_package = __esm(() => {
1129
1129
  package_default = {
1130
1130
  name: "@playcademy/sandbox",
1131
- version: "0.10.1-beta.1",
1131
+ version: "0.10.1-beta.2",
1132
1132
  description: "Local development server for Playcademy game development",
1133
1133
  type: "module",
1134
1134
  exports: {
@@ -11002,6 +11002,32 @@ function scorePlatformAssessmentItem(item, responses) {
11002
11002
  isCorrect: verdicts.length > 0 && verdicts.every(Boolean)
11003
11003
  };
11004
11004
  }
11005
+ function reportReviewRequiredPairUnavailable(review, administeredItemIdentifiers2, report) {
11006
+ if (!review.requireBothQuestions) {
11007
+ return;
11008
+ }
11009
+ const administered = new Set(administeredItemIdentifiers2);
11010
+ const failures = [];
11011
+ for (const standard of review.standards) {
11012
+ const key = assessmentStandardRefKey(standard);
11013
+ const selected = new Set(review.selections.filter((selection) => assessmentStandardRefKey(selection.standard) === key).map((selection) => selection.itemIdentifier));
11014
+ const administeredCount = [...selected].filter((identifier) => administered.has(identifier)).length;
11015
+ if (selected.size < 2 || administeredCount === 1) {
11016
+ failures.push({
11017
+ standard,
11018
+ reason: selected.size < 2 ? "selection_shortage" : "not_administered",
11019
+ requiredCount: 2,
11020
+ selectedCount: selected.size,
11021
+ administeredCount
11022
+ });
11023
+ }
11024
+ }
11025
+ if (failures.length > 0) {
11026
+ try {
11027
+ report({ code: "REVIEW_REQUIRED_PAIR_UNAVAILABLE", failures });
11028
+ } catch {}
11029
+ }
11030
+ }
11005
11031
  function reviewStandardFieldsWithinLimits(input) {
11006
11032
  return input.framework.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength && input.identifier.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength;
11007
11033
  }
@@ -11054,7 +11080,11 @@ function normalizeReviewRequest(request) {
11054
11080
  if (standards.length === 0) {
11055
11081
  throw new Error("At least one review standard is required");
11056
11082
  }
11057
- return { standards, candidateItemsPerStandard };
11083
+ return {
11084
+ standards,
11085
+ candidateItemsPerStandard,
11086
+ ...request.requireBothQuestions ? { requireBothQuestions: true } : {}
11087
+ };
11058
11088
  }
11059
11089
  function reviewRequestFingerprint(request, policy) {
11060
11090
  const normalized = normalizeReviewRequest(request);
@@ -11064,6 +11094,7 @@ function reviewRequestFingerprint(request, policy) {
11064
11094
  version: policy.version
11065
11095
  },
11066
11096
  candidateItemsPerStandard: normalized.candidateItemsPerStandard,
11097
+ ...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
11067
11098
  standardKeys: normalized.standards.map(assessmentStandardRefKey)
11068
11099
  });
11069
11100
  }
@@ -11646,7 +11677,7 @@ function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersi
11646
11677
  });
11647
11678
  }
11648
11679
  function isReviewAttemptMetadata(value) {
11649
- return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
11680
+ return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && (value.requireBothQuestions === undefined || typeof value.requireBothQuestions === "boolean") && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
11650
11681
  }
11651
11682
  function isOptionalInteger(value) {
11652
11683
  return value === undefined || Number.isInteger(value);
@@ -12344,6 +12375,7 @@ var init_assessment_runtime2 = __esm(() => {
12344
12375
  }),
12345
12376
  StartAssessmentBaseSchema.extend({
12346
12377
  purpose: exports_external.literal("review"),
12378
+ requireBothQuestions: exports_external.boolean().optional(),
12347
12379
  standards: exports_external.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
12348
12380
  candidateItemsPerStandard: exports_external.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
12349
12381
  }),
@@ -100314,6 +100346,7 @@ function buildConventionalAssessmentSnapshot(input) {
100314
100346
  purpose: metadata2.purpose,
100315
100347
  standards: [...metadata2.review.standards],
100316
100348
  candidateItemsPerStandard: metadata2.review.candidateItemsPerStandard,
100349
+ ...metadata2.review.requireBothQuestions ? { requireBothQuestions: true } : {},
100317
100350
  selections: [...metadata2.review.selections],
100318
100351
  fulfillment: metadata2.review.fulfillment
100319
100352
  };
@@ -102785,11 +102818,13 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
102785
102818
  review: {
102786
102819
  requestFingerprint: reviewRequestFingerprint({
102787
102820
  standards: plan.review.standards,
102788
- candidateItemsPerStandard: plan.review.candidateItemsPerStandard
102821
+ candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
102822
+ requireBothQuestions: plan.review.requireBothQuestions
102789
102823
  }, DEFAULT_REVIEW_SELECTION_POLICY),
102790
102824
  bankRevision: plan.review.bankRevision,
102791
102825
  standards: plan.review.standards,
102792
102826
  candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
102827
+ ...plan.review.requireBothQuestions ? { requireBothQuestions: true } : {},
102793
102828
  selections: plan.review.selections,
102794
102829
  fulfillment: plan.review.fulfillment
102795
102830
  }
@@ -103972,6 +104007,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
103972
104007
  bankRevision: catalog.bank.bankRevision,
103973
104008
  standards: [...normalized.standards],
103974
104009
  candidateItemsPerStandard: normalized.candidateItemsPerStandard,
104010
+ ...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
103975
104011
  selections: [...selected.selections],
103976
104012
  fulfillment: selected.fulfillment
103977
104013
  }
@@ -104243,6 +104279,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
104243
104279
  bankRevision: source.bank.bankRevision,
104244
104280
  standards: [...normalizedRequest.standards],
104245
104281
  candidateItemsPerStandard: normalizedRequest.candidateItemsPerStandard,
104282
+ ...normalizedRequest.requireBothQuestions ? { requireBothQuestions: true } : {},
104246
104283
  selections: [...selected.selections],
104247
104284
  fulfillment: selected.fulfillment
104248
104285
  }
@@ -104518,6 +104555,16 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
104518
104555
  };
104519
104556
  });
104520
104557
  if (accepted.repair) {
104558
+ const { metadata: metadata2 } = accepted.repair.graded;
104559
+ if (metadata2.purpose === "review") {
104560
+ reportReviewRequiredPairUnavailable(metadata2.review, metadata2.itemSubmissions.map((item) => item.itemIdentifier), (error88) => addEvent("assessment.review_required_pair_unavailable", {
104561
+ "app.assessment.attempt_id": params.attemptId,
104562
+ "app.assessment.submission_id": input.submissionId,
104563
+ "app.error.code": error88.code,
104564
+ "app.assessment.review.required_pair_failure_count": error88.failures.length,
104565
+ "app.assessment.review.required_pair_failures": error88.failures.map((failure) => JSON.stringify(failure))
104566
+ }));
104567
+ }
104521
104568
  this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
104522
104569
  }
104523
104570
  if (accepted.emission) {
package/dist/server.js CHANGED
@@ -1127,7 +1127,7 @@ var package_default;
1127
1127
  var init_package = __esm(() => {
1128
1128
  package_default = {
1129
1129
  name: "@playcademy/sandbox",
1130
- version: "0.10.1-beta.1",
1130
+ version: "0.10.1-beta.2",
1131
1131
  description: "Local development server for Playcademy game development",
1132
1132
  type: "module",
1133
1133
  exports: {
@@ -11001,6 +11001,32 @@ function scorePlatformAssessmentItem(item, responses) {
11001
11001
  isCorrect: verdicts.length > 0 && verdicts.every(Boolean)
11002
11002
  };
11003
11003
  }
11004
+ function reportReviewRequiredPairUnavailable(review, administeredItemIdentifiers2, report) {
11005
+ if (!review.requireBothQuestions) {
11006
+ return;
11007
+ }
11008
+ const administered = new Set(administeredItemIdentifiers2);
11009
+ const failures = [];
11010
+ for (const standard of review.standards) {
11011
+ const key = assessmentStandardRefKey(standard);
11012
+ const selected = new Set(review.selections.filter((selection) => assessmentStandardRefKey(selection.standard) === key).map((selection) => selection.itemIdentifier));
11013
+ const administeredCount = [...selected].filter((identifier) => administered.has(identifier)).length;
11014
+ if (selected.size < 2 || administeredCount === 1) {
11015
+ failures.push({
11016
+ standard,
11017
+ reason: selected.size < 2 ? "selection_shortage" : "not_administered",
11018
+ requiredCount: 2,
11019
+ selectedCount: selected.size,
11020
+ administeredCount
11021
+ });
11022
+ }
11023
+ }
11024
+ if (failures.length > 0) {
11025
+ try {
11026
+ report({ code: "REVIEW_REQUIRED_PAIR_UNAVAILABLE", failures });
11027
+ } catch {}
11028
+ }
11029
+ }
11004
11030
  function reviewStandardFieldsWithinLimits(input) {
11005
11031
  return input.framework.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength && input.identifier.trim().length <= TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength;
11006
11032
  }
@@ -11053,7 +11079,11 @@ function normalizeReviewRequest(request) {
11053
11079
  if (standards.length === 0) {
11054
11080
  throw new Error("At least one review standard is required");
11055
11081
  }
11056
- return { standards, candidateItemsPerStandard };
11082
+ return {
11083
+ standards,
11084
+ candidateItemsPerStandard,
11085
+ ...request.requireBothQuestions ? { requireBothQuestions: true } : {}
11086
+ };
11057
11087
  }
11058
11088
  function reviewRequestFingerprint(request, policy) {
11059
11089
  const normalized = normalizeReviewRequest(request);
@@ -11063,6 +11093,7 @@ function reviewRequestFingerprint(request, policy) {
11063
11093
  version: policy.version
11064
11094
  },
11065
11095
  candidateItemsPerStandard: normalized.candidateItemsPerStandard,
11096
+ ...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
11066
11097
  standardKeys: normalized.standards.map(assessmentStandardRefKey)
11067
11098
  });
11068
11099
  }
@@ -11645,7 +11676,7 @@ function isRoutedDiagnosticAttemptMetadata(value, itemSubmissions, responseVersi
11645
11676
  });
11646
11677
  }
11647
11678
  function isReviewAttemptMetadata(value) {
11648
- return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
11679
+ return isRecord(value) && typeof value.requestFingerprint === "string" && typeof value.bankRevision === "string" && Array.isArray(value.standards) && value.standards.every(isAssessmentStandardRef) && Number.isInteger(value.candidateItemsPerStandard) && (value.requireBothQuestions === undefined || typeof value.requireBothQuestions === "boolean") && Array.isArray(value.selections) && value.selections.every((selection) => isRecord(selection) && isAssessmentStandardRef(selection.standard) && typeof selection.itemIdentifier === "string") && isReviewFulfillment(value.fulfillment);
11649
11680
  }
11650
11681
  function isOptionalInteger(value) {
11651
11682
  return value === undefined || Number.isInteger(value);
@@ -12343,6 +12374,7 @@ var init_assessment_runtime2 = __esm(() => {
12343
12374
  }),
12344
12375
  StartAssessmentBaseSchema.extend({
12345
12376
  purpose: exports_external.literal("review"),
12377
+ requireBothQuestions: exports_external.boolean().optional(),
12346
12378
  standards: exports_external.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
12347
12379
  candidateItemsPerStandard: exports_external.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
12348
12380
  }),
@@ -100313,6 +100345,7 @@ function buildConventionalAssessmentSnapshot(input) {
100313
100345
  purpose: metadata2.purpose,
100314
100346
  standards: [...metadata2.review.standards],
100315
100347
  candidateItemsPerStandard: metadata2.review.candidateItemsPerStandard,
100348
+ ...metadata2.review.requireBothQuestions ? { requireBothQuestions: true } : {},
100316
100349
  selections: [...metadata2.review.selections],
100317
100350
  fulfillment: metadata2.review.fulfillment
100318
100351
  };
@@ -102784,11 +102817,13 @@ function buildPreparedAssessmentMetadata(plan, timestamp6, testName) {
102784
102817
  review: {
102785
102818
  requestFingerprint: reviewRequestFingerprint({
102786
102819
  standards: plan.review.standards,
102787
- candidateItemsPerStandard: plan.review.candidateItemsPerStandard
102820
+ candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
102821
+ requireBothQuestions: plan.review.requireBothQuestions
102788
102822
  }, DEFAULT_REVIEW_SELECTION_POLICY),
102789
102823
  bankRevision: plan.review.bankRevision,
102790
102824
  standards: plan.review.standards,
102791
102825
  candidateItemsPerStandard: plan.review.candidateItemsPerStandard,
102826
+ ...plan.review.requireBothQuestions ? { requireBothQuestions: true } : {},
102792
102827
  selections: plan.review.selections,
102793
102828
  fulfillment: plan.review.fulfillment
102794
102829
  }
@@ -103971,6 +104006,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
103971
104006
  bankRevision: catalog.bank.bankRevision,
103972
104007
  standards: [...normalized.standards],
103973
104008
  candidateItemsPerStandard: normalized.candidateItemsPerStandard,
104009
+ ...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
103974
104010
  selections: [...selected.selections],
103975
104011
  fulfillment: selected.fulfillment
103976
104012
  }
@@ -104242,6 +104278,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
104242
104278
  bankRevision: source.bank.bankRevision,
104243
104279
  standards: [...normalizedRequest.standards],
104244
104280
  candidateItemsPerStandard: normalizedRequest.candidateItemsPerStandard,
104281
+ ...normalizedRequest.requireBothQuestions ? { requireBothQuestions: true } : {},
104245
104282
  selections: [...selected.selections],
104246
104283
  fulfillment: selected.fulfillment
104247
104284
  }
@@ -104517,6 +104554,16 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
104517
104554
  };
104518
104555
  });
104519
104556
  if (accepted.repair) {
104557
+ const { metadata: metadata2 } = accepted.repair.graded;
104558
+ if (metadata2.purpose === "review") {
104559
+ reportReviewRequiredPairUnavailable(metadata2.review, metadata2.itemSubmissions.map((item) => item.itemIdentifier), (error88) => addEvent("assessment.review_required_pair_unavailable", {
104560
+ "app.assessment.attempt_id": params.attemptId,
104561
+ "app.assessment.submission_id": input.submissionId,
104562
+ "app.error.code": error88.code,
104563
+ "app.assessment.review.required_pair_failure_count": error88.failures.length,
104564
+ "app.assessment.review.required_pair_failures": error88.failures.map((failure) => JSON.stringify(failure))
104565
+ }));
104566
+ }
104520
104567
  this.reconcileTranscriptChildren(accepted.repair.context, accepted.repair.graded).catch((error88) => this.recordTranscriptFailure("completion_repair", params.attemptId, error88));
104521
104568
  }
104522
104569
  if (accepted.emission) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/sandbox",
3
- "version": "0.10.1-beta.1",
3
+ "version": "0.10.1-beta.2",
4
4
  "description": "Local development server for Playcademy game development",
5
5
  "type": "module",
6
6
  "exports": {