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 +17 -3
- package/docs/RESEARCH_LOOP.md +11 -3
- package/package.json +2 -1
- package/src/cli/index.js +4 -4
- package/src/commands/maintainer.js +8 -1
- package/src/commands/pull.js +174 -7
- package/src/commands/seed.js +26 -2
- package/src/commands/sunflower.js +7 -0
- package/src/runtime/breakthroughs.js +197 -0
- package/src/runtime/maintainer-seed.js +67 -1
- package/src/runtime/problem-artifacts.js +2 -0
- package/src/runtime/sunflower.js +5 -0
- package/src/upstream/public-search.js +139 -0
- package/src/upstream/site.js +41 -0
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 --
|
|
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 --
|
|
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
|
|
package/docs/RESEARCH_LOOP.md
CHANGED
|
@@ -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.
|
|
30
|
-
9.
|
|
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 --
|
|
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.
|
|
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));
|
package/src/commands/pull.js
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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(
|
|
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(
|
|
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
|
}
|
package/src/commands/seed.js
CHANGED
|
@@ -16,7 +16,8 @@ function parseSeedArgs(args) {
|
|
|
16
16
|
|
|
17
17
|
const parsed = {
|
|
18
18
|
problemId: value,
|
|
19
|
-
includeSite:
|
|
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(
|
|
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) {
|
package/src/runtime/sunflower.js
CHANGED
|
@@ -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(/ /g, ' ')
|
|
8
|
+
.replace(/&/g, '&')
|
|
9
|
+
.replace(/"/g, '"')
|
|
10
|
+
.replace(/'/g, "'")
|
|
11
|
+
.replace(/</g, '<')
|
|
12
|
+
.replace(/>/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
|
+
}
|
package/src/upstream/site.js
CHANGED
|
@@ -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
|
}
|