erdos-problems 0.1.13 → 0.2.0
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 +77 -4
- package/docs/RESEARCH_LOOP.md +14 -2
- package/package.json +1 -1
- package/packs/number-theory/README.md +13 -0
- package/packs/number-theory/problems/1/CONTEXT.md +8 -0
- package/packs/number-theory/problems/1/context.yaml +25 -0
- package/packs/number-theory/problems/2/CONTEXT.md +8 -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/OPS_DETAILS.yaml +39 -0
- package/packs/sunflower/problems/856/OPS_DETAILS.yaml +39 -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 +14 -2
- package/src/commands/archive.js +46 -0
- package/src/commands/maintainer.js +20 -2
- package/src/commands/problem.js +3 -0
- package/src/commands/pull.js +127 -4
- package/src/commands/sunflower.js +290 -12
- package/src/commands/upstream.js +129 -0
- package/src/commands/workspace.js +4 -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/paths.js +16 -0
- package/src/runtime/state.js +32 -3
- package/src/runtime/sunflower.js +329 -2
- package/src/runtime/workspace.js +4 -0
- package/src/upstream/literature.js +83 -0
|
@@ -397,9 +397,18 @@ function renderAgentStartMarkdown(problemId, record) {
|
|
|
397
397
|
`- Harness depth: ${record.harness.depth}`,
|
|
398
398
|
`- Site status: ${record.status.site_status}`,
|
|
399
399
|
'',
|
|
400
|
+
'Read in this order:',
|
|
401
|
+
'- `STATEMENT.md`',
|
|
402
|
+
'- `REFERENCES.md`',
|
|
403
|
+
'- `EVIDENCE.md`',
|
|
404
|
+
'- `FORMALIZATION.md`',
|
|
405
|
+
'- `PUBLIC_STATUS_REVIEW.md`',
|
|
406
|
+
'- `AGENT_WEBSEARCH_BRIEF.md`',
|
|
407
|
+
'',
|
|
400
408
|
'First honest move:',
|
|
401
409
|
`- tighten the local dossier for problem ${problemId} against its pull bundle, references, and upstream provenance before widening claims.`,
|
|
402
410
|
'- read `PUBLIC_STATUS_REVIEW.md` and run the suggested queries in `AGENT_WEBSEARCH_BRIEF.md` before trusting a single public status surface.',
|
|
411
|
+
'- write down the smallest route hypothesis that would make the next session cleaner, even if it remains provisional.',
|
|
403
412
|
'',
|
|
404
413
|
].join('\n');
|
|
405
414
|
}
|
|
@@ -421,6 +430,10 @@ function renderRoutesMarkdown(problemId, record) {
|
|
|
421
430
|
`- Current seeded route placeholder for problem ${problemId}: \`${activeRoute}\``,
|
|
422
431
|
'- Treat this as a workspace-level route marker until a curated route tree is written.',
|
|
423
432
|
'- Keep route progress separate from global problem status.',
|
|
433
|
+
'- Suggested route-writing prompts:',
|
|
434
|
+
' - What is the smallest honest route name?',
|
|
435
|
+
' - What theorem, evidence bundle, or survey note does it point at?',
|
|
436
|
+
' - What would count as a route breakthrough versus only route clarification?',
|
|
424
437
|
'',
|
|
425
438
|
].join('\n');
|
|
426
439
|
}
|
|
@@ -439,6 +452,7 @@ function renderCheckpointNotesMarkdown(problemId, record) {
|
|
|
439
452
|
'- Which upstream/public truth changed, if any?',
|
|
440
453
|
'- What did the public-status review and agent websearch brief surface beyond erdosproblems.com?',
|
|
441
454
|
'- Which artifact or literature bundle should the next agent read first?',
|
|
455
|
+
'- What route, evidence, and formalization notes should be promoted out of scratch space into canonical dossier files?',
|
|
442
456
|
'',
|
|
443
457
|
].join('\n');
|
|
444
458
|
}
|
|
@@ -508,3 +522,59 @@ export function seedProblemFromPullBundle(problemId, options = {}) {
|
|
|
508
522
|
starterLoopArtifacts: STARTER_LOOP_ARTIFACTS,
|
|
509
523
|
};
|
|
510
524
|
}
|
|
525
|
+
|
|
526
|
+
function renderReviewChecklist(problemId, bundle, destinationDir) {
|
|
527
|
+
return [
|
|
528
|
+
`# Problem ${problemId} Maintainer Review Checklist`,
|
|
529
|
+
'',
|
|
530
|
+
`- Pull bundle: ${bundle.pullDir}`,
|
|
531
|
+
`- Proposed destination: ${destinationDir}`,
|
|
532
|
+
'',
|
|
533
|
+
'## Provenance',
|
|
534
|
+
'',
|
|
535
|
+
`- Upstream record included: ${bundle.upstreamRecord ? 'yes' : 'no'}`,
|
|
536
|
+
`- Site snapshot included: ${bundle.siteExtract || bundle.siteSummary ? 'yes' : 'no'}`,
|
|
537
|
+
`- Public search review included: ${bundle.publicStatusReview || bundle.publicStatusReviewMarkdown ? 'yes' : 'no'}`,
|
|
538
|
+
'',
|
|
539
|
+
'## Review questions',
|
|
540
|
+
'',
|
|
541
|
+
'- Is the public site status clearly open right now?',
|
|
542
|
+
'- Does the seeded short statement capture the actual mathematical focus cleanly?',
|
|
543
|
+
'- Are the cluster and family tags honest?',
|
|
544
|
+
'- Does the dossier need a deeper route starter before publication?',
|
|
545
|
+
'- Should this problem remain dossier-depth or enter a family pack later?',
|
|
546
|
+
'',
|
|
547
|
+
'## Ready-to-promote checks',
|
|
548
|
+
'',
|
|
549
|
+
'- Statement reviewed',
|
|
550
|
+
'- References reviewed',
|
|
551
|
+
'- Evidence starter reviewed',
|
|
552
|
+
'- Formalization starter reviewed',
|
|
553
|
+
'- Public-status review read',
|
|
554
|
+
'- Agent websearch brief read',
|
|
555
|
+
'',
|
|
556
|
+
].join('\n');
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export function reviewPullBundleForSeeding(problemId, options = {}) {
|
|
560
|
+
const bundle = loadPullBundle(problemId, options.fromPullDir);
|
|
561
|
+
const destinationRoot = path.resolve(options.destRoot ?? path.join(repoRoot, 'problems'));
|
|
562
|
+
const destinationDir = path.join(destinationRoot, String(problemId));
|
|
563
|
+
const reviewPath = path.resolve(options.dest ?? path.join(bundle.pullDir, 'REVIEW_CHECKLIST.md'));
|
|
564
|
+
const title = deriveTitle(problemId, bundle, options.title);
|
|
565
|
+
const shortStatement = deriveShortStatement(problemId, bundle, title);
|
|
566
|
+
|
|
567
|
+
writeText(reviewPath, renderReviewChecklist(problemId, bundle, destinationDir));
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
problemId: String(problemId),
|
|
571
|
+
reviewPath,
|
|
572
|
+
pullDir: bundle.pullDir,
|
|
573
|
+
destinationDir,
|
|
574
|
+
usedUpstreamRecord: Boolean(bundle.upstreamRecord),
|
|
575
|
+
usedSiteSnapshot: Boolean(bundle.siteExtract || bundle.siteSummary),
|
|
576
|
+
usedPublicStatusReview: Boolean(bundle.publicStatusReview || bundle.publicStatusReviewMarkdown),
|
|
577
|
+
title,
|
|
578
|
+
shortStatement,
|
|
579
|
+
};
|
|
580
|
+
}
|
package/src/runtime/paths.js
CHANGED
|
@@ -120,6 +120,22 @@ export function getWorkspaceCheckpointsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
|
120
120
|
return path.join(getWorkspaceDir(workspaceRoot), 'checkpoints');
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
export function getWorkspaceRunsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
124
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'runs');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function getWorkspaceRunDir(runId, workspaceRoot = getWorkspaceRoot()) {
|
|
128
|
+
return path.join(getWorkspaceRunsDir(workspaceRoot), String(runId));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function getWorkspaceArchivesDir(workspaceRoot = getWorkspaceRoot()) {
|
|
132
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'archives');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function getWorkspaceArchiveDir(problemId, workspaceRoot = getWorkspaceRoot()) {
|
|
136
|
+
return path.join(getWorkspaceArchivesDir(workspaceRoot), String(problemId));
|
|
137
|
+
}
|
|
138
|
+
|
|
123
139
|
export function getWorkspaceProblemCheckpointsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
124
140
|
return path.join(getWorkspaceCheckpointsDir(workspaceRoot), 'problem-checkpoints');
|
|
125
141
|
}
|
package/src/runtime/state.js
CHANGED
|
@@ -35,6 +35,9 @@ function defaultState(config, workspaceRoot) {
|
|
|
35
35
|
routeStory: null,
|
|
36
36
|
checkpointFocus: null,
|
|
37
37
|
nextHonestMove: 'Select or bootstrap an Erdős problem to begin.',
|
|
38
|
+
packArtifacts: null,
|
|
39
|
+
activeTicketId: null,
|
|
40
|
+
activeAtomId: null,
|
|
38
41
|
lastCheckpointSyncAt: null,
|
|
39
42
|
};
|
|
40
43
|
}
|
|
@@ -101,6 +104,13 @@ function renderStateMarkdown(state) {
|
|
|
101
104
|
|
|
102
105
|
- ${state.checkpointFocus || '(none yet)'}
|
|
103
106
|
|
|
107
|
+
## Pack Artifacts
|
|
108
|
+
|
|
109
|
+
- Frontier Note: ${state.packArtifacts?.frontierNotePath || '(none)'}
|
|
110
|
+
- Route History: ${state.packArtifacts?.routeHistoryPath || '(none)'}
|
|
111
|
+
- Checkpoint Template: ${state.packArtifacts?.checkpointTemplatePath || '(none)'}
|
|
112
|
+
- Report Template: ${state.packArtifacts?.reportTemplatePath || '(none)'}
|
|
113
|
+
|
|
104
114
|
## Next Honest Move
|
|
105
115
|
|
|
106
116
|
- ${state.nextHonestMove}
|
|
@@ -146,6 +156,9 @@ function deriveGenericProblemSummary(problem) {
|
|
|
146
156
|
: 'No active route is recorded for this dossier yet.',
|
|
147
157
|
checkpointFocus: 'Keep dossier truth, upstream provenance, and local route state sharply separated.',
|
|
148
158
|
nextHonestMove,
|
|
159
|
+
packArtifacts: null,
|
|
160
|
+
activeTicketId: null,
|
|
161
|
+
activeAtomId: null,
|
|
149
162
|
questionLedger: {
|
|
150
163
|
openQuestions: ['What is the next smallest honest route or evidence obligation for this problem?'],
|
|
151
164
|
activeRouteNotes: activeRoute ? [`Current active route: ${activeRoute}`] : [],
|
|
@@ -170,10 +183,10 @@ function deriveProblemSummary(problem) {
|
|
|
170
183
|
: sunflower.frontierDetail || sunflower.computeSummary || sunflower.bootstrapFocus || problem.shortStatement;
|
|
171
184
|
const routeStory = sunflower.activeTicket
|
|
172
185
|
? `Work ${sunflower.activeTicket.ticketId} (${sunflower.activeTicket.ticketName}) without blurring ticket-local pressure into solved-problem claims.`
|
|
173
|
-
: (sunflower.routeStory || sunflower.bootstrapFocus || null);
|
|
186
|
+
: (sunflower.activeRouteDetail?.summary || sunflower.routeStory || sunflower.bootstrapFocus || null);
|
|
174
187
|
const checkpointFocus = sunflower.activeTicket
|
|
175
188
|
? `Keep the board packet honest around ${sunflower.activeTicket.ticketId} while preserving the open-problem / active-route / route-breakthrough ladder.`
|
|
176
|
-
: (sunflower.checkpointFocus || null);
|
|
189
|
+
: (sunflower.activeRouteDetail?.whyNow || sunflower.checkpointFocus || null);
|
|
177
190
|
return {
|
|
178
191
|
familyRole: sunflower.familyRole,
|
|
179
192
|
harnessProfile: sunflower.harnessProfile,
|
|
@@ -187,7 +200,20 @@ function deriveProblemSummary(problem) {
|
|
|
187
200
|
},
|
|
188
201
|
routeStory,
|
|
189
202
|
checkpointFocus,
|
|
190
|
-
nextHonestMove:
|
|
203
|
+
nextHonestMove:
|
|
204
|
+
sunflower.activeAtomDetail?.nextMove
|
|
205
|
+
|| sunflower.activeTicketDetail?.nextMove
|
|
206
|
+
|| sunflower.nextHonestMove
|
|
207
|
+
|| sunflower.computeNextAction
|
|
208
|
+
|| 'Refresh the active route and package a new honest checkpoint.',
|
|
209
|
+
packArtifacts: {
|
|
210
|
+
frontierNotePath: sunflower.frontierNotePath,
|
|
211
|
+
routeHistoryPath: sunflower.routeHistoryPath,
|
|
212
|
+
checkpointTemplatePath: sunflower.checkpointTemplatePath,
|
|
213
|
+
reportTemplatePath: sunflower.reportTemplatePath,
|
|
214
|
+
},
|
|
215
|
+
activeTicketId: sunflower.activeTicket?.ticketId ?? null,
|
|
216
|
+
activeAtomId: sunflower.firstReadyAtom?.atomId ?? null,
|
|
191
217
|
questionLedger: sunflower.questionLedger,
|
|
192
218
|
};
|
|
193
219
|
}
|
|
@@ -273,6 +299,9 @@ export function syncState(workspaceRoot = getWorkspaceRoot()) {
|
|
|
273
299
|
routeStory: summary.routeStory,
|
|
274
300
|
checkpointFocus: summary.checkpointFocus,
|
|
275
301
|
nextHonestMove: summary.nextHonestMove,
|
|
302
|
+
packArtifacts: summary.packArtifacts,
|
|
303
|
+
activeTicketId: summary.activeTicketId,
|
|
304
|
+
activeAtomId: summary.activeAtomId,
|
|
276
305
|
lastCheckpointSyncAt: existing.lastCheckpointSyncAt ?? null,
|
|
277
306
|
};
|
|
278
307
|
|
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,
|