@stage5/lumine 0.3.42 → 0.3.43
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-featured.js +3 -2
- package/lib/admin.js +57 -8
- package/lib/build-review.js +41 -2
- package/lib/commands.js +2 -0
- package/package.json +1 -1
- package/sdk/LUMINE_ADMIN.md +29 -1
package/lib/admin-featured.js
CHANGED
|
@@ -253,6 +253,7 @@ export async function runFeaturedWorkflow({
|
|
|
253
253
|
operation,
|
|
254
254
|
authToken,
|
|
255
255
|
runId,
|
|
256
|
+
sourceRunId = 0,
|
|
256
257
|
request = requestJson,
|
|
257
258
|
}) {
|
|
258
259
|
const apiUrl = String(options.apiUrl).replace(/\/$/, "");
|
|
@@ -273,9 +274,9 @@ export async function runFeaturedWorkflow({
|
|
|
273
274
|
options.adminFile,
|
|
274
275
|
options.adminApprove,
|
|
275
276
|
);
|
|
276
|
-
if (plan.runId !== runId)
|
|
277
|
+
if (plan.runId !== runId && !(sourceRunId > 0 && plan.runId === sourceRunId))
|
|
277
278
|
throw invalid(
|
|
278
|
-
"This plan belongs to a different run; prepare and approve a fresh plan.",
|
|
279
|
+
"This plan belongs to a different run; prepare and approve a fresh plan, or apply it from an approvals run started on its full run (--scope approvals --source-run).",
|
|
279
280
|
);
|
|
280
281
|
const result = await call(
|
|
281
282
|
"/plan/apply",
|
package/lib/admin.js
CHANGED
|
@@ -1312,6 +1312,8 @@ export async function adminCommand(options) {
|
|
|
1312
1312
|
});
|
|
1313
1313
|
let runId = 0;
|
|
1314
1314
|
let runScope = null;
|
|
1315
|
+
// The full run an approvals run applies (0 for every other run).
|
|
1316
|
+
let sourceRunId = 0;
|
|
1315
1317
|
let correctionSessionId = 0;
|
|
1316
1318
|
let correctionSession = null;
|
|
1317
1319
|
let commentSessionId = 0;
|
|
@@ -1406,6 +1408,8 @@ export async function adminCommand(options) {
|
|
|
1406
1408
|
if (selectedRun) {
|
|
1407
1409
|
runScope = canonicalAdminRunScope(selectedRun);
|
|
1408
1410
|
assertAdminOperationAllowedForRunScope({ operation, runScope });
|
|
1411
|
+
sourceRunId =
|
|
1412
|
+
runScope === "approvals" ? Number(selectedRun.sourceRunId || 0) : 0;
|
|
1409
1413
|
}
|
|
1410
1414
|
if (options.adminIdentity && commentSession) {
|
|
1411
1415
|
const requestedIdentity = parseIdentity(options.adminIdentity);
|
|
@@ -1519,6 +1523,8 @@ export async function adminCommand(options) {
|
|
|
1519
1523
|
operation,
|
|
1520
1524
|
authToken: auth.token,
|
|
1521
1525
|
runId,
|
|
1526
|
+
// An approvals run applies its source full run's Featured plan.
|
|
1527
|
+
sourceRunId,
|
|
1522
1528
|
});
|
|
1523
1529
|
} else if (operation.name === "build.review") {
|
|
1524
1530
|
result = await runManagedBuildReview({
|
|
@@ -2302,7 +2308,8 @@ export function canonicalAdminRunScope(run) {
|
|
|
2302
2308
|
const raw = run?.runScope;
|
|
2303
2309
|
if (raw === undefined || raw === null || raw === "") return "full";
|
|
2304
2310
|
const scope = String(raw);
|
|
2305
|
-
if (["full", "featured", "newspaper"].includes(scope))
|
|
2311
|
+
if (["full", "featured", "newspaper", "approvals"].includes(scope))
|
|
2312
|
+
return scope;
|
|
2306
2313
|
throw cliValidationError(
|
|
2307
2314
|
"The API returned an invalid administrator run scope.",
|
|
2308
2315
|
);
|
|
@@ -2334,6 +2341,17 @@ const FEATURED_RUN_OPERATIONS = new Set([
|
|
|
2334
2341
|
"daily-run.fail",
|
|
2335
2342
|
]);
|
|
2336
2343
|
|
|
2344
|
+
// An approvals run acts on Mikey's decisions whenever he gets to them: a full
|
|
2345
|
+
// run's write work without the newspaper, sponsor scan or bank seeding (the
|
|
2346
|
+
// API holds the same line through the run's scopes).
|
|
2347
|
+
const APPROVALS_RUN_EXCLUDED_OPERATIONS = new Set([
|
|
2348
|
+
"news.claim",
|
|
2349
|
+
"news.print",
|
|
2350
|
+
"news.submit",
|
|
2351
|
+
"sponsor.integrity.scan",
|
|
2352
|
+
"reward-bank.seed",
|
|
2353
|
+
]);
|
|
2354
|
+
|
|
2337
2355
|
export function assertAdminOperationAllowedForRunScope({
|
|
2338
2356
|
operation,
|
|
2339
2357
|
runScope,
|
|
@@ -2354,6 +2372,12 @@ export function assertAdminOperationAllowedForRunScope({
|
|
|
2354
2372
|
`A newspaper-only run does not authorize ${operation.name}.`,
|
|
2355
2373
|
);
|
|
2356
2374
|
}
|
|
2375
|
+
if (runScope === "approvals") {
|
|
2376
|
+
if (!APPROVALS_RUN_EXCLUDED_OPERATIONS.has(operation.name)) return;
|
|
2377
|
+
throw cliValidationError(
|
|
2378
|
+
`An approvals run does not do ${operation.name}; that belongs to a full daily run.`,
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2357
2381
|
if (runScope !== "featured") return;
|
|
2358
2382
|
const subjectCommentAlias =
|
|
2359
2383
|
operation.name === "post.comments" &&
|
|
@@ -3788,8 +3812,23 @@ export function parseAdminOperation(options) {
|
|
|
3788
3812
|
if (namespace === "daily-run") {
|
|
3789
3813
|
if (action === "start") {
|
|
3790
3814
|
const runScope = parseDailyRunScope(options.adminScope || "full");
|
|
3791
|
-
|
|
3792
|
-
|
|
3815
|
+
// An approvals run acts on decisions, so it posts unless told otherwise.
|
|
3816
|
+
const commentMode = parseCommentMode(
|
|
3817
|
+
options.commentMode || (runScope === "approvals" ? "post" : "off"),
|
|
3818
|
+
);
|
|
3819
|
+
// Optional: lets an approvals run reuse that run's plans and drafts.
|
|
3820
|
+
const sourceRunId =
|
|
3821
|
+
runScope === "approvals" && options.adminSourceRun
|
|
3822
|
+
? parseRequiredInteger(options.adminSourceRun, "--source-run", 1)
|
|
3823
|
+
: undefined;
|
|
3824
|
+
if (runScope !== "approvals" && options.adminSourceRun) {
|
|
3825
|
+
throw cliValidationError("--source-run is only for --scope approvals.");
|
|
3826
|
+
}
|
|
3827
|
+
if (
|
|
3828
|
+
runScope !== "full" &&
|
|
3829
|
+
runScope !== "approvals" &&
|
|
3830
|
+
commentMode !== "off"
|
|
3831
|
+
) {
|
|
3793
3832
|
throw cliValidationError("A scoped run requires --comment-mode off.");
|
|
3794
3833
|
}
|
|
3795
3834
|
return writeOperation(
|
|
@@ -3805,6 +3844,7 @@ export function parseAdminOperation(options) {
|
|
|
3805
3844
|
commentMode,
|
|
3806
3845
|
scope: runScope,
|
|
3807
3846
|
runKey: options.runKey || defaultDailyRunKey(runScope),
|
|
3847
|
+
...(sourceRunId ? { sourceRunId } : {}),
|
|
3808
3848
|
},
|
|
3809
3849
|
);
|
|
3810
3850
|
}
|
|
@@ -5455,8 +5495,10 @@ function parseDailyRunScope(value) {
|
|
|
5455
5495
|
const scope = String(value || "full")
|
|
5456
5496
|
.trim()
|
|
5457
5497
|
.toLowerCase();
|
|
5458
|
-
if (!["full", "featured", "newspaper"].includes(scope)) {
|
|
5459
|
-
throw cliValidationError(
|
|
5498
|
+
if (!["full", "featured", "newspaper", "approvals"].includes(scope)) {
|
|
5499
|
+
throw cliValidationError(
|
|
5500
|
+
"--scope must be full, featured, newspaper, or approvals.",
|
|
5501
|
+
);
|
|
5460
5502
|
}
|
|
5461
5503
|
return scope;
|
|
5462
5504
|
}
|
|
@@ -5788,17 +5830,24 @@ function printAdminEnergyBudget(energyBudget) {
|
|
|
5788
5830
|
);
|
|
5789
5831
|
}
|
|
5790
5832
|
console.log(
|
|
5791
|
-
"day | charged | overflow | users | rechg | runs | calls/run avg/p90 | $/run avg/p90 | stops chg/unchg | busy | queue rst/stop/resume | telemetry rows",
|
|
5833
|
+
"day | charged | overflow | users | rechg | runs | calls/run avg/p90 | $/run avg/p90 | stops chg/unchg | busy | queue rst/stop/resume/wait-cancel | telemetry rows",
|
|
5792
5834
|
);
|
|
5793
5835
|
for (const day of energyBudget.byDay || []) {
|
|
5794
5836
|
const m = day.telemetry.metrics;
|
|
5795
5837
|
const runs = day.runs.total;
|
|
5796
5838
|
// Queue metrics are absent from APIs older than the queued-request telemetry.
|
|
5797
|
-
|
|
5839
|
+
// worker_wait_cancelled: a request cancelled because the Build worker
|
|
5840
|
+
// stayed unreachable through the API handoff wait.
|
|
5841
|
+
const queue = [
|
|
5842
|
+
"queued_restored",
|
|
5843
|
+
"queued_stopped",
|
|
5844
|
+
"busy_resume_requested",
|
|
5845
|
+
"worker_wait_cancelled",
|
|
5846
|
+
]
|
|
5798
5847
|
.map((metric) => String(m[metric]?.count ?? "-"))
|
|
5799
5848
|
.join("/");
|
|
5800
5849
|
console.log(
|
|
5801
|
-
`${day.dayKey}${day.inProgress ? "*" : " "}| ${formatAdminUsd(day.chargedUsd).padStart(7)} | ${formatAdminUsd(day.overflowUsd).padStart(8)} | ${String(day.users).padStart(5)} | ${String(day.recharges).padStart(5)} | ${String(runs.runs).padStart(4)} | ${String(runs.callsPerRun.avg).padStart(7)}/${String(runs.callsPerRun.p90).padEnd(9)} | ${formatAdminUsd(runs.usdPerRun.avg).padStart(6)}/${formatAdminUsd(runs.usdPerRun.p90).padEnd(6)} | ${String(m.budget_stop_changed.count).padStart(7)}/${String(m.budget_stop_unchanged.count).padEnd(7)} | ${String(m.busy_refusal.count).padStart(4)} | ${queue.padEnd(
|
|
5850
|
+
`${day.dayKey}${day.inProgress ? "*" : " "}| ${formatAdminUsd(day.chargedUsd).padStart(7)} | ${formatAdminUsd(day.overflowUsd).padStart(8)} | ${String(day.users).padStart(5)} | ${String(day.recharges).padStart(5)} | ${String(runs.runs).padStart(4)} | ${String(runs.callsPerRun.avg).padStart(7)}/${String(runs.callsPerRun.p90).padEnd(9)} | ${formatAdminUsd(runs.usdPerRun.avg).padStart(6)}/${formatAdminUsd(runs.usdPerRun.p90).padEnd(6)} | ${String(m.budget_stop_changed.count).padStart(7)}/${String(m.budget_stop_unchanged.count).padEnd(7)} | ${String(m.busy_refusal.count).padStart(4)} | ${queue.padEnd(33)} | ${day.telemetry.rowCount}`,
|
|
5802
5851
|
);
|
|
5803
5852
|
}
|
|
5804
5853
|
console.log("* = in-progress UTC day; never headline it.");
|
package/lib/build-review.js
CHANGED
|
@@ -909,15 +909,19 @@ async function runChromeReview({
|
|
|
909
909
|
}
|
|
910
910
|
}
|
|
911
911
|
await cdp.send("Browser.close").catch(() => {});
|
|
912
|
+
let forcedTermination = false;
|
|
912
913
|
const closeResult = await Promise.race([
|
|
913
914
|
closed,
|
|
914
915
|
delay(5_000).then(() => {
|
|
916
|
+
forcedTermination = true;
|
|
915
917
|
child.kill("SIGTERM");
|
|
916
918
|
return closed;
|
|
917
919
|
}),
|
|
918
920
|
]);
|
|
919
921
|
return {
|
|
920
922
|
...closeResult,
|
|
923
|
+
closeRequested: true,
|
|
924
|
+
forcedTermination,
|
|
921
925
|
stdout,
|
|
922
926
|
stderr,
|
|
923
927
|
consoleLines: consoleLines.slice(-MAX_CAPTURED_LOG_LINES),
|
|
@@ -934,6 +938,31 @@ async function runChromeReview({
|
|
|
934
938
|
}
|
|
935
939
|
}
|
|
936
940
|
|
|
941
|
+
const CHROME_KILLED_AFTER_CLOSE_CODE = 2;
|
|
942
|
+
|
|
943
|
+
// Chrome sometimes exits 2 (RESULT_CODE_KILLED) while tearing down a
|
|
944
|
+
// GPU-heavy page after our own Browser.close. Every piece of evidence was
|
|
945
|
+
// captured before that close, so that one shutdown result is cleanup, not a
|
|
946
|
+
// failed review. Any other code, a signal, or our forced kill still fails.
|
|
947
|
+
export function reviewBrowserExitedCleanly(browser) {
|
|
948
|
+
return (
|
|
949
|
+
browser?.code === 0 ||
|
|
950
|
+
(browser?.code === CHROME_KILLED_AFTER_CLOSE_CODE &&
|
|
951
|
+
!browser.signal &&
|
|
952
|
+
browser.closeRequested === true &&
|
|
953
|
+
browser.forcedTermination === false)
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// The last few Chrome stderr lines, for diagnosing a nonzero exit.
|
|
958
|
+
function chromeStderrTail(stderr) {
|
|
959
|
+
return String(stderr || "")
|
|
960
|
+
.split(/\r?\n/)
|
|
961
|
+
.filter(Boolean)
|
|
962
|
+
.slice(-20)
|
|
963
|
+
.map((line) => line.slice(0, 400));
|
|
964
|
+
}
|
|
965
|
+
|
|
937
966
|
function captureConsoleEvidence(stderr, cdpLines = []) {
|
|
938
967
|
const chromeLines = String(stderr || "")
|
|
939
968
|
.split(/\r?\n/)
|
|
@@ -1003,8 +1032,9 @@ export async function runManagedBuildReview({ options, authToken, buildId }) {
|
|
|
1003
1032
|
const interactionOutcome = browser.interaction || null;
|
|
1004
1033
|
const interactionCompleted =
|
|
1005
1034
|
!interaction || interactionOutcome?.status === "completed";
|
|
1035
|
+
const browserExitedCleanly = reviewBrowserExitedCleanly(browser);
|
|
1006
1036
|
const confirmed =
|
|
1007
|
-
|
|
1037
|
+
browserExitedCleanly &&
|
|
1008
1038
|
screenshotBytes > 0 &&
|
|
1009
1039
|
browser.runtimeReadiness?.ready === true &&
|
|
1010
1040
|
beforeVersion === afterVersion &&
|
|
@@ -1025,6 +1055,10 @@ export async function runManagedBuildReview({ options, authToken, buildId }) {
|
|
|
1025
1055
|
executable,
|
|
1026
1056
|
exitCode: browser.code,
|
|
1027
1057
|
signal: browser.signal,
|
|
1058
|
+
closeRequested: browser.closeRequested === true,
|
|
1059
|
+
forcedTermination: browser.forcedTermination === true,
|
|
1060
|
+
exitedCleanly: browserExitedCleanly,
|
|
1061
|
+
stderrTail: browser.code === 0 ? null : chromeStderrTail(browser.stderr),
|
|
1028
1062
|
runtimeReadiness: browser.runtimeReadiness,
|
|
1029
1063
|
},
|
|
1030
1064
|
screenshot: {
|
|
@@ -1059,7 +1093,12 @@ export async function runManagedBuildReview({ options, authToken, buildId }) {
|
|
|
1059
1093
|
? "The Build was republished during review; review the new artifact before commenting."
|
|
1060
1094
|
: browser.runtimeReadiness?.runtimeErrorVisible
|
|
1061
1095
|
? "The isolated Build runtime exposed a runtime error instead of a reviewable app."
|
|
1062
|
-
:
|
|
1096
|
+
: !browserExitedCleanly &&
|
|
1097
|
+
screenshotBytes > 0 &&
|
|
1098
|
+
browser.runtimeReadiness?.ready === true &&
|
|
1099
|
+
interactionCompleted
|
|
1100
|
+
? `Chrome exited with ${browser.signal ? `signal ${browser.signal}` : `code ${browser.code}`}${browser.forcedTermination ? " after the CLI had to stop it" : ""} once the review evidence was captured. The app loaded and the screenshots are in the receipt, but the receipt is not confirmed; see browser.stderrTail and review again.`
|
|
1101
|
+
: interactionFailure
|
|
1063
1102
|
? `Interaction step ${interactionOutcome?.failedStep?.index ?? "?"} (${interactionOutcome?.failedStep?.kind ?? "?"}) failed: ${interactionOutcome?.failedStep?.error ?? "unknown"} The receipt lists the ${interactionOutcome?.stepsCompleted ?? 0} completed step(s) and their screenshots; fix the script and review again.`
|
|
1064
1103
|
: "The isolated Build runtime did not become visibly reviewable before the observation window ended.",
|
|
1065
1104
|
interactionFailure
|
package/lib/commands.js
CHANGED
|
@@ -2798,6 +2798,7 @@ export function parseArgs(args) {
|
|
|
2798
2798
|
adminAs: raw.as ? String(raw.as) : "",
|
|
2799
2799
|
adminContextLimit: raw.limit === undefined ? 20 : Number(raw.limit),
|
|
2800
2800
|
adminRun: raw.run ? String(raw.run) : "",
|
|
2801
|
+
adminSourceRun: raw.sourceRun ? String(raw.sourceRun) : "",
|
|
2801
2802
|
adminTarget: raw.target ? String(raw.target) : "",
|
|
2802
2803
|
adminActions: raw.actions ? String(raw.actions) : "",
|
|
2803
2804
|
adminDate: raw.date ? String(raw.date) : "",
|
|
@@ -3235,6 +3236,7 @@ export function printHelp() {
|
|
|
3235
3236
|
lumine admin ai-email-policy get --email <address> [--json]
|
|
3236
3237
|
lumine admin ai-email-policy set --email <address> --mode <automatic|separate_accounts> --note <text> [--json]
|
|
3237
3238
|
lumine admin daily-run start [--scope full|featured|newspaper] [--identity zero|ciel|auto] [--comment-mode off|draft|post] [--run-key <key>] [--json]
|
|
3239
|
+
lumine admin daily-run start --scope approvals [--source-run <run-id>] [--identity zero|ciel] [--comment-mode off|draft|post] [--json] # act on Mikey's decisions; --source-run reuses that run's plans and drafts
|
|
3238
3240
|
lumine admin daily-run status|report|complete|fail [--run <completed-run-id>] [--reason <text>] [--json]
|
|
3239
3241
|
lumine admin daily-run escalation add --target <target> --note <summary> [--severity attention|urgent] [--json]
|
|
3240
3242
|
lumine admin escalation list [--status open|acknowledged|resolved|all] [--limit <number>] [--json]
|
package/package.json
CHANGED
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -28,6 +28,30 @@ canonical structured data.
|
|
|
28
28
|
`off`. It cannot inspect queues, conduct, logs, Builds, or Featured.
|
|
29
29
|
Completion does not advance full-review windows or carry-over telemetry.
|
|
30
30
|
Its distinct `/daily-runs/start/newspaper` endpoint fails closed on older APIs.
|
|
31
|
+
- `--scope approvals [--source-run <id>]` acts on Mikey's decisions whenever
|
|
32
|
+
he gets to them (Mikey, 10-17: don't be trapped by the morning run). It has
|
|
33
|
+
a full run's write work: Featured plans and changes, Featured-comment and
|
|
34
|
+
generic recommendations, rewards, comments (draft, post, edit) and chat. It
|
|
35
|
+
has none of the newspaper, sponsor scan, bank seeding or reading duties, and
|
|
36
|
+
never counts as a full review, so review windows don't move.
|
|
37
|
+
- `--source-run` is optional. It lets the run use that run's prepared
|
|
38
|
+
Featured plan (same hash) and review receipts for up to 7 days, abandoned
|
|
39
|
+
runs included. A stale board is still refused at apply, so prepare a new
|
|
40
|
+
plan then.
|
|
41
|
+
- Source drafts expire after 30 minutes: re-draft an approved comment with
|
|
42
|
+
its approved text (with a fresh `builds review` receipt for an app). The
|
|
43
|
+
API refuses a second published comment by the same identity in the same
|
|
44
|
+
place for the same source run.
|
|
45
|
+
- Identity defaults to the source run's (else today's scheduled one) and
|
|
46
|
+
`--identity` overrides it. Either way it is named explicitly, so Bangkok
|
|
47
|
+
midnight doesn't end the run. `--comment-mode` defaults to `post`.
|
|
48
|
+
Its distinct `/daily-runs/start/approvals` endpoint fails closed on older
|
|
49
|
+
APIs.
|
|
50
|
+
- **Complete a full run when its report is delivered** (`daily-run complete`),
|
|
51
|
+
even with Mikey's decisions still pending. Do not hold it open for them: an
|
|
52
|
+
expired run can never be completed, so the review would not count, its
|
|
53
|
+
report would be unavailable, and the next run would re-read the whole
|
|
54
|
+
window. When Mikey answers, start an approvals run on it.
|
|
31
55
|
- The public content actor is Zero or Ciel. Mikey's operator ID is retained in
|
|
32
56
|
private audit rows and is not embedded in public comment metadata.
|
|
33
57
|
- Delegated HTTP work never authenticates as the bot, opens a bot socket, changes
|
|
@@ -1017,6 +1041,8 @@ identity, or advances rotation.
|
|
|
1017
1041
|
lumine admin daily-run start --identity auto --comment-mode off --json
|
|
1018
1042
|
lumine admin daily-run start --scope featured --identity auto --json
|
|
1019
1043
|
lumine admin daily-run start --scope newspaper --identity auto --json
|
|
1044
|
+
lumine admin daily-run start --scope approvals --source-run 125 --json
|
|
1045
|
+
lumine admin daily-run start --scope approvals --identity zero --json
|
|
1020
1046
|
lumine admin daily-run start --identity ciel --comment-mode draft \
|
|
1021
1047
|
--run-key daily:2026-08-06:review --json
|
|
1022
1048
|
lumine admin daily-run status --json
|
|
@@ -2769,7 +2795,9 @@ inferred. The CLI then reads the live board and verifies exact membership/order.
|
|
|
2769
2795
|
Retry the identical apply command after uncertain transport failure; it reuses
|
|
2770
2796
|
the same idempotency key. If the final read fails or finds an intervening change,
|
|
2771
2797
|
the error retains the canonical apply receipt and never restores old state.
|
|
2772
|
-
|
|
2798
|
+
A different run requires a fresh plan and renewed approval, except an
|
|
2799
|
+
approvals run started with `--source-run <the plan's run>`, which can apply
|
|
2800
|
+
that exact plan with the same hash (or prepare a new one).
|
|
2773
2801
|
|
|
2774
2802
|
Use the following workflow for full daily comment review, or only when Mikey's
|
|
2775
2803
|
Featured slice includes comment encouragement:
|