@stage5/lumine 0.2.80 → 0.2.81

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
@@ -221,7 +221,7 @@ export function readRewardConfigFile(filePath) {
221
221
  const normalizedPath = String(filePath || "").trim();
222
222
  if (!normalizedPath) {
223
223
  throw cliValidationError(
224
- "Pass the earning rules with --config <rules.json> (dailyXP, dailyCoins, userDailyXP, userDailyCoins, lifetimeXP, lifetimeCoins, rules[]).",
224
+ "Pass the earning rules with --config <rules.json> (userDailyXP, userDailyCoins, optional userDailyClaims, rules[]).",
225
225
  );
226
226
  }
227
227
  let contents;
@@ -3944,7 +3944,7 @@ function printRewardReviewResult({ operation, data }) {
3944
3944
  const config = review.config || {};
3945
3945
  if (Array.isArray(config.rules)) {
3946
3946
  console.log(
3947
- `Budgets: day ${config.dailyXP} XP/${config.dailyCoins} Coins · per user/day ${config.userDailyXP} XP/${config.userDailyCoins} Coins${config.userDailyClaims ? ` · ${config.userDailyClaims} claim(s)` : ""} · lifetime ${config.lifetimeXP} XP/${config.lifetimeCoins} Coins`,
3947
+ `Budgets: per user/day ${config.userDailyXP} XP/${config.userDailyCoins} Coins${config.userDailyClaims ? ` · ${config.userDailyClaims} claim(s)` : ""}`,
3948
3948
  );
3949
3949
  for (const rule of config.rules)
3950
3950
  console.log(` ${formatRewardRuleLine(rule)}`);
package/lib/rewards.js CHANGED
@@ -214,7 +214,7 @@ function printRewardsHelp() {
214
214
  lumine rewards sheet --show Summarize the sheet on file (never prints answer keys)
215
215
 
216
216
  rewards.json (project root) declares the economy the reviewer approves:
217
- { "dailyXP", "dailyCoins", "userDailyXP", "userDailyCoins", "lifetimeXP", "lifetimeCoins", "userDailyClaims"?,
217
+ { "userDailyXP", "userDailyCoins", "userDailyClaims"?,
218
218
  "rules": [{ "id", "title", "xp", "coins", "verifier": "numeric-quiz" | "completion",
219
219
  "maxAttempts"?, "retry"?: { "xpPercent", "coinsPercent", "paidAttempts"? }, "minSeconds"? (completion), "progression"?: "dated" | "until-earned" (quiz) }] }
220
220
  Questions and answer keys never go in project files; they belong in the sheet.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.2.80",
3
+ "version": "0.2.81",
4
4
  "description": "Command line tools for launching Lumine builds on Twinkle.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,8 +1,8 @@
1
1
  # Build SDK Index
2
2
 
3
- Version: 1.45.0
3
+ Version: 1.45.2
4
4
  Updated: 2026-09-14
5
- Generated: 2026-09-15T02:06:06.654Z
5
+ Generated: 2026-09-19T04:22:12.342Z
6
6
 
7
7
  ## Notes
8
8
  - This SDK is injected into Build iframes via the Build preview/runtime.
@@ -33,7 +33,7 @@ Generated: 2026-09-15T02:06:06.654Z
33
33
  - Use Twinkle.live for one-way app livestreams and Twinkle.chat for the accompanying thread. Free livestreams require a verified host, end after at most 15 minutes, and issue at most 10 private viewer grants. Twinkle keeps platform-owned live-status/end controls above active hosts, so app code cannot hide or replace the broadcaster's Stop path.
34
34
  - Media Energy is separate from AI Energy. Replace Media Energy UI only from canonical mediaEnergy/getUsage responses; never decrement, reserve, or synthesize it in app code.
35
35
  - Twinkle.rewards awards real XP and Coins only in the current approved published release. Drafts, local previews, private apps and superseded releases cannot earn. The server supplies a published-runtime grant; app code cannot choose a recipient or award amount.
36
- - The creator's agent designs the rewards. Declare the economy in a project file `rewards.json` at the root: budgets (dailyXP, dailyCoins, userDailyXP, userDailyCoins, lifetimeXP, lifetimeCoins, optional userDailyClaims) and rules [{ id, title, xp, coins, verifier: 'numeric-quiz' | 'completion', maxAttempts?, retry?: { xpPercent, coinsPercent, paidAttempts? }, minSeconds? (completion), progression?: 'dated' | 'until-earned' (quiz) }]. Wire the matching Twinkle.rewards calls with those literal rule ids. Questions and answer keys NEVER go in project files (published source is readable by every player): quiz rules get them from the private question sheet uploaded with `lumine rewards sheet <file.json>` ({ rules: { <ruleId>: { questions?, sets? } } }); `lumine rewards check` validates both together. A review request freezes the code and proposes rewards.json merged with the sheet; the administrator reads the code, checks the amounts and whether the app is exploitable, may change any amount, and approves. Creators are kids and teens: show approval status and one Send for review action; do not ask them to fill in technical forms. Every code update that retains rewards needs a new approval before publishing. Removing the SDK automatically clears its gate. Apps read amounts, tries and sets from getStatus, never from their own file.
36
+ - The creator's agent designs the rewards. Declare the economy in a project file `rewards.json` at the root: budgets (userDailyXP, userDailyCoins, optional userDailyClaims; there is no app-wide daily or lifetime budget, only what one learner can earn per day) and rules [{ id, title, xp, coins, verifier: 'numeric-quiz' | 'completion', maxAttempts?, retry?: { xpPercent, coinsPercent, paidAttempts? }, minSeconds? (completion), progression?: 'dated' | 'until-earned' (quiz) }]. Wire the matching Twinkle.rewards calls with those literal rule ids. Questions and answer keys NEVER go in project files (published source is readable by every player): quiz rules get them from the private question sheet uploaded with `lumine rewards sheet <file.json>` ({ rules: { <ruleId>: { questions?, sets? } } }); `lumine rewards check` validates both together. A review request freezes the code and proposes rewards.json merged with the sheet; the administrator reads the code, checks the amounts and whether the app is exploitable, may change any amount, and approves. Creators are kids and teens: show approval status and one Send for review action; do not ask them to fill in technical forms. Every code update that retains rewards needs a new approval before publishing. Removing the SDK automatically clears its gate. Apps read amounts, tries and sets from getStatus, never from their own file.
37
37
  - Verifiers: 'numeric-quiz' pays for server-checked numeric answers (retry share, attempt limits, dated sets or until-earned sets that stay up until somebody earns them, after-answer guides). 'completion' pays when the app reports an activity finished — a cleared stage, a finished round — at least minSeconds after start({ ruleId }); the server checks only the elapsed time, once per learner per site day (UTC midnight), and the budgets. Call start when the activity begins and claim({ challengeId }) with no answers when it ends; keep completion amounts and userDailyXP small enough that a player scripting the calls would not matter, because nothing else is verified.
38
38
  - Numeric quiz answers are verified on the server; client scores, privateDb state, timers and completion booleans are not verified reward evidence. Limits reset at UTC midnight. Rules are earned once per viewer per UTC day; attempt limits and retry payouts come from the approved rule. Challenges expire at the UTC day boundary. Budgets apply across release changes.
39
39
 
@@ -1143,17 +1143,18 @@ Review questions to settle with Mikey before approving:
1143
1143
  it, then the next one comes up the following site day (UTC midnight, 9:00 AM Korea)) keep them honest.
1144
1144
  - Do the rule IDs in `rewards.json` match what the code starts? Unknown IDs
1145
1145
  simply never pay.
1146
- - Are the amounts and the per-user, per-app and lifetime budgets conservative
1147
- for what the app actually asks of people?
1146
+ - Are the amounts and the per-learner daily budgets right for what the app
1147
+ actually asks of people? There is no app-wide budget, per day or lifetime:
1148
+ a good app keeps paying everyone who plays it. Older configs may still
1149
+ carry `dailyXP`/`dailyCoins`/`lifetimeXP`/`lifetimeCoins`; the API accepts
1150
+ and ignores them, so never add or tune them.
1148
1151
 
1149
1152
  `rules.json` (what `--config` takes, and what the app's `rewards.json` plus
1150
1153
  sheet compose into):
1151
1154
 
1152
1155
  ```json
1153
1156
  {
1154
- "dailyXP": 2000000, "dailyCoins": 0,
1155
1157
  "userDailyXP": 10000, "userDailyCoins": 0,
1156
- "lifetimeXP": 200000000, "lifetimeCoins": 0,
1157
1158
  "rules": [
1158
1159
  { "id": "stage-1", "title": "Clear Stage 1", "xp": 300, "coins": 0,
1159
1160
  "verifier": "completion", "minSeconds": 20 },
@@ -1191,8 +1192,8 @@ day, elementary 50,000 XP + 1,000 Coins, middle 70,000 + 5,000, high
1191
1192
  `userDailyClaims` 1; until-earned sets authored from the Korean curriculum.
1192
1193
  Arcade Typing (Mikey, 2026-09-12): XP for clearing campaign stages, up to
1193
1194
  10,000 XP per learner per day, no Coins. Platform ceilings: 100,000 XP /
1194
- 10,000 Coins per rule and per learner per day, 10,000,000 XP / 1,000,000 Coins
1195
- per app per day, 1,000,000,000 XP / 100,000,000 Coins per app lifetime.
1195
+ 10,000 Coins per rule and per learner per day. No app-wide ceiling exists,
1196
+ per day or lifetime.
1196
1197
 
1197
1198
  Approval freezes these rules with the reviewed snapshot and publishes that
1198
1199
  snapshot immediately (the result carries `published.version`); an approval
@@ -2670,6 +2671,24 @@ Read every line, and put these in the daily report verbatim:
2670
2671
  line;
2671
2672
  - the `day-over-day … worker_heap=…` delta.
2672
2673
 
2674
+ **The memory report looks back only 24 hours; the run's window is often longer.**
2675
+ On 2026-09-19 a four-day window hid two heap-OOM aborts (09-16 and 09-17) that
2676
+ `memory-daily` no longer showed. In every full review, also search the reviewed
2677
+ error log for `[cluster] worker exited` lines that are not planned or operator
2678
+ recycles, across the whole window since the last completed full run, and treat
2679
+ each one exactly like a non-zero `oom_aborts` / `unexpected_worker_exits`.
2680
+
2681
+ Each unexpected exit is followed by a `[cluster] worker last work slot=… pid=…
2682
+ in_flight=[…] recent=[…]` line (added 2026-09-19). The worker writes that trail
2683
+ synchronously as each piece of work begins and ends, so it survives an abort
2684
+ inside a single synchronous burst. `in_flight` names the requests or socket
2685
+ events that were running when the process died; `recent` lists the last
2686
+ sixteen begin/label/end records with their age before exit. Quote both lists
2687
+ verbatim in the report and in the todo: they are the evidence that pinpoints
2688
+ the code path. `unavailable (no trail file)` means the worker died before its
2689
+ trail existed or an older generation is still running; say so rather than
2690
+ guessing.
2691
+
2673
2692
  Escalate in the report (a todo, and a note for Mikey) when any of these hold:
2674
2693
  `oom_aborts` > 0, `service_restarts` > 0, `unexpected_worker_exits` > 0, any
2675
2694
  worker `heap_high_water_pct` ≥ 95, or `heap_recycles` ≥ 4 in a day (the