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.
Files changed (60) hide show
  1. package/README.md +117 -4
  2. package/docs/RESEARCH_LOOP.md +21 -2
  3. package/package.json +1 -1
  4. package/packs/number-theory/README.md +17 -0
  5. package/packs/number-theory/problems/1/CHECKPOINT_TEMPLATE.md +7 -0
  6. package/packs/number-theory/problems/1/CONTEXT.md +8 -0
  7. package/packs/number-theory/problems/1/FRONTIER_NOTE.md +8 -0
  8. package/packs/number-theory/problems/1/OPS_DETAILS.yaml +25 -0
  9. package/packs/number-theory/problems/1/REPORT_TEMPLATE.md +7 -0
  10. package/packs/number-theory/problems/1/ROUTE_HISTORY.md +5 -0
  11. package/packs/number-theory/problems/1/ROUTE_PACKET.yaml +13 -0
  12. package/packs/number-theory/problems/1/context.yaml +25 -0
  13. package/packs/number-theory/problems/2/CHECKPOINT_TEMPLATE.md +7 -0
  14. package/packs/number-theory/problems/2/CONTEXT.md +8 -0
  15. package/packs/number-theory/problems/2/FRONTIER_NOTE.md +8 -0
  16. package/packs/number-theory/problems/2/OPS_DETAILS.yaml +25 -0
  17. package/packs/number-theory/problems/2/REPORT_TEMPLATE.md +7 -0
  18. package/packs/number-theory/problems/2/ROUTE_HISTORY.md +5 -0
  19. package/packs/number-theory/problems/2/ROUTE_PACKET.yaml +13 -0
  20. package/packs/number-theory/problems/2/context.yaml +25 -0
  21. package/packs/sunflower/README.md +17 -4
  22. package/packs/sunflower/problems/20/CHECKPOINT_TEMPLATE.md +29 -0
  23. package/packs/sunflower/problems/20/FRONTIER_NOTE.md +13 -0
  24. package/packs/sunflower/problems/20/OPS_DETAILS.yaml +44 -0
  25. package/packs/sunflower/problems/20/REPORT_TEMPLATE.md +23 -0
  26. package/packs/sunflower/problems/20/ROUTE_HISTORY.md +18 -0
  27. package/packs/sunflower/problems/536/CHECKPOINT_TEMPLATE.md +7 -0
  28. package/packs/sunflower/problems/536/FRONTIER_NOTE.md +8 -0
  29. package/packs/sunflower/problems/536/OPS_DETAILS.yaml +39 -0
  30. package/packs/sunflower/problems/536/REPORT_TEMPLATE.md +7 -0
  31. package/packs/sunflower/problems/536/ROUTE_HISTORY.md +5 -0
  32. package/packs/sunflower/problems/856/CHECKPOINT_TEMPLATE.md +7 -0
  33. package/packs/sunflower/problems/856/FRONTIER_NOTE.md +8 -0
  34. package/packs/sunflower/problems/856/OPS_DETAILS.yaml +39 -0
  35. package/packs/sunflower/problems/856/REPORT_TEMPLATE.md +7 -0
  36. package/packs/sunflower/problems/856/ROUTE_HISTORY.md +5 -0
  37. package/packs/sunflower/problems/857/CHECKPOINT_TEMPLATE.md +32 -0
  38. package/packs/sunflower/problems/857/FRONTIER_NOTE.md +18 -0
  39. package/packs/sunflower/problems/857/OPS_DETAILS.yaml +65 -0
  40. package/packs/sunflower/problems/857/REPORT_TEMPLATE.md +26 -0
  41. package/packs/sunflower/problems/857/ROUTE_HISTORY.md +25 -0
  42. package/src/cli/index.js +22 -3
  43. package/src/commands/archive.js +46 -0
  44. package/src/commands/cluster.js +4 -0
  45. package/src/commands/maintainer.js +20 -2
  46. package/src/commands/number-theory.js +199 -0
  47. package/src/commands/problem.js +3 -0
  48. package/src/commands/pull.js +180 -5
  49. package/src/commands/sunflower.js +290 -12
  50. package/src/commands/upstream.js +129 -0
  51. package/src/commands/workspace.js +20 -0
  52. package/src/runtime/archive.js +87 -0
  53. package/src/runtime/checkpoints.js +27 -0
  54. package/src/runtime/maintainer-seed.js +70 -0
  55. package/src/runtime/number-theory.js +169 -0
  56. package/src/runtime/paths.js +16 -0
  57. package/src/runtime/state.js +63 -3
  58. package/src/runtime/sunflower.js +329 -2
  59. package/src/runtime/workspace.js +4 -0
  60. package/src/upstream/literature.js +83 -0
@@ -1,6 +1,13 @@
1
1
  import { getProblem } from '../atlas/catalog.js';
