erdos-problems 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/docs/RESEARCH_LOOP.md +47 -0
- package/package.json +1 -1
- package/packs/sunflower/compute/20/u3_uniform_transfer_window_v0.yaml +16 -0
- package/packs/sunflower/problems/20/CONTEXT.md +5 -0
- package/packs/sunflower/problems/20/context.yaml +13 -0
- package/packs/sunflower/problems/857/CONTEXT.md +5 -0
- package/packs/sunflower/problems/857/context.yaml +14 -0
- package/src/cli/index.js +18 -0
- package/src/commands/bootstrap.js +7 -0
- package/src/commands/checkpoints.js +36 -0
- package/src/commands/continuation.js +60 -0
- package/src/commands/preflight.js +44 -0
- package/src/commands/problem.js +4 -0
- package/src/commands/state.js +57 -0
- package/src/commands/sunflower.js +5 -0
- package/src/commands/workspace.js +18 -0
- package/src/runtime/checkpoints.js +208 -0
- package/src/runtime/config.js +37 -0
- package/src/runtime/continuation.js +65 -0
- package/src/runtime/git.js +52 -0
- package/src/runtime/paths.js +67 -31
- package/src/runtime/preflight.js +106 -0
- package/src/runtime/state.js +269 -0
- package/src/runtime/sunflower.js +40 -1
- package/src/runtime/workspace.js +40 -24
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { loadLocalProblems, getProblem } from '../atlas/catalog.js';
|
|
3
|
+
import { loadConfig } from './config.js';
|
|
4
|
+
import { ensureDir, writeJson, writeText } from './files.js';
|
|
5
|
+
import {
|
|
6
|
+
getWorkspaceCheckpointIndexPath,
|
|
7
|
+
getWorkspaceCheckpointJsonPath,
|
|
8
|
+
getWorkspaceCheckpointsDir,
|
|
9
|
+
getWorkspaceProblemCheckpointsDir,
|
|
10
|
+
getWorkspaceRoot,
|
|
11
|
+
getWorkspaceRouteCheckpointsDir,
|
|
12
|
+
} from './paths.js';
|
|
13
|
+
import { getProblemArtifactInventory } from './problem-artifacts.js';
|
|
14
|
+
import { buildSunflowerStatusSnapshot } from './sunflower.js';
|
|
15
|
+
import { loadState, saveState, syncState } from './state.js';
|
|
16
|
+
|
|
17
|
+
function safeName(value) {
|
|
18
|
+
return String(value).replace(/[^a-zA-Z0-9_-]+/g, '-');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function renderCanonicalArtifacts(inventory) {
|
|
22
|
+
return inventory.canonicalArtifacts
|
|
23
|
+
.map((artifact) => `- ${artifact.label}: ${artifact.exists ? 'present' : 'missing'} (${artifact.path})`)
|
|
24
|
+
.join('\n');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function renderProblemCheckpoint(problem, state) {
|
|
28
|
+
const inventory = getProblemArtifactInventory(problem);
|
|
29
|
+
const sunflower = problem.cluster === 'sunflower' ? buildSunflowerStatusSnapshot(problem) : null;
|
|
30
|
+
const research = problem.researchState ?? {};
|
|
31
|
+
const activeRoute = sunflower?.activeRoute ?? research.active_route ?? '(none)';
|
|
32
|
+
const routeBreakthrough = sunflower?.routeBreakthrough ?? Boolean(research.route_breakthrough);
|
|
33
|
+
const problemSolved = sunflower?.problemSolved ?? Boolean(research.problem_solved);
|
|
34
|
+
const nextHonestMove = sunflower?.nextHonestMove
|
|
35
|
+
?? (activeRoute !== '(none)' ? `Advance ${activeRoute} against the dossier and evidence bundle.` : 'Seed or choose an active route.');
|
|
36
|
+
const frontier = sunflower?.frontierDetail ?? problem.shortStatement;
|
|
37
|
+
const checkpointFocus = sunflower?.checkpointFocus ?? 'Keep local route claims and upstream public status cleanly separated.';
|
|
38
|
+
|
|
39
|
+
return `# Problem ${problem.problemId} Checkpoint
|
|
40
|
+
|
|
41
|
+
## Status Ladder
|
|
42
|
+
|
|
43
|
+
- Open Problem: ${problem.problemId}
|
|
44
|
+
- Active Route: ${activeRoute}
|
|
45
|
+
- Route Breakthrough: ${routeBreakthrough ? 'yes' : 'no'}
|
|
46
|
+
- Problem Solved: ${problemSolved ? 'yes' : 'no'}
|
|
47
|
+
|
|
48
|
+
## Problem Record
|
|
49
|
+
|
|
50
|
+
- Title: ${problem.title}
|
|
51
|
+
- Cluster: ${problem.cluster}
|
|
52
|
+
- Source: ${problem.sourceUrl}
|
|
53
|
+
- Site status: ${problem.siteStatus}
|
|
54
|
+
- Repo status: ${problem.repoStatus}
|
|
55
|
+
- Harness depth: ${problem.harnessDepth}
|
|
56
|
+
|
|
57
|
+
## Current Frontier
|
|
58
|
+
|
|
59
|
+
- ${frontier}
|
|
60
|
+
|
|
61
|
+
## Checkpoint Focus
|
|
62
|
+
|
|
63
|
+
- ${checkpointFocus}
|
|
64
|
+
|
|
65
|
+
## Next Honest Move
|
|
66
|
+
|
|
67
|
+
- ${nextHonestMove}
|
|
68
|
+
|
|
69
|
+
## Canonical Artifacts
|
|
70
|
+
|
|
71
|
+
${renderCanonicalArtifacts(inventory)}
|
|
72
|
+
|
|
73
|
+
## Continuation Frame
|
|
74
|
+
|
|
75
|
+
- Active agent: ${state.activeAgent || '(none)'}
|
|
76
|
+
- Continuation mode: ${state.continuation.mode}
|
|
77
|
+
- Stop rule: ${state.continuation.stopRule}
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function renderRouteCheckpoint(problem, state) {
|
|
82
|
+
const sunflower = problem.cluster === 'sunflower' ? buildSunflowerStatusSnapshot(problem) : null;
|
|
83
|
+
const frontier = sunflower?.frontierDetail ?? state.currentFrontier.detail;
|
|
84
|
+
const routeStory = sunflower?.routeStory ?? state.routeStory ?? '(none yet)';
|
|
85
|
+
const checkpointFocus = sunflower?.checkpointFocus ?? state.checkpointFocus ?? '(none yet)';
|
|
86
|
+
|
|
87
|
+
return `# Problem ${problem.problemId} Active Route Checkpoint
|
|
88
|
+
|
|
89
|
+
## Status Ladder
|
|
90
|
+
|
|
91
|
+
- Open Problem: ${problem.problemId}
|
|
92
|
+
- Active Route: ${state.activeRoute || '(none)'}
|
|
93
|
+
- Route Breakthrough: ${state.routeBreakthrough ? 'yes' : 'no'}
|
|
94
|
+
- Problem Solved: ${state.problemSolved ? 'yes' : 'no'}
|
|
95
|
+
|
|
96
|
+
## Current Frontier
|
|
97
|
+
|
|
98
|
+
- ${frontier}
|
|
99
|
+
|
|
100
|
+
## Route Story
|
|
101
|
+
|
|
102
|
+
- ${routeStory}
|
|
103
|
+
|
|
104
|
+
## Checkpoint Focus
|
|
105
|
+
|
|
106
|
+
- ${checkpointFocus}
|
|
107
|
+
|
|
108
|
+
## Continuation Frame
|
|
109
|
+
|
|
110
|
+
- Continuation mode: ${state.continuation.mode}
|
|
111
|
+
- Stop rule: ${state.continuation.stopRule}
|
|
112
|
+
- Next honest move: ${state.nextHonestMove}
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function renderIndex(state, checkpoints) {
|
|
117
|
+
const problemRows = checkpoints.filter((entry) => entry.kind === 'problem');
|
|
118
|
+
const routeRows = checkpoints.filter((entry) => entry.kind === 'route');
|
|
119
|
+
|
|
120
|
+
return [
|
|
121
|
+
'# Erdos Checkpoints',
|
|
122
|
+
'',
|
|
123
|
+
'This is the human-facing checkpoint shelf for the local research-loop runtime.',
|
|
124
|
+
'Canonical truth still lives in dossiers, pack artifacts, upstream snapshots, and generated workspace bundles.',
|
|
125
|
+
'',
|
|
126
|
+
'## Current Status Ladder',
|
|
127
|
+
'',
|
|
128
|
+
`- Open Problem: ${state.activeProblem || '(none)'}`,
|
|
129
|
+
`- Active Route: ${state.activeRoute || '(none)'}`,
|
|
130
|
+
`- Route Breakthrough: ${state.routeBreakthrough ? 'yes' : 'no'}`,
|
|
131
|
+
`- Problem Solved: ${state.problemSolved ? 'yes' : 'no'}`,
|
|
132
|
+
'',
|
|
133
|
+
'## Working Context',
|
|
134
|
+
'',
|
|
135
|
+
`- Continuation Mode: ${state.continuation.mode}`,
|
|
136
|
+
`- Next Honest Move: ${state.nextHonestMove}`,
|
|
137
|
+
'',
|
|
138
|
+
'## Problem Checkpoints',
|
|
139
|
+
'',
|
|
140
|
+
...(problemRows.length > 0 ? problemRows.map((row) => `- [${row.label}](${row.relativePath})`) : ['- *(none)*']),
|
|
141
|
+
'',
|
|
142
|
+
'## Route Checkpoints',
|
|
143
|
+
'',
|
|
144
|
+
...(routeRows.length > 0 ? routeRows.map((row) => `- [${row.label}](${row.relativePath})`) : ['- *(none)*']),
|
|
145
|
+
'',
|
|
146
|
+
].join('\n');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function syncCheckpoints(workspaceRoot = getWorkspaceRoot()) {
|
|
150
|
+
const config = loadConfig(workspaceRoot);
|
|
151
|
+
const state = syncState(workspaceRoot);
|
|
152
|
+
const problems = loadLocalProblems();
|
|
153
|
+
|
|
154
|
+
ensureDir(getWorkspaceCheckpointsDir(workspaceRoot));
|
|
155
|
+
ensureDir(getWorkspaceProblemCheckpointsDir(workspaceRoot));
|
|
156
|
+
ensureDir(getWorkspaceRouteCheckpointsDir(workspaceRoot));
|
|
157
|
+
|
|
158
|
+
const checkpoints = [];
|
|
159
|
+
|
|
160
|
+
for (const problem of problems) {
|
|
161
|
+
const filename = `problem-${problem.problemId}.md`;
|
|
162
|
+
const fullPath = path.join(getWorkspaceProblemCheckpointsDir(workspaceRoot), filename);
|
|
163
|
+
writeText(fullPath, renderProblemCheckpoint(problem, state));
|
|
164
|
+
checkpoints.push({
|
|
165
|
+
kind: 'problem',
|
|
166
|
+
label: `Problem ${problem.problemId}`,
|
|
167
|
+
path: fullPath,
|
|
168
|
+
relativePath: path.relative(getWorkspaceCheckpointsDir(workspaceRoot), fullPath),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (state.activeProblem && state.activeRoute) {
|
|
173
|
+
const problem = getProblem(state.activeProblem);
|
|
174
|
+
if (problem) {
|
|
175
|
+
const filename = `problem-${state.activeProblem}--${safeName(state.activeRoute)}.md`;
|
|
176
|
+
const fullPath = path.join(getWorkspaceRouteCheckpointsDir(workspaceRoot), filename);
|
|
177
|
+
writeText(fullPath, renderRouteCheckpoint(problem, state));
|
|
178
|
+
checkpoints.push({
|
|
179
|
+
kind: 'route',
|
|
180
|
+
label: `Problem ${state.activeProblem} / ${state.activeRoute}`,
|
|
181
|
+
path: fullPath,
|
|
182
|
+
relativePath: path.relative(getWorkspaceCheckpointsDir(workspaceRoot), fullPath),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
writeText(getWorkspaceCheckpointIndexPath(workspaceRoot), renderIndex(state, checkpoints));
|
|
188
|
+
writeJson(getWorkspaceCheckpointJsonPath(workspaceRoot), {
|
|
189
|
+
syncedAt: new Date().toISOString(),
|
|
190
|
+
activeProblem: state.activeProblem,
|
|
191
|
+
activeRoute: state.activeRoute,
|
|
192
|
+
continuationMode: state.continuation.mode,
|
|
193
|
+
activeAgent: config.preferredAgent,
|
|
194
|
+
checkpoints,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const nextState = saveState({
|
|
198
|
+
...loadState(workspaceRoot),
|
|
199
|
+
lastCheckpointSyncAt: new Date().toISOString(),
|
|
200
|
+
}, workspaceRoot);
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
indexPath: getWorkspaceCheckpointIndexPath(workspaceRoot),
|
|
204
|
+
checkpointJsonPath: getWorkspaceCheckpointJsonPath(workspaceRoot),
|
|
205
|
+
checkpoints,
|
|
206
|
+
state: nextState,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { fileExists, readJson, writeJson } from './files.js';
|
|
2
|
+
import { getWorkspaceConfigPath, getWorkspaceRoot } from './paths.js';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_CONFIG = {
|
|
5
|
+
preferredAgent: 'erdos',
|
|
6
|
+
continuation: 'route',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function defaultConfig() {
|
|
10
|
+
return { ...DEFAULT_CONFIG };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function loadConfig(workspaceRoot = getWorkspaceRoot()) {
|
|
14
|
+
const configPath = getWorkspaceConfigPath(workspaceRoot);
|
|
15
|
+
if (!fileExists(configPath)) {
|
|
16
|
+
return defaultConfig();
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
...DEFAULT_CONFIG,
|
|
20
|
+
...readJson(configPath),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function saveConfig(config, workspaceRoot = getWorkspaceRoot()) {
|
|
25
|
+
const payload = {
|
|
26
|
+
...DEFAULT_CONFIG,
|
|
27
|
+
...config,
|
|
28
|
+
};
|
|
29
|
+
writeJson(getWorkspaceConfigPath(workspaceRoot), payload);
|
|
30
|
+
return payload;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ensureConfig(workspaceRoot = getWorkspaceRoot()) {
|
|
34
|
+
const payload = loadConfig(workspaceRoot);
|
|
35
|
+
saveConfig(payload, workspaceRoot);
|
|
36
|
+
return payload;
|
|
37
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const PRESETS = {
|
|
2
|
+
atom: {
|
|
3
|
+
mode: 'atom',
|
|
4
|
+
reviewCadence: 'tight',
|
|
5
|
+
maxUnattendedMinutes: 25,
|
|
6
|
+
checkpointAfterLoadBearingResult: true,
|
|
7
|
+
stopRule: 'Stop after one load-bearing atomic step or blocker.',
|
|
8
|
+
},
|
|
9
|
+
route: {
|
|
10
|
+
mode: 'route',
|
|
11
|
+
reviewCadence: 'balanced',
|
|
12
|
+
maxUnattendedMinutes: 90,
|
|
13
|
+
checkpointAfterLoadBearingResult: true,
|
|
14
|
+
stopRule: 'Continue until the active route reaches a real boundary or blocker.',
|
|
15
|
+
},
|
|
16
|
+
milestone: {
|
|
17
|
+
mode: 'milestone',
|
|
18
|
+
reviewCadence: 'sparse',
|
|
19
|
+
maxUnattendedMinutes: 180,
|
|
20
|
+
checkpointAfterLoadBearingResult: false,
|
|
21
|
+
stopRule: 'Continue until the problem milestone boundary or blocker.',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const ALIASES = {
|
|
26
|
+
phase: 'route',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function continuationModes() {
|
|
30
|
+
return ['atom', 'route', 'phase', 'milestone'];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function normalizeContinuationMode(mode = 'route') {
|
|
34
|
+
const raw = String(mode || 'route');
|
|
35
|
+
if (PRESETS[raw]) {
|
|
36
|
+
return raw;
|
|
37
|
+
}
|
|
38
|
+
if (ALIASES[raw]) {
|
|
39
|
+
return ALIASES[raw];
|
|
40
|
+
}
|
|
41
|
+
return 'route';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function continuationDisplay(continuation = {}) {
|
|
45
|
+
const requested = continuation.requestedMode || continuation.mode || 'route';
|
|
46
|
+
const normalized = normalizeContinuationMode(requested);
|
|
47
|
+
if (requested === 'phase' && normalized === 'route') {
|
|
48
|
+
return 'route (phase-style)';
|
|
49
|
+
}
|
|
50
|
+
return normalized;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function resolveContinuation(raw = {}) {
|
|
54
|
+
const requested = raw.requestedMode || raw.mode || 'route';
|
|
55
|
+
const mode = normalizeContinuationMode(requested);
|
|
56
|
+
const rest = { ...raw };
|
|
57
|
+
delete rest.mode;
|
|
58
|
+
delete rest.requestedMode;
|
|
59
|
+
return {
|
|
60
|
+
...PRESETS[mode],
|
|
61
|
+
...rest,
|
|
62
|
+
mode,
|
|
63
|
+
requestedMode: requested,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
function runGit(workspaceRoot, args) {
|
|
4
|
+
try {
|
|
5
|
+
return execFileSync('git', ['-C', workspaceRoot, ...args], {
|
|
6
|
+
encoding: 'utf8',
|
|
7
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
8
|
+
}).trim();
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function gitSummary(workspaceRoot) {
|
|
15
|
+
if (!workspaceRoot) {
|
|
16
|
+
return {
|
|
17
|
+
exists: false,
|
|
18
|
+
isGitRepo: false,
|
|
19
|
+
clean: null,
|
|
20
|
+
branch: null,
|
|
21
|
+
detail: 'missing',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const inside = runGit(workspaceRoot, ['rev-parse', '--is-inside-work-tree']);
|
|
26
|
+
if (inside !== 'true') {
|
|
27
|
+
return {
|
|
28
|
+
exists: true,
|
|
29
|
+
isGitRepo: false,
|
|
30
|
+
clean: null,
|
|
31
|
+
branch: null,
|
|
32
|
+
detail: 'not a git repository',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const branch = runGit(workspaceRoot, ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
37
|
+
const porcelain = runGit(workspaceRoot, ['status', '--porcelain']) ?? '';
|
|
38
|
+
const clean = porcelain.trim().length === 0;
|
|
39
|
+
const changedPaths = porcelain
|
|
40
|
+
.split('\n')
|
|
41
|
+
.map((line) => line.trim())
|
|
42
|
+
.filter(Boolean);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
exists: true,
|
|
46
|
+
isGitRepo: true,
|
|
47
|
+
clean,
|
|
48
|
+
branch,
|
|
49
|
+
detail: clean ? 'clean' : `${changedPaths.length} changed path(s)`,
|
|
50
|
+
changedPaths,
|
|
51
|
+
};
|
|
52
|
+
}
|
package/src/runtime/paths.js
CHANGED
|
@@ -8,72 +8,108 @@ export function getWorkspaceRoot() {
|
|
|
8
8
|
return process.cwd();
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function getWorkspaceDir() {
|
|
12
|
-
return path.join(
|
|
11
|
+
export function getWorkspaceDir(workspaceRoot = getWorkspaceRoot()) {
|
|
12
|
+
return path.join(workspaceRoot, '.erdos');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getWorkspaceConfigPath(workspaceRoot = getWorkspaceRoot()) {
|
|
16
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'config.json');
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export function getWorkspaceRegistryDir(workspaceRoot = getWorkspaceRoot()) {
|
|
16
|
-
return path.join(workspaceRoot, '
|
|
20
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'registry');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getWorkspaceRegistryBucketDir(bucket, workspaceRoot = getWorkspaceRoot()) {
|
|
24
|
+
return path.join(getWorkspaceRegistryDir(workspaceRoot), String(bucket));
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
export function getWorkspaceComputeRegistryDir(workspaceRoot = getWorkspaceRoot()) {
|
|
20
28
|
return path.join(getWorkspaceRegistryDir(workspaceRoot), 'compute');
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
export function getWorkspaceStatePath() {
|
|
24
|
-
return path.join(getWorkspaceDir(), 'state.json');
|
|
31
|
+
export function getWorkspaceStatePath(workspaceRoot = getWorkspaceRoot()) {
|
|
32
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'state.json');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getWorkspaceStateMarkdownPath(workspaceRoot = getWorkspaceRoot()) {
|
|
36
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'STATE.md');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getWorkspaceQuestionLedgerPath(workspaceRoot = getWorkspaceRoot()) {
|
|
40
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'QUESTION-LEDGER.md');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getCurrentProblemPath(workspaceRoot = getWorkspaceRoot()) {
|
|
44
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'current-problem.json');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getWorkspaceReportsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
48
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'reports');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getWorkspaceDiffPath(workspaceRoot = getWorkspaceRoot()) {
|
|
52
|
+
return path.join(getWorkspaceReportsDir(workspaceRoot), 'UPSTREAM_DIFF.md');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getWorkspaceUpstreamDir(workspaceRoot = getWorkspaceRoot()) {
|
|
56
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'upstream', 'erdosproblems');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getWorkspaceUpstreamYamlPath(workspaceRoot = getWorkspaceRoot()) {
|
|
60
|
+
return path.join(getWorkspaceUpstreamDir(workspaceRoot), 'problems.yaml');
|
|
25
61
|
}
|
|
26
62
|
|
|
27
|
-
export function
|
|
28
|
-
return path.join(
|
|
63
|
+
export function getWorkspaceUpstreamIndexPath(workspaceRoot = getWorkspaceRoot()) {
|
|
64
|
+
return path.join(getWorkspaceUpstreamDir(workspaceRoot), 'PROBLEMS_INDEX.json');
|
|
29
65
|
}
|
|
30
66
|
|
|
31
|
-
export function
|
|
32
|
-
return path.join(
|
|
67
|
+
export function getWorkspaceUpstreamManifestPath(workspaceRoot = getWorkspaceRoot()) {
|
|
68
|
+
return path.join(getWorkspaceUpstreamDir(workspaceRoot), 'SYNC_MANIFEST.json');
|
|
33
69
|
}
|
|
34
70
|
|
|
35
|
-
export function
|
|
36
|
-
return path.join(
|
|
71
|
+
export function getWorkspaceScaffoldsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
72
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'scaffolds');
|
|
37
73
|
}
|
|
38
74
|
|
|
39
|
-
export function
|
|
40
|
-
return path.join(
|
|
75
|
+
export function getWorkspaceProblemScaffoldDir(problemId, workspaceRoot = getWorkspaceRoot()) {
|
|
76
|
+
return path.join(getWorkspaceScaffoldsDir(workspaceRoot), String(problemId));
|
|
41
77
|
}
|
|
42
78
|
|
|
43
|
-
export function
|
|
44
|
-
return path.join(
|
|
79
|
+
export function getWorkspacePullsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
80
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'pulls');
|
|
45
81
|
}
|
|
46
82
|
|
|
47
|
-
export function
|
|
48
|
-
return path.join(
|
|
83
|
+
export function getWorkspaceProblemPullDir(problemId, workspaceRoot = getWorkspaceRoot()) {
|
|
84
|
+
return path.join(getWorkspacePullsDir(workspaceRoot), String(problemId));
|
|
49
85
|
}
|
|
50
86
|
|
|
51
|
-
export function
|
|
52
|
-
return path.join(
|
|
87
|
+
export function getWorkspaceProblemArtifactDir(problemId, workspaceRoot = getWorkspaceRoot()) {
|
|
88
|
+
return path.join(getWorkspaceProblemPullDir(problemId, workspaceRoot), 'artifacts');
|
|
53
89
|
}
|
|
54
90
|
|
|
55
|
-
export function
|
|
56
|
-
return path.join(
|
|
91
|
+
export function getWorkspaceProblemLiteratureDir(problemId, workspaceRoot = getWorkspaceRoot()) {
|
|
92
|
+
return path.join(getWorkspaceProblemPullDir(problemId, workspaceRoot), 'literature');
|
|
57
93
|
}
|
|
58
94
|
|
|
59
|
-
export function
|
|
60
|
-
return path.join(
|
|
95
|
+
export function getWorkspaceCheckpointsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
96
|
+
return path.join(getWorkspaceDir(workspaceRoot), 'checkpoints');
|
|
61
97
|
}
|
|
62
98
|
|
|
63
|
-
export function
|
|
64
|
-
return path.join(
|
|
99
|
+
export function getWorkspaceProblemCheckpointsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
100
|
+
return path.join(getWorkspaceCheckpointsDir(workspaceRoot), 'problem-checkpoints');
|
|
65
101
|
}
|
|
66
102
|
|
|
67
|
-
export function
|
|
68
|
-
return path.join(
|
|
103
|
+
export function getWorkspaceRouteCheckpointsDir(workspaceRoot = getWorkspaceRoot()) {
|
|
104
|
+
return path.join(getWorkspaceCheckpointsDir(workspaceRoot), 'route-checkpoints');
|
|
69
105
|
}
|
|
70
106
|
|
|
71
|
-
export function
|
|
72
|
-
return path.join(
|
|
107
|
+
export function getWorkspaceCheckpointIndexPath(workspaceRoot = getWorkspaceRoot()) {
|
|
108
|
+
return path.join(getWorkspaceCheckpointsDir(workspaceRoot), 'CHECKPOINTS.md');
|
|
73
109
|
}
|
|
74
110
|
|
|
75
|
-
export function
|
|
76
|
-
return path.join(
|
|
111
|
+
export function getWorkspaceCheckpointJsonPath(workspaceRoot = getWorkspaceRoot()) {
|
|
112
|
+
return path.join(getWorkspaceCheckpointsDir(workspaceRoot), 'CHECKPOINTS.json');
|
|
77
113
|
}
|
|
78
114
|
|
|
79
115
|
export function getProblemDir(problemId) {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getProblem,
|
|
3
|
+
} from '../atlas/catalog.js';
|
|
4
|
+
import { ensureConfig, loadConfig } from './config.js';
|
|
5
|
+
import { fileExists, writeJson } from './files.js';
|
|
6
|
+
import { gitSummary } from './git.js';
|
|
7
|
+
import {
|
|
8
|
+
getWorkspaceCheckpointIndexPath,
|
|
9
|
+
getWorkspaceConfigPath,
|
|
10
|
+
getWorkspaceDir,
|
|
11
|
+
getWorkspaceQuestionLedgerPath,
|
|
12
|
+
getWorkspaceRegistryBucketDir,
|
|
13
|
+
getWorkspaceRoot,
|
|
14
|
+
getWorkspaceStateMarkdownPath,
|
|
15
|
+
getWorkspaceStatePath,
|
|
16
|
+
} from './paths.js';
|
|
17
|
+
import { getProblemArtifactInventory } from './problem-artifacts.js';
|
|
18
|
+
import { syncState } from './state.js';
|
|
19
|
+
|
|
20
|
+
function checkpointPath(workspaceRoot) {
|
|
21
|
+
return getWorkspaceCheckpointIndexPath(workspaceRoot);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function buildPreflightReport(options = {}, workspaceRoot = getWorkspaceRoot()) {
|
|
25
|
+
ensureConfig(workspaceRoot);
|
|
26
|
+
const config = loadConfig(workspaceRoot);
|
|
27
|
+
const state = syncState(workspaceRoot);
|
|
28
|
+
const problem = state.activeProblem ? getProblem(state.activeProblem) : null;
|
|
29
|
+
const inventory = problem ? getProblemArtifactInventory(problem) : null;
|
|
30
|
+
const git = gitSummary(workspaceRoot);
|
|
31
|
+
|
|
32
|
+
const checks = {
|
|
33
|
+
erdosRuntime: {
|
|
34
|
+
ok: fileExists(getWorkspaceDir(workspaceRoot)),
|
|
35
|
+
detail: getWorkspaceDir(workspaceRoot),
|
|
36
|
+
},
|
|
37
|
+
configFile: {
|
|
38
|
+
ok: fileExists(getWorkspaceConfigPath(workspaceRoot)),
|
|
39
|
+
detail: getWorkspaceConfigPath(workspaceRoot),
|
|
40
|
+
},
|
|
41
|
+
stateFile: {
|
|
42
|
+
ok: fileExists(getWorkspaceStatePath(workspaceRoot)),
|
|
43
|
+
detail: getWorkspaceStatePath(workspaceRoot),
|
|
44
|
+
},
|
|
45
|
+
stateMarkdown: {
|
|
46
|
+
ok: fileExists(getWorkspaceStateMarkdownPath(workspaceRoot)),
|
|
47
|
+
detail: getWorkspaceStateMarkdownPath(workspaceRoot),
|
|
48
|
+
},
|
|
49
|
+
questionLedger: {
|
|
50
|
+
ok: fileExists(getWorkspaceQuestionLedgerPath(workspaceRoot)),
|
|
51
|
+
detail: getWorkspaceQuestionLedgerPath(workspaceRoot),
|
|
52
|
+
},
|
|
53
|
+
checkpointShelf: {
|
|
54
|
+
ok: fileExists(checkpointPath(workspaceRoot)),
|
|
55
|
+
detail: checkpointPath(workspaceRoot),
|
|
56
|
+
},
|
|
57
|
+
activeProblem: {
|
|
58
|
+
ok: Boolean(problem),
|
|
59
|
+
detail: problem ? `${problem.problemId} (${problem.title})` : '(none)',
|
|
60
|
+
},
|
|
61
|
+
activeRoute: {
|
|
62
|
+
ok: !problem || problem.harnessDepth !== 'deep' ? true : Boolean(state.activeRoute),
|
|
63
|
+
detail: state.activeRoute || '(none)',
|
|
64
|
+
},
|
|
65
|
+
canonicalDossier: {
|
|
66
|
+
ok: !inventory ? false : inventory.canonicalArtifacts.every((artifact) => artifact.exists),
|
|
67
|
+
detail: inventory ? `${inventory.canonicalArtifacts.filter((artifact) => artifact.exists).length}/${inventory.canonicalArtifacts.length} canonical file(s)` : '(no active problem)',
|
|
68
|
+
},
|
|
69
|
+
upstreamSnapshot: {
|
|
70
|
+
ok: Boolean(inventory?.upstreamSnapshot),
|
|
71
|
+
detail: inventory?.upstreamSnapshot?.kind ?? '(missing)',
|
|
72
|
+
},
|
|
73
|
+
workspaceGit: {
|
|
74
|
+
ok: options.allowDirty ? true : git.clean !== false,
|
|
75
|
+
detail: git.isGitRepo ? `${git.branch || '(unknown branch)'} / ${git.detail}` : git.detail,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
let verdict = 'ok';
|
|
80
|
+
if (!checks.erdosRuntime.ok || !checks.configFile.ok || !checks.stateFile.ok || !checks.activeProblem.ok || !checks.canonicalDossier.ok || !checks.upstreamSnapshot.ok) {
|
|
81
|
+
verdict = 'blocked';
|
|
82
|
+
} else if (!checks.questionLedger.ok || !checks.checkpointShelf.ok || !checks.activeRoute.ok || !checks.workspaceGit.ok) {
|
|
83
|
+
verdict = 'needs_attention';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const record = {
|
|
87
|
+
checkedAt: new Date().toISOString(),
|
|
88
|
+
workspaceRoot,
|
|
89
|
+
verdict,
|
|
90
|
+
activeProblem: state.activeProblem,
|
|
91
|
+
problemTitle: state.problemTitle,
|
|
92
|
+
activeRoute: state.activeRoute,
|
|
93
|
+
routeBreakthrough: state.routeBreakthrough,
|
|
94
|
+
problemSolved: state.problemSolved,
|
|
95
|
+
continuationMode: state.continuation.mode,
|
|
96
|
+
continuationDisplay: state.continuation.requestedMode === 'phase' ? 'route (phase-style)' : state.continuation.mode,
|
|
97
|
+
activeAgent: config.preferredAgent,
|
|
98
|
+
currentFrontier: state.currentFrontier,
|
|
99
|
+
nextHonestMove: state.nextHonestMove,
|
|
100
|
+
checks,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const registryPath = `${Date.now()}.json`;
|
|
104
|
+
writeJson(`${getWorkspaceRegistryBucketDir('preflight', workspaceRoot)}/${registryPath}`, record);
|
|
105
|
+
return record;
|
|
106
|
+
}
|