@stage5/lumine 0.2.29 → 0.2.30
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/package.json +1 -1
- package/sdk/LUMINE_ADMIN.md +181 -3
package/package.json
CHANGED
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -1063,7 +1063,8 @@ end every run report with an **"Insights for Mikey"** section carrying only
|
|
|
1063
1063
|
the deltas and anomalies worth his time, next to the escalation list. Never
|
|
1064
1064
|
dump raw sections at him.
|
|
1065
1065
|
|
|
1066
|
-
|
|
1066
|
+
Nine sections (Mikey's chosen cut 2026-08-10; behavioral-insight and
|
|
1067
|
+
farm-signal sections added the same day):
|
|
1067
1068
|
|
|
1068
1069
|
- `economy` — `topGainers` (coin-ledger aggregation over the window: gained,
|
|
1069
1070
|
spent, net, current balance per user, Zero/Ciel excluded) and `topBalances`
|
|
@@ -1117,14 +1118,62 @@ Four sections (Mikey's chosen cut, 2026-08-10):
|
|
|
1117
1118
|
calendar day before the exact `window.sinceTs`.
|
|
1118
1119
|
Report the contrast between the buckets, and treat `genuinelyInterested` as
|
|
1119
1120
|
notable-users-but-for-teachers.
|
|
1121
|
+
- `engagementPulse` — distinct users per surface (activeUsers, subjects,
|
|
1122
|
+
comments, recommendations, wordle, reflections, dailyTasks, aiChat,
|
|
1123
|
+
lumineBuildChat, buildsEdited, buildsPlayed) for the current window vs the
|
|
1124
|
+
equal-length previous window, each as `{ current, previous, delta }`.
|
|
1125
|
+
Presence, build edits, and build plays come from durable action/version/view
|
|
1126
|
+
events, not mutable `lastActive`/`updatedAt` snapshots. Zero/Ciel are excluded
|
|
1127
|
+
from authored surfaces. Wordle, daily tasks, and AI chat use the equal
|
|
1128
|
+
calendar-bucket ranges in `dayWindow`; those can begin before the exact
|
|
1129
|
+
timestamp window but always compare the same number of days. This is the
|
|
1130
|
+
"where do users actually live, and is it shifting?" section: report only the
|
|
1131
|
+
deltas that mean something, and read a surface's absolute size before
|
|
1132
|
+
dramatizing a small delta.
|
|
1133
|
+
- `launchMetrics` — readouts for recently shipped features so nothing ships
|
|
1134
|
+
unmeasured. v1 carries `firstBuildRescue` (offers recorded and redemptions
|
|
1135
|
+
in the window, each `{ total, byEventType }`, plus first-Lumine-exchange
|
|
1136
|
+
claims) and `wordleSkipShield` (`startDayIndex`, `active`, judged dodges,
|
|
1137
|
+
and skip covers split `earned` vs `fromRescue`). Wordle metrics cover only
|
|
1138
|
+
completed, actually judged days in `judgedWindow`; today's in-progress game
|
|
1139
|
+
is never called a dodge. Offers without redemptions are a reason to inspect
|
|
1140
|
+
sample size, event type, and offer age — not proof of a broken funnel by
|
|
1141
|
+
themselves.
|
|
1142
|
+
- `goneQuiet` — the inverse of `notableCandidates`: users whose `lastActive`
|
|
1143
|
+
fell in the 14 days before the window (so they were around, then stopped),
|
|
1144
|
+
ranked by how regular they were in the prior 30 days (daily tasks and
|
|
1145
|
+
Wordle), capped at 15 with `daysQuiet`. Use it for product signal (what did
|
|
1146
|
+
they stop doing?) and gentle outreach candidates; never guilt a child in
|
|
1147
|
+
public about absence.
|
|
1148
|
+
- `newUserFunnel` — signups in the window with `activeOnDayOne` (any
|
|
1149
|
+
XP-ledger event within 24h of joining) and `returnedAfterDayOne`
|
|
1150
|
+
(`lastActive` beyond their first day), plus the newest few accounts.
|
|
1151
|
+
Deliberately coarse: it is an onboarding health check, not per-child
|
|
1152
|
+
session tracking.
|
|
1153
|
+
- `farmSignals` — AI-cost farm signatures derivable with ZERO new data
|
|
1154
|
+
collection: `inboxFamilies` (verified emails from accounts active in the
|
|
1155
|
+
last `inboxFamilyActivityDays`, with only Gmail/googlemail's documented
|
|
1156
|
+
plus-tag and dot aliases collapsed, flagging inboxes behind 3+ accounts) and
|
|
1157
|
+
`youngAccountAiUsage` (accounts under 30 days old drawing battery in the
|
|
1158
|
+
whole-day `aiUsageDayWindow`). SIGNAL ONLY: siblings legitimately share a
|
|
1159
|
+
parent inbox, so an inbox family is a reason to look, never proof or grounds
|
|
1160
|
+
for action. Feed real suspicions to the AI-cost escalation category. Shared
|
|
1161
|
+
AI device/IP risk evidence is already in `aiSpending.topRiskGroups`; do not
|
|
1162
|
+
guess it from inbox similarity.
|
|
1120
1163
|
|
|
1121
1164
|
The command needs only an active run's `content:read` scope and mutates
|
|
1122
1165
|
nothing; reading the brief is not audited content action. Window boundaries
|
|
1123
1166
|
on the big append-only ledgers are found by binary-searching the PRIMARY key
|
|
1124
1167
|
(several tables have no timeStamp index), avoiding lifetime scans; aggregation
|
|
1125
|
-
is still bounded to the selected 1–30 day window.
|
|
1168
|
+
is still bounded to the selected 1–30 day window. The optional sections run
|
|
1169
|
+
serially around the existing core report so this low-frequency command cannot
|
|
1170
|
+
occupy the production reader pool; one unavailable section does not suppress
|
|
1171
|
+
the others.
|
|
1126
1172
|
|
|
1127
1173
|
```ts
|
|
1174
|
+
type InsightUnavailable = { unavailable: true; error: string };
|
|
1175
|
+
type WindowDelta = { current: number; previous: number; delta: number };
|
|
1176
|
+
|
|
1128
1177
|
type TeacherInsight = {
|
|
1129
1178
|
userId: number;
|
|
1130
1179
|
username: string | null;
|
|
@@ -1200,7 +1249,115 @@ type InsightsBrief = Success<{
|
|
|
1200
1249
|
topAccounts: unknown[];
|
|
1201
1250
|
topRiskGroups: unknown[];
|
|
1202
1251
|
}
|
|
1203
|
-
|
|
|
1252
|
+
| InsightUnavailable;
|
|
1253
|
+
engagementPulse:
|
|
1254
|
+
| {
|
|
1255
|
+
windowDays: number;
|
|
1256
|
+
dayWindow: {
|
|
1257
|
+
currentStartDayIndex: number;
|
|
1258
|
+
currentEndDayIndex: number;
|
|
1259
|
+
previousStartDayIndex: number;
|
|
1260
|
+
previousEndDayIndex: number;
|
|
1261
|
+
dayCount: number;
|
|
1262
|
+
};
|
|
1263
|
+
surfaces: {
|
|
1264
|
+
activeUsers: WindowDelta;
|
|
1265
|
+
subjects: WindowDelta;
|
|
1266
|
+
comments: WindowDelta;
|
|
1267
|
+
recommendations: WindowDelta;
|
|
1268
|
+
wordle: WindowDelta;
|
|
1269
|
+
reflections: WindowDelta;
|
|
1270
|
+
dailyTasks: WindowDelta;
|
|
1271
|
+
aiChat: WindowDelta;
|
|
1272
|
+
lumineBuildChat: WindowDelta;
|
|
1273
|
+
buildsEdited: WindowDelta;
|
|
1274
|
+
buildsPlayed: WindowDelta;
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
| InsightUnavailable;
|
|
1278
|
+
launchMetrics:
|
|
1279
|
+
| {
|
|
1280
|
+
firstBuildRescue: {
|
|
1281
|
+
offersRecorded: {
|
|
1282
|
+
total: number;
|
|
1283
|
+
byEventType: Record<string, number>;
|
|
1284
|
+
};
|
|
1285
|
+
redemptions: {
|
|
1286
|
+
total: number;
|
|
1287
|
+
byEventType: Record<string, number>;
|
|
1288
|
+
};
|
|
1289
|
+
firstLumineExchangeClaims: number;
|
|
1290
|
+
};
|
|
1291
|
+
wordleSkipShield: {
|
|
1292
|
+
startDayIndex: number;
|
|
1293
|
+
active: boolean;
|
|
1294
|
+
judgedWindow: {
|
|
1295
|
+
startDayIndex: number;
|
|
1296
|
+
endDayIndex: number;
|
|
1297
|
+
} | null;
|
|
1298
|
+
judgedDodges: number;
|
|
1299
|
+
skipCovers: { earned: number; fromRescue: number };
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
| InsightUnavailable;
|
|
1303
|
+
goneQuiet:
|
|
1304
|
+
| {
|
|
1305
|
+
users: Array<{
|
|
1306
|
+
userId: number;
|
|
1307
|
+
username: string | null;
|
|
1308
|
+
lastActive: number | null;
|
|
1309
|
+
daysQuiet: number | null;
|
|
1310
|
+
dailyTasksPrior30d: number;
|
|
1311
|
+
wordlePlaysPrior30d: number;
|
|
1312
|
+
regularityScore: number;
|
|
1313
|
+
}>;
|
|
1314
|
+
totals: { wentQuiet: number; previouslyRegular: number };
|
|
1315
|
+
}
|
|
1316
|
+
| InsightUnavailable;
|
|
1317
|
+
newUserFunnel:
|
|
1318
|
+
| {
|
|
1319
|
+
totals: {
|
|
1320
|
+
signups: number;
|
|
1321
|
+
activeOnDayOne: number;
|
|
1322
|
+
returnedAfterDayOne: number;
|
|
1323
|
+
};
|
|
1324
|
+
newest: Array<{
|
|
1325
|
+
userId: number;
|
|
1326
|
+
username: string | null;
|
|
1327
|
+
joinedAt: number | null;
|
|
1328
|
+
activeOnDayOne: boolean;
|
|
1329
|
+
returnedAfterDayOne: boolean;
|
|
1330
|
+
}>;
|
|
1331
|
+
}
|
|
1332
|
+
| InsightUnavailable;
|
|
1333
|
+
farmSignals:
|
|
1334
|
+
| {
|
|
1335
|
+
inboxFamilyActivityDays: number;
|
|
1336
|
+
inboxFamilies: Array<{
|
|
1337
|
+
inbox: string;
|
|
1338
|
+
accounts: Array<{
|
|
1339
|
+
userId: number;
|
|
1340
|
+
username: string | null;
|
|
1341
|
+
joinedAt: number | null;
|
|
1342
|
+
lastActive: number | null;
|
|
1343
|
+
}>;
|
|
1344
|
+
accountCount: number;
|
|
1345
|
+
youngAccounts: number;
|
|
1346
|
+
}>;
|
|
1347
|
+
aiUsageDayWindow: {
|
|
1348
|
+
startDayIndex: number;
|
|
1349
|
+
endDayIndex: number;
|
|
1350
|
+
};
|
|
1351
|
+
youngAccountAiUsage: Array<{
|
|
1352
|
+
userId: number;
|
|
1353
|
+
username: string | null;
|
|
1354
|
+
joinedAt: number | null;
|
|
1355
|
+
energyUnits: number;
|
|
1356
|
+
replies: number;
|
|
1357
|
+
}>;
|
|
1358
|
+
notes: string;
|
|
1359
|
+
}
|
|
1360
|
+
| InsightUnavailable;
|
|
1204
1361
|
}>;
|
|
1205
1362
|
```
|
|
1206
1363
|
|
|
@@ -1334,6 +1491,27 @@ tried it — ask the author about it instead. This is the standing rule for
|
|
|
1334
1491
|
every composed comment, applied to apps: never claim an experience the
|
|
1335
1492
|
session did not actually have.
|
|
1336
1493
|
|
|
1494
|
+
**Offer a Lumine prompt when the moment invites it (Mikey's direction,
|
|
1495
|
+
2026-08-10).** Zero and Ciel may include one concrete, copy-pasteable Lumine
|
|
1496
|
+
prompt in a comment or reply — a genuinely powerful one, tailored to what the
|
|
1497
|
+
kid is already doing — when the occasion naturally calls for it. Appropriate
|
|
1498
|
+
occasions: a kid describes an idea they wish existed, asks how something on
|
|
1499
|
+
the site was made, hits the edge of what a post/drawing/story can do, shares
|
|
1500
|
+
a Build app that could grow a specific feature, or shows an interest (space,
|
|
1501
|
+
cats, chess, comics) that maps cleanly onto something Lumine could build with
|
|
1502
|
+
them. On those occasions, the prompt IS the helpful answer: quote it so it
|
|
1503
|
+
can be copied as-is, keep it specific to their interest, and mention it works
|
|
1504
|
+
in the Build workspace chat. Restraint rules: never more than one prompt per
|
|
1505
|
+
comment; never in condolence, conflict, wellbeing, or moderation-adjacent
|
|
1506
|
+
threads; never as a reflex closing line on ordinary comments — if the comment
|
|
1507
|
+
is complete without the prompt, post it without the prompt. A run where only
|
|
1508
|
+
a few comments carry a prompt is healthy; a run where most do is shameless
|
|
1509
|
+
plugging, which is exactly what Mikey asked to avoid. SDK-aware prompt ideas
|
|
1510
|
+
are especially good ("ask Lumine to make a magazine that pulls real Twinkle
|
|
1511
|
+
posts with Twinkle.subjects.search") because kids do not know the content
|
|
1512
|
+
APIs exist — but only suggest SDK capabilities that actually exist; check
|
|
1513
|
+
TWINKLE_BUILD_SDK.md if unsure.
|
|
1514
|
+
|
|
1337
1515
|
A composed draft (`--file`, plain UTF-8 text, at most the website's 10,000
|
|
1338
1516
|
character comment limit) flows through the identical draft lifecycle —
|
|
1339
1517
|
reservation, idempotency, context-revision CAS, publish fencing, audit
|