@stage5/lumine 0.3.82 → 0.3.83
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-history.js +43 -0
- package/lib/admin.js +53 -8
- package/lib/commands.js +1 -1
- package/package.json +1 -1
- package/sdk/LUMINE_ADMIN.md +3 -0
|
@@ -20,6 +20,46 @@ function invalid(message) {
|
|
|
20
20
|
return error;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// Resolve once before pagination so every page uses the same canonical board.
|
|
24
|
+
export function resolveCurrentFeaturedHistory({ operation, receipt, all }) {
|
|
25
|
+
const subjects = receipt?.data?.subjects;
|
|
26
|
+
if (
|
|
27
|
+
receipt?.ok !== true || receipt.status !== "success" ||
|
|
28
|
+
!Array.isArray(subjects) || receipt.data.count !== subjects.length ||
|
|
29
|
+
subjects.some((subject) => !Number.isSafeInteger(subject?.id) || subject.id < 1)
|
|
30
|
+
) {
|
|
31
|
+
throw invalid("Could not read the canonical current Featured board; history was not requested.");
|
|
32
|
+
}
|
|
33
|
+
const subjectIds = subjects.map((subject) => subject.id);
|
|
34
|
+
if (new Set(subjectIds).size !== subjectIds.length) {
|
|
35
|
+
throw invalid("The current Featured board contains duplicate subject IDs; history was not requested.");
|
|
36
|
+
}
|
|
37
|
+
if (subjectIds.length > 20_000) {
|
|
38
|
+
throw invalid("Featured history accepts at most 20000 subject IDs per CLI scan.");
|
|
39
|
+
}
|
|
40
|
+
if (subjectIds.length > FEATURED_HISTORY_BATCH_SIZE && !all) {
|
|
41
|
+
throw invalid("The current Featured board has more than 100 subjects; pass --all to batch history reads.");
|
|
42
|
+
}
|
|
43
|
+
const url = new URL(operation.path, "https://lumine.invalid");
|
|
44
|
+
url.searchParams.set("subjectIds", subjectIds.join(","));
|
|
45
|
+
return {
|
|
46
|
+
...operation,
|
|
47
|
+
path: `${url.pathname}${url.search}`,
|
|
48
|
+
pagination: {
|
|
49
|
+
...operation.pagination,
|
|
50
|
+
summaryKeys: [...operation.pagination.summaryKeys, "subjectSelection"],
|
|
51
|
+
filters: { subjectIds },
|
|
52
|
+
},
|
|
53
|
+
featuredHistorySelection: {
|
|
54
|
+
source: "current-featured-board",
|
|
55
|
+
subjectIds,
|
|
56
|
+
message: subjectIds.length
|
|
57
|
+
? `Defaulted to the current Featured board (${subjectIds.length} subjects).`
|
|
58
|
+
: "Defaulted to the current Featured board, which is empty; no history was requested.",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
23
63
|
export async function runBatchedFeaturedHistory(args) {
|
|
24
64
|
const { options, operation, runId } = args;
|
|
25
65
|
if (options.adminCursor) {
|
|
@@ -135,6 +175,9 @@ export async function runBatchedFeaturedHistory(args) {
|
|
|
135
175
|
? firstCoverage
|
|
136
176
|
: { complete: false, startedAt: null },
|
|
137
177
|
subjects: results.flatMap((result) => result.data.subjects),
|
|
178
|
+
...(operation.featuredHistorySelection
|
|
179
|
+
? { subjectSelection: operation.featuredHistorySelection }
|
|
180
|
+
: {}),
|
|
138
181
|
pagination: {
|
|
139
182
|
nextCursor: null,
|
|
140
183
|
hasMore: false,
|
package/lib/admin.js
CHANGED
|
@@ -86,6 +86,7 @@ import {
|
|
|
86
86
|
} from "./admin-featured.js";
|
|
87
87
|
import {
|
|
88
88
|
FEATURED_HISTORY_BATCH_SIZE,
|
|
89
|
+
resolveCurrentFeaturedHistory,
|
|
89
90
|
runBatchedFeaturedHistory,
|
|
90
91
|
} from "./admin-featured-history.js";
|
|
91
92
|
|
|
@@ -1379,7 +1380,7 @@ export async function adminCommand(options) {
|
|
|
1379
1380
|
if (result && result.ok === false) process.exitCode = 1;
|
|
1380
1381
|
return result;
|
|
1381
1382
|
}
|
|
1382
|
-
|
|
1383
|
+
let operation = parseAdminOperation(options);
|
|
1383
1384
|
if (operation.name === "news.validate") {
|
|
1384
1385
|
const validation = validateNewsEditorial({
|
|
1385
1386
|
claim: operation.claim,
|
|
@@ -1607,15 +1608,54 @@ export async function adminCommand(options) {
|
|
|
1607
1608
|
}
|
|
1608
1609
|
return receipt;
|
|
1609
1610
|
};
|
|
1610
|
-
|
|
1611
|
-
|
|
1611
|
+
if (operation.featuredHistoryCurrentBoard) {
|
|
1612
|
+
// The empty-board path skips pagination, but must preserve its files too.
|
|
1613
|
+
if (options.adminAll && options.adminCheckpoint && options.adminOutput &&
|
|
1614
|
+
path.resolve(options.adminCheckpoint) === path.resolve(options.adminOutput)) {
|
|
1615
|
+
throw cliValidationError("The pagination checkpoint and result output must use different files.");
|
|
1616
|
+
}
|
|
1617
|
+
if (options.adminAll && options.adminCursor) {
|
|
1618
|
+
throw cliValidationError("Use --resume with the scan checkpoint instead of combining --all with --cursor.");
|
|
1619
|
+
}
|
|
1620
|
+
if (options.adminResume && !options.adminAll) {
|
|
1621
|
+
throw cliValidationError("--resume requires --all or post skip-batch.");
|
|
1622
|
+
}
|
|
1623
|
+
operation = resolveCurrentFeaturedHistory({
|
|
1624
|
+
operation,
|
|
1625
|
+
receipt: await fetchOperation("/cli/admin/subjects/featured"),
|
|
1626
|
+
all: options.adminAll,
|
|
1627
|
+
});
|
|
1628
|
+
if (!operation.featuredHistorySelection.subjectIds.length && (options.adminResume || options.adminCursor)) {
|
|
1629
|
+
throw cliValidationError("The current Featured board is empty; it cannot supply the subject IDs for a previous history scan. Use explicit --subject-ids for a fresh scan or cursor continuation.");
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
const transformResult = (rawResult) => {
|
|
1633
|
+
const transformed = transformAdminResult({
|
|
1612
1634
|
operation,
|
|
1613
1635
|
result: rawResult,
|
|
1614
1636
|
options,
|
|
1615
1637
|
recommendationContentTypes,
|
|
1616
1638
|
viewFilter,
|
|
1617
1639
|
});
|
|
1618
|
-
|
|
1640
|
+
return operation.featuredHistorySelection && transformed.ok
|
|
1641
|
+
? { ...transformed, data: { ...transformed.data, subjectSelection: operation.featuredHistorySelection } }
|
|
1642
|
+
: transformed;
|
|
1643
|
+
};
|
|
1644
|
+
if (operation.featuredHistorySelection?.subjectIds.length === 0) {
|
|
1645
|
+
// An empty board is canonical; there is no valid nonempty history query.
|
|
1646
|
+
// Do not invent audit coverage or a history snapshot for this case.
|
|
1647
|
+
result = {
|
|
1648
|
+
ok: true,
|
|
1649
|
+
status: "success",
|
|
1650
|
+
data: {
|
|
1651
|
+
subjectSelection: operation.featuredHistorySelection,
|
|
1652
|
+
subjects: [],
|
|
1653
|
+
events: [],
|
|
1654
|
+
coverage: null,
|
|
1655
|
+
pagination: { hasMore: false, exhausted: true, nextCursor: null },
|
|
1656
|
+
},
|
|
1657
|
+
};
|
|
1658
|
+
} else if (operation.name === "spending.report") {
|
|
1619
1659
|
result = await completeSpendingReport(await fetchOperation(), options);
|
|
1620
1660
|
if (result.status === "partial") process.exitCode = 1;
|
|
1621
1661
|
} else if (operation.featuredWorkflow) {
|
|
@@ -4395,10 +4435,10 @@ export function parseAdminOperation(options) {
|
|
|
4395
4435
|
return readOperation("featured.list", "/cli/admin/subjects/featured");
|
|
4396
4436
|
}
|
|
4397
4437
|
if (action === "history") {
|
|
4398
|
-
const
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4438
|
+
const currentBoard = !String(options.adminIds || "").trim();
|
|
4439
|
+
const subjectIds = currentBoard
|
|
4440
|
+
? []
|
|
4441
|
+
: parseFeaturedSubjectIds(options.adminIds, "--subject-ids");
|
|
4402
4442
|
if (subjectIds.length > 20_000) {
|
|
4403
4443
|
throw cliValidationError(
|
|
4404
4444
|
"Featured history accepts at most 20000 subject IDs per CLI scan.",
|
|
@@ -4420,6 +4460,7 @@ export function parseAdminOperation(options) {
|
|
|
4420
4460
|
limit: options.limit,
|
|
4421
4461
|
}),
|
|
4422
4462
|
{
|
|
4463
|
+
...(currentBoard ? { featuredHistoryCurrentBoard: true } : {}),
|
|
4423
4464
|
pagination: {
|
|
4424
4465
|
collectionKey: "events",
|
|
4425
4466
|
summaryKeys: ["coverage", "subjects"],
|
|
@@ -8442,6 +8483,10 @@ function printAdminResult({ operation, result }) {
|
|
|
8442
8483
|
}
|
|
8443
8484
|
|
|
8444
8485
|
function printFeaturedHistorySummary(data) {
|
|
8486
|
+
if (data.subjectSelection) {
|
|
8487
|
+
console.log(data.subjectSelection.message);
|
|
8488
|
+
if (data.subjectSelection.subjectIds.length === 0) return;
|
|
8489
|
+
}
|
|
8445
8490
|
const coverage = data.coverage || {};
|
|
8446
8491
|
console.log(
|
|
8447
8492
|
coverage.complete
|
package/lib/commands.js
CHANGED
|
@@ -3389,7 +3389,7 @@ export function printHelp() {
|
|
|
3389
3389
|
lumine admin subject creator set-made-by-poster <subject-id> [--json]
|
|
3390
3390
|
lumine admin subject feature|unfeature <subject-id> [--json]
|
|
3391
3391
|
lumine admin featured list [--unviewed|--viewed] [--json]
|
|
3392
|
-
lumine admin featured history --subject-ids <id,id,...> [--all --checkpoint <file> [--resume]] [--cursor <cursor>] [--json]
|
|
3392
|
+
lumine admin featured history [--subject-ids <id,id,...>] [--all --checkpoint <file> [--resume]] [--cursor <cursor>] [--json]
|
|
3393
3393
|
lumine admin featured add --subject-ids <id,id,...> --posted-after <ISO-8601-or-Unix-time> [--json]
|
|
3394
3394
|
lumine admin featured reorder --subject-ids <id,id,...> [--json]
|
|
3395
3395
|
lumine admin featured rotate --remove-subject-ids <id,id,...> --add-subject-ids <id,id,...> [--posted-after <timestamp>] [--json]
|
package/package.json
CHANGED
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -2671,6 +2671,7 @@ lumine admin subject creator set-made-by-poster 123 --json
|
|
|
2671
2671
|
lumine admin subject feature 123 --json
|
|
2672
2672
|
lumine admin subject unfeature 123 --json
|
|
2673
2673
|
lumine admin featured list --json
|
|
2674
|
+
lumine admin featured history --all --json # defaults to current Featured board
|
|
2674
2675
|
lumine admin featured history --subject-ids 50,40 --all --json
|
|
2675
2676
|
lumine admin featured add --subject-ids 50,40 \
|
|
2676
2677
|
--posted-after 2026-08-27T00:00:00+07:00 --json
|
|
@@ -2789,6 +2790,8 @@ unknown/deleted IDs, missing current members, non-subject rows, and more than
|
|
|
2789
2790
|
100 subjects. Permanent pins and editorial ordering policy are deliberately not
|
|
2790
2791
|
hardcoded.
|
|
2791
2792
|
|
|
2793
|
+
When `lumine admin featured history` omits `--subject-ids`, the CLI reads the current Featured board once and requests history for those IDs in board order. Human output explicitly says that it defaulted to the current board; JSON and `--output` include `data.subjectSelection` with `source: "current-featured-board"`, `subjectIds`, and the same message. An empty board succeeds with empty subjects/events and `coverage: null`, explicitly stating that no history was requested. A failed or malformed board read fails rather than being treated as empty. Explicit `--subject-ids` keeps its existing behavior and skips the board read. Pagination retains the resolved IDs for the entire scan. A no-ID `--all --resume` requires the same board IDs in the same order; a changed board is rejected by checkpoint scope validation. If the board changes, use the previous result's explicit `subjectIds` for `--cursor`, or start a fresh `--all` scan with those IDs and a new checkpoint. An empty board cannot resume a prior scan or supply IDs for its cursor.
|
|
2794
|
+
|
|
2792
2795
|
`featured history` is the compact canonical evidence path. It returns only
|
|
2793
2796
|
coverage metadata, per-subject lifetime summaries, and paginated mutation
|
|
2794
2797
|
events; it does not repeat full audit before/after board snapshots. An event of
|