erdos-problems 0.1.13 → 0.2.1
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 +117 -4
- package/docs/RESEARCH_LOOP.md +21 -2
- package/package.json +1 -1
- package/packs/number-theory/README.md +17 -0
- package/packs/number-theory/problems/1/CHECKPOINT_TEMPLATE.md +7 -0
- package/packs/number-theory/problems/1/CONTEXT.md +8 -0
- package/packs/number-theory/problems/1/FRONTIER_NOTE.md +8 -0
- package/packs/number-theory/problems/1/OPS_DETAILS.yaml +25 -0
- package/packs/number-theory/problems/1/REPORT_TEMPLATE.md +7 -0
- package/packs/number-theory/problems/1/ROUTE_HISTORY.md +5 -0
- package/packs/number-theory/problems/1/ROUTE_PACKET.yaml +13 -0
- package/packs/number-theory/problems/1/context.yaml +25 -0
- package/packs/number-theory/problems/2/CHECKPOINT_TEMPLATE.md +7 -0
- package/packs/number-theory/problems/2/CONTEXT.md +8 -0
- package/packs/number-theory/problems/2/FRONTIER_NOTE.md +8 -0
- package/packs/number-theory/problems/2/OPS_DETAILS.yaml +25 -0
- package/packs/number-theory/problems/2/REPORT_TEMPLATE.md +7 -0
- package/packs/number-theory/problems/2/ROUTE_HISTORY.md +5 -0
- package/packs/number-theory/problems/2/ROUTE_PACKET.yaml +13 -0
- package/packs/number-theory/problems/2/context.yaml +25 -0
- package/packs/sunflower/README.md +17 -4
- package/packs/sunflower/problems/20/CHECKPOINT_TEMPLATE.md +29 -0
- package/packs/sunflower/problems/20/FRONTIER_NOTE.md +13 -0
- package/packs/sunflower/problems/20/OPS_DETAILS.yaml +44 -0
- package/packs/sunflower/problems/20/REPORT_TEMPLATE.md +23 -0
- package/packs/sunflower/problems/20/ROUTE_HISTORY.md +18 -0
- package/packs/sunflower/problems/536/CHECKPOINT_TEMPLATE.md +7 -0
- package/packs/sunflower/problems/536/FRONTIER_NOTE.md +8 -0
- package/packs/sunflower/problems/536/OPS_DETAILS.yaml +39 -0
- package/packs/sunflower/problems/536/REPORT_TEMPLATE.md +7 -0
- package/packs/sunflower/problems/536/ROUTE_HISTORY.md +5 -0
- package/packs/sunflower/problems/856/CHECKPOINT_TEMPLATE.md +7 -0
- package/packs/sunflower/problems/856/FRONTIER_NOTE.md +8 -0
- package/packs/sunflower/problems/856/OPS_DETAILS.yaml +39 -0
- package/packs/sunflower/problems/856/REPORT_TEMPLATE.md +7 -0
- package/packs/sunflower/problems/856/ROUTE_HISTORY.md +5 -0
- package/packs/sunflower/problems/857/CHECKPOINT_TEMPLATE.md +32 -0
- package/packs/sunflower/problems/857/FRONTIER_NOTE.md +18 -0
- package/packs/sunflower/problems/857/OPS_DETAILS.yaml +65 -0
- package/packs/sunflower/problems/857/REPORT_TEMPLATE.md +26 -0
- package/packs/sunflower/problems/857/ROUTE_HISTORY.md +25 -0
- package/src/cli/index.js +22 -3
- package/src/commands/archive.js +46 -0
- package/src/commands/cluster.js +4 -0
- package/src/commands/maintainer.js +20 -2
- package/src/commands/number-theory.js +199 -0
- package/src/commands/problem.js +3 -0
- package/src/commands/pull.js +180 -5
- package/src/commands/sunflower.js +290 -12
- package/src/commands/upstream.js +129 -0
- package/src/commands/workspace.js +20 -0
- package/src/runtime/archive.js +87 -0
- package/src/runtime/checkpoints.js +27 -0
- package/src/runtime/maintainer-seed.js +70 -0
- package/src/runtime/number-theory.js +169 -0
- package/src/runtime/paths.js +16 -0
- package/src/runtime/state.js +63 -3
- package/src/runtime/sunflower.js +329 -2
- package/src/runtime/workspace.js +4 -0
- package/src/upstream/literature.js +83 -0
package/src/runtime/sunflower.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { parse } from 'yaml';
|
|
4
|
-
import { writeJson } from './files.js';
|
|
4
|
+
import { ensureDir, writeJson, writeText } from './files.js';
|
|
5
5
|
import { buildBreakthroughsComputeView } from './breakthroughs.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getPackDir,
|
|
8
|
+
getPackProblemDir,
|
|
9
|
+
getWorkspaceComputeRegistryDir,
|
|
10
|
+
getWorkspaceRunDir,
|
|
11
|
+
} from './paths.js';
|
|
7
12
|
|
|
8
13
|
const CLAIM_LEVEL_PRIORITY = {
|
|
9
14
|
Exact: 4,
|
|
@@ -63,6 +68,26 @@ function getSunflowerAtomicBoardMarkdownPath(problemId) {
|
|
|
63
68
|
return path.join(getSunflowerProblemDir(problemId), 'ATOMIC_BOARD.md');
|
|
64
69
|
}
|
|
65
70
|
|
|
71
|
+
function getSunflowerFrontierNotePath(problemId) {
|
|
72
|
+
return path.join(getSunflowerProblemDir(problemId), 'FRONTIER_NOTE.md');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getSunflowerRouteHistoryPath(problemId) {
|
|
76
|
+
return path.join(getSunflowerProblemDir(problemId), 'ROUTE_HISTORY.md');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getSunflowerCheckpointTemplatePath(problemId) {
|
|
80
|
+
return path.join(getSunflowerProblemDir(problemId), 'CHECKPOINT_TEMPLATE.md');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getSunflowerReportTemplatePath(problemId) {
|
|
84
|
+
return path.join(getSunflowerProblemDir(problemId), 'REPORT_TEMPLATE.md');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getSunflowerOpsDetailsPath(problemId) {
|
|
88
|
+
return path.join(getSunflowerProblemDir(problemId), 'OPS_DETAILS.yaml');
|
|
89
|
+
}
|
|
90
|
+
|
|
66
91
|
function parseStringList(value) {
|
|
67
92
|
if (!Array.isArray(value)) {
|
|
68
93
|
return [];
|
|
@@ -247,6 +272,73 @@ function parseMirageFrontiers(value) {
|
|
|
247
272
|
return text ? [text] : [];
|
|
248
273
|
}
|
|
249
274
|
|
|
275
|
+
function parseOpsRouteEntries(value) {
|
|
276
|
+
if (!Array.isArray(value)) {
|
|
277
|
+
return [];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return value
|
|
281
|
+
.filter((entry) => entry && typeof entry === 'object')
|
|
282
|
+
.map((entry) => ({
|
|
283
|
+
routeId: compactText(entry.route_id ?? entry.route),
|
|
284
|
+
title: compactText(entry.title),
|
|
285
|
+
status: compactText(entry.status),
|
|
286
|
+
theoremModule: compactText(entry.theorem_module),
|
|
287
|
+
summary: compactText(entry.summary),
|
|
288
|
+
whyNow: compactText(entry.why_now),
|
|
289
|
+
nextMove: compactText(entry.next_move),
|
|
290
|
+
ticketIds: parseStringList(entry.ticket_ids),
|
|
291
|
+
sourcePaths: parseStringList(entry.source_paths),
|
|
292
|
+
}));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function parseOpsTicketEntries(value) {
|
|
296
|
+
if (!Array.isArray(value)) {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return value
|
|
301
|
+
.filter((entry) => entry && typeof entry === 'object')
|
|
302
|
+
.map((entry) => ({
|
|
303
|
+
ticketId: compactText(entry.ticket_id),
|
|
304
|
+
title: compactText(entry.title ?? entry.ticket_name),
|
|
305
|
+
routeId: compactText(entry.route_id),
|
|
306
|
+
routeLeaf: compactText(entry.route_leaf),
|
|
307
|
+
status: compactText(entry.status),
|
|
308
|
+
summary: compactText(entry.summary),
|
|
309
|
+
gateStory: compactText(entry.gate_story),
|
|
310
|
+
currentBlocker: compactText(entry.current_blocker),
|
|
311
|
+
nextMove: compactText(entry.next_move),
|
|
312
|
+
atomIds: parseStringList(entry.atom_ids),
|
|
313
|
+
sourcePaths: parseStringList(entry.source_paths),
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function parseOpsAtomEntries(value) {
|
|
318
|
+
if (!Array.isArray(value)) {
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return value
|
|
323
|
+
.filter((entry) => entry && typeof entry === 'object')
|
|
324
|
+
.map((entry) => ({
|
|
325
|
+
atomId: compactText(entry.atom_id),
|
|
326
|
+
title: compactText(entry.title),
|
|
327
|
+
ticketId: compactText(entry.ticket_id),
|
|
328
|
+
routeId: compactText(entry.route_id),
|
|
329
|
+
gateId: compactText(entry.gate_id),
|
|
330
|
+
tier: compactText(entry.tier),
|
|
331
|
+
kind: compactText(entry.kind),
|
|
332
|
+
status: compactText(entry.status),
|
|
333
|
+
summary: compactText(entry.summary),
|
|
334
|
+
whyNow: compactText(entry.why_now),
|
|
335
|
+
nextMove: compactText(entry.next_move),
|
|
336
|
+
verificationHook: parseStringList(entry.verification_hook),
|
|
337
|
+
dependencies: parseStringList(entry.dependencies),
|
|
338
|
+
sourcePaths: parseStringList(entry.source_paths),
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
|
|
250
342
|
function chooseActiveTicket(board) {
|
|
251
343
|
if (!board) {
|
|
252
344
|
return null;
|
|
@@ -301,6 +393,48 @@ function readSunflowerAtomicBoard(problemId) {
|
|
|
301
393
|
};
|
|
302
394
|
}
|
|
303
395
|
|
|
396
|
+
function readSunflowerOpsDetails(problemId) {
|
|
397
|
+
const opsDetailsPath = getSunflowerOpsDetailsPath(problemId);
|
|
398
|
+
if (!fs.existsSync(opsDetailsPath)) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const parsed = parse(fs.readFileSync(opsDetailsPath, 'utf8')) ?? {};
|
|
403
|
+
const routes = parseOpsRouteEntries(parsed.routes);
|
|
404
|
+
const tickets = parseOpsTicketEntries(parsed.tickets);
|
|
405
|
+
const atoms = parseOpsAtomEntries(parsed.atoms);
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
packetId: compactText(parsed.packet_id),
|
|
409
|
+
summary: compactText(parsed.summary),
|
|
410
|
+
routes,
|
|
411
|
+
tickets,
|
|
412
|
+
atoms,
|
|
413
|
+
path: opsDetailsPath,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function findRouteDetail(opsDetails, routeId) {
|
|
418
|
+
if (!opsDetails || !routeId) {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
return opsDetails.routes.find((entry) => entry.routeId === routeId) ?? null;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function findTicketDetail(opsDetails, ticketId) {
|
|
425
|
+
if (!opsDetails || !ticketId) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
return opsDetails.tickets.find((entry) => entry.ticketId === ticketId) ?? null;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function findAtomDetail(opsDetails, atomId) {
|
|
432
|
+
if (!opsDetails || !atomId) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
return opsDetails.atoms.find((entry) => entry.atomId === atomId) ?? null;
|
|
436
|
+
}
|
|
437
|
+
|
|
304
438
|
function chooseActivePacket(packets) {
|
|
305
439
|
if (packets.length === 0) {
|
|
306
440
|
return null;
|
|
@@ -436,6 +570,21 @@ function compactAtomicBoard(board) {
|
|
|
436
570
|
};
|
|
437
571
|
}
|
|
438
572
|
|
|
573
|
+
function compactOpsDetails(opsDetails) {
|
|
574
|
+
if (!opsDetails) {
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return {
|
|
579
|
+
packetId: opsDetails.packetId,
|
|
580
|
+
summary: opsDetails.summary,
|
|
581
|
+
path: opsDetails.path,
|
|
582
|
+
routes: opsDetails.routes,
|
|
583
|
+
tickets: opsDetails.tickets,
|
|
584
|
+
atoms: opsDetails.atoms,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
439
588
|
function deriveRouteState(problem, context) {
|
|
440
589
|
const researchState = problem.researchState ?? {};
|
|
441
590
|
const solvedBySite = String(problem.siteStatus ?? '').toLowerCase() === 'solved';
|
|
@@ -470,6 +619,7 @@ export function buildSunflowerStatusSnapshot(problem) {
|
|
|
470
619
|
const context = readSunflowerContext(problem.problemId);
|
|
471
620
|
const routePacket = readSunflowerRoutePacket(problem.problemId);
|
|
472
621
|
const atomicBoard = readSunflowerAtomicBoard(problem.problemId);
|
|
622
|
+
const opsDetails = readSunflowerOpsDetails(problem.problemId);
|
|
473
623
|
const packets = listSunflowerComputePackets(problem.problemId);
|
|
474
624
|
const activePacket = chooseActivePacket(packets);
|
|
475
625
|
const summary = deriveSummary(activePacket);
|
|
@@ -478,8 +628,15 @@ export function buildSunflowerStatusSnapshot(problem) {
|
|
|
478
628
|
const agentStartPath = getSunflowerAgentStartPath(problem.problemId);
|
|
479
629
|
const checkpointPacketPath = getSunflowerCheckpointPacketPath(problem.problemId);
|
|
480
630
|
const reportPacketPath = getSunflowerReportPacketPath(problem.problemId);
|
|
631
|
+
const frontierNotePath = getSunflowerFrontierNotePath(problem.problemId);
|
|
632
|
+
const routeHistoryPath = getSunflowerRouteHistoryPath(problem.problemId);
|
|
633
|
+
const checkpointTemplatePath = getSunflowerCheckpointTemplatePath(problem.problemId);
|
|
634
|
+
const reportTemplatePath = getSunflowerReportTemplatePath(problem.problemId);
|
|
481
635
|
|
|
482
636
|
const firstReadyAtom = atomicBoard?.readyQueue?.[0] ?? null;
|
|
637
|
+
const activeRouteDetail = findRouteDetail(opsDetails, routeState.activeRoute ?? atomicBoard?.activeRoute);
|
|
638
|
+
const activeTicketDetail = findTicketDetail(opsDetails, atomicBoard?.activeTicket?.ticketId);
|
|
639
|
+
const activeAtomDetail = findAtomDetail(opsDetails, firstReadyAtom?.atomId);
|
|
483
640
|
|
|
484
641
|
return {
|
|
485
642
|
generatedAt: new Date().toISOString(),
|
|
@@ -524,6 +681,14 @@ export function buildSunflowerStatusSnapshot(problem) {
|
|
|
524
681
|
checkpointPacketPath: fs.existsSync(checkpointPacketPath) ? checkpointPacketPath : null,
|
|
525
682
|
reportPacketPresent: fs.existsSync(reportPacketPath),
|
|
526
683
|
reportPacketPath: fs.existsSync(reportPacketPath) ? reportPacketPath : null,
|
|
684
|
+
frontierNotePresent: fs.existsSync(frontierNotePath),
|
|
685
|
+
frontierNotePath: fs.existsSync(frontierNotePath) ? frontierNotePath : null,
|
|
686
|
+
routeHistoryPresent: fs.existsSync(routeHistoryPath),
|
|
687
|
+
routeHistoryPath: fs.existsSync(routeHistoryPath) ? routeHistoryPath : null,
|
|
688
|
+
checkpointTemplatePresent: fs.existsSync(checkpointTemplatePath),
|
|
689
|
+
checkpointTemplatePath: fs.existsSync(checkpointTemplatePath) ? checkpointTemplatePath : null,
|
|
690
|
+
reportTemplatePresent: fs.existsSync(reportTemplatePath),
|
|
691
|
+
reportTemplatePath: fs.existsSync(reportTemplatePath) ? reportTemplatePath : null,
|
|
527
692
|
atomicBoardPresent: Boolean(atomicBoard),
|
|
528
693
|
atomicBoardPath: atomicBoard?.atomicBoardPath ?? null,
|
|
529
694
|
atomicBoardMarkdownPath: atomicBoard?.atomicBoardMarkdownExists ? atomicBoard.atomicBoardMarkdownPath : null,
|
|
@@ -532,6 +697,11 @@ export function buildSunflowerStatusSnapshot(problem) {
|
|
|
532
697
|
readyAtomCount: atomicBoard?.readyQueue?.length ?? 0,
|
|
533
698
|
firstReadyAtom,
|
|
534
699
|
mirageFrontierCount: atomicBoard?.mirageFrontiers?.length ?? 0,
|
|
700
|
+
opsDetailsPresent: Boolean(opsDetails),
|
|
701
|
+
opsDetailsPath: opsDetails?.path ?? null,
|
|
702
|
+
activeRouteDetail,
|
|
703
|
+
activeTicketDetail,
|
|
704
|
+
activeAtomDetail,
|
|
535
705
|
computeLanePresent: Boolean(activePacket),
|
|
536
706
|
computeLaneCount: packets.length,
|
|
537
707
|
computeSummary: summary.computeSummary,
|
|
@@ -543,6 +713,7 @@ export function buildSunflowerStatusSnapshot(problem) {
|
|
|
543
713
|
activePacket: compactPacket(activePacket),
|
|
544
714
|
computePackets: packets.map((packet) => compactPacket(packet)),
|
|
545
715
|
atomicBoardSummary: compactAtomicBoard(atomicBoard),
|
|
716
|
+
opsDetails: compactOpsDetails(opsDetails),
|
|
546
717
|
};
|
|
547
718
|
}
|
|
548
719
|
|
|
@@ -559,3 +730,159 @@ export function writeSunflowerStatusRecord(problem, snapshot, workspaceRoot) {
|
|
|
559
730
|
latestPath,
|
|
560
731
|
};
|
|
561
732
|
}
|
|
733
|
+
|
|
734
|
+
export function getSunflowerRouteSnapshot(problem, routeId) {
|
|
735
|
+
const snapshot = buildSunflowerStatusSnapshot(problem);
|
|
736
|
+
const boardRoute = snapshot.atomicBoardSummary?.routeStatus?.find((route) => route.route === routeId) ?? null;
|
|
737
|
+
const routeDetail = findRouteDetail(snapshot.opsDetails, routeId);
|
|
738
|
+
if (!boardRoute && !routeDetail) {
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
return {
|
|
743
|
+
problemId: problem.problemId,
|
|
744
|
+
displayName: problem.displayName,
|
|
745
|
+
routeId,
|
|
746
|
+
activeRoute: snapshot.activeRoute,
|
|
747
|
+
routeBreakthrough: snapshot.routeBreakthrough,
|
|
748
|
+
boardRoute,
|
|
749
|
+
routeDetail,
|
|
750
|
+
activeTicket: snapshot.activeTicket,
|
|
751
|
+
firstReadyAtom: snapshot.firstReadyAtom,
|
|
752
|
+
snapshot,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function getSunflowerTicketSnapshot(problem, ticketId) {
|
|
757
|
+
const snapshot = buildSunflowerStatusSnapshot(problem);
|
|
758
|
+
const boardTicket = snapshot.atomicBoardSummary?.tickets?.find((ticket) => ticket.ticketId === ticketId) ?? null;
|
|
759
|
+
const ticketDetail = findTicketDetail(snapshot.opsDetails, ticketId);
|
|
760
|
+
if (!boardTicket && !ticketDetail) {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return {
|
|
765
|
+
problemId: problem.problemId,
|
|
766
|
+
displayName: problem.displayName,
|
|
767
|
+
ticketId,
|
|
768
|
+
activeTicketId: snapshot.activeTicket?.ticketId ?? null,
|
|
769
|
+
boardTicket,
|
|
770
|
+
ticketDetail,
|
|
771
|
+
firstReadyAtom: snapshot.firstReadyAtom,
|
|
772
|
+
snapshot,
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export function getSunflowerAtomSnapshot(problem, atomId) {
|
|
777
|
+
const snapshot = buildSunflowerStatusSnapshot(problem);
|
|
778
|
+
const boardAtom = snapshot.atomicBoardSummary?.readyQueue?.find((atom) => atom.atomId === atomId) ?? null;
|
|
779
|
+
const atomDetail = findAtomDetail(snapshot.opsDetails, atomId);
|
|
780
|
+
if (!boardAtom && !atomDetail) {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return {
|
|
785
|
+
problemId: problem.problemId,
|
|
786
|
+
displayName: problem.displayName,
|
|
787
|
+
atomId,
|
|
788
|
+
boardAtom,
|
|
789
|
+
atomDetail,
|
|
790
|
+
firstReadyAtom: snapshot.firstReadyAtom,
|
|
791
|
+
snapshot,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
export function runSunflowerLocalScout(problem, workspaceRoot) {
|
|
796
|
+
const snapshot = buildSunflowerStatusSnapshot(problem);
|
|
797
|
+
const governance = snapshot.computeGovernance;
|
|
798
|
+
|
|
799
|
+
if (!snapshot.activePacket || !governance) {
|
|
800
|
+
throw new Error(`Problem ${problem.problemId} does not have an admitted sunflower compute packet.`);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (governance.dispatchResult.action !== 'run_local' || governance.selectedRung.spendClass !== 'local_unmetered') {
|
|
804
|
+
throw new Error(
|
|
805
|
+
`Problem ${problem.problemId} is not currently admitted for a local scout run. Current action: ${governance.dispatchResult.action}.`,
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const runId = `${new Date().toISOString().replaceAll(':', '-')}__sunflower_p${problem.problemId}__${snapshot.activePacket.laneId}`;
|
|
810
|
+
const runDir = getWorkspaceRunDir(runId, workspaceRoot);
|
|
811
|
+
ensureDir(runDir);
|
|
812
|
+
|
|
813
|
+
const runRecord = {
|
|
814
|
+
runId,
|
|
815
|
+
generatedAt: new Date().toISOString(),
|
|
816
|
+
problemId: problem.problemId,
|
|
817
|
+
displayName: problem.displayName,
|
|
818
|
+
cluster: problem.cluster,
|
|
819
|
+
laneId: snapshot.activePacket.laneId,
|
|
820
|
+
packetStatus: snapshot.activePacket.status,
|
|
821
|
+
dispatchAction: governance.dispatchResult.action,
|
|
822
|
+
selectedRung: governance.selectedRung,
|
|
823
|
+
question: governance.question,
|
|
824
|
+
currentFrontier: snapshot.firstReadyAtom
|
|
825
|
+
? `${snapshot.firstReadyAtom.atomId} — ${snapshot.firstReadyAtom.title}`
|
|
826
|
+
: snapshot.frontierDetail,
|
|
827
|
+
artifacts: {
|
|
828
|
+
statusRecordPath: path.join(runDir, 'STATUS_RECORD.json'),
|
|
829
|
+
runSummaryPath: path.join(runDir, 'RUN_SUMMARY.md'),
|
|
830
|
+
runLogPath: path.join(runDir, 'RUN_LOG.txt'),
|
|
831
|
+
governancePath: path.join(runDir, 'GOVERNANCE.json'),
|
|
832
|
+
orpPacketPath: path.join(runDir, 'ORP_COMPUTE_PACKET.json'),
|
|
833
|
+
},
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
writeJson(path.join(runDir, 'RUN.json'), runRecord);
|
|
837
|
+
writeJson(path.join(runDir, 'STATUS_RECORD.json'), snapshot);
|
|
838
|
+
writeJson(path.join(runDir, 'GOVERNANCE.json'), governance);
|
|
839
|
+
writeJson(path.join(runDir, 'ORP_COMPUTE_PACKET.json'), governance.orpPacket);
|
|
840
|
+
writeText(
|
|
841
|
+
path.join(runDir, 'RUN_LOG.txt'),
|
|
842
|
+
[
|
|
843
|
+
`sunflower local scout`,
|
|
844
|
+
`problem=${problem.problemId}`,
|
|
845
|
+
`lane=${snapshot.activePacket.laneId}`,
|
|
846
|
+
`action=${governance.dispatchResult.action}`,
|
|
847
|
+
`rung=${governance.selectedRung.label}`,
|
|
848
|
+
`question=${governance.question}`,
|
|
849
|
+
`frontier=${runRecord.currentFrontier}`,
|
|
850
|
+
`when=${governance.when}`,
|
|
851
|
+
].join('\n') + '\n',
|
|
852
|
+
);
|
|
853
|
+
writeText(
|
|
854
|
+
path.join(runDir, 'RUN_SUMMARY.md'),
|
|
855
|
+
[
|
|
856
|
+
`# Sunflower Local Scout Run`,
|
|
857
|
+
'',
|
|
858
|
+
`- Problem: ${problem.displayName}`,
|
|
859
|
+
`- Lane: ${snapshot.activePacket.laneId}`,
|
|
860
|
+
`- Dispatch action: ${governance.dispatchResult.action}`,
|
|
861
|
+
`- Selected rung: ${governance.selectedRung.label} [${governance.selectedRung.spendClass}]`,
|
|
862
|
+
`- Question: ${governance.question}`,
|
|
863
|
+
`- Current frontier: ${runRecord.currentFrontier}`,
|
|
864
|
+
'',
|
|
865
|
+
'Why this run exists:',
|
|
866
|
+
`- ${snapshot.computeSummary}`,
|
|
867
|
+
`- ${governance.when}`,
|
|
868
|
+
'',
|
|
869
|
+
'Run outputs:',
|
|
870
|
+
'- RUN.json',
|
|
871
|
+
'- STATUS_RECORD.json',
|
|
872
|
+
'- GOVERNANCE.json',
|
|
873
|
+
'- ORP_COMPUTE_PACKET.json',
|
|
874
|
+
'- RUN_LOG.txt',
|
|
875
|
+
'',
|
|
876
|
+
'Important boundary:',
|
|
877
|
+
'- This is a governed local-scout artifact bundle. It does not upgrade problem-level claims by itself.',
|
|
878
|
+
'',
|
|
879
|
+
].join('\n'),
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
return {
|
|
883
|
+
runId,
|
|
884
|
+
runDir,
|
|
885
|
+
runRecord,
|
|
886
|
+
snapshot,
|
|
887
|
+
};
|
|
888
|
+
}
|
package/src/runtime/workspace.js
CHANGED
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
getWorkspaceProblemPullDir,
|
|
14
14
|
getWorkspaceProblemScaffoldDir,
|
|
15
15
|
getWorkspaceQuestionLedgerPath,
|
|
16
|
+
getWorkspaceArchivesDir,
|
|
16
17
|
getWorkspaceRoot,
|
|
18
|
+
getWorkspaceRunsDir,
|
|
17
19
|
getWorkspaceSeededProblemDir,
|
|
18
20
|
getWorkspaceSeededProblemsDir,
|
|
19
21
|
getWorkspaceStateMarkdownPath,
|
|
@@ -93,6 +95,8 @@ export function getWorkspaceSummary(workspaceRoot = getWorkspaceRoot()) {
|
|
|
93
95
|
artifactDir: activeProblem ? getWorkspaceProblemArtifactDir(activeProblem, workspaceRoot) : getWorkspaceProblemArtifactDir('<problem-id>', workspaceRoot),
|
|
94
96
|
literatureDir: activeProblem ? getWorkspaceProblemLiteratureDir(activeProblem, workspaceRoot) : getWorkspaceProblemLiteratureDir('<problem-id>', workspaceRoot),
|
|
95
97
|
seededProblemDir: activeProblem ? getWorkspaceSeededProblemDir(activeProblem, workspaceRoot) : getWorkspaceSeededProblemDir('<problem-id>', workspaceRoot),
|
|
98
|
+
runsDir: getWorkspaceRunsDir(workspaceRoot),
|
|
99
|
+
archivesDir: getWorkspaceArchivesDir(workspaceRoot),
|
|
96
100
|
updatedAt: state?.updatedAt ?? null,
|
|
97
101
|
continuationMode: state?.continuation?.mode ?? null,
|
|
98
102
|
activeRoute: state?.activeRoute ?? null,
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
function normalizeText(value) {
|
|
2
|
+
return String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function uniqueStrings(values) {
|
|
6
|
+
return [...new Set(values.map((value) => normalizeText(value)).filter(Boolean))];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function buildLiteratureQueries(problemId, title) {
|
|
10
|
+
const cleanTitle = normalizeText(title);
|
|
11
|
+
return uniqueStrings([
|
|
12
|
+
`Erdos Problem ${problemId}`,
|
|
13
|
+
cleanTitle,
|
|
14
|
+
cleanTitle ? `${cleanTitle} Erdos` : null,
|
|
15
|
+
cleanTitle ? `${cleanTitle} combinatorics` : null,
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function fetchCrossrefLiterature(problemId, title) {
|
|
20
|
+
const query = buildLiteratureQueries(problemId, title)[0] ?? `Erdos Problem ${problemId}`;
|
|
21
|
+
const url = `https://api.crossref.org/works?query.title=${encodeURIComponent(query)}&rows=5&sort=relevance`;
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
headers: {
|
|
24
|
+
'User-Agent': 'erdos-problems-cli',
|
|
25
|
+
Accept: 'application/json',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(`Crossref lookup failed for problem ${problemId}: ${response.status}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const payload = await response.json();
|
|
34
|
+
const items = Array.isArray(payload?.message?.items) ? payload.message.items : [];
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
provider: 'crossref',
|
|
38
|
+
fetchedAt: new Date().toISOString(),
|
|
39
|
+
problemId: String(problemId),
|
|
40
|
+
query,
|
|
41
|
+
results: items.slice(0, 5).map((item) => ({
|
|
42
|
+
title: normalizeText(Array.isArray(item?.title) ? item.title[0] : item?.title),
|
|
43
|
+
url: normalizeText(item?.URL),
|
|
44
|
+
doi: normalizeText(item?.DOI),
|
|
45
|
+
publisher: normalizeText(item?.publisher),
|
|
46
|
+
published:
|
|
47
|
+
item?.issued?.['date-parts']?.[0]?.filter((value) => value !== null && value !== undefined).join('-')
|
|
48
|
+
?? null,
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function fetchOpenAlexLiterature(problemId, title) {
|
|
54
|
+
const query = buildLiteratureQueries(problemId, title)[0] ?? `Erdos Problem ${problemId}`;
|
|
55
|
+
const url = `https://api.openalex.org/works?search=${encodeURIComponent(query)}&per-page=5&sort=relevance_score:desc`;
|
|
56
|
+
const response = await fetch(url, {
|
|
57
|
+
headers: {
|
|
58
|
+
'User-Agent': 'erdos-problems-cli',
|
|
59
|
+
Accept: 'application/json',
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`OpenAlex lookup failed for problem ${problemId}: ${response.status}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const payload = await response.json();
|
|
68
|
+
const results = Array.isArray(payload?.results) ? payload.results : [];
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
provider: 'openalex',
|
|
72
|
+
fetchedAt: new Date().toISOString(),
|
|
73
|
+
problemId: String(problemId),
|
|
74
|
+
query,
|
|
75
|
+
results: results.slice(0, 5).map((item) => ({
|
|
76
|
+
title: normalizeText(item?.title),
|
|
77
|
+
url: normalizeText(item?.primary_location?.landing_page_url ?? item?.id),
|
|
78
|
+
doi: normalizeText(item?.doi),
|
|
79
|
+
citedByCount: Number(item?.cited_by_count ?? 0),
|
|
80
|
+
publicationYear: item?.publication_year ?? null,
|
|
81
|
+
})),
|
|
82
|
+
};
|
|
83
|
+
}
|