@stage5/lumine 0.2.27 → 0.2.29

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/README.md CHANGED
@@ -181,6 +181,7 @@ lumine admin subject feature 123 --json
181
181
  lumine admin subject unfeature 123 --json
182
182
  lumine admin featured reorder --subject-ids 30,20,10 --json
183
183
  lumine admin brief --days 3 --json
184
+ lumine admin notable add Minecrarft_guy --json
184
185
  lumine admin post recommend comment:456 --anyone-can-reward --reward-twinkles 3 --json
185
186
  lumine admin post reward comment:456 --twinkles 3 --json
186
187
  lumine admin daily-run complete --json
package/lib/admin.js CHANGED
@@ -585,6 +585,24 @@ export function parseAdminOperation(options) {
585
585
  );
586
586
  }
587
587
 
588
+ if (namespace === "notable" && action === "add") {
589
+ const rawTarget = String(target || "").trim();
590
+ if (!rawTarget) {
591
+ throw cliValidationError(
592
+ "Usage: lumine admin notable add <userId|username>.",
593
+ );
594
+ }
595
+ const body = /^\d+$/.test(rawTarget)
596
+ ? { userId: parseRequiredInteger(rawTarget, "user ID", 1) }
597
+ : { username: rawTarget };
598
+ return writeOperation(
599
+ "notable.add",
600
+ "POST",
601
+ "/cli/admin/notable-users",
602
+ body,
603
+ );
604
+ }
605
+
588
606
  if (namespace === "brief" && !action) {
589
607
  if (options.adminDays) {
590
608
  const days = Number(options.adminDays);
@@ -683,7 +701,7 @@ export function parseAdminOperation(options) {
683
701
  }
684
702
 
685
703
  throw cliValidationError(
686
- "Usage: lumine admin identity|daily-run|recommendations|post|subjects|subject|featured|comment|news|audit|brief ...",
704
+ "Usage: lumine admin identity|daily-run|recommendations|post|subjects|subject|featured|comment|news|audit|brief|notable ...",
687
705
  );
688
706
  }
689
707
 
@@ -1088,6 +1106,12 @@ function printAdminResult({ operation, result }) {
1088
1106
  }
1089
1107
  return;
1090
1108
  }
