@stage5/lumine 0.3.35 → 0.3.37

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/lib/admin.js CHANGED
@@ -3509,7 +3509,7 @@ export function parseAdminOperation(options) {
3509
3509
  // long as two GPT-6.1 Sol calls, so it gets its own timeout. The daily
3510
3510
  // website management run calls it in a bounded loop (Mikey, 2026-09-28).
3511
3511
  const usage =
3512
- "Usage: lumine admin reward-bank coverage <buildId> [--max-step 3] | seed <buildId> [--max-step 3] [--identity zero|ciel|auto] | report <buildId> [--days 1].";
3512
+ "Usage: lumine admin reward-bank coverage <buildId> [--max-step 3] | seed <buildId> [--max-step 3] [--identity zero|ciel|auto] | report <buildId> [--days 1] | question <buildId> --question-id <id> | repair <buildId> --file <repair.json> [--dry-run].";
3513
3513
  const buildId = parseRequiredInteger(
3514
3514
  target || options.adminBuild,
3515
3515
  "Build ID",
@@ -3519,6 +3519,59 @@ export function parseAdminOperation(options) {
3519
3519
  ? parseRequiredInteger(options.adminMaxStep, "--max-step", 1)
3520
3520
  : 3;
3521
3521
  if (maxStep > 10) throw cliValidationError("--max-step must be at most 10.");
3522
+ if (action === "question") {
3523
+ const questionId = parseRequiredInteger(
3524
+ options.adminQuestionId,
3525
+ "--question-id",
3526
+ 1,
3527
+ );
3528
+ return readOperation(
3529
+ "reward-bank.question",
3530
+ withQuery("/cli/admin/reward-bank/question", {
3531
+ buildId: String(buildId),
3532
+ questionId: String(questionId),
3533
+ }),
3534
+ { requiresRun: false },
3535
+ );
3536
+ }
3537
+ if (action === "repair") {
3538
+ const repair = readAdminJsonFile(
3539
+ options.adminFile,
3540
+ "--file <repair.json>",
3541
+ { maxBytes: 16 * 1024 },
3542
+ );
3543
+ const fields = ["questionId", "expectedFingerprint", "answer", "reason"];
3544
+ if (
3545
+ !repair ||
3546
+ typeof repair !== "object" ||
3547
+ Array.isArray(repair) ||
3548
+ Object.keys(repair).some((key) => !fields.includes(key)) ||
3549
+ !Number.isSafeInteger(repair.questionId) ||
3550
+ repair.questionId <= 0 ||
3551
+ typeof repair.answer !== "number" ||
3552
+ !Number.isFinite(repair.answer) ||
3553
+ typeof repair.expectedFingerprint !== "string" ||
3554
+ !/^[a-f0-9]{64}$/.test(repair.expectedFingerprint) ||
3555
+ typeof repair.reason !== "string" ||
3556
+ !repair.reason.trim() ||
3557
+ repair.reason.length > 2000
3558
+ ) {
3559
+ throw cliValidationError(
3560
+ "Repair JSON needs questionId, the inspected expectedFingerprint, a finite numeric answer and a reason (1–2000 characters); no other fields.",
3561
+ );
3562
+ }
3563
+ return writeOperation(
3564
+ "reward-bank.repair",
3565
+ "POST",
3566
+ "/cli/admin/reward-bank/repair",
3567
+ {
3568
+ buildId,
3569
+ repair,
3570
+ dryRun: Boolean(options.adminDryRun),
3571
+ },
3572
+ { requiresRun: false, mutates: !options.adminDryRun },
3573
+ );
3574
+ }
3522
3575
  if (action === "report") {
3523
3576
  const days = options.adminDays
3524
3577
  ? parseRequiredInteger(options.adminDays, "--days", 1)
@@ -7124,6 +7177,13 @@ function printRewardBankReport(data) {
7124
7177
  );
7125
7178
  }
7126
7179
  }
7180
+ if (data.numericAnswers) {
7181
+ const n = data.numericAnswers;
7182
+ console.log(`Numeric answers (${n.serverAnswerContract}): ${n.decimalQuestions} decimal questions / ${n.questions} total · ${n.decimalsCreatedInWindow} new decimals`);
7183
+ console.log(` Decimal follow-up: ${n.decimalClaims.served} served · ${n.decimalClaims.answered} answered · first-try ${pct(n.decimalClaims.firstTryRate)} · ${n.decimalClaims.coverage}`);
7184
+ } else {
7185
+ console.log("Numeric answer telemetry: unavailable on this server version.");
7186
+ }
7127
7187
  console.log(`Learners: ${data.learners.total}`);
7128
7188
  for (const row of data.learners.byRuleStep) {
7129
7189
  console.log(
@@ -7378,6 +7438,26 @@ function printAdminResult({ operation, result }) {
7378
7438
  }
7379
7439
  }
7380
7440
  if (operation.name.startsWith("reward-bank.")) {
7441
+ if (
7442
+ ["reward-bank.question", "reward-bank.repair"].includes(operation.name)
7443
+ ) {
7444
+ const question = data.question;
7445
+ console.log(
7446
+ `Question #${question.id} · Build ${question.buildId} · ${question.mode} · ${question.status}`,
7447
+ );
7448
+ console.log(question.prompt);
7449
+ console.log(`Stored answer: ${question.answer}`);
7450
+ if (question.hint) console.log(`Hint: ${question.hint}`);
7451
+ if (question.guide) console.log(`Guide: ${question.guide}`);
7452
+ console.log(`Fingerprint: ${data.fingerprint}`);
7453
+ console.log(`History: ${JSON.stringify(data.impact)}`);
7454
+ if (Object.hasOwn(data, "proposedAnswer"))
7455
+ console.log(
7456
+ `Proposed answer: ${data.proposedAnswer} · would change: ${data.wouldChange}`,
7457
+ );
7458
+ console.log(data.historyPolicy);
7459
+ return;
7460
+ }
7381
7461
  if (operation.name === "reward-bank.seed") {
7382
7462
  console.log(
7383
7463
  data.seeded
package/lib/api.js CHANGED
@@ -372,7 +372,7 @@ export async function saveProjectFiles({
372
372
  // Required when the workspace has no filesHash (old/stale checkout).
373
373
  ...(force ? { force: true } : {}),
374
374
  },
375
- timeoutMs: options.timeoutMs,
375
+ timeoutMs: options.validationTimeoutMs ?? options.timeoutMs,
376
376
  });
377
377
  }
378
378
 
package/lib/commands.js CHANGED
@@ -1316,7 +1316,7 @@ export async function check(options) {
1316
1316
  const result = await requestJson({
1317
1317
  url: `${options.apiUrl}/cli/build/${buildId}/launch-check`,
1318
1318
  authToken: auth.token,
1319
- timeoutMs: options.timeoutMs,
1319
+ timeoutMs: options.validationTimeoutMs ?? options.timeoutMs,
1320
1320
  });
1321
1321
  printCheck(result);
1322
1322
  if (!result.ok) process.exitCode = 1;
@@ -1373,7 +1373,7 @@ export async function launch(options) {
1373
1373
  const checkResult = await requestJson({
1374
1374
  url: `${options.apiUrl}/cli/build/${buildId}/launch-check`,
1375
1375
  authToken: auth.token,
1376
- timeoutMs: options.timeoutMs,
1376
+ timeoutMs: options.validationTimeoutMs ?? options.timeoutMs,
1377
1377
  });
1378
1378
  printCheck(checkResult);
1379
1379
  const launchOk =
@@ -2803,6 +2803,7 @@ export function parseArgs(args) {
2803
2803
  adminEditionId: raw.editionId ? String(raw.editionId) : "",
2804
2804
  adminLeaseToken: raw.leaseToken ? String(raw.leaseToken) : "",
2805
2805
  adminFile: raw.file ? String(raw.file) : "",
2806
+ adminQuestionId: raw.questionId ? String(raw.questionId) : "",
2806
2807
  rewardPreflight: Boolean(raw.preflight),
2807
2808
  rewardApprovalFile: raw.approvalFile ? String(raw.approvalFile) : "",
2808
2809
  // admin craft revise --item <id|name> [--why] [--emoji] [--category]
@@ -2939,6 +2940,13 @@ export function parseArgs(args) {
2939
2940
  DEFAULT_TIMEOUT_MS,
2940
2941
  1000,
2941
2942
  ),
2943
+ // Large saved projects can take longer than the ordinary 20s request
2944
+ // deadline. The server bounds compiler work at 120s; leave transport room.
2945
+ // An explicit --timeout-ms / environment setting still wins.
2946
+ validationTimeoutMs: Math.max(
2947
+ Number(raw.timeoutMs || process.env.TWINKLE_TIMEOUT_MS) || 150000,
2948
+ 1000,
2949
+ ),
2942
2950
  help: !!raw.help || command === "help",
2943
2951
  };
2944
2952
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.3.35",
3
+ "version": "0.3.37",
4
4
  "description": "Command line tools for launching Lumine builds on Twinkle.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1029,9 +1029,9 @@ const result = await Twinkle.characters.chat({ character: 'zero', thinkingMode:
1029
1029
  - Example: const { level } = await Twinkle.minecraft.getPlayArea({ id: 'twinkle-keep' });
1030
1030
  - async getPlayAreaChunks({ id, chunks: [[cx, cz], ...] }) | scopes: content:read
1031
1031
  - Returns: { scenes: [{ cx, cz, scene: { origin: [x,y,z], size: [w,h,d], palette, blocks, light, biomes, count, ... } | null }] }
1032
- - Up to 9 chunk columns of a play area in the /snap scene format (packed visible blocks with per-face light), cropped to the area's box. Stream the ones around the player.
1032
+ - Up to 24 chunk columns of a play area in the /snap scene format (packed visible blocks with per-face light), cropped to the area's box. Stream the ones around the player.
1033
1033
  - Same scene format as getSnap; only blocks with a visible face inside the area are included. scene is null for a chunk that is not part of the area.
1034
- - At most 9 chunks per call; reads share the 180-per-minute build read limit, so cache chunks and load the nearest first.
1034
+ - At most 24 chunks per call; reads share the 180-per-minute build read limit, so cache chunks and load the nearest first.
1035
1035
  - Example: const { scenes } = await Twinkle.minecraft.getPlayAreaChunks({ id: level.id, chunks: [[64, 29], [65, 29]] });
1036
1036
  - async createPlayArea({ name, world, box, spawn, id }) | scopes: content:write
1037
1037
  - Returns: { level }
@@ -1714,8 +1714,32 @@ them so the first learner on a unit never waits or pays.
1714
1714
  lumine admin reward-bank coverage <buildId> [--max-step 3] [--json] # read-only, no run
1715
1715
  lumine admin reward-bank seed <buildId> [--max-step 3] [--json] # delegated full run
1716
1716
  lumine admin reward-bank report <buildId> [--days 1] [--json] # read-only telemetry
1717
+ lumine admin reward-bank question <buildId> --question-id <id> [--json]
1718
+ lumine admin reward-bank repair <buildId> --file <repair.json> --dry-run [--json]
1719
+ lumine admin reward-bank repair <buildId> --file <repair.json> [--json]
1717
1720
  ```
1718
1721
 
1722
+ `question` returns the complete prompt, answer, hint and guide, question
1723
+ statistics, a canonical `fingerprint`, and aggregate challenge history. It is
1724
+ owner-only and does not need a daily run. A report excerpt is not an exact
1725
+ question inspection.
1726
+
1727
+ `repair` accepts exactly `{ "questionId": 41, "expectedFingerprint": "<hash
1728
+ from inspection>", "answer": 0.5, "reason": "Verified explanation and arithmetic" }`.
1729
+ Inspect and verify the mathematics before preparing a repair. `--dry-run`
1730
+ checks the same guard without writing. The real command locks and rechecks the
1731
+ question, changes only its numeric answer, returns canonical readback and
1732
+ records an idempotent transaction audit. A stale fingerprint is rejected;
1733
+ inspect again instead of guessing a new hash. Unknown plan fields are rejected.
1734
+ No model call or AI Energy charge is involved.
1735
+
1736
+ The repaired answer governs subsequent attempts, including already-issued
1737
+ unsolved challenges. Past attempts, grades, ladders, question statistics and
1738
+ rewards stay unchanged; the receipt states this explicitly. The history counts
1739
+ are observations, not a lock over every learner's activity. If the old answer
1740
+ affected a learner, inspect that history and prepare a separate, exact remedy;
1741
+ this command never guesses compensating rewards or rewrites past grades.
1742
+
1719
1743
  `report` covers the last `--days` (1-31): generation rounds by source
1720
1744
  (learner or seed) with outcomes (`accepted`, `off_brief`: the checker judged
1721
1745
  the question outside the brief's language, level or LaTeX rule;
@@ -2502,6 +2526,9 @@ Daily Reflection post reads and recommendation candidates include the stored
2502
2526
  An absent grade is unknown. Verify `grade === "Masterpiece"` before placing one
2503
2527
  of Mikey's reflections anywhere on the newspaper front page; lower or missing
2504
2528
  grades can be considered for inside pages. The CLI does not infer a grade.
2529
+ Masterpiece is only an eligibility condition for his reflections: it does not
2530
+ award front-page placement, the lead, or editorial priority. Read the actual
2531
+ content and compare its significance to members with the day's other stories.
2505
2532
 
2506
2533
  ## Subject and Featured mutations
2507
2534
 
@@ -3019,6 +3046,12 @@ entertainment, likely comments and opportunities to join in are not the only
3019
3046
  measures of importance. Explain the values being communicated; do not reduce
3020
3047
  them to founder biography or an administrative announcement.
3021
3048
 
3049
+ **Editorial judgment (Mikey, 2026-10-15):** a Masterpiece grade never automatically
3050
+ earns a front-page slot or the lead. Use the project's canonical `CORE_VALUES.md`
3051
+ to assess the actual story's relevance to the community and its culture, alongside
3052
+ the other eligible stories. Give the substantive reason for the placement;
3053
+ neither the grade nor Mikey's authorship supplies that reason.
3054
+
3022
3055
  Use clear, lively language that respects readers' intelligence. Give enough
3023
3056
  context for someone who missed the original post, and make the interesting
3024
3057
  part clear in the headline and opening. Avoid talking down to readers, forced