erdos-problems 0.1.4 → 0.1.6

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 (40) hide show
  1. package/README.md +103 -13
  2. package/docs/ERDOS_PROBLEMS_PROBLEM_SCHEMA.md +65 -60
  3. package/docs/ERDOS_PROBLEMS_REPO_SPEC.md +97 -214
  4. package/docs/RESEARCH_LOOP.md +47 -0
  5. package/package.json +1 -1
  6. package/packs/sunflower/README.md +6 -4
  7. package/packs/sunflower/compute/20/u3_uniform_transfer_window_v0.yaml +16 -0
  8. package/packs/sunflower/problems/20/CONTEXT.md +14 -0
  9. package/packs/sunflower/problems/20/context.yaml +31 -0
  10. package/packs/sunflower/problems/536/CONTEXT.md +9 -0
  11. package/packs/sunflower/problems/536/context.yaml +17 -0
  12. package/packs/sunflower/problems/856/CONTEXT.md +9 -0
  13. package/packs/sunflower/problems/856/context.yaml +17 -0
  14. package/packs/sunflower/problems/857/CONTEXT.md +14 -0
  15. package/packs/sunflower/problems/857/context.yaml +32 -0
  16. package/problems/20/problem.yaml +8 -2
  17. package/problems/536/problem.yaml +9 -3
  18. package/problems/856/problem.yaml +9 -3
  19. package/src/cli/index.js +24 -0
  20. package/src/commands/bootstrap.js +7 -0
  21. package/src/commands/checkpoints.js +36 -0
  22. package/src/commands/continuation.js +60 -0
  23. package/src/commands/maintainer.js +182 -0
  24. package/src/commands/preflight.js +44 -0
  25. package/src/commands/problem.js +10 -0
  26. package/src/commands/pull.js +192 -41
  27. package/src/commands/state.js +57 -0
  28. package/src/commands/sunflower.js +12 -0
  29. package/src/commands/workspace.js +22 -0
  30. package/src/runtime/checkpoints.js +208 -0
  31. package/src/runtime/config.js +37 -0
  32. package/src/runtime/continuation.js +65 -0
  33. package/src/runtime/git.js +52 -0
  34. package/src/runtime/maintainer-seed.js +294 -0
  35. package/src/runtime/paths.js +75 -27
  36. package/src/runtime/preflight.js +106 -0
  37. package/src/runtime/problem-artifacts.js +63 -1
  38. package/src/runtime/state.js +269 -0
  39. package/src/runtime/sunflower.js +126 -6
  40. package/src/runtime/workspace.js +42 -22
@@ -1,15 +1,19 @@
1
1
  import path from 'node:path';
2
2
  import { getProblem } from '../atlas/catalog.js';
3
- import { ensureDir, writeJson, writeText } from '../runtime/files.js';
4
- import { getWorkspaceProblemPullDir } from '../runtime/paths.js';
5
- import { scaffoldProblem } from '../runtime/problem-artifacts.js';
3
+ import { copyFileIfPresent, ensureDir, writeJson, writeText } from '../runtime/files.js';
4
+ import {
5
+ getWorkspaceProblemArtifactDir,
6
+ getWorkspaceProblemLiteratureDir,
7
+ getWorkspaceProblemPullDir,
8
+ } from '../runtime/paths.js';
9
+ import { getProblemArtifactInventory, scaffoldProblem } from '../runtime/problem-artifacts.js';
6
10
  import { loadActiveUpstreamSnapshot, syncUpstream } from '../upstream/sync.js';
7
11
  import { fetchProblemSiteSnapshot } from '../upstream/site.js';
8
12
 