2
2
  import { getWorkspaceRoot } from '../runtime/paths.js';
3
- import { buildSunflowerStatusSnapshot, writeSunflowerStatusRecord } from '../runtime/sunflower.js';
3
+ import {
4
+ buildSunflowerStatusSnapshot,
5
+ getSunflowerAtomSnapshot,
6
+ getSunflowerRouteSnapshot,
7
+ getSunflowerTicketSnapshot,
8
+ runSunflowerLocalScout,
9
+ writeSunflowerStatusRecord,
10
+ } from '../runtime/sunflower.js';
4
11
  import { readCurrentProblem } from '../runtime/workspace.js';
5
12
 
6
13
  function parseStatusArgs(args) {
@@ -45,6 +52,53 @@ function parseTicketsArgs(args) {
45
52
  return parseStatusArgs(args);
46
53
  }
47
54
 
55
+ function parseFrontierArgs(args) {
56
+ return parseStatusArgs(args);
57
+ }
58
+
59
+ function parseEntityArgs(args, entityLabel) {
60
+ const parsed = {
61
+ problemId: null,
62
+ entityId: null,
63
+ asJson: false,
64
+ };
65
+
66
+ for (let index = 0; index < args.length; index += 1) {
67
+ const token = args[index];
68
+ if (token === '--json') {
69
+ parsed.asJson = true;
70
+ continue;
71
+ }
72
+ if (!parsed.problemId) {
73
+ parsed.problemId = token;
74
+ continue;
75
+ }
76
+ if (!parsed.entityId) {
77
+ parsed.entityId = token;
78
+ continue;
79
+ }
80
+ return { error: `Unknown sunflower ${entityLabel} option: ${token}` };
81
+ }
82
+
83
+ return parsed;
84
+ }
85
+
86
+ function parseComputeArgs(args) {
87
+ const [computeCommand, ...rest] = args;
88
+ if (!computeCommand || computeCommand === 'help' || computeCommand === '--help') {
89
+ return { help: true };
90
+ }
91
+ if (computeCommand !== 'run') {
92
+ return { error: `Unknown sunflower compute subcommand: ${computeCommand}` };
93
+ }
94
+
95
+ const parsed = parseStatusArgs(rest);
96
+ return {
97
+ ...parsed,
98
+ computeCommand,
99
+ };
100
+ }
101
+
48
102
  function getBoard(snapshot) {
49
103
  return snapshot.atomicBoardSummary;
50
104
  }
@@ -91,6 +145,24 @@ function ticketProgressLabel(ticket, snapshot) {
91
145
  return 'open';
92
146
  }
93
147
 
148
+ function resolveSunflowerProblem(problemId) {
149
+ const resolvedProblemId = problemId ?? readCurrentProblem();
150
+ if (!resolvedProblemId) {
151
+ return { error: 'Missing problem id and no active problem is selected.' };
152
+ }
153
+
154
+ const problem = getProblem(resolvedProblemId);
155
+ if (!problem) {
156
+ return { error: `Unknown problem: ${resolvedProblemId}` };
157
+ }
158
+
159
+ if (problem.cluster !== 'sunflower') {
160
+ return { error: `Problem ${problem.problemId} is not in the sunflower harness.` };
161
+ }
162
+
163
+ return { problem };
164
+ }
165
+
94
166
  function printSunflowerStatus(snapshot, registryPaths) {
95
167
  console.log(`${snapshot.displayName} sunflower harness`);
96
168
  console.log(`Title: ${snapshot.title}`);
@@ -140,6 +212,10 @@ function printSunflowerStatus(snapshot, registryPaths) {
140
212
  console.log(`Agent start packet: ${snapshot.agentStartPresent ? snapshot.agentStartPath : '(missing)'}`);
141
213
  console.log(`Checkpoint packet: ${snapshot.checkpointPacketPresent ? snapshot.checkpointPacketPath : '(missing)'}`);
142
214
  console.log(`Report packet: ${snapshot.reportPacketPresent ? snapshot.reportPacketPath : '(missing)'}`);
215
+ console.log(`Frontier note: ${snapshot.frontierNotePresent ? snapshot.frontierNotePath : '(missing)'}`);
216
+ console.log(`Route history: ${snapshot.routeHistoryPresent ? snapshot.routeHistoryPath : '(missing)'}`);
217
+ console.log(`Checkpoint template: ${snapshot.checkpointTemplatePresent ? snapshot.checkpointTemplatePath : '(missing)'}`);
218
+ console.log(`Report template: ${snapshot.reportTemplatePresent ? snapshot.reportTemplatePath : '(missing)'}`);
143
219
  console.log(`Compute lane present: ${snapshot.computeLanePresent ? 'yes' : 'no'}`);
144
220
  console.log(`Compute lane count: ${snapshot.computeLaneCount}`);
145
221
  console.log(`Compute summary: ${snapshot.computeSummary}`);
@@ -368,6 +444,124 @@ function printSunflowerTickets(snapshot) {
368
444
  }
369
445
  }
370
446
 
447
+ function printSunflowerFrontier(snapshot) {
448
+ console.log(`${snapshot.displayName} sunflower frontier`);
449
+ console.log(`Active route: ${snapshot.activeRoute ?? '(none)'}`);
450
+ console.log(`Active ticket: ${snapshot.activeTicket?.ticketId ?? '(none)'}`);
451
+ console.log(`Frontier label: ${snapshot.frontierLabel ?? '(none)'}`);
452
+ console.log(`Frontier detail: ${snapshot.frontierDetail ?? '(none)'}`);
453
+ console.log(`Checkpoint focus: ${snapshot.checkpointFocus ?? '(none)'}`);
454
+ console.log(`Next honest move: ${snapshot.nextHonestMove}`);
455
+ console.log(`Ready atoms: ${snapshot.readyAtomCount}`);
456
+ console.log(`Mirage frontiers: ${snapshot.mirageFrontierCount}`);
457
+ if (snapshot.firstReadyAtom) {
458
+ console.log(`First ready atom: ${snapshot.firstReadyAtom.atomId} — ${snapshot.firstReadyAtom.title}`);
459
+ }
460
+ if (snapshot.activeRouteDetail) {
461
+ console.log(`Route focus: ${snapshot.activeRouteDetail.title ?? snapshot.activeRouteDetail.routeId}`);
462
+ console.log(`Route why now: ${snapshot.activeRouteDetail.whyNow ?? '(none)'}`);
463
+ }
464
+ if (snapshot.activeTicketDetail) {
465
+ console.log(`Ticket focus: ${snapshot.activeTicketDetail.title ?? snapshot.activeTicketDetail.ticketId}`);
466
+ console.log(`Ticket blocker: ${snapshot.activeTicketDetail.currentBlocker ?? '(none)'}`);
467
+ }
468
+ if (snapshot.activeAtomDetail) {
469
+ console.log(`Atom focus: ${snapshot.activeAtomDetail.title ?? snapshot.activeAtomDetail.atomId}`);
470
+ console.log(`Atom why now: ${snapshot.activeAtomDetail.whyNow ?? '(none)'}`);
471
+ }
472
+ console.log(`Frontier note: ${snapshot.frontierNotePresent ? snapshot.frontierNotePath : '(missing)'}`);
473
+ console.log(`Route history: ${snapshot.routeHistoryPresent ? snapshot.routeHistoryPath : '(missing)'}`);
474
+ }
475
+
476
+ function printSunflowerRouteDetail(routeSnapshot) {
477
+ const detail = routeSnapshot.routeDetail;
478
+ const boardRoute = routeSnapshot.boardRoute;
479
+
480
+ console.log(`${routeSnapshot.displayName} sunflower route ${routeSnapshot.routeId}`);
481
+ console.log(`Active route: ${routeSnapshot.activeRoute ?? '(none)'}`);
482
+ if (detail) {
483
+ console.log(`Title: ${detail.title ?? '(none)'}`);
484
+ console.log(`Status: ${detail.status ?? '(none)'}`);
485
+ console.log(`Summary: ${detail.summary ?? '(none)'}`);
486
+ console.log(`Why now: ${detail.whyNow ?? '(none)'}`);
487
+ console.log(`Next move: ${detail.nextMove ?? '(none)'}`);
488
+ console.log(`Theorem module: ${detail.theoremModule ?? '(none)'}`);
489
+ console.log(`Ticket ids: ${detail.ticketIds.join(', ') || '(none)'}`);
490
+ }
491
+ if (boardRoute) {
492
+ console.log(`Loose progress: ${boardRoute.looseDone}/${boardRoute.looseTotal}`);
493
+ console.log(`Strict progress: ${boardRoute.strictDone}/${boardRoute.strictTotal}`);
494
+ }
495
+ if (routeSnapshot.firstReadyAtom) {
496
+ console.log(`First ready atom: ${routeSnapshot.firstReadyAtom.atomId} — ${routeSnapshot.firstReadyAtom.title}`);
497
+ }
498
+ }
499
+
500
+ function printSunflowerTicketDetail(ticketSnapshot) {
501
+ const detail = ticketSnapshot.ticketDetail;
502
+ const boardTicket = ticketSnapshot.boardTicket;
503
+
504
+ console.log(`${ticketSnapshot.displayName} sunflower ticket ${ticketSnapshot.ticketId}`);
505
+ console.log(`Active ticket: ${ticketSnapshot.activeTicketId ?? '(none)'}`);
506
+ if (detail) {
507
+ console.log(`Title: ${detail.title ?? '(none)'}`);
508
+ console.log(`Route: ${detail.routeId ?? '(none)'}`);
509
+ console.log(`Status: ${detail.status ?? '(none)'}`);
510
+ console.log(`Summary: ${detail.summary ?? '(none)'}`);
511
+ console.log(`Gate story: ${detail.gateStory ?? '(none)'}`);
512
+ console.log(`Current blocker: ${detail.currentBlocker ?? '(none)'}`);
513
+ console.log(`Next move: ${detail.nextMove ?? '(none)'}`);
514
+ console.log(`Atom ids: ${detail.atomIds.join(', ') || '(none)'}`);
515
+ }
516
+ if (boardTicket) {
517
+ console.log(`Leaf theorem: ${boardTicket.routeLeaf ?? '(none)'}`);
518
+ console.log(`Leaf status: ${boardTicket.leafStatus ?? '(none)'}`);
519
+ console.log(`Gate progress: ${boardTicket.gatesDone}/${boardTicket.gatesTotal}`);
520
+ console.log(`Atom progress: ${boardTicket.atomsDone}/${boardTicket.atomsTotal}`);
521
+ }
522
+ if (ticketSnapshot.firstReadyAtom?.ticketId === ticketSnapshot.ticketId) {
523
+ console.log(`First ready atom: ${ticketSnapshot.firstReadyAtom.atomId} — ${ticketSnapshot.firstReadyAtom.title}`);
524
+ }
525
+ }
526
+
527
+ function printSunflowerAtomDetail(atomSnapshot) {
528
+ const detail = atomSnapshot.atomDetail;
529
+ const boardAtom = atomSnapshot.boardAtom;
530
+
531
+ console.log(`${atomSnapshot.displayName} sunflower atom ${atomSnapshot.atomId}`);
532
+ if (detail) {
533
+ console.log(`Title: ${detail.title ?? '(none)'}`);
534
+ console.log(`Route: ${detail.routeId ?? '(none)'}`);
535
+ console.log(`Ticket: ${detail.ticketId ?? '(none)'}`);
536
+ console.log(`Gate: ${detail.gateId ?? '(none)'}`);
537
+ console.log(`Tier: ${detail.tier ?? '(none)'}`);
538
+ console.log(`Kind: ${detail.kind ?? '(none)'}`);
539
+ console.log(`Status: ${detail.status ?? '(none)'}`);
540
+ console.log(`Summary: ${detail.summary ?? '(none)'}`);
541
+ console.log(`Why now: ${detail.whyNow ?? '(none)'}`);
542
+ console.log(`Next move: ${detail.nextMove ?? '(none)'}`);
543
+ console.log(`Dependencies: ${detail.dependencies.join(', ') || '(none)'}`);
544
+ console.log(`Verification hook: ${detail.verificationHook.join(' | ') || '(none)'}`);
545
+ }
546
+ if (boardAtom) {
547
+ console.log(`Board queue status: ${boardAtom.status ?? '(none)'}`);
548
+ }
549
+ if (atomSnapshot.firstReadyAtom?.atomId === atomSnapshot.atomId) {
550
+ console.log('Current frontier atom: yes');
551
+ }
552
+ }
553
+
554
+ function printSunflowerComputeRun(result) {
555
+ console.log(`Sunflower local scout run created for problem ${result.snapshot.problemId}`);
556
+ console.log(`Run id: ${result.runId}`);
557
+ console.log(`Run dir: ${result.runDir}`);
558
+ console.log(`Lane: ${result.snapshot.activePacket?.laneId ?? '(none)'}`);
559
+ console.log(`Dispatch action: ${result.snapshot.computeGovernance?.dispatchResult.action ?? '(none)'}`);
560
+ console.log(`Selected rung: ${result.snapshot.computeGovernance?.selectedRung?.label ?? '(none)'}`);
561
+ console.log(`Current frontier: ${result.runRecord.currentFrontier}`);
562
+ console.log(`Run summary: ${result.runRecord.artifacts.runSummaryPath}`);
563
+ }
564
+
371
565
  export function runSunflowerCommand(args) {
372
566
  const [subcommand, ...rest] = args;
373
567
 
@@ -379,10 +573,15 @@ export function runSunflowerCommand(args) {
379
573
  console.log(' erdos sunflower ladder [<id>] [--json]');
380
574
  console.log(' erdos sunflower routes [<id>] [--json]');
381
575
  console.log(' erdos sunflower tickets [<id>] [--json]');
576
+ console.log(' erdos sunflower frontier [<id>] [--json]');
577
+ console.log(' erdos sunflower route <problem-id> <route-id> [--json]');
578
+ console.log(' erdos sunflower ticket <problem-id> <ticket-id> [--json]');
579
+ console.log(' erdos sunflower atom <problem-id> <atom-id> [--json]');
580
+ console.log(' erdos sunflower compute run [<id>] [--json]');
382
581
  return 0;
383
582
  }
384
583
 
385
- if (!['status', 'board', 'ready', 'ladder', 'routes', 'tickets'].includes(subcommand)) {
584
+ if (!['status', 'board', 'ready', 'ladder', 'routes', 'tickets', 'frontier', 'route', 'ticket', 'atom', 'compute'].includes(subcommand)) {
386
585
  console.error(`Unknown sunflower subcommand: ${subcommand}`);
387
586
  return 1;
388
587
  }
@@ -398,29 +597,103 @@ export function runSunflowerCommand(args) {
398
597
  parsed = parseRoutesArgs(rest);
399
598
  } else if (subcommand === 'tickets') {
400
599
  parsed = parseTicketsArgs(rest);
600
+ } else if (subcommand === 'frontier') {
601
+ parsed = parseFrontierArgs(rest);
602
+ } else if (subcommand === 'route') {
603
+ parsed = parseEntityArgs(rest, 'route');
604
+ } else if (subcommand === 'ticket') {
605
+ parsed = parseEntityArgs(rest, 'ticket');
606
+ } else if (subcommand === 'atom') {
607
+ parsed = parseEntityArgs(rest, 'atom');
608
+ } else if (subcommand === 'compute') {
609
+ parsed = parseComputeArgs(rest);
401
610
  } else {
402
611
  parsed = parseStatusArgs(rest);
403
612
  }
613
+
614
+ if (parsed?.help) {
615
+ console.log('Usage:');
616
+ console.log(' erdos sunflower compute run [<id>] [--json]');
617
+ return 0;
618
+ }
404
619
  if (parsed.error) {
405
620
  console.error(parsed.error);
406
621
  return 1;
407
622
  }
408
623
 
409
- const problemId = parsed.problemId ?? readCurrentProblem();
410
- if (!problemId) {
411
- console.error('Missing problem id and no active problem is selected.');
624
+ const { problem, error } = resolveSunflowerProblem(parsed.problemId);
625
+ if (error) {
626
+ console.error(error);
412
627
  return 1;
413
628
  }
414
629
 
415
- const problem = getProblem(problemId);
416
- if (!problem) {
417
- console.error(`Unknown problem: ${problemId}`);
418
- return 1;
630
+ if (subcommand === 'route') {
631
+ if (!parsed.entityId) {
632
+ console.error('Missing route id.');
633
+ return 1;
634
+ }
635
+ const routeSnapshot = getSunflowerRouteSnapshot(problem, parsed.entityId);
636
+ if (!routeSnapshot) {
637
+ console.error(`Unknown sunflower route: ${parsed.entityId}`);
638
+ return 1;
639
+ }
640
+ if (parsed.asJson) {
641
+ console.log(JSON.stringify(routeSnapshot, null, 2));
642
+ return 0;
643
+ }
644
+ printSunflowerRouteDetail(routeSnapshot);
645
+ return 0;
419
646
  }
420
647
 
421
- if (problem.cluster !== 'sunflower') {
422
- console.error(`Problem ${problem.problemId} is not in the sunflower harness.`);
423
- return 1;
648
+ if (subcommand === 'ticket') {
649
+ if (!parsed.entityId) {
650
+ console.error('Missing ticket id.');
651
+ return 1;
652
+ }
653
+ const ticketSnapshot = getSunflowerTicketSnapshot(problem, parsed.entityId);
654
+ if (!ticketSnapshot) {
655
+ console.error(`Unknown sunflower ticket: ${parsed.entityId}`);
656
+ return 1;
657
+ }
658
+ if (parsed.asJson) {
659
+ console.log(JSON.stringify(ticketSnapshot, null, 2));
660
+ return 0;
661
+ }
662
+ printSunflowerTicketDetail(ticketSnapshot);
663
+ return 0;
664
+ }
665
+
666
+ if (subcommand === 'atom') {
667
+ if (!parsed.entityId) {
668
+ console.error('Missing atom id.');
669
+ return 1;
670
+ }
671
+ const atomSnapshot = getSunflowerAtomSnapshot(problem, parsed.entityId);
672
+ if (!atomSnapshot) {
673
+ console.error(`Unknown sunflower atom: ${parsed.entityId}`);
674
+ return 1;
675
+ }
676
+ if (parsed.asJson) {
677
+ console.log(JSON.stringify(atomSnapshot, null, 2));
678
+ return 0;
679
+ }
680
+ printSunflowerAtomDetail(atomSnapshot);
681
+ return 0;
682
+ }
683
+
684
+ if (subcommand === 'compute') {
685
+ try {
686
+ const result = runSunflowerLocalScout(problem, getWorkspaceRoot());
687
+ if (parsed.asJson) {
688
+ console.log(JSON.stringify(result, null, 2));
689
+ return 0;
690
+ }
691
+ printSunflowerComputeRun(result);
692
+ return 0;
693
+ } catch (runError) {
694
+ console.error(String(runError.message ?? runError));
695
+ return 1;
696
+ }
424
697
  }
425
698
 
426
699
  const snapshot = buildSunflowerStatusSnapshot(problem);
@@ -456,6 +729,11 @@ export function runSunflowerCommand(args) {
456
729
  return 0;
457
730
  }
458
731
 
732
+ if (subcommand === 'frontier') {
733
+ printSunflowerFrontier(snapshot);
734
+ return 0;
735
+ }
736
+
459
737
  printSunflowerStatus(snapshot, registryPaths);
460
738
  return 0;
461
739
  }
@@ -1,5 +1,34 @@
1
+ import { getProblem } from '../atlas/catalog.js';
2
+ import { fetchProblemSiteSnapshot } from '../upstream/site.js';
1
3
  import { buildUpstreamDiff, loadActiveUpstreamSnapshot, syncUpstream, writeDiffArtifacts } from '../upstream/sync.js';
2
4
 
5
+ function parseDriftArgs(args) {
6
+ const parsed = {
7
+ problemId: null,
8
+ includeSite: false,
9
+ asJson: false,
10
+ };
11
+
12
+ for (let index = 0; index < args.length; index += 1) {
13
+ const token = args[index];
14
+ if (token === '--include-site') {
15
+ parsed.includeSite = true;
16
+ continue;
17
+ }
18
+ if (token === '--json') {
19
+ parsed.asJson = true;
20
+ continue;
21
+ }
22
+ if (!parsed.problemId) {
23
+ parsed.problemId = token;
24
+ continue;
25
+ }
26
+ return { error: `Unknown upstream drift option: ${token}` };
27
+ }
28
+
29
+ return parsed;
30
+ }
31
+
3
32
  export async function runUpstreamCommand(args) {
4
33
  const [subcommand, ...rest] = args;
5
34
 
@@ -8,6 +37,7 @@ export async function runUpstreamCommand(args) {
8
37
  console.log(' erdos upstream show');
9
38
  console.log(' erdos upstream sync [--write-package-snapshot]');
10
39
  console.log(' erdos upstream diff [--write-package-report]');
40
+ console.log(' erdos upstream drift [<id>] [--include-site] [--json]');
11
41
  return 0;
12
42
  }
13
43
 
@@ -55,6 +85,105 @@ export async function runUpstreamCommand(args) {
55
85
  return 0;
56
86
  }
57
87
 
88
+ if (subcommand === 'drift') {
89
+ const parsed = parseDriftArgs(rest);
90
+ if (parsed.error) {
91
+ console.error(parsed.error);
92
+ return 1;
93
+ }
94
+
95
+ const diff = buildUpstreamDiff();
96
+ if (!parsed.problemId) {
97
+ const statusDrifts = diff.overlaps.filter((row) => !row.statusMatches);
98
+ const formalizationDrifts = diff.overlaps.filter((row) => !row.formalizedMatches);
99
+ const tagDrifts = diff.overlaps.filter((row) => row.localOnlyTags.length > 0 || row.upstreamOnlyTags.length > 0);
100
+ const payload = {
101
+ localProblemCount: diff.localProblemCount,
102
+ upstreamProblemCount: diff.upstreamProblemCount,
103
+ statusDriftCount: statusDrifts.length,
104
+ formalizationDriftCount: formalizationDrifts.length,
105
+ tagDriftCount: tagDrifts.length,
106
+ statusDrifts: statusDrifts.slice(0, 10),
107
+ formalizationDrifts: formalizationDrifts.slice(0, 10),
108
+ tagDrifts: tagDrifts.slice(0, 10),
109
+ };
110
+
111
+ if (parsed.asJson) {
112
+ console.log(JSON.stringify(payload, null, 2));
113
+ return 0;
114
+ }
115
+
116
+ console.log('Upstream drift dashboard');
117
+ console.log(`Local seeded problems: ${payload.localProblemCount}`);
118
+ console.log(`Upstream total problems: ${payload.upstreamProblemCount}`);
119
+ console.log(`Site-status drifts: ${payload.statusDriftCount}`);
120
+ console.log(`Formalization drifts: ${payload.formalizationDriftCount}`);
121
+ console.log(`Tag drifts: ${payload.tagDriftCount}`);
122
+ return 0;
123
+ }
124
+
125
+ const problem = getProblem(parsed.problemId);
126
+ const snapshot = loadActiveUpstreamSnapshot();
127
+ const upstreamRecord = snapshot?.index?.by_number?.[String(parsed.problemId)] ?? null;
128
+ if (!problem && !upstreamRecord) {
129
+ console.error(`Unknown problem: ${parsed.problemId}`);
130
+ return 1;
131
+ }
132
+
133
+ let siteSnapshot = null;
134
+ let siteError = null;
135
+ if (parsed.includeSite) {
136
+ try {
137
+ siteSnapshot = await fetchProblemSiteSnapshot(parsed.problemId);
138
+ } catch (error) {
139
+ siteError = String(error.message ?? error);
140
+ }
141
+ }
142
+
143
+ const payload = {
144
+ problemId: String(parsed.problemId),
145
+ local: problem
146
+ ? {
147
+ siteStatus: problem.siteStatus,
148
+ repoStatus: problem.repoStatus,
149
+ title: problem.title,
150
+ }
151
+ : null,
152
+ upstream: upstreamRecord
153
+ ? {
154
+ siteStatus: upstreamRecord.status?.state ?? null,
155
+ formalizedState: upstreamRecord.formalized?.state ?? null,
156
+ tags: upstreamRecord.tags ?? [],
157
+ }
158
+ : null,
159
+ site: siteSnapshot
160
+ ? {
161
+ siteStatus: siteSnapshot.siteStatus,
162
+ statusLine: siteSnapshot.siteStatusRaw,
163
+ title: siteSnapshot.title,
164
+ }
165
+ : null,
166
+ siteError,
167
+ };
168
+
169
+ if (parsed.asJson) {
170
+ console.log(JSON.stringify(payload, null, 2));
171
+ return 0;
172
+ }
173
+
174
+ console.log(`Upstream drift for problem ${parsed.problemId}`);
175
+ console.log(`Local site status: ${payload.local?.siteStatus ?? '(none)'}`);
176
+ console.log(`Upstream site status: ${payload.upstream?.siteStatus ?? '(none)'}`);
177
+ console.log(`Site snapshot status: ${payload.site?.siteStatus ?? '(not fetched)'}`);
178
+ console.log(`Local repo status: ${payload.local?.repoStatus ?? '(none)'}`);
179
+ console.log(`Upstream formalized state: ${payload.upstream?.formalizedState ?? '(none)'}`);
180
+ console.log(`Upstream tags: ${payload.upstream?.tags?.join(', ') || '(none)'}`);
181
+ if (siteError) {
182
+ console.log(`Site fetch note: ${siteError}`);
183
+ }
184
+ return 0;
185
+ }
186
+
58
187
  console.error(`Unknown upstream subcommand: ${subcommand}`);
59
188
  return 1;
60
189
  }
@@ -1,5 +1,6 @@
1
1
  import { getProblem } from '../atlas/catalog.js';
2
2
  import { loadConfig } from '../runtime/config.js';
3
+ import { buildNumberTheoryStatusSnapshot } from '../runtime/number-theory.js';
3
4
  import { buildSunflowerStatusSnapshot } from '../runtime/sunflower.js';
4
5
  import { getWorkspaceSummary } from '../runtime/workspace.js';
5
6
 
@@ -38,6 +39,8 @@ export function runWorkspaceCommand(args) {
38
39
  console.log(`Workspace pull dir: ${summary.pullDir}`);
39
40
  console.log(`Workspace artifact dir: ${summary.artifactDir}`);
40
41
  console.log(`Workspace literature dir: ${summary.literatureDir}`);
42
+ console.log(`Workspace runs dir: ${summary.runsDir}`);
43
+ console.log(`Workspace archives dir: ${summary.archivesDir}`);
41
44
  console.log(`Active seeded dossier dir: ${summary.seededProblemDir}`);
42
45
  console.log(`Preferred agent: ${config.preferredAgent}`);
43
46
  console.log(`Continuation mode: ${summary.continuationMode ?? config.continuation}`);
@@ -58,6 +61,8 @@ export function runWorkspaceCommand(args) {
58
61
  console.log(`Sunflower harness profile: ${sunflower.harnessProfile ?? '(none)'}`);
59
62
  console.log(`Sunflower route: ${sunflower.activeRoute ?? '(none)'}`);
60
63
  console.log(`Sunflower frontier: ${sunflower.frontierDetail ?? '(none)'}`);
64
+ console.log(`Sunflower frontier note: ${sunflower.frontierNotePath ?? '(none)'}`);
65
+ console.log(`Sunflower route history: ${sunflower.routeHistoryPath ?? '(none)'}`);
61
66
  console.log(`Sunflower board: ${sunflower.atomicBoardPresent ? 'yes' : 'no'}`);
62
67
  if (sunflower.atomicBoardSummary) {
63
68
  console.log(`Sunflower board title: ${sunflower.atomicBoardSummary.boardTitle ?? '(none)'}`);
@@ -72,6 +77,21 @@ export function runWorkspaceCommand(args) {
72
77
  }
73
78
  console.log(`Sunflower compute next: ${sunflower.computeNextAction}`);
74
79
  }
80
+ if (problem?.cluster === 'number-theory') {
81
+ const numberTheory = buildNumberTheoryStatusSnapshot(problem);
82
+ console.log(`Number-theory family role: ${numberTheory.familyRole ?? '(none)'}`);
83
+ console.log(`Number-theory harness profile: ${numberTheory.harnessProfile ?? '(none)'}`);
84
+ console.log(`Number-theory route: ${numberTheory.activeRoute ?? '(none)'}`);
85
+ console.log(`Number-theory frontier: ${numberTheory.frontierDetail ?? '(none)'}`);
86
+ console.log(`Number-theory frontier note: ${numberTheory.frontierNotePath ?? '(none)'}`);
87
+ console.log(`Number-theory route history: ${numberTheory.routeHistoryPath ?? '(none)'}`);
88
+ console.log(`Number-theory archive mode: ${numberTheory.archiveMode ?? '(none)'}`);
89
+ console.log(`Number-theory active ticket: ${numberTheory.activeTicketDetail?.ticket_id ?? '(none)'}`);
90
+ console.log(`Number-theory ready atoms: ${numberTheory.readyAtomCount}`);
91
+ if (numberTheory.firstReadyAtom) {
92
+ console.log(`Number-theory first ready atom: ${numberTheory.firstReadyAtom.atom_id} — ${numberTheory.firstReadyAtom.title}`);
93
+ }
94
+ }
75
95
  }
76
96
  return 0;
77
97
  }
@@ -0,0 +1,87 @@
1
+ import path from 'node:path';
2
+ import { getProblem } from '../atlas/catalog.js';
3
+ import { ensureDir, writeJson, writeText } from './files.js';
4
+ import { getWorkspaceArchiveDir, getWorkspaceRoot } from './paths.js';
5
+
6
+ function isSolved(problem) {
7
+ return String(problem?.siteStatus ?? '').toLowerCase() === 'solved'
8
+ || Boolean(problem?.researchState?.problem_solved);
9
+ }
10
+
11
+ export function getArchiveView(problemId) {
12
+ const problem = getProblem(problemId);
13
+ if (!problem) {
14
+ return null;
15
+ }
16
+
17
+ return {
18
+ problemId: problem.problemId,
19
+ displayName: problem.displayName,
20
+ title: problem.title,
21
+ siteStatus: problem.siteStatus,
22
+ repoStatus: problem.repoStatus,
23
+ solved: isSolved(problem),
24
+ archiveMode: isSolved(problem) ? 'method_exemplar' : 'inactive',
25
+ nextMove: isSolved(problem)
26
+ ? 'Extract reusable methods, references, and formalization hooks without treating this as an open-problem cockpit.'
27
+ : 'This problem is not currently in solved archival mode.',
28
+ };
29
+ }
30
+
31
+ export function scaffoldArchive(problemId, workspaceRoot = getWorkspaceRoot()) {
32
+ const problem = getProblem(problemId);
33
+ if (!problem) {
34
+ throw new Error(`Unknown problem: ${problemId}`);
35
+ }
36
+ if (!isSolved(problem)) {
37
+ throw new Error(`Problem ${problemId} is not solved, so archival mode is not active.`);
38
+ }
39
+
40
+ const archiveDir = getWorkspaceArchiveDir(problemId, workspaceRoot);
41
+ ensureDir(archiveDir);
42
+
43
+ const payload = {
44
+ generatedAt: new Date().toISOString(),
45
+ problemId: problem.problemId,
46
+ displayName: problem.displayName,
47
+ title: problem.title,
48
+ siteStatus: problem.siteStatus,
49
+ repoStatus: problem.repoStatus,
50
+ archiveMode: 'method_exemplar',
51
+ };
52
+
53
+ writeJson(path.join(archiveDir, 'ARCHIVE.json'), payload);
54
+ writeText(
55
+ path.join(archiveDir, 'ARCHIVE_SUMMARY.md'),
56
+ [
57
+ `# ${problem.displayName} Archive Summary`,
58
+ '',
59
+ `- Title: ${problem.title}`,
60
+ `- Site status: ${problem.siteStatus}`,
61
+ `- Repo status: ${problem.repoStatus}`,
62
+ '',
63
+ 'Archive posture:',
64
+ '- treat this solved problem as a method exemplar',
65
+ '- extract reusable arguments, formalization hooks, and references',
66
+ '- do not run the open-problem frontier loop here',
67
+ '',
68
+ ].join('\n'),
69
+ );
70
+ writeText(
71
+ path.join(archiveDir, 'METHOD_PACKET.md'),
72
+ [
73
+ `# ${problem.displayName} Method Packet`,
74
+ '',
75
+ 'Prompts:',
76
+ '- What method or reduction pattern is reusable?',
77
+ '- What verification surface is already clean here?',
78
+ '- What should be ported into open-problem packs as technique, not as status inflation?',
79
+ '',
80
+ ].join('\n'),
81
+ );
82
+
83
+ return {
84
+ archiveDir,
85
+ payload,
86
+ };
87
+ }