1109
+ if (data.notableUser) {
1110
+ console.log(
1111
+ `${result.status || "success"}: ${data.notableUser.username || "unknown"} (#${data.notableUser.userId}).`,
1112
+ );
1113
+ return;
1114
+ }
1091
1115
  if (data.skip) {
1092
1116
  console.log(
1093
1117
  `${result.status}: ${data.skip.contentType}:${data.skip.contentId} skipped.`,
package/lib/commands.js CHANGED
@@ -2528,6 +2528,7 @@ export function printHelp() {
2528
2528
  lumine admin comment post --draft-id <id> [--json]
2529
2529
  lumine admin comment edit <comment-id> --file <comment.md> [--json]
2530
2530
  lumine admin brief [--days <1..30>] [--json]
2531
+ lumine admin notable add <user-id|username> [--json]
2531
2532
  lumine admin audit [list] [--run current|last|<run-id>] [--target <target>] [--actions <a,b>] [--full] [--cursor <cursor>] [--json]
2532
2533
 
2533
2534
  Examples:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.2.27",
3
+ "version": "0.2.29",
4
4
  "description": "Command line tools for launching Lumine builds on Twinkle.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -139,6 +139,17 @@ Escalate, with the canonical `https://www.twin-kle.com/subjects/<id>` or
139
139
  pay-me-to-win contests, and anything that teaches other children a method for
140
140
  any of these. Note the recommendation count: a manipulation how-to that other
141
141
  kids are recommending is spreading, and that is the urgent part.
142
+ - **AI-cost exploits** — patterns that convert free AI allowances into farmable
143
+ value: clusters of young accounts with heavy AI/battery usage, one person
144
+ operating many accounts that feed a single build through team branches,
145
+ plus-tagged or dot-variant email families (`kid+1@`, `k.id@`) behind multiple
146
+ active accounts, repeated first exchanges across accounts already linked by
147
+ independent evidence, or a rescue claim by an account created under 30 days
148
+ ago (the API maturity gate should make that last case impossible). A new
149
+ member's one first exchange is intended onboarding and is not suspicious by
150
+ itself. The daily battery is real provider money; treat farming signatures
151
+ with the same seriousness as coin farming. Escalate the account list and
152
+ evidence; never auto-enforce.
142
153
  - **Bug reports** the run encountered, even secondhand in a comment thread.
143
154
 
144
155
  Two rules that keep the list worth reading:
@@ -1041,6 +1052,8 @@ type NewsSubmit = NewsStatus; // "success"; newspaper includes revisionNumber
1041
1052
  ```bash
1042
1053
  lumine admin brief --json
1043
1054
  lumine admin brief --days 3 --json
1055
+ lumine admin notable add 12647 --json
1056
+ lumine admin notable add Minecrarft_guy --json
1044
1057
  ```
1045
1058
 
1046
1059
  Read-only management insights for the delegated workflow, windowed since the
@@ -1066,21 +1079,44 @@ Four sections (Mikey's chosen cut, 2026-08-10):
1066
1079
  report period can begin up to one day before or after the exact brief window,
1067
1080
  so use those bounds when describing it. Flag accounts that jumped tiers or
1068
1081
  dominate that report period. May be `{ unavailable: true }` if the cost
1069
- report fails; say so rather than guessing.
1082
+ report fails; say so rather than guessing. This section is also the run's
1083
+ AI-cost exploit watch: while reading it, actively look for the signatures the
1084
+ brief actually exposes — one risk group spanning several user IDs, repeated
1085
+ plus-tag or dot-variant email families among top accounts, or heavy spend by
1086
+ accounts that `economy.topGainers` or `notableCandidates` independently marks
1087
+ as recent signups. Cross-check those signals against the escalation
1088
+ categories. Missing join-date or community data is unknown, not evidence that
1089
+ an account is young or empty. A run that reads the spending report without
1090
+ asking "could any of this be one person with many accounts?" has skipped a
1091
+ duty.
1070
1092
  - `notableCandidates` — kids (never bots, staff `userType`s, or users already
1071
1093
  on the Notable Users list) ranked by authored activity in the window, with
1072
1094
  `isNewUser` marking window-new signups. Use it to find the overlooked and
1073
1095
  rising users the editorial priorities exist for, and propose additions to
1074
- Mikey's Notable Users list in the report — the run never edits that list
1075
- itself.
1076
- - `teachers` — every `userType='supermod'` account with per-window
1077
- `subjectsPosted`, `commentsPosted`, `recommendationsGiven`, `rewardsGiven`,
1078
- `rewardTwinklesGiven`, `lastActive`/`daysSinceActive`, ordered by an
1079
- engagement score. Mikey's standing question here: which teachers genuinely
1080
- use the website to its fullest and which only work through it. Authored
1081
- posts and comments signal personal engagement; recommendations and rewards
1082
- are the "work" verbs — report the contrast, not just the totals, and treat
1083
- it as notable-users-but-for-teachers.
1096
+ Mikey's Notable Users list in the report. When Mikey approves additions,
1097
+ execute them with `lumine admin notable add <userId|username>` (idempotent —
1098
+ an existing member returns `already_done`; requires the `notable:write`
1099
+ scope, audited as `notable.add`, and writes through the management page's
1100
+ own canonical service). Without his approval the run only proposes.
1101
+ - `teachers` — the mentor/sage achievement holders (the accounts the website
1102
+ titles teacher/headteacher): real classroom teachers, NOT the
1103
+ `userType='supermod'` Korean operations staff, whose work-only usage is
1104
+ expected and deliberately excluded. Mikey's standing question here: which
1105
+ teachers genuinely use the website for themselves and which only work
1106
+ through it. Recommendations and rewards are classroom "work" verbs and
1107
+ count for nothing toward interest; genuine personal interest is measured by
1108
+ what a teacher does for themselves — authored subjects/comments,
1109
+ `reflections` (Daily Reflections answered), `dailyTasksCompleted`,
1110
+ `wordlePlays`, `buildsTouched` (Lumine builds they own that changed in the
1111
+ window), `aiStories`, and bounded `xpEvents` (XP-ledger activity). Shape:
1112
+ `genuinelyInterested` (interestScore > 0, ordered by it, each row carrying
1113
+ all the per-window counters plus `rank: mentor|sage` and `workScore`),
1114
+ `workOnly` (classroom verbs only, zero personal signals), `activeButSilent`
1115
+ (window-active log-ins with nothing at all, capped at 40), and `totals`.
1116
+ Daily-task counts use canonical whole-day indices and can begin up to one
1117
+ calendar day before the exact `window.sinceTs`.
1118
+ Report the contrast between the buckets, and treat `genuinelyInterested` as
1119
+ notable-users-but-for-teachers.
1084
1120
 
1085
1121
  The command needs only an active run's `content:read` scope and mutates
1086
1122
  nothing; reading the brief is not audited content action. Window boundaries
@@ -1089,6 +1125,27 @@ on the big append-only ledgers are found by binary-searching the PRIMARY key
1089
1125
  is still bounded to the selected 1–30 day window.
1090
1126
 
1091
1127
  ```ts
1128
+ type TeacherInsight = {
1129
+ userId: number;
1130
+ username: string | null;
1131
+ rank: 'mentor' | 'sage';
1132
+ lastActive: number | null;
1133
+ daysSinceActive: number | null;
1134
+ subjectsPosted: number;
1135
+ commentsPosted: number;
1136
+ reflections: number;
1137
+ dailyTasksCompleted: number;
1138
+ wordlePlays: number;
1139
+ buildsTouched: number;
1140
+ aiStories: number;
1141
+ xpEvents: number;
1142
+ recommendationsGiven: number;
1143
+ rewardsGiven: number;
1144
+ rewardTwinklesGiven: number;
1145
+ interestScore: number;
1146
+ workScore: number;
1147
+ };
1148
+
1092
1149
  type InsightsBrief = Success<{
1093
1150
  window: {
1094
1151
  sinceTs: number;
@@ -1124,18 +1181,16 @@ type InsightsBrief = Success<{
1124
1181
  isNewUser: boolean;
1125
1182
  lastActive: number | null;
1126
1183
  }>;
1127
- teachers: Array<{
1128
- userId: number;
1129
- username: string | null;
1130
- lastActive: number | null;
1131
- daysSinceActive: number | null;
1132
- subjectsPosted: number;
1133
- commentsPosted: number;
1134
- recommendationsGiven: number;
1135
- rewardsGiven: number;
1136
- rewardTwinklesGiven: number;
1137
- engagementScore: number;
1138
- }>;
1184
+ teachers: {
1185
+ genuinelyInterested: TeacherInsight[];
1186
+ workOnly: TeacherInsight[];
1187
+ activeButSilent: TeacherInsight[];
1188
+ totals: {
1189
+ total: number;
1190
+ activeInWindow: number;
1191
+ interestedInWindow: number;
1192
+ };
1193
+ };
1139
1194
  aiSpending:
1140
1195
  | {
1141
1196
  days: number;