9
13
  function parsePullArgs(args) {
10
14
  const [kind, value, ...rest] = args;
11
- if (kind !== 'problem') {
12
- return { error: 'Only `erdos pull problem <id>` is supported right now.' };
15
+ if (!['problem', 'artifacts', 'literature'].includes(kind)) {
16
+ return { error: 'Usage: erdos pull problem|artifacts|literature <id> [--dest <path>] [--include-site] [--refresh-upstream]' };
13
17
  }
14
18
 
15
19
  let destination = null;
@@ -38,6 +42,7 @@ function parsePullArgs(args) {
38
42
  }
39
43
 
40
44
  return {
45
+ kind,
41
46
  problemId: value,
42
47
  destination,
43
48
  includeSite,
@@ -45,31 +50,36 @@ function parsePullArgs(args) {
45
50
  };
46
51
  }
47
52
 
48
- function writeUpstreamOnlyBundle(problemId, destination, upstreamRecord, snapshot) {
53
+ function buildProblemRecord(problemId, localProblem, upstreamRecord) {
54
+ return {
55
+ generatedAt: new Date().toISOString(),
56
+ problemId: String(problemId),
57
+ title: localProblem?.title ?? `Erdos Problem #${problemId}`,
58
+ cluster: localProblem?.cluster ?? null,
59
+ siteStatus: localProblem?.siteStatus ?? upstreamRecord?.status?.state ?? 'unknown',
60
+ repoStatus: localProblem?.repoStatus ?? 'upstream-only',
61
+ harnessDepth: localProblem?.harnessDepth ?? 'unseeded',
62
+ sourceUrl: localProblem?.sourceUrl ?? `https://www.erdosproblems.com/${problemId}`,
63
+ activeRoute: localProblem?.researchState?.active_route ?? null,
64
+ };
65
+ }
66
+
67
+ function writeUpstreamOnlyBundle(problemId, destination, upstreamRecord, snapshot, readmeTitle) {
49
68
  ensureDir(destination);
50
69
 
51
70
  if (upstreamRecord) {
52
71
  writeJson(path.join(destination, 'UPSTREAM_RECORD.json'), upstreamRecord);
53
72
  }
54
73
 
55
- const generatedAt = new Date().toISOString();
56
- writeJson(path.join(destination, 'PROBLEM.json'), {
57
- generatedAt,
58
- problemId,
59
- title: `Erdos Problem #${problemId}`,
60
- cluster: null,
61
- siteStatus: upstreamRecord?.status?.state ?? 'unknown',
62
- repoStatus: 'upstream-only',
63
- harnessDepth: 'unseeded',
64
- sourceUrl: `https://www.erdosproblems.com/${problemId}`,
65
- activeRoute: null,
66
- });
67
-
74
+ const problemRecord = buildProblemRecord(problemId, null, upstreamRecord);
75
+ writeJson(path.join(destination, 'PROBLEM.json'), problemRecord);
68
76
  writeJson(path.join(destination, 'ARTIFACT_INDEX.json'), {
69
- generatedAt,
70
- problemId,
77
+ generatedAt: problemRecord.generatedAt,
78
+ problemId: String(problemId),
71
79
  copiedArtifacts: [],
72
80
  canonicalArtifacts: [],
81
+ packProblemArtifactsInventory: [],
82
+ computePackets: [],
73
83
  upstreamSnapshot: snapshot
74
84
  ? {
75
85
  kind: snapshot.kind,
@@ -82,11 +92,10 @@ function writeUpstreamOnlyBundle(problemId, destination, upstreamRecord, snapsho
82
92
  : null,
83
93
  includedUpstreamRecord: Boolean(upstreamRecord),
84
94
  });
85
-
86
95
  writeText(
87
96
  path.join(destination, 'README.md'),
88
97
  [
89
- `# Erdos Problem ${problemId} Pull Bundle`,
98
+ `# ${readmeTitle}`,
90
99
  '',
91
100
  'This bundle was generated from upstream public metadata.',
92
101
  '',
@@ -97,6 +106,20 @@ function writeUpstreamOnlyBundle(problemId, destination, upstreamRecord, snapsho
97
106
  '',
98
107
  ].join('\n'),
99
108
  );
109
+
110
+ return {
111
+ problemRecord,
112
+ artifactsCopied: 0,
113
+ inventory: null,
114
+ };
115
+ }
116
+
117
+ function writeArtifactsLane(problemId, destination, localProblem, upstreamRecord, snapshot) {
118
+ if (localProblem) {
119
+ return scaffoldProblem(localProblem, destination);
120
+ }
121
+
122
+ return writeUpstreamOnlyBundle(problemId, destination, upstreamRecord, snapshot, `Erdos Problem ${problemId} Artifact Bundle`);
100
123
  }
101
124
 
102
125
  async function maybeWriteSiteBundle(problemId, destination, includeSite) {
@@ -136,10 +159,110 @@ async function maybeWriteSiteBundle(problemId, destination, includeSite) {
136
159
  }
137
160
  }
138
161
 
162
+ async function writeLiteratureLane(problemId, destination, localProblem, upstreamRecord, includeSite) {
163
+ ensureDir(destination);
164
+
165
+ const includedFiles = [];
166
+ const copied = (sourcePath, destinationName, label) => {
167
+ const destinationPath = path.join(destination, destinationName);
168
+ if (copyFileIfPresent(sourcePath, destinationPath)) {
169
+ includedFiles.push({ label, sourcePath, destinationPath });
170
+ return true;
171
+ }
172
+ return false;
173
+ };
174
+
175
+ if (upstreamRecord) {
176
+ writeJson(path.join(destination, 'UPSTREAM_RECORD.json'), upstreamRecord);
177
+ }
178
+
179
+ let inventory = null;
180
+ if (localProblem) {
181
+ inventory = getProblemArtifactInventory(localProblem);
182
+ copied(localProblem.referencesPath, 'REFERENCES.md', 'REFERENCES.md');
183
+ copied(localProblem.statementPath, 'STATEMENT.md', 'STATEMENT.md');
184
+ if (inventory.packContext?.exists) {
185
+ copied(inventory.packContext.path, 'PACK_CONTEXT.md', inventory.packContext.label);
186
+ }
187
+ for (const artifact of inventory.packProblemArtifacts) {
188
+ copied(artifact.path, path.join('PACK_PROBLEM', artifact.destinationName), artifact.label);
189
+ }
190
+ }
191
+
192
+ const siteStatus = await maybeWriteSiteBundle(problemId, destination, includeSite);
193
+ const problemRecord = buildProblemRecord(problemId, localProblem, upstreamRecord);
194
+ writeJson(path.join(destination, 'PROBLEM.json'), problemRecord);
195
+ writeJson(path.join(destination, 'LITERATURE_INDEX.json'), {
196
+ generatedAt: new Date().toISOString(),
197
+ problemId: String(problemId),
198
+ includedFiles,
199
+ includedUpstreamRecord: Boolean(upstreamRecord),
200
+ includedSiteSnapshot: siteStatus.included,
201
+ siteSnapshotError: siteStatus.error,
202
+ });
203
+ writeText(
204
+ path.join(destination, 'README.md'),
205
+ [
206
+ `# Erdos Problem ${problemId} Literature Bundle`,
207
+ '',
208
+ 'This literature lane was generated by the erdos CLI.',
209
+ '',
210
+ `- Local dossier included: ${localProblem ? 'yes' : 'no'}`,
211
+ `- Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`,
212
+ `- Live site snapshot included: ${siteStatus.included ? 'yes' : 'no'}`,
213
+ '',
214
+ ].join('\n'),
215
+ );
216
+
217
+ return {
218
+ destination,
219
+ includedFiles,
220
+ siteStatus,
221
+ };
222
+ }
223
+
224
+ function writeRootProblemBundle(rootDir, problemId, localProblem, upstreamRecord, snapshot, artifactsDir, literatureDir) {
225
+ ensureDir(rootDir);
226
+ const problemRecord = buildProblemRecord(problemId, localProblem, upstreamRecord);
227
+
228
+ if (upstreamRecord) {
229
+ writeJson(path.join(rootDir, 'UPSTREAM_RECORD.json'), upstreamRecord);
230
+ }
231
+
232
+ writeJson(path.join(rootDir, 'PROBLEM.json'), problemRecord);
233
+ writeJson(path.join(rootDir, 'PULL_INDEX.json'), {
234
+ generatedAt: new Date().toISOString(),
235
+ problemId: String(problemId),
236
+ rootDir,
237
+ artifactsDir,
238
+ literatureDir,
239
+ snapshotKind: snapshot?.kind ?? null,
240
+ upstreamCommit: snapshot?.manifest.upstream_commit ?? null,
241
+ });
242
+ writeText(
243
+ path.join(rootDir, 'README.md'),
244
+ [
245
+ `# Erdos Problem ${problemId} Pull Bundle`,
246
+ '',
247
+ 'This pull bundle contains per-problem workspace lanes for artifacts and literature.',
248
+ '',
249
+ `- Artifacts lane: ${artifactsDir}`,
250
+ `- Literature lane: ${literatureDir}`,
251
+ `- Local canonical dossier available: ${localProblem ? 'yes' : 'no'}`,
252
+ `- Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`,
253
+ '',
254
+ ].join('\n'),
255
+ );
256
+
257
+ return problemRecord;
258
+ }
259
+
139
260
  export async function runPullCommand(args) {
140
261
  if (args.length === 0 || args[0] === 'help' || args[0] === '--help') {
141
262
  console.log('Usage:');
142
263
  console.log(' erdos pull problem <id> [--dest <path>] [--include-site] [--refresh-upstream]');
264
+ console.log(' erdos pull artifacts <id> [--dest <path>] [--refresh-upstream]');
265
+ console.log(' erdos pull literature <id> [--dest <path>] [--include-site] [--refresh-upstream]');
143
266
  return 0;
144
267
  }
145
268
 
@@ -166,38 +289,66 @@ export async function runPullCommand(args) {
166
289
  return 1;
167
290
  }
168
291
 
169
- const destination = parsed.destination
170
- ? path.resolve(parsed.destination)
171
- : getWorkspaceProblemPullDir(parsed.problemId);
292
+ if (parsed.kind === 'artifacts') {
293
+ const destination = parsed.destination
294
+ ? path.resolve(parsed.destination)
295
+ : getWorkspaceProblemArtifactDir(parsed.problemId);
296
+ const result = writeArtifactsLane(String(parsed.problemId), destination, localProblem, upstreamRecord, snapshot);
297
+ console.log(`Artifact bundle created: ${destination}`);
298
+ console.log(`Local canonical dossier included: ${localProblem ? 'yes' : 'no'}`);
299
+ console.log(`Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`);
300
+ console.log(`Artifacts copied: ${result.copiedArtifacts?.length ?? result.artifactsCopied ?? 0}`);
301
+ return 0;
302
+ }
172
303
 
173
- let scaffoldResult = null;
174
- if (localProblem) {
175
- scaffoldResult = scaffoldProblem(localProblem, destination);
176
- } else {
177
- writeUpstreamOnlyBundle(String(parsed.problemId), destination, upstreamRecord, snapshot);
304
+ if (parsed.kind === 'literature') {
305
+ const destination = parsed.destination
306
+ ? path.resolve(parsed.destination)
307
+ : getWorkspaceProblemLiteratureDir(parsed.problemId);
308
+ const result = await writeLiteratureLane(String(parsed.problemId), destination, localProblem, upstreamRecord, parsed.includeSite);
309
+ console.log(`Literature bundle created: ${destination}`);
310
+ console.log(`Local dossier context included: ${localProblem ? 'yes' : 'no'}`);
311
+ console.log(`Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`);
312
+ console.log(`Live site snapshot included: ${result.siteStatus.included ? 'yes' : 'no'}`);
313
+ if (result.siteStatus.error) {
314
+ console.log(`Live site snapshot note: ${result.siteStatus.error}`);
315
+ }
316
+ return 0;
178
317
  }
179
318
 
319
+ const rootDestination = parsed.destination
320
+ ? path.resolve(parsed.destination)
321
+ : getWorkspaceProblemPullDir(parsed.problemId);
322
+ const artifactDestination = path.join(rootDestination, 'artifacts');
323
+ const literatureDestination = path.join(rootDestination, 'literature');
180
324
 
181
- const siteStatus = await maybeWriteSiteBundle(String(parsed.problemId), destination, parsed.includeSite);
325
+ writeRootProblemBundle(rootDestination, String(parsed.problemId), localProblem, upstreamRecord, snapshot, artifactDestination, literatureDestination);
326
+ const artifactResult = writeArtifactsLane(String(parsed.problemId), artifactDestination, localProblem, upstreamRecord, snapshot);
327
+ const literatureResult = await writeLiteratureLane(String(parsed.problemId), literatureDestination, localProblem, upstreamRecord, parsed.includeSite);
182
328
 
183
- writeJson(path.join(destination, 'PULL_STATUS.json'), {
329
+ writeJson(path.join(rootDestination, 'PULL_STATUS.json'), {
184
330
  generatedAt: new Date().toISOString(),
185
331
  problemId: String(parsed.problemId),
186
332
  usedLocalDossier: Boolean(localProblem),
187
333
  includedUpstreamRecord: Boolean(upstreamRecord),
188
334
  upstreamSnapshotKind: snapshot?.kind ?? null,
189
- siteSnapshotAttempted: siteStatus.attempted,
190
- siteSnapshotIncluded: siteStatus.included,
191
- siteSnapshotError: siteStatus.error,
192
- scaffoldArtifactsCopied: scaffoldResult?.copiedArtifacts.length ?? 0,
335
+ artifactLanePath: artifactDestination,
336
+ literatureLanePath: literatureDestination,
337
+ artifactArtifactsCopied: artifactResult.copiedArtifacts?.length ?? artifactResult.artifactsCopied ?? 0,
338
+ literatureFilesCopied: literatureResult.includedFiles.length,
339
+ siteSnapshotAttempted: literatureResult.siteStatus.attempted,
340
+ siteSnapshotIncluded: literatureResult.siteStatus.included,
341
+ siteSnapshotError: literatureResult.siteStatus.error,
193
342
  });
194
343
 
195
- console.log(`Pull bundle created: ${destination}`);
344
+ console.log(`Pull bundle created: ${rootDestination}`);
345
+ console.log(`Artifact lane: ${artifactDestination}`);
346
+ console.log(`Literature lane: ${literatureDestination}`);
196
347
  console.log(`Local canonical dossier included: ${localProblem ? 'yes' : 'no'}`);
197
348
  console.log(`Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`);
198
- console.log(`Live site snapshot included: ${siteStatus.included ? 'yes' : 'no'}`);
199
- if (siteStatus.error) {
200
- console.log(`Live site snapshot note: ${siteStatus.error}`);
349
+ console.log(`Live site snapshot included: ${literatureResult.siteStatus.included ? 'yes' : 'no'}`);
350
+ if (literatureResult.siteStatus.error) {
351
+ console.log(`Live site snapshot note: ${literatureResult.siteStatus.error}`);
201
352
  }
202
353
  return 0;
203
354
  }
@@ -0,0 +1,57 @@
1
+ import { getWorkspaceQuestionLedgerPath, getWorkspaceStateMarkdownPath } from '../runtime/paths.js';
2
+ import { loadState, syncState } from '../runtime/state.js';
3
+
4
+ function printState(state) {
5
+ console.log('Erdos research state');
6
+ console.log(`Workspace root: ${state.workspaceRoot}`);
7
+ console.log(`Open problem: ${state.activeProblem || '(none)'}`);
8
+ console.log(`Problem title: ${state.problemTitle || '(none)'}`);
9
+ console.log(`Cluster: ${state.cluster || '(none)'}`);
10
+ console.log(`Family role: ${state.familyRole || '(none)'}`);
11
+ console.log(`Harness profile: ${state.harnessProfile || '(none)'}`);
12
+ console.log(`Active route: ${state.activeRoute || '(none)'}`);
13
+ console.log(`Route breakthrough: ${state.routeBreakthrough ? 'yes' : 'no'}`);
14
+ console.log(`Problem solved: ${state.problemSolved ? 'yes' : 'no'}`);
15
+ console.log(`Continuation mode: ${state.continuation.mode}`);
16
+ console.log(`Current frontier: ${state.currentFrontier.kind} / ${state.currentFrontier.detail}`);
17
+ console.log(`Route story: ${state.routeStory || '(none)'}`);
18
+ console.log(`Checkpoint focus: ${state.checkpointFocus || '(none)'}`);
19
+ console.log(`Next honest move: ${state.nextHonestMove}`);
20
+ console.log(`State markdown: ${getWorkspaceStateMarkdownPath()}`);
21
+ console.log(`Question ledger: ${getWorkspaceQuestionLedgerPath()}`);
22
+ }
23
+
24
+ export function runStateCommand(args) {
25
+ const [subcommand, ...rest] = args;
26
+ const asJson = rest.includes('--json');
27
+
28
+ if (!subcommand || subcommand === 'help' || subcommand === '--help') {
29
+ console.log('Usage:');
30
+ console.log(' erdos state sync [--json]');
31
+ console.log(' erdos state show [--json]');
32
+ return 0;
33
+ }
34
+
35
+ if (subcommand === 'sync') {
36
+ const state = syncState();
37
+ if (asJson) {
38
+ console.log(JSON.stringify(state, null, 2));
39
+ return 0;
40
+ }
41
+ printState(state);
42
+ return 0;
43
+ }
44
+
45
+ if (subcommand === 'show') {
46
+ const state = loadState();
47
+ if (asJson) {
48
+ console.log(JSON.stringify(state, null, 2));
49
+ return 0;
50
+ }
51
+ printState(state);
52
+ return 0;
53
+ }
54
+
55
+ console.error(`Unknown state subcommand: ${subcommand}`);
56
+ return 1;
57
+ }
@@ -28,10 +28,22 @@ function parseStatusArgs(args) {
28
28
  function printSunflowerStatus(snapshot, registryPaths) {
29
29
  console.log(`${snapshot.displayName} sunflower harness`);
30
30
  console.log(`Title: ${snapshot.title}`);
31
+ console.log(`Family role: ${snapshot.familyRole ?? '(none)'}`);
32
+ console.log(`Harness profile: ${snapshot.harnessProfile ?? '(none)'}`);
31
33
  console.log(`Active route: ${snapshot.activeRoute ?? '(none)'}`);
32
34
  console.log(`Route breakthrough: ${snapshot.routeBreakthrough ? 'yes' : 'no'}`);
33
35
  console.log(`Open problem: ${snapshot.openProblem ? 'yes' : 'no'}`);
34
36
  console.log(`Problem solved: ${snapshot.problemSolved ? 'yes' : 'no'}`);
37
+ console.log(`Bootstrap focus: ${snapshot.bootstrapFocus ?? '(none)'}`);
38
+ console.log(`Route story: ${snapshot.routeStory ?? '(none)'}`);
39
+ console.log(`Frontier label: ${snapshot.frontierLabel ?? '(none)'}`);
40
+ console.log(`Frontier detail: ${snapshot.frontierDetail ?? '(none)'}`);
41
+ console.log(`Checkpoint focus: ${snapshot.checkpointFocus ?? '(none)'}`);
42
+ console.log(`Next honest move: ${snapshot.nextHonestMove}`);
43
+ console.log(`Related core problems: ${snapshot.relatedCoreProblems.join(', ') || '(none)'}`);
44
+ console.log(`Literature focus: ${snapshot.literatureFocus.join(', ') || '(none)'}`);
45
+ console.log(`Artifact focus: ${snapshot.artifactFocus.join(', ') || '(none)'}`);
46
+ console.log(`Context file: ${snapshot.contextPath ?? '(none)'}`);
35
47
  console.log(`Compute lane present: ${snapshot.computeLanePresent ? 'yes' : 'no'}`);
36
48
  console.log(`Compute lane count: ${snapshot.computeLaneCount}`);
37
49
  console.log(`Compute summary: ${snapshot.computeSummary}`);
@@ -1,4 +1,5 @@
1
1
  import { getProblem } from '../atlas/catalog.js';
2
+ import { loadConfig } from '../runtime/config.js';
2
3
  import { buildSunflowerStatusSnapshot } from '../runtime/sunflower.js';
3
4
  import { getWorkspaceSummary } from '../runtime/workspace.js';
4
5
 
@@ -17,19 +18,40 @@ export function runWorkspaceCommand(args) {
17
18
  }
18
19
 
19
20
  const summary = getWorkspaceSummary();
21
+ const config = loadConfig();
20
22
  console.log(`Workspace root: ${summary.workspaceRoot}`);
21
23
  console.log(`State dir: ${summary.stateDir}`);
22
24
  console.log(`Initialized: ${summary.hasState ? 'yes' : 'no'}`);
23
25
  console.log(`Active problem: ${summary.activeProblem ?? '(none)'}`);
26
+ console.log(`Config path: ${summary.configPath}`);
27
+ console.log(`State file: ${summary.statePath}`);
28
+ console.log(`State markdown: ${summary.stateMarkdownPath}`);
29
+ console.log(`Question ledger: ${summary.questionLedgerPath}`);
30
+ console.log(`Checkpoint shelf: ${summary.checkpointIndexPath}`);
24
31
  console.log(`Workspace upstream dir: ${summary.upstreamDir}`);
25
32
  console.log(`Workspace scaffold dir: ${summary.scaffoldDir}`);
26
33
  console.log(`Workspace pull dir: ${summary.pullDir}`);
34
+ console.log(`Workspace artifact dir: ${summary.artifactDir}`);
35
+ console.log(`Workspace literature dir: ${summary.literatureDir}`);
36
+ console.log(`Preferred agent: ${config.preferredAgent}`);
37
+ console.log(`Continuation mode: ${summary.continuationMode ?? config.continuation}`);
38
+ console.log(`Active route: ${summary.activeRoute ?? '(none)'}`);
39
+ console.log(`Route breakthrough: ${summary.routeBreakthrough ? 'yes' : 'no'}`);
40
+ console.log(`Problem solved: ${summary.problemSolved ? 'yes' : 'no'}`);
41
+ console.log(`Checkpoint synced at: ${summary.lastCheckpointSyncAt ?? '(never)'}`);
42
+ console.log(`Next honest move: ${summary.nextHonestMove ?? '(none)'}`);
43
+ if (summary.currentFrontier) {
44
+ console.log(`Current frontier: ${summary.currentFrontier.kind} / ${summary.currentFrontier.detail}`);
45
+ }
27
46
  console.log(`Updated at: ${summary.updatedAt ?? '(none)'}`);
28
47
  if (summary.activeProblem) {
29
48
  const problem = getProblem(summary.activeProblem);
30
49
  if (problem?.cluster === 'sunflower') {
31
50
  const sunflower = buildSunflowerStatusSnapshot(problem);
51
+ console.log(`Sunflower family role: ${sunflower.familyRole ?? '(none)'}`);
52
+ console.log(`Sunflower harness profile: ${sunflower.harnessProfile ?? '(none)'}`);
32
53
  console.log(`Sunflower route: ${sunflower.activeRoute ?? '(none)'}`);
54
+ console.log(`Sunflower frontier: ${sunflower.frontierDetail ?? '(none)'}`);
33
55
  console.log(`Sunflower compute: ${sunflower.computeLanePresent ? 'yes' : 'no'}`);
34
56
  if (sunflower.activePacket) {
35
57
  console.log(`Sunflower compute lane: ${sunflower.activePacket.laneId} [${sunflower.activePacket.status}]`);
@@ -0,0 +1,208 @@
1
+ import path from 'node:path';
2
+ import { loadLocalProblems, getProblem } from '../atlas/catalog.js';
3
+ import { loadConfig } from './config.js';
4
+ import { ensureDir, writeJson, writeText } from './files.js';
5
+ import {
6
+ getWorkspaceCheckpointIndexPath,
7
+ getWorkspaceCheckpointJsonPath,
8
+ getWorkspaceCheckpointsDir,
9
+ getWorkspaceProblemCheckpointsDir,
10
+ getWorkspaceRoot,
11
+ getWorkspaceRouteCheckpointsDir,
12
+ } from './paths.js';
13
+ import { getProblemArtifactInventory } from './problem-artifacts.js';
14
+ import { buildSunflowerStatusSnapshot } from './sunflower.js';
15
+ import { loadState, saveState, syncState } from './state.js';
16
+
17
+ function safeName(value) {
18
+ return String(value).replace(/[^a-zA-Z0-9_-]+/g, '-');
19
+ }
20
+
21
+ function renderCanonicalArtifacts(inventory) {
22
+ return inventory.canonicalArtifacts
23
+ .map((artifact) => `- ${artifact.label}: ${artifact.exists ? 'present' : 'missing'} (${artifact.path})`)
24
+ .join('\n');
25
+ }
26
+
27
+ function renderProblemCheckpoint(problem, state) {
28
+ const inventory = getProblemArtifactInventory(problem);
29
+ const sunflower = problem.cluster === 'sunflower' ? buildSunflowerStatusSnapshot(problem) : null;
30
+ const research = problem.researchState ?? {};
31
+ const activeRoute = sunflower?.activeRoute ?? research.active_route ?? '(none)';
32
+ const routeBreakthrough = sunflower?.routeBreakthrough ?? Boolean(research.route_breakthrough);
33
+ const problemSolved = sunflower?.problemSolved ?? Boolean(research.problem_solved);
34
+ const nextHonestMove = sunflower?.nextHonestMove
35
+ ?? (activeRoute !== '(none)' ? `Advance ${activeRoute} against the dossier and evidence bundle.` : 'Seed or choose an active route.');
36
+ const frontier = sunflower?.frontierDetail ?? problem.shortStatement;
37
+ const checkpointFocus = sunflower?.checkpointFocus ?? 'Keep local route claims and upstream public status cleanly separated.';
38
+
39
+ return `# Problem ${problem.problemId} Checkpoint
40
+
41
+ ## Status Ladder
42
+
43
+ - Open Problem: ${problem.problemId}
44
+ - Active Route: ${activeRoute}
45
+ - Route Breakthrough: ${routeBreakthrough ? 'yes' : 'no'}
46
+ - Problem Solved: ${problemSolved ? 'yes' : 'no'}
47
+
48
+ ## Problem Record
49
+
50
+ - Title: ${problem.title}
51
+ - Cluster: ${problem.cluster}
52
+ - Source: ${problem.sourceUrl}
53
+ - Site status: ${problem.siteStatus}
54
+ - Repo status: ${problem.repoStatus}
55
+ - Harness depth: ${problem.harnessDepth}
56
+
57
+ ## Current Frontier
58
+
59
+ - ${frontier}
60
+
61
+ ## Checkpoint Focus
62
+
63
+ - ${checkpointFocus}
64
+
65
+ ## Next Honest Move
66
+
67
+ - ${nextHonestMove}
68
+
69
+ ## Canonical Artifacts
70
+
71
+ ${renderCanonicalArtifacts(inventory)}
72
+
73
+ ## Continuation Frame
74
+
75
+ - Active agent: ${state.activeAgent || '(none)'}
76
+ - Continuation mode: ${state.continuation.mode}
77
+ - Stop rule: ${state.continuation.stopRule}
78
+ `;
79
+ }
80
+
81
+ function renderRouteCheckpoint(problem, state) {
82
+ const sunflower = problem.cluster === 'sunflower' ? buildSunflowerStatusSnapshot(problem) : null;
83
+ const frontier = sunflower?.frontierDetail ?? state.currentFrontier.detail;
84
+ const routeStory = sunflower?.routeStory ?? state.routeStory ?? '(none yet)';
85
+ const checkpointFocus = sunflower?.checkpointFocus ?? state.checkpointFocus ?? '(none yet)';
86
+
87
+ return `# Problem ${problem.problemId} Active Route Checkpoint
88
+
89
+ ## Status Ladder
90
+
91
+ - Open Problem: ${problem.problemId}
92
+ - Active Route: ${state.activeRoute || '(none)'}
93
+ - Route Breakthrough: ${state.routeBreakthrough ? 'yes' : 'no'}
94
+ - Problem Solved: ${state.problemSolved ? 'yes' : 'no'}
95
+
96
+ ## Current Frontier
97
+
98
+ - ${frontier}
99
+
100
+ ## Route Story
101
+
102
+ - ${routeStory}
103
+
104
+ ## Checkpoint Focus
105
+
106
+ - ${checkpointFocus}
107
+
108
+ ## Continuation Frame
109
+
110
+ - Continuation mode: ${state.continuation.mode}
111
+ - Stop rule: ${state.continuation.stopRule}
112
+ - Next honest move: ${state.nextHonestMove}
113
+ `;
114
+ }
115
+
116
+ function renderIndex(state, checkpoints) {
117
+ const problemRows = checkpoints.filter((entry) => entry.kind === 'problem');
118
+ const routeRows = checkpoints.filter((entry) => entry.kind === 'route');
119
+
120
+ return [
121
+ '# Erdos Checkpoints',
122
+ '',
123
+ 'This is the human-facing checkpoint shelf for the local research-loop runtime.',
124
+ 'Canonical truth still lives in dossiers, pack artifacts, upstream snapshots, and generated workspace bundles.',
125
+ '',
126
+ '## Current Status Ladder',
127
+ '',
128
+ `- Open Problem: ${state.activeProblem || '(none)'}`,
129
+ `- Active Route: ${state.activeRoute || '(none)'}`,
130
+ `- Route Breakthrough: ${state.routeBreakthrough ? 'yes' : 'no'}`,
131
+ `- Problem Solved: ${state.problemSolved ? 'yes' : 'no'}`,
132
+ '',
133
+ '## Working Context',
134
+ '',
135
+ `- Continuation Mode: ${state.continuation.mode}`,
136
+ `- Next Honest Move: ${state.nextHonestMove}`,
137
+ '',
138
+ '## Problem Checkpoints',
139
+ '',
140
+ ...(problemRows.length > 0 ? problemRows.map((row) => `- [${row.label}](${row.relativePath})`) : ['- *(none)*']),
141
+ '',
142
+ '## Route Checkpoints',
143
+ '',
144
+ ...(routeRows.length > 0 ? routeRows.map((row) => `- [${row.label}](${row.relativePath})`) : ['- *(none)*']),
145
+ '',
146
+ ].join('\n');
147
+ }
148
+
149
+ export function syncCheckpoints(workspaceRoot = getWorkspaceRoot()) {
150
+ const config = loadConfig(workspaceRoot);
151
+ const state = syncState(workspaceRoot);
152
+ const problems = loadLocalProblems();
153
+
154
+ ensureDir(getWorkspaceCheckpointsDir(workspaceRoot));
155
+ ensureDir(getWorkspaceProblemCheckpointsDir(workspaceRoot));
156
+ ensureDir(getWorkspaceRouteCheckpointsDir(workspaceRoot));
157
+
158
+ const checkpoints = [];
159
+
160
+ for (const problem of problems) {
161
+ const filename = `problem-${problem.problemId}.md`;
162
+ const fullPath = path.join(getWorkspaceProblemCheckpointsDir(workspaceRoot), filename);
163
+ writeText(fullPath, renderProblemCheckpoint(problem, state));
164
+ checkpoints.push({
165
+ kind: 'problem',
166
+ label: `Problem ${problem.problemId}`,
167
+ path: fullPath,
168
+ relativePath: path.relative(getWorkspaceCheckpointsDir(workspaceRoot), fullPath),
169
+ });
170
+ }
171
+
172
+ if (state.activeProblem && state.activeRoute) {
173
+ const problem = getProblem(state.activeProblem);
174
+ if (problem) {
175
+ const filename = `problem-${state.activeProblem}--${safeName(state.activeRoute)}.md`;
176
+ const fullPath = path.join(getWorkspaceRouteCheckpointsDir(workspaceRoot), filename);
177
+ writeText(fullPath, renderRouteCheckpoint(problem, state));
178
+ checkpoints.push({
179
+ kind: 'route',
180
+ label: `Problem ${state.activeProblem} / ${state.activeRoute}`,
181
+ path: fullPath,
182
+ relativePath: path.relative(getWorkspaceCheckpointsDir(workspaceRoot), fullPath),
183
+ });
184
+ }
185
+ }
186
+
187
+ writeText(getWorkspaceCheckpointIndexPath(workspaceRoot), renderIndex(state, checkpoints));
188
+ writeJson(getWorkspaceCheckpointJsonPath(workspaceRoot), {
189
+ syncedAt: new Date().toISOString(),
190
+ activeProblem: state.activeProblem,
191
+ activeRoute: state.activeRoute,
192
+ continuationMode: state.continuation.mode,
193
+ activeAgent: config.preferredAgent,
194
+ checkpoints,
195
+ });
196
+
197
+ const nextState = saveState({
198
+ ...loadState(workspaceRoot),
199
+ lastCheckpointSyncAt: new Date().toISOString(),
200
+ }, workspaceRoot);
201
+
202
+ return {
203
+ indexPath: getWorkspaceCheckpointIndexPath(workspaceRoot),
204
+ checkpointJsonPath: getWorkspaceCheckpointJsonPath(workspaceRoot),
205
+ checkpoints,
206
+ state: nextState,
207
+ };
208
+ }