erdos-problems 0.1.9 → 0.1.10

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 CHANGED
@@ -45,7 +45,7 @@ erdos dossier show 857
45
45
  For an unseeded problem, the one-step self-seeding flow is now:
46
46
 
47
47
  ```bash
48
- erdos seed problem 25 --include-site --cluster number-theory
48
+ erdos seed problem 25 --cluster number-theory
49
49
  erdos problem show 25
50
50
  erdos workspace show
51
51
  ```
@@ -60,6 +60,8 @@ What `bootstrap` does:
60
60
 
61
61
  What `seed` does:
62
62
  - creates a pull bundle for any problem in the upstream snapshot
63
+ - defaults to a live site snapshot plus a public-search review bundle
64
+ - treats a problem as seed-admissible only when the public site says `open`, unless you intentionally pass `--allow-non-open`
63
65
  - promotes that bundle into `.erdos/seeded-problems/<id>/`
64
66
  - auto-selects the problem in the workspace
65
67
  - syncs the bundled ORP workspace kit into `.erdos/orp/`
@@ -69,6 +71,8 @@ What `seed` does:
69
71
  - `AGENT_START.md`
70
72
  - `ROUTES.md`
71
73
  - `CHECKPOINT_NOTES.md`
74
+ - `PUBLIC_STATUS_REVIEW.md`
75
+ - `AGENT_WEBSEARCH_BRIEF.md`
72
76
 
73
77
  ## Pull lanes
74
78
 
@@ -78,7 +82,7 @@ For any problem number in the upstream snapshot, you can create a workspace bund
78
82
  erdos pull problem 857
79
83
  erdos pull artifacts 857
80
84
  erdos pull literature 857
81
- erdos pull problem 999 --include-site
85
+ erdos pull problem 999 --include-site --include-public-search
82
86
  erdos pull problem 999 --refresh-upstream
83
87
  ```
84
88
 
@@ -91,6 +95,10 @@ What the pull lanes do:
91
95
  - `erdos pull literature <id>` creates only the literature lane
92
96
  - when a problem is locally seeded, the artifact lane includes the canonical dossier, pack context, and compute packets
93
97
  - when `--include-site` is used, the literature lane can include a live site snapshot and plain-text extract
98
+ - when `--include-public-search` is used, the literature lane also includes:
99
+ - `PUBLIC_STATUS_REVIEW.md`
100
+ - `PUBLIC_STATUS_REVIEW.json`
101
+ - `AGENT_WEBSEARCH_BRIEF.md`
94
102
 
95
103
  ## Maintainer seeding
96
104
 
@@ -115,6 +123,9 @@ What maintainer seeding does:
115
123
  - `AGENT_START.md`
116
124
  - `ROUTES.md`
117
125
  - `CHECKPOINT_NOTES.md`
126
+ - public-truth starter files:
127
+ - `PUBLIC_STATUS_REVIEW.md`
128
+ - `AGENT_WEBSEARCH_BRIEF.md`
118
129
  - preserves upstream/site provenance in the local record
119
130
 
120
131
  ## Sunflower pack
@@ -193,7 +204,7 @@ erdos upstream diff
193
204
  erdos scaffold problem 857
194
205
  erdos bootstrap problem 857
195
206
  erdos bootstrap problem 857 --sync-upstream
196
- erdos seed problem 25 --include-site --cluster number-theory
207
+ erdos seed problem 25 --cluster number-theory
197
208
  erdos pull problem 857
198
209
  erdos pull artifacts 857
199
210
  erdos pull literature 857
@@ -221,6 +232,8 @@ Many seeded dossiers now also carry starter-loop artifacts:
221
232
  - `AGENT_START.md`
222
233
  - `ROUTES.md`
223
234
  - `CHECKPOINT_NOTES.md`
235
+ - `PUBLIC_STATUS_REVIEW.md`
236
+ - `AGENT_WEBSEARCH_BRIEF.md`
224
237
 
225
238
  The CLI can surface these directly:
226
239
  - `erdos problem artifacts <id>` shows the canonical inventory
@@ -236,6 +249,7 @@ For sunflower problems, the CLI also surfaces pack-specific artifacts:
236
249
  - per-problem context files under `packs/sunflower/problems/<id>/`
237
250
  - route packets and checkpoint/report packets for `20` and `857`
238
251
  - compute packets under `packs/sunflower/compute/<id>/` when available
252
+ - compute-governance evaluation under `breakthroughs`, surfaced through `erdos sunflower status`
239
253
 
240
254
  ## Notes
241
255
 
@@ -26,8 +26,9 @@ Workspace runtime files live under `.erdos/`:
26
26
  5. Set continuation mode.
27
27
  6. Sync checkpoints.
28
28
  7. Pull or scaffold artifacts.
29
- 8. Work the active route.
30
- 9. Sync checkpoints again at honest boundaries.
29
+ 8. Review public status and agent websearch brief if the problem was freshly seeded.
30
+ 9. Work the active route.
31
+ 10. Sync checkpoints again at honest boundaries.
31
32
 
32
33
  ## Commands
33
34
 
@@ -44,7 +45,7 @@ erdos workspace show
44
45
  For problems that are not yet packaged as native dossiers, the loop can start with one-step self-seeding:
45
46
 
46
47
  ```bash
47
- erdos seed problem 25 --include-site --cluster number-theory
48
+ erdos seed problem 25 --cluster number-theory
48
49
  erdos preflight
49
50
  erdos continuation use route
50
51
  erdos checkpoints sync
@@ -56,6 +57,8 @@ The seeded dossier now also includes starter-loop artifacts:
56
57
  - `AGENT_START.md`
57
58
  - `ROUTES.md`
58
59
  - `CHECKPOINT_NOTES.md`
60
+ - `PUBLIC_STATUS_REVIEW.md`
61
+ - `AGENT_WEBSEARCH_BRIEF.md`
59
62
 
60
63
  The ORP kit travels with the workspace too:
61
64
  - `PROTOCOL.md`
@@ -64,6 +67,11 @@ The ORP kit travels with the workspace too:
64
67
  - `templates/VERIFICATION_RECORD.md`
65
68
  - `templates/FAILED_TOPIC.md`
66
69
 
70
+ For sunflower compute lanes, ORP now sits above `breakthroughs`:
71
+ - `erdos sunflower status <id>` evaluates the packaged compute lane with `breakthroughs`
72
+ - the CLI surfaces the selected rung, dispatch action, and the reason compute is admissible
73
+ - this is compute governance and traceability, not an automatic compute launch
74
+
67
75
  ## Status ladder
68
76
 
69
77
  The public package uses the same ladder we converged on in the lab:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "erdos-problems",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "CLI atlas and staged research harness for Paul Erdos problems.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,6 +31,7 @@
31
31
  "sunflower"
32
32
  ],
33
33
  "dependencies": {
34
+ "breakthroughs": "^0.1.1",
34
35
  "yaml": "^2.8.3"
35
36
  },
36
37
  "license": "MIT"
package/src/cli/index.js CHANGED
@@ -42,11 +42,11 @@ function printUsage() {
42
42
  console.log(' erdos upstream diff [--write-package-report]');
43
43
  console.log(' erdos scaffold problem <id> [--dest <path>]');
44
44
  console.log(' erdos bootstrap problem <id> [--dest <path>] [--sync-upstream]');
45
- console.log(' erdos seed problem <id> [--include-site] [--refresh-upstream] [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--title <title>] [--family-tag <tag>] [--related <id>] [--formalization-status <status>] [--active-route <route>] [--route-breakthrough] [--problem-solved] [--dest-root <path>] [--no-activate] [--no-loop-sync] [--force] [--json]');
46
- console.log(' erdos pull problem <id> [--dest <path>] [--include-site] [--refresh-upstream]');
45
+ console.log(' erdos seed problem <id> [--include-site|--no-site] [--include-public-search|--no-public-search] [--refresh-upstream] [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--title <title>] [--family-tag <tag>] [--related <id>] [--formalization-status <status>] [--active-route <route>] [--route-breakthrough] [--problem-solved] [--allow-non-open] [--dest-root <path>] [--no-activate] [--no-loop-sync] [--force] [--json]');
46
+ console.log(' erdos pull problem <id> [--dest <path>] [--include-site] [--include-public-search] [--refresh-upstream]');
47
47
  console.log(' erdos pull artifacts <id> [--dest <path>] [--refresh-upstream]');
48
- console.log(' erdos pull literature <id> [--dest <path>] [--include-site] [--refresh-upstream]');
49
- console.log(' erdos maintainer seed problem <id> [--from-pull <path>] [--dest-root <path>] [--cluster <name>]');
48
+ console.log(' erdos pull literature <id> [--dest <path>] [--include-site] [--include-public-search] [--refresh-upstream]');
49
+ console.log(' erdos maintainer seed problem <id> [--from-pull <path>] [--dest-root <path>] [--cluster <name>] [--allow-non-open]');
50
50
  }
51
51
 
52
52
  const args = process.argv.slice(2);
@@ -21,6 +21,7 @@ function parseMaintainerSeedArgs(args) {
21
21
  activeRoute: null,
22
22
  routeBreakthrough: false,
23
23
  problemSolved: false,
24
+ allowNonOpen: false,
24
25
  force: false,
25
26
  };
26
27
 
@@ -116,6 +117,10 @@ function parseMaintainerSeedArgs(args) {
116
117
  parsed.problemSolved = true;
117
118
  continue;
118
119
  }
120
+ if (token === '--allow-non-open') {
121
+ parsed.allowNonOpen = true;
122
+ continue;
123
+ }
119
124
  if (token === '--force') {
120
125
  parsed.force = true;
121
126
  continue;
@@ -131,7 +136,7 @@ export function runMaintainerCommand(args) {
131
136
 
132
137
  if (!subcommand || subcommand === 'help' || subcommand === '--help') {
133
138
  console.log('Usage:');
134
- console.log(' erdos maintainer seed problem <id> [--from-pull <path>] [--dest-root <path>] [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--title <title>] [--family-tag <tag>] [--related <id>] [--formalization-status <status>] [--active-route <route>] [--route-breakthrough] [--problem-solved] [--force]');
139
+ console.log(' erdos maintainer seed problem <id> [--from-pull <path>] [--dest-root <path>] [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--title <title>] [--family-tag <tag>] [--related <id>] [--formalization-status <status>] [--active-route <route>] [--route-breakthrough] [--problem-solved] [--allow-non-open] [--force]');
135
140
  return 0;
136
141
  }
137
142
 
@@ -164,6 +169,7 @@ export function runMaintainerCommand(args) {
164
169
  activeRoute: parsed.activeRoute,
165
170
  routeBreakthrough: parsed.routeBreakthrough,
166
171
  problemSolved: parsed.problemSolved,
172
+ allowNonOpen: parsed.allowNonOpen,
167
173
  force: parsed.force,
168
174
  });
169
175
 
@@ -174,6 +180,7 @@ export function runMaintainerCommand(args) {
174
180
  console.log(`Harness depth: ${result.record.harness.depth}`);
175
181
  console.log(`Upstream record used: ${result.usedUpstreamRecord ? 'yes' : 'no'}`);
176
182
  console.log(`Site snapshot used: ${result.usedSiteSnapshot ? 'yes' : 'no'}`);
183
+ console.log(`Public status review used: ${result.usedPublicStatusReview ? 'yes' : 'no'}`);
177
184
  return 0;
178
185
  } catch (error) {
179
186
  console.error(String(error.message ?? error));
@@ -9,6 +9,7 @@ import {
9
9
  import { getProblemArtifactInventory, scaffoldProblem } from '../runtime/problem-artifacts.js';
10
10
  import { loadActiveUpstreamSnapshot, syncUpstream } from '../upstream/sync.js';
11
11
  import { fetchProblemSiteSnapshot } from '../upstream/site.js';
12
+ import { buildProblemSearchQueries, fetchProblemPublicSearchReview } from '../upstream/public-search.js';
12
13
 
13
14
  function normalizeClusterLabel(rawTag) {
14
15
  return String(rawTag ?? '')
@@ -48,6 +49,7 @@ function parsePullArgs(args) {
48
49
 
49
50
  let destination = null;
50
51
  let includeSite = false;
52
+ let includePublicSearch = false;
51
53
  let refreshUpstream = false;
52
54
 
53
55
  for (let index = 0; index < rest.length; index += 1) {
@@ -64,6 +66,10 @@ function parsePullArgs(args) {
64
66
  includeSite = true;
65
67
  continue;
66
68
  }
69
+ if (token === '--include-public-search') {
70
+ includePublicSearch = true;
71
+ continue;
72
+ }
67
73
  if (token === '--refresh-upstream') {
68
74
  refreshUpstream = true;
69
75
  continue;
@@ -76,6 +82,7 @@ function parsePullArgs(args) {
76
82
  problemId: value,
77
83
  destination,
78
84
  includeSite,
85
+ includePublicSearch,
79
86
  refreshUpstream,
80
87
  };
81
88
  }
@@ -165,6 +172,8 @@ async function maybeWriteSiteBundle(problemId, destination, includeSite) {
165
172
  url: siteSnapshot.url,
166
173
  fetchedAt: siteSnapshot.fetchedAt,
167
174
  title: siteSnapshot.title,
175
+ siteStatus: siteSnapshot.siteStatus,
176
+ siteStatusRaw: siteSnapshot.siteStatusRaw,
168
177
  previewLines: siteSnapshot.previewLines,
169
178
  });
170
179
  writeText(
@@ -175,6 +184,8 @@ async function maybeWriteSiteBundle(problemId, destination, includeSite) {
175
184
  `Source: ${siteSnapshot.url}`,
176
185
  `Fetched at: ${siteSnapshot.fetchedAt}`,
177
186
  `Title: ${siteSnapshot.title}`,
187
+ `Site status: ${siteSnapshot.siteStatus}`,
188
+ `Status line: ${siteSnapshot.siteStatusRaw ?? '(unknown)'}`,
178
189
  '',
179
190
  '## Preview',
180
191
  '',
@@ -182,14 +193,132 @@ async function maybeWriteSiteBundle(problemId, destination, includeSite) {
182
193
  '',
183
194
  ].join('\n'),
184
195
  );
185
- return { attempted: true, included: true, error: null };
196
+ return {
197
+ attempted: true,
198
+ included: true,
199
+ error: null,
200
+ siteStatus: siteSnapshot.siteStatus,
201
+ siteStatusRaw: siteSnapshot.siteStatusRaw,
202
+ title: siteSnapshot.title,
203
+ previewLines: siteSnapshot.previewLines,
204
+ };
186
205
  } catch (error) {
187
206
  writeText(path.join(destination, 'SITE_FETCH_ERROR.txt'), String(error.message ?? error));
188
- return { attempted: true, included: false, error: String(error.message ?? error) };
207
+ return {
208
+ attempted: true,
209
+ included: false,
210
+ error: String(error.message ?? error),
211
+ siteStatus: 'unknown',
212
+ siteStatusRaw: null,
213
+ title: null,
214
+ previewLines: [],
215
+ };
216
+ }
217
+ }
218
+
219
+ async function maybeWritePublicSearchBundle(problemId, title, destination, includePublicSearch) {
220
+ const queries = buildProblemSearchQueries(problemId, title);
221
+ const briefLines = [
222
+ `# Erdős Problem #${problemId} Agent Websearch Brief`,
223
+ '',
224
+ 'Why this exists:',
225
+ '- do not rely on erdosproblems.com alone as the canonical public truth surface',
226
+ '- compare the site status with current publicized discussion, literature, and formalization chatter',
227
+ '',
228
+ 'Suggested queries:',
229
+ ...queries.map((query) => `- ${query}`),
230
+ '',
231
+ 'Review prompts:',
232
+ '- Does the problem still appear publicly open?',
233
+ '- Are there recent solution claims, partial claims, or major status updates?',
234
+ '- Are there recent formalization artifacts, surveys, or project pages worth pulling into the dossier?',
235
+ '',
236
+ ];
237
+
238
+ writeText(path.join(destination, 'AGENT_WEBSEARCH_BRIEF.md'), briefLines.join('\n'));
239
+
240
+ if (!includePublicSearch) {
241
+ writeText(
242
+ path.join(destination, 'PUBLIC_STATUS_REVIEW.md'),
243
+ [
244
+ `# Erdős Problem #${problemId} Public Status Review`,
245
+ '',
246
+ '- A live public search was not requested for this pull bundle.',
247
+ '- Use `AGENT_WEBSEARCH_BRIEF.md` to run the suggested queries before widening public-status claims.',
248
+ '',
249
+ ].join('\n'),
250
+ );
251
+ return {
252
+ attempted: false,
253
+ included: false,
254
+ error: null,
255
+ queries,
256
+ combinedResults: [],
257
+ };
258
+ }
259
+
260
+ try {
261
+ const review = await fetchProblemPublicSearchReview(problemId, title);
262
+ writeJson(path.join(destination, 'PUBLIC_STATUS_REVIEW.json'), review);
263
+ writeText(
264
+ path.join(destination, 'PUBLIC_STATUS_REVIEW.md'),
265
+ [
266
+ `# Erdős Problem #${problemId} Public Status Review`,
267
+ '',
268
+ `Fetched at: ${review.fetchedAt}`,
269
+ `Provider: ${review.provider}`,
270
+ '',
271
+ 'Queries run:',
272
+ ...review.queries.map((query) => `- ${query}`),
273
+ '',
274
+ 'Top public results:',
275
+ ...(review.combinedResults.length > 0
276
+ ? review.combinedResults.map((result) => `- [${result.title}](${result.url})${result.snippet ? ` — ${result.snippet}` : ''}`)
277
+ : ['- *(no results captured)*']),
278
+ '',
279
+ ...(review.errors.length > 0
280
+ ? [
281
+ 'Search notes:',
282
+ ...review.errors.map((entry) => `- ${entry.query}: ${entry.error}`),
283
+ '',
284
+ ]
285
+ : []),
286
+ ].join('\n'),
287
+ );
288
+ return {
289
+ attempted: true,
290
+ included: true,
291
+ error: null,
292
+ queries: review.queries,
293
+ combinedResults: review.combinedResults,
294
+ };
295
+ } catch (error) {
296
+ const message = String(error?.message ?? error);
297
+ writeText(
298
+ path.join(destination, 'PUBLIC_STATUS_REVIEW.md'),
299
+ [
300
+ `# Erdős Problem #${problemId} Public Status Review`,
301
+ '',
302
+ '- A live public search was attempted but did not complete cleanly.',
303
+ `- Error: ${message}`,
304
+ '',
305
+ 'Suggested queries:',
306
+ ...queries.map((query) => `- ${query}`),
307
+ '',
308
+ ].join('\n'),
309
+ );
310
+ writeText(path.join(destination, 'PUBLIC_STATUS_REVIEW_ERROR.txt'), message);
311
+ return {
312
+ attempted: true,
313
+ included: false,
314
+ error: message,
315
+ queries,
316
+ combinedResults: [],
317
+ };
189
318
  }
190
319
  }
191
320
 
192
- async function writeLiteratureLane(problemId, destination, localProblem, upstreamRecord, includeSite) {
321
+ async function writeLiteratureLane(problemId, destination, localProblem, upstreamRecord, includeSite, includePublicSearch) {
193
322
  ensureDir(destination);
194
323
 
195
324
  const includedFiles = [];
@@ -220,6 +349,12 @@ async function writeLiteratureLane(problemId, destination, localProblem, upstrea
220
349
  }
221
350
 
222
351
  const siteStatus = await maybeWriteSiteBundle(problemId, destination, includeSite);
352
+ const publicSearch = await maybeWritePublicSearchBundle(
353
+ problemId,
354
+ localProblem?.title ?? upstreamRecord?.title ?? `Erdos Problem #${problemId}`,
355
+ destination,
356
+ includePublicSearch,
357
+ );
223
358
  const problemRecord = buildProblemRecord(problemId, localProblem, upstreamRecord);
224
359
  writeJson(path.join(destination, 'PROBLEM.json'), problemRecord);
225
360
  writeJson(path.join(destination, 'LITERATURE_INDEX.json'), {
@@ -229,6 +364,10 @@ async function writeLiteratureLane(problemId, destination, localProblem, upstrea
229
364
  includedUpstreamRecord: Boolean(upstreamRecord),
230
365
  includedSiteSnapshot: siteStatus.included,
231
366
  siteSnapshotError: siteStatus.error,
367
+ siteStatus: siteStatus.siteStatus,
368
+ includedPublicSearch: publicSearch.included,
369
+ publicSearchError: publicSearch.error,
370
+ publicSearchQueries: publicSearch.queries,
232
371
  });
233
372
  writeText(
234
373
  path.join(destination, 'README.md'),
@@ -240,6 +379,7 @@ async function writeLiteratureLane(problemId, destination, localProblem, upstrea
240
379
  `- Local dossier included: ${localProblem ? 'yes' : 'no'}`,
241
380
  `- Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`,
242
381
  `- Live site snapshot included: ${siteStatus.included ? 'yes' : 'no'}`,
382
+ `- Public search review included: ${publicSearch.included ? 'yes' : 'no'}`,
243
383
  '',
244
384
  ].join('\n'),
245
385
  );
@@ -248,6 +388,7 @@ async function writeLiteratureLane(problemId, destination, localProblem, upstrea
248
388
  destination,
249
389
  includedFiles,
250
390
  siteStatus,
391
+ publicSearch,
251
392
  };
252
393
  }
253
394
 
@@ -292,9 +433,9 @@ export async function runPullCommand(args, options = {}) {
292
433
  if (args.length === 0 || args[0] === 'help' || args[0] === '--help') {
293
434
  if (!silent) {
294
435
  console.log('Usage:');
295
- console.log(' erdos pull problem <id> [--dest <path>] [--include-site] [--refresh-upstream]');
436
+ console.log(' erdos pull problem <id> [--dest <path>] [--include-site] [--include-public-search] [--refresh-upstream]');
296
437
  console.log(' erdos pull artifacts <id> [--dest <path>] [--refresh-upstream]');
297
- console.log(' erdos pull literature <id> [--dest <path>] [--include-site] [--refresh-upstream]');
438
+ console.log(' erdos pull literature <id> [--dest <path>] [--include-site] [--include-public-search] [--refresh-upstream]');
298
439
  }
299
440
  return 0;
300
441
  }
@@ -346,15 +487,26 @@ export async function runPullCommand(args, options = {}) {
346
487
  const destination = parsed.destination
347
488
  ? path.resolve(parsed.destination)
348
489
  : getWorkspaceProblemLiteratureDir(parsed.problemId);
349
- const result = await writeLiteratureLane(String(parsed.problemId), destination, localProblem, upstreamRecord, parsed.includeSite);
490
+ const result = await writeLiteratureLane(
491
+ String(parsed.problemId),
492
+ destination,
493
+ localProblem,
494
+ upstreamRecord,
495
+ parsed.includeSite,
496
+ parsed.includePublicSearch,
497
+ );
350
498
  if (!silent) {
351
499
  console.log(`Literature bundle created: ${destination}`);
352
500
  console.log(`Local dossier context included: ${localProblem ? 'yes' : 'no'}`);
353
501
  console.log(`Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`);
354
502
  console.log(`Live site snapshot included: ${result.siteStatus.included ? 'yes' : 'no'}`);
503
+ console.log(`Public search review included: ${result.publicSearch.included ? 'yes' : 'no'}`);
355
504
  if (result.siteStatus.error) {
356
505
  console.log(`Live site snapshot note: ${result.siteStatus.error}`);
357
506
  }
507
+ if (result.publicSearch.error) {
508
+ console.log(`Public search note: ${result.publicSearch.error}`);
509
+ }
358
510
  }
359
511
  return 0;
360
512
  }
@@ -367,7 +519,14 @@ export async function runPullCommand(args, options = {}) {
367
519
 
368
520
  writeRootProblemBundle(rootDestination, String(parsed.problemId), localProblem, upstreamRecord, snapshot, artifactDestination, literatureDestination);
369
521
  const artifactResult = writeArtifactsLane(String(parsed.problemId), artifactDestination, localProblem, upstreamRecord, snapshot);
370
- const literatureResult = await writeLiteratureLane(String(parsed.problemId), literatureDestination, localProblem, upstreamRecord, parsed.includeSite);
522
+ const literatureResult = await writeLiteratureLane(
523
+ String(parsed.problemId),
524
+ literatureDestination,
525
+ localProblem,
526
+ upstreamRecord,
527
+ parsed.includeSite,
528
+ parsed.includePublicSearch,
529
+ );
371
530
 
372
531
  writeJson(path.join(rootDestination, 'PULL_STATUS.json'), {
373
532
  generatedAt: new Date().toISOString(),
@@ -382,6 +541,10 @@ export async function runPullCommand(args, options = {}) {
382
541
  siteSnapshotAttempted: literatureResult.siteStatus.attempted,
383
542
  siteSnapshotIncluded: literatureResult.siteStatus.included,
384
543
  siteSnapshotError: literatureResult.siteStatus.error,
544
+ siteStatus: literatureResult.siteStatus.siteStatus,
545
+ publicSearchAttempted: literatureResult.publicSearch.attempted,
546
+ publicSearchIncluded: literatureResult.publicSearch.included,
547
+ publicSearchError: literatureResult.publicSearch.error,
385
548
  });
386
549
 
387
550
  if (!silent) {
@@ -391,9 +554,13 @@ export async function runPullCommand(args, options = {}) {
391
554
  console.log(`Local canonical dossier included: ${localProblem ? 'yes' : 'no'}`);
392
555
  console.log(`Upstream record included: ${upstreamRecord ? 'yes' : 'no'}`);
393
556
  console.log(`Live site snapshot included: ${literatureResult.siteStatus.included ? 'yes' : 'no'}`);
557
+ console.log(`Public search review included: ${literatureResult.publicSearch.included ? 'yes' : 'no'}`);
394
558
  if (literatureResult.siteStatus.error) {
395
559
  console.log(`Live site snapshot note: ${literatureResult.siteStatus.error}`);
396
560
  }
561
+ if (literatureResult.publicSearch.error) {
562
+ console.log(`Public search note: ${literatureResult.publicSearch.error}`);
563
+ }
397
564
  }
398
565
  return 0;
399
566
  }
@@ -16,7 +16,8 @@ function parseSeedArgs(args) {
16
16
 
17
17
  const parsed = {
18
18
  problemId: value,
19
- includeSite: false,
19
+ includeSite: true,
20
+ includePublicSearch: true,
20
21
  refreshUpstream: false,
21
22
  cluster: null,
22
23
  repoStatus: 'local_seeded',
@@ -28,6 +29,7 @@ function parseSeedArgs(args) {
28
29
  activeRoute: null,
29
30
  routeBreakthrough: false,
30
31
  problemSolved: false,
32
+ allowNonOpen: false,
31
33
  destRoot: null,
32
34
  noActivate: false,
33
35
  noLoopSync: false,
@@ -41,6 +43,18 @@ function parseSeedArgs(args) {
41
43
  parsed.includeSite = true;
42
44
  continue;
43
45
  }
46
+ if (token === '--no-site') {
47
+ parsed.includeSite = false;
48
+ continue;
49
+ }
50
+ if (token === '--include-public-search') {
51
+ parsed.includePublicSearch = true;
52
+ continue;
53
+ }
54
+ if (token === '--no-public-search') {
55
+ parsed.includePublicSearch = false;
56
+ continue;
57
+ }
44
58
  if (token === '--refresh-upstream') {
45
59
  parsed.refreshUpstream = true;
46
60
  continue;
@@ -119,6 +133,10 @@ function parseSeedArgs(args) {
119
133
  parsed.problemSolved = true;
120
134
  continue;
121
135
  }
136
+ if (token === '--allow-non-open') {
137
+ parsed.allowNonOpen = true;
138
+ continue;
139
+ }
122
140
  if (token === '--dest-root') {
123
141
  parsed.destRoot = rest[index + 1];
124
142
  if (!parsed.destRoot) {
@@ -152,7 +170,7 @@ function parseSeedArgs(args) {
152
170
  export async function runSeedCommand(args) {
153
171
  if (args.length === 0 || args[0] === 'help' || args[0] === '--help') {
154
172
  console.log('Usage:');
155
- console.log(' erdos seed problem <id> [--include-site] [--refresh-upstream] [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--title <title>] [--family-tag <tag>] [--related <id>] [--formalization-status <status>] [--active-route <route>] [--route-breakthrough] [--problem-solved] [--dest-root <path>] [--no-activate] [--no-loop-sync] [--force] [--json]');
173
+ console.log(' erdos seed problem <id> [--include-site|--no-site] [--include-public-search|--no-public-search] [--refresh-upstream] [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--title <title>] [--family-tag <tag>] [--related <id>] [--formalization-status <status>] [--active-route <route>] [--route-breakthrough] [--problem-solved] [--allow-non-open] [--dest-root <path>] [--no-activate] [--no-loop-sync] [--force] [--json]');
156
174
  return 0;
157
175
  }
158
176
 
@@ -179,6 +197,9 @@ export async function runSeedCommand(args) {
179
197
  if (parsed.includeSite) {
180
198
  pullArgs.push('--include-site');
181
199
  }
200
+ if (parsed.includePublicSearch) {
201
+ pullArgs.push('--include-public-search');
202
+ }
182
203
  if (parsed.refreshUpstream) {
183
204
  pullArgs.push('--refresh-upstream');
184
205
  }
@@ -202,6 +223,7 @@ export async function runSeedCommand(args) {
202
223
  activeRoute: parsed.activeRoute ?? (parsed.problemSolved ? null : 'seed_route_pending'),
203
224
  routeBreakthrough: parsed.routeBreakthrough,
204
225
  problemSolved: parsed.problemSolved,
226
+ allowNonOpen: parsed.allowNonOpen,
205
227
  force: parsed.force,
206
228
  });
207
229
 
@@ -233,6 +255,7 @@ export async function runSeedCommand(args) {
233
255
  checkpointShelf: checkpoints?.indexPath ?? null,
234
256
  usedUpstreamRecord: result.usedUpstreamRecord,
235
257
  usedSiteSnapshot: result.usedSiteSnapshot,
258
+ usedPublicStatusReview: result.usedPublicStatusReview,
236
259
  workspaceOverlayVisible: seedsIntoWorkspaceOverlay,
237
260
  orpProtocol: orp.protocolPath,
238
261
  };
@@ -250,6 +273,7 @@ export async function runSeedCommand(args) {
250
273
  console.log(`Harness depth: ${result.record.harness.depth}`);
251
274
  console.log(`Upstream record used: ${result.usedUpstreamRecord ? 'yes' : 'no'}`);
252
275
  console.log(`Site snapshot used: ${result.usedSiteSnapshot ? 'yes' : 'no'}`);
276
+ console.log(`Public status review used: ${result.usedPublicStatusReview ? 'yes' : 'no'}`);
253
277
  console.log(`ORP protocol: ${orp.protocolPath}`);
254
278
  console.log(`Workspace overlay visible: ${seedsIntoWorkspaceOverlay ? 'yes' : 'no'}`);
255
279
  console.log(`Activated: ${activated ? 'yes' : 'no'}`);
@@ -57,6 +57,8 @@ function printSunflowerStatus(snapshot, registryPaths) {
57
57
  console.log(`Compute lane present: ${snapshot.computeLanePresent ? 'yes' : 'no'}`);
58
58
  console.log(`Compute lane count: ${snapshot.computeLaneCount}`);
59
59
  console.log(`Compute summary: ${snapshot.computeSummary}`);
60
+ console.log(`Compute reason: ${snapshot.computeReason ?? '(none)'}`);
61
+ console.log(`Compute when: ${snapshot.computeWhen}`);
60
62
  console.log(`Compute next: ${snapshot.computeNextAction}`);
61
63
  if (snapshot.activePacket) {
62
64
  console.log(`Compute lane: ${snapshot.activePacket.laneId} [${snapshot.activePacket.status}]`);
@@ -66,6 +68,11 @@ function printSunflowerStatus(snapshot, registryPaths) {
66
68
  console.log(`Price checked: ${snapshot.activePacket.priceCheckedLocalDate || '(unknown)'}`);
67
69
  console.log(`Packet file: ${snapshot.activePacket.packetFileName}`);
68
70
  }
71
+ if (snapshot.computeGovernance) {
72
+ console.log(`Breakthroughs engine: ${snapshot.computeGovernance.engine}`);
73
+ console.log(`Dispatch action: ${snapshot.computeGovernance.dispatchResult.action}`);
74
+ console.log(`Dispatch rung: ${snapshot.computeGovernance.selectedRung.label} [${snapshot.computeGovernance.selectedRung.spendClass}]`);
75
+ }
69
76
  console.log(`Registry record: ${registryPaths.latestPath}`);
70
77
  }
71
78
 
@@ -0,0 +1,197 @@
1
+ import {
2
+ buildOrpComputeGateResult,
3
+ buildOrpComputePacket,
4
+ defineComputePacket,
5
+ defineDecision,
6
+ definePolicy,
7
+ defineRung,
8
+ evaluateDispatch,
9
+ } from 'breakthroughs';
10
+
11
+ function normalizeRungId(value, fallback) {
12
+ const text = String(value ?? '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
13
+ return text || fallback;
14
+ }
15
+
16
+ function parseSpendClass(rawRung) {
17
+ const raw = String(rawRung?.role ?? rawRung?.mode ?? '').trim().toLowerCase();
18
+ if (raw === 'local_unmetered' || raw === 'local') {
19
+ return 'local_unmetered';
20
+ }
21
+ return 'paid_metered';
22
+ }
23
+
24
+ function buildRungs(packet) {
25
+ const rawRungs = Array.isArray(packet?.rungs) ? packet.rungs : [];
26
+ const rungs = rawRungs.map((rawRung, index) => {
27
+ const label = String(rawRung?.name ?? rawRung?.label ?? `rung_${index + 1}`).trim();
28
+ return defineRung({
29
+ id: normalizeRungId(label, `rung-${index + 1}`),
30
+ label,
31
+ spendClass: parseSpendClass(rawRung),
32
+ admitted: true,
33
+ metadata: {
34
+ note: String(rawRung?.note ?? rawRung?.goal ?? '').trim(),
35
+ },
36
+ });
37
+ });
38
+
39
+ if (rungs.length > 0) {
40
+ return rungs;
41
+ }
42
+
43
+ return [
44
+ defineRung({
45
+ id: 'local-scout',
46
+ label: 'local_scout',
47
+ spendClass: 'local_unmetered',
48
+ admitted: true,
49
+ }),
50
+ ];
51
+ }
52
+
53
+ function selectRung(packet, rungs) {
54
+ const localRung = rungs.find((rung) => rung.spendClass === 'local_unmetered') ?? rungs[0];
55
+ const paidRung = rungs.find((rung) => rung.spendClass === 'paid_metered') ?? rungs[0];
56
+ const status = String(packet?.status ?? '').trim().toLowerCase();
57
+
58
+ if (status === 'ready_for_paid_transfer' || status === 'paid_active') {
59
+ return paidRung;
60
+ }
61
+ return localRung;
62
+ }
63
+
64
+ function buildPolicy(packet, selectedRung) {
65
+ const approvedRungs = [];
66
+ const status = String(packet?.status ?? '').trim().toLowerCase();
67
+ if (
68
+ selectedRung.spendClass === 'paid_metered'
69
+ && (status === 'paid_active' || packet?.approvalRequired === false)
70
+ ) {
71
+ approvedRungs.push(selectedRung.id);
72
+ }
73
+
74
+ return definePolicy({
75
+ local: {
76
+ defaultAction: 'allow',
77
+ },
78
+ paid: {
79
+ defaultAction: 'require_explicit_approval',
80
+ approvedRungs,
81
+ },
82
+ });
83
+ }
84
+
85
+ function buildRequiredOutputs(packet) {
86
+ const base = [
87
+ 'run_manifest.json',
88
+ 'impact_note.md',
89
+ 'traceability_record.json',
90
+ ];
91
+ const canonicalPacket = packet?.sourceRepo?.canonical_packet_path;
92
+ if (canonicalPacket) {
93
+ base.push(canonicalPacket);
94
+ }
95
+ return [...new Set(base)];
96
+ }
97
+
98
+ function describeWhen(dispatchAction, selectedRung) {
99
+ if (!dispatchAction || !selectedRung) {
100
+ return 'No admitted compute rung is currently selected.';
101
+ }
102
+ if (dispatchAction === 'run_local') {
103
+ return `Run now on the local ${selectedRung.label} rung if the packet is still bounded and the local scout artifacts can be captured cleanly.`;
104
+ }
105
+ if (dispatchAction === 'request_paid_approval') {
106
+ return `Do not launch yet; request explicit approval before using the ${selectedRung.label} paid rung.`;
107
+ }
108
+ if (dispatchAction === 'run_paid') {
109
+ return `A paid rung is already admitted for this packet; run only with traceable artifact capture on ${selectedRung.label}.`;
110
+ }
111
+ return 'Hold the packet until the compute policy mismatch is resolved.';
112
+ }
113
+
114
+ export function buildBreakthroughsComputeView(problem, packet) {
115
+ if (!packet) {
116
+ return null;
117
+ }
118
+
119
+ const requiredOutputs = buildRequiredOutputs(packet);
120
+ const decision = defineDecision({
121
+ id: `erdos-${problem.problemId}-${packet.laneId}`,
122
+ question: packet.question,
123
+ requiredOutputs,
124
+ metadata: {
125
+ problemId: problem.problemId,
126
+ cluster: problem.cluster,
127
+ claimLevelGoal: packet.claimLevelGoal,
128
+ },
129
+ });
130
+
131
+ const rungs = buildRungs(packet);
132
+ const selectedRung = selectRung(packet, rungs);
133
+ const policy = buildPolicy(packet, selectedRung);
134
+ const computePacket = defineComputePacket({
135
+ id: packet.laneId,
136
+ decisionId: decision.id,
137
+ rungId: selectedRung.id,
138
+ question: packet.question,
139
+ successBar: {
140
+ claimLevelGoal: packet.claimLevelGoal,
141
+ statusTarget: packet.status,
142
+ },
143
+ stopCondition: 'Hold if the packet stops reducing uncertainty honestly or the required artifact bundle cannot be returned cleanly.',
144
+ requiredOutputs,
145
+ metadata: {
146
+ problemId: problem.problemId,
147
+ sourceRepo: packet.sourceRepo,
148
+ recommendation: packet.recommendation,
149
+ },
150
+ });
151
+ const dispatchResult = evaluateDispatch({
152
+ decision,
153
+ rung: selectedRung,
154
+ policy,
155
+ packet: computePacket,
156
+ });
157
+
158
+ const gate = buildOrpComputeGateResult({
159
+ gateId: `breakthroughs_${packet.laneId}`,
160
+ command: `erdos sunflower status ${problem.problemId}`,
161
+ status: dispatchResult.action === 'hold_packet' ? 'fail' : 'pass',
162
+ exitCode: dispatchResult.action === 'hold_packet' ? 1 : 0,
163
+ durationMs: 0,
164
+ evidencePaths: requiredOutputs,
165
+ evidenceStatus: 'process_only',
166
+ evidenceNote: 'This is a compute-admission record only. Evidence remains in canonical artifact paths.',
167
+ });
168
+
169
+ const orpPacket = buildOrpComputePacket({
170
+ repoRoot: packet.sourceRepo?.canonical_packet_path ?? '',
171
+ decision,
172
+ packet: computePacket,
173
+ dispatchResult: {
174
+ ...dispatchResult,
175
+ runId: `${packet.laneId}-${problem.problemId}`,
176
+ },
177
+ gateResults: [gate],
178
+ boardId: `${problem.cluster}_compute`,
179
+ problemId: problem.problemId,
180
+ stateNote: `Compute policy evaluated for ${packet.laneId} with action ${dispatchResult.action}.`,
181
+ });
182
+
183
+ return {
184
+ engine: 'breakthroughs',
185
+ decisionId: decision.id,
186
+ question: decision.question,
187
+ selectedRung: {
188
+ id: selectedRung.id,
189
+ label: selectedRung.label,
190
+ spendClass: selectedRung.spendClass,
191
+ },
192
+ dispatchResult,
193
+ when: describeWhen(dispatchResult.action, selectedRung),
194
+ requiredOutputs,
195
+ orpPacket,
196
+ };
197
+ }
@@ -35,6 +35,8 @@ const STARTER_LOOP_ARTIFACTS = [
35
35
  'AGENT_START.md',
36
36
  'ROUTES.md',
37
37
  'CHECKPOINT_NOTES.md',
38
+ 'PUBLIC_STATUS_REVIEW.md',
39
+ 'AGENT_WEBSEARCH_BRIEF.md',
38
40
  ];
39
41
 
40
42
  function normalizeTitle(rawTitle, problemId) {
@@ -118,6 +120,9 @@ function loadPullBundle(problemId, fromPullDir) {
118
120
  const literatureSiteExtractText = readOptionalText(path.join(pullDir, 'literature', 'SITE_EXTRACT.txt'));
119
121
  const literatureReferences = readOptionalText(path.join(pullDir, 'literature', 'REFERENCES.md'));
120
122
  const literatureStatement = readOptionalText(path.join(pullDir, 'literature', 'STATEMENT.md'));
123
+ const publicStatusReview = readOptionalJson(path.join(pullDir, 'literature', 'PUBLIC_STATUS_REVIEW.json'));
124
+ const publicStatusReviewMarkdown = readOptionalText(path.join(pullDir, 'literature', 'PUBLIC_STATUS_REVIEW.md'));
125
+ const agentWebsearchBrief = readOptionalText(path.join(pullDir, 'literature', 'AGENT_WEBSEARCH_BRIEF.md'));
121
126
 
122
127
  return {
123
128
  pullDir,
@@ -129,6 +134,9 @@ function loadPullBundle(problemId, fromPullDir) {
129
134
  siteExtractText: literatureSiteExtractText,
130
135
  references: literatureReferences,
131
136
  statement: literatureStatement,
137
+ publicStatusReview,
138
+ publicStatusReviewMarkdown,
139
+ agentWebsearchBrief,
132
140
  };
133
141
  }
134
142
 
@@ -185,7 +193,12 @@ function buildResearchState(options, siteStatus) {
185
193
 
186
194
  function buildProblemRecord(problemId, bundle, options) {
187
195
  const upstreamRecord = bundle.upstreamRecord ?? {};
188
- const siteStatus = String(upstreamRecord.status?.state ?? bundle.problemRecord?.siteStatus ?? 'unknown').trim();
196
+ const siteStatus = String(
197
+ bundle.siteExtract?.siteStatus
198
+ ?? upstreamRecord.status?.state
199
+ ?? bundle.problemRecord?.siteStatus
200
+ ?? 'unknown',
201
+ ).trim();
189
202
  const title = deriveTitle(problemId, bundle, options.title);
190
203
  const shortStatement = deriveShortStatement(problemId, bundle, title);
191
204
  const familyTags = [
@@ -219,6 +232,7 @@ function buildProblemRecord(problemId, bundle, options) {
219
232
  kind: 'pull_bundle',
220
233
  upstream_record_included: Boolean(bundle.upstreamRecord),
221
234
  site_snapshot_included: Boolean(bundle.siteExtract || bundle.siteSummary),
235
+ public_search_review_included: Boolean(bundle.publicStatusReview || bundle.publicStatusReviewMarkdown),
222
236
  },
223
237
  },
224
238
  status: {
@@ -258,6 +272,29 @@ function buildProblemRecord(problemId, bundle, options) {
258
272
  return record;
259
273
  }
260
274
 
275
+ function assertSeedAdmission(record, bundle, options) {
276
+ if (options.allowNonOpen) {
277
+ return;
278
+ }
279
+
280
+ const siteStatus = String(bundle.siteExtract?.siteStatus ?? '').trim().toLowerCase();
281
+ const upstreamStatus = String(bundle.upstreamRecord?.status?.state ?? '').trim().toLowerCase();
282
+ if (!siteStatus) {
283
+ if (upstreamStatus === 'open') {
284
+ return;
285
+ }
286
+ throw new Error(
287
+ `Seed admission failed for problem ${record.problem_id}: live erdosproblems.com status was not captured in the pull bundle and upstream status is not clearly open. Re-run with a live site snapshot or pass --allow-non-open to bypass this gate.`,
288
+ );
289
+ }
290
+
291
+ if (siteStatus !== 'open') {
292
+ throw new Error(
293
+ `Seed admission failed for problem ${record.problem_id}: erdosproblems.com currently reports site status "${siteStatus}", not "open". Pass --allow-non-open to bypass this gate intentionally.`,
294
+ );
295
+ }
296
+ }
297
+
261
298
  function renderStatementMarkdown(problemId, record, bundle) {
262
299
  const previewLines = Array.isArray(bundle.siteExtract?.previewLines)
263
300
  ? bundle.siteExtract.previewLines.filter((line) => String(line ?? '').trim())
@@ -316,6 +353,7 @@ function renderEvidenceMarkdown(problemId, record, bundle) {
316
353
  `- This dossier was seeded for Erdos Problem #${problemId} from a pull bundle.`,
317
354
  `- Upstream record included: ${bundle.upstreamRecord ? 'yes' : 'no'}`,
318
355
  `- Site snapshot included: ${bundle.siteExtract || bundle.siteSummary ? 'yes' : 'no'}`,
356
+ `- Public status review included: ${bundle.publicStatusReview || bundle.publicStatusReviewMarkdown ? 'yes' : 'no'}`,
319
357
  `- Repo status at seed time: ${record.status.repo_status}`,
320
358
  `- Harness depth at seed time: ${record.harness.depth}`,
321
359
  '',
@@ -357,9 +395,11 @@ function renderAgentStartMarkdown(problemId, record) {
357
395
  `- Active route: ${activeRoute}`,
358
396
  `- Repo status: ${record.status.repo_status}`,
359
397
  `- Harness depth: ${record.harness.depth}`,
398
+ `- Site status: ${record.status.site_status}`,
360
399
  '',
361
400
  'First honest move:',
362
401
  `- tighten the local dossier for problem ${problemId} against its pull bundle, references, and upstream provenance before widening claims.`,
402
+ '- read `PUBLIC_STATUS_REVIEW.md` and run the suggested queries in `AGENT_WEBSEARCH_BRIEF.md` before trusting a single public status surface.',
363
403
  '',
364
404
  ].join('\n');
365
405
  }
@@ -397,6 +437,7 @@ function renderCheckpointNotesMarkdown(problemId, record) {
397
437
  '- What changed in the active route since the last honest checkpoint?',
398
438
  '- Which claim level is justified right now: Exact, Verified, Heuristic, or Conjecture?',
399
439
  '- Which upstream/public truth changed, if any?',
440
+ '- What did the public-status review and agent websearch brief surface beyond erdosproblems.com?',
400
441
  '- Which artifact or literature bundle should the next agent read first?',
401
442
  '',
402
443
  ].join('\n');
@@ -424,6 +465,7 @@ export function seedProblemFromPullBundle(problemId, options = {}) {
424
465
  routeBreakthrough: options.routeBreakthrough ?? false,
425
466
  problemSolved: options.problemSolved ?? false,
426
467
  });
468
+ assertSeedAdmission(record, bundle, options);
427
469
 
428
470
  writeText(path.join(destinationDir, 'problem.yaml'), stringify(record));
429
471
  writeText(path.join(destinationDir, 'STATEMENT.md'), renderStatementMarkdown(problemId, record, bundle));
@@ -433,12 +475,36 @@ export function seedProblemFromPullBundle(problemId, options = {}) {
433
475
  writeText(path.join(destinationDir, 'AGENT_START.md'), renderAgentStartMarkdown(problemId, record));
434
476
  writeText(path.join(destinationDir, 'ROUTES.md'), renderRoutesMarkdown(problemId, record));
435
477
  writeText(path.join(destinationDir, 'CHECKPOINT_NOTES.md'), renderCheckpointNotesMarkdown(problemId, record));
478
+ writeText(
479
+ path.join(destinationDir, 'PUBLIC_STATUS_REVIEW.md'),
480
+ bundle.publicStatusReviewMarkdown
481
+ ?? [
482
+ '# Public Status Review',
483
+ '',
484
+ '- No live public search review markdown was present in the pull bundle.',
485
+ '- Re-run the pull with a public search lane before widening any public-status claim.',
486
+ '',
487
+ ].join('\n'),
488
+ );
489
+ writeText(
490
+ path.join(destinationDir, 'AGENT_WEBSEARCH_BRIEF.md'),
491
+ bundle.agentWebsearchBrief
492
+ ?? [
493
+ '# Agent Websearch Brief',
494
+ '',
495
+ `- Problem: ${problemId}`,
496
+ `- Site status at seed time: ${record.status.site_status}`,
497
+ '- Run a fresh web search before treating the public status surface as settled.',
498
+ '',
499
+ ].join('\n'),
500
+ );
436
501
 
437
502
  return {
438
503
  destinationDir,
439
504
  record,
440
505
  usedSiteSnapshot: Boolean(bundle.siteExtract || bundle.siteSummary),
441
506
  usedUpstreamRecord: Boolean(bundle.upstreamRecord),
507
+ usedPublicStatusReview: Boolean(bundle.publicStatusReview || bundle.publicStatusReviewMarkdown),
442
508
  starterLoopArtifacts: STARTER_LOOP_ARTIFACTS,
443
509
  };
444
510
  }
@@ -17,6 +17,8 @@ const STARTER_LOOP_FILES = [
17
17
  ['AGENT_START.md', 'AGENT_START.md'],
18
18
  ['ROUTES.md', 'ROUTES.md'],
19
19
  ['CHECKPOINT_NOTES.md', 'CHECKPOINT_NOTES.md'],
20
+ ['PUBLIC_STATUS_REVIEW.md', 'PUBLIC_STATUS_REVIEW.md'],
21
+ ['AGENT_WEBSEARCH_BRIEF.md', 'AGENT_WEBSEARCH_BRIEF.md'],
20
22
  ];
21
23
 
22
24
  function getPackContextPath(problem) {
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { parse } from 'yaml';
4
4
  import { writeJson } from './files.js';
5
+ import { buildBreakthroughsComputeView } from './breakthroughs.js';
5
6
  import { getPackDir, getPackProblemDir, getWorkspaceComputeRegistryDir } from './paths.js';
6
7
 
7
8
  const CLAIM_LEVEL_PRIORITY = {
@@ -313,6 +314,7 @@ export function buildSunflowerStatusSnapshot(problem) {
313
314
  const packets = listSunflowerComputePackets(problem.problemId);
314
315
  const activePacket = chooseActivePacket(packets);
315
316
  const summary = deriveSummary(activePacket);
317
+ const computeGovernance = buildBreakthroughsComputeView(problem, activePacket);
316
318
  const routeState = deriveRouteState(problem, context);
317
319
  const agentStartPath = getSunflowerAgentStartPath(problem.problemId);
318
320
  const checkpointPacketPath = getSunflowerCheckpointPacketPath(problem.problemId);
@@ -356,6 +358,9 @@ export function buildSunflowerStatusSnapshot(problem) {
356
358
  computeSummary: summary.computeSummary,
357
359
  computeNextAction: summary.computeNextAction,
358
360
  budgetState: summary.budgetState,
361
+ computeReason: activePacket?.question ?? null,
362
+ computeWhen: computeGovernance?.when ?? 'No compute packet is currently admitted.',
363
+ computeGovernance,
359
364
  activePacket: compactPacket(activePacket),
360
365
  computePackets: packets.map((packet) => compactPacket(packet)),
361
366
  };
@@ -0,0 +1,139 @@
1
+ const DDG_HTML_URL = 'https://html.duckduckgo.com/html/';
2
+
3
+ function decodeEntities(text) {
4
+ return String(text ?? '')
5
+ .replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16)))
6
+ .replace(/&#(\d+);/g, (_, decimal) => String.fromCodePoint(Number.parseInt(decimal, 10)))
7
+ .replace(/&nbsp;/g, ' ')
8
+ .replace(/&amp;/g, '&')
9
+ .replace(/&quot;/g, '"')
10
+ .replace(/&#39;/g, "'")
11
+ .replace(/&lt;/g, '<')
12
+ .replace(/&gt;/g, '>');
13
+ }
14
+
15
+ function stripTags(text) {
16
+ return decodeEntities(String(text ?? '').replace(/<[^>]+>/g, ' ')).replace(/\s+/g, ' ').trim();
17
+ }
18
+
19
+ function uniqueStrings(values) {
20
+ return [...new Set(values.map((value) => String(value ?? '').trim()).filter(Boolean))];
21
+ }
22
+
23
+ function resolveDuckDuckGoUrl(rawUrl) {
24
+ const value = String(rawUrl ?? '').trim();
25
+ if (!value) {
26
+ return null;
27
+ }
28
+ if (value.startsWith('//')) {
29
+ return `https:${value}`;
30
+ }
31
+ if (value.startsWith('/l/?')) {
32
+ const params = new URLSearchParams(value.slice(4));
33
+ return params.get('uddg') ?? null;
34
+ }
35
+ return value;
36
+ }
37
+
38
+ function extractResultSnippet(resultHtml) {
39
+ const snippetMatch = resultHtml.match(/<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/i)
40
+ ?? resultHtml.match(/<div[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/div>/i);
41
+ return stripTags(snippetMatch?.[1] ?? '');
42
+ }
43
+
44
+ function parseDuckDuckGoResults(html, query) {
45
+ const blockPattern = /<div[^>]*class="result(?:\s|")([\s\S]*?)<\/div>\s*<\/div>/gi;
46
+ const results = [];
47
+ let match = blockPattern.exec(html);
48
+ while (match) {
49
+ const block = match[0];
50
+ const anchor = block.match(/<a[^>]*class="result__a"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i);
51
+ if (anchor) {
52
+ const url = resolveDuckDuckGoUrl(anchor[1]);
53
+ const title = stripTags(anchor[2]);
54
+ if (url && title) {
55
+ results.push({
56
+ query,
57
+ title,
58
+ url,
59
+ snippet: extractResultSnippet(block),
60
+ });
61
+ }
62
+ }
63
+ match = blockPattern.exec(html);
64
+ }
65
+ return results;
66
+ }
67
+
68
+ export function buildProblemSearchQueries(problemId, title) {
69
+ const cleanTitle = String(title ?? '').trim();
70
+ return uniqueStrings([
71
+ `"Erdos Problem #${problemId}"`,
72
+ `erdos problem ${problemId}`,
73
+ cleanTitle ? `"${cleanTitle}"` : null,
74
+ cleanTitle ? `"${cleanTitle}" erdos` : null,
75
+ cleanTitle ? `"${cleanTitle}" sunflower` : null,
76
+ ]);
77
+ }
78
+
79
+ async function fetchDuckDuckGoQuery(query) {
80
+ const url = `${DDG_HTML_URL}?q=${encodeURIComponent(query)}`;
81
+ const response = await fetch(url, {
82
+ headers: {
83
+ 'User-Agent': 'erdos-problems-cli',
84
+ Accept: 'text/html',
85
+ },
86
+ });
87
+
88
+ if (!response.ok) {
89
+ throw new Error(`DuckDuckGo search failed for "${query}": ${response.status}`);
90
+ }
91
+
92
+ const html = await response.text();
93
+ return {
94
+ provider: 'duckduckgo',
95
+ query,
96
+ url,
97
+ results: parseDuckDuckGoResults(html, query).slice(0, 5),
98
+ };
99
+ }
100
+
101
+ export async function fetchProblemPublicSearchReview(problemId, title) {
102
+ const queries = buildProblemSearchQueries(problemId, title).slice(0, 3);
103
+ const searchRuns = [];
104
+ const errors = [];
105
+
106
+ for (const query of queries) {
107
+ try {
108
+ searchRuns.push(await fetchDuckDuckGoQuery(query));
109
+ } catch (error) {
110
+ errors.push({
111
+ query,
112
+ error: String(error?.message ?? error),
113
+ });
114
+ }
115
+ }
116
+
117
+ const combinedResults = [];
118
+ const seenUrls = new Set();
119
+ for (const run of searchRuns) {
120
+ for (const result of run.results) {
121
+ if (seenUrls.has(result.url)) {
122
+ continue;
123
+ }
124
+ seenUrls.add(result.url);
125
+ combinedResults.push(result);
126
+ }
127
+ }
128
+
129
+ return {
130
+ fetchedAt: new Date().toISOString(),
131
+ problemId: String(problemId),
132
+ title: String(title ?? '').trim() || `Erdos Problem #${problemId}`,
133
+ provider: 'duckduckgo',
134
+ queries,
135
+ searchRuns,
136
+ combinedResults: combinedResults.slice(0, 10),
137
+ errors,
138
+ };
139
+ }
@@ -1,5 +1,16 @@
1
1
  const SITE_BASE_URL = 'https://www.erdosproblems.com';
2
2
 
3
+ const SITE_STATUS_MAP = {
4
+ OPEN: 'open',
5
+ SOLVED: 'solved',
6
+ PROVED: 'proved',
7
+ 'PROVED (LEAN)': 'proved',
8
+ DISPROVED: 'disproved',
9
+ PARTIAL: 'partial',
10
+ DECIDABLE: 'decidable',
11
+ VERIFIABLE: 'verifiable',
12
+ };
13
+
3
14
  function decodeEntities(text) {
4
15
  return text
5
16
  .replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16)))
@@ -51,6 +62,33 @@ function selectPreviewLines(lines) {
51
62
  return lines.slice(0, 24);
52
63
  }
53
64
 
65
+ export function normalizeSiteStatus(value) {
66
+ const raw = String(value ?? '').trim().toUpperCase();
67
+ return SITE_STATUS_MAP[raw] ?? (raw.toLowerCase() || 'unknown');
68
+ }
69
+
70
+ export function extractProblemPageStatus(lines) {
71
+ const statusLine = (Array.isArray(lines) ? lines : [])
72
+ .map((line) => String(line ?? '').trim())
73
+ .find((line) => /^(OPEN|SOLVED|PROVED|PARTIAL|DISPROVED|DECIDABLE|VERIFIABLE)\b/i.test(line));
74
+
75
+ if (!statusLine) {
76
+ return {
77
+ raw: null,
78
+ normalized: 'unknown',
79
+ };
80
+ }
81
+
82
+ const raw = statusLine
83
+ .replace(/\s+\$\d[\d,]*(?:\s*-\s*\$\d[\d,]*)?.*$/i, '')
84
+ .trim();
85
+
86
+ return {
87
+ raw,
88
+ normalized: normalizeSiteStatus(raw),
89
+ };
90
+ }
91
+
54
92
  export async function fetchProblemSiteSnapshot(problemId) {
55
93
  const url = `${SITE_BASE_URL}/${problemId}`;
56
94
  const response = await fetch(url, {
@@ -68,6 +106,7 @@ export async function fetchProblemSiteSnapshot(problemId) {
68
106
  const text = htmlToReadableText(html);
69
107
  const title = extractTitle(html, problemId);
70
108
  const lines = text.split('\n').filter(Boolean);
109
+ const status = extractProblemPageStatus(lines);
71
110
 
72
111
  return {
73
112
  url,
@@ -75,6 +114,8 @@ export async function fetchProblemSiteSnapshot(problemId) {
75
114
  html,
76
115
  title,
77
116
  text,
117
+ siteStatus: status.normalized,
118
+ siteStatusRaw: status.raw,
78
119
  previewLines: selectPreviewLines(lines),
79
120
  };
80
121
  }