erdos-problems 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +73 -3
  2. package/data/upstream/erdosproblems/PROBLEMS_INDEX.json +44420 -0
  3. package/data/upstream/erdosproblems/SYNC_MANIFEST.json +9 -0
  4. package/data/upstream/erdosproblems/problems.yaml +13077 -0
  5. package/docs/ERDOS_PROBLEMS_PROBLEM_SCHEMA.md +104 -119
  6. package/package.json +5 -1
  7. package/problems/1008/EVIDENCE.md +4 -0
  8. package/problems/1008/FORMALIZATION.md +5 -0
  9. package/problems/1008/REFERENCES.md +5 -0
  10. package/problems/1008/STATEMENT.md +9 -0
  11. package/problems/1008/problem.yaml +37 -0
  12. package/problems/18/EVIDENCE.md +4 -0
  13. package/problems/18/FORMALIZATION.md +5 -0
  14. package/problems/18/REFERENCES.md +6 -0
  15. package/problems/18/STATEMENT.md +12 -0
  16. package/problems/18/problem.yaml +38 -0
  17. package/problems/20/problem.yaml +30 -8
  18. package/problems/536/problem.yaml +30 -8
  19. package/problems/542/EVIDENCE.md +6 -0
  20. package/problems/542/FORMALIZATION.md +5 -0
  21. package/problems/542/REFERENCES.md +5 -0
  22. package/problems/542/STATEMENT.md +11 -0
  23. package/problems/542/problem.yaml +38 -0
  24. package/problems/856/problem.yaml +30 -8
  25. package/problems/857/problem.yaml +31 -9
  26. package/problems/89/EVIDENCE.md +4 -0
  27. package/problems/89/FORMALIZATION.md +5 -0
  28. package/problems/89/REFERENCES.md +6 -0
  29. package/problems/89/STATEMENT.md +10 -0
  30. package/problems/89/problem.yaml +37 -0
  31. package/src/atlas/catalog.js +83 -72
  32. package/src/cli/index.js +16 -1
  33. package/src/commands/bootstrap.js +81 -0
  34. package/src/commands/dossier.js +19 -11
  35. package/src/commands/problem.js +117 -8
  36. package/src/commands/scaffold.js +60 -0
  37. package/src/commands/upstream.js +60 -0
  38. package/src/commands/workspace.js +2 -0
  39. package/src/runtime/files.js +37 -0
  40. package/src/runtime/paths.js +64 -0
  41. package/src/runtime/problem-artifacts.js +150 -0
  42. package/src/runtime/workspace.js +7 -7
  43. package/src/upstream/sync.js +272 -0
@@ -1,23 +1,45 @@
1
1
  problem_id: "857"
2
2
  display_name: "Erdos Problem #857"
3
- title: "Sunflower Conjecture"
3
+ title: Sunflower Conjecture
4
4
  source:
5
- site: "erdosproblems.com"
6
- url: "https://www.erdosproblems.com/857"
5
+ site: erdosproblems.com
6
+ url: https://www.erdosproblems.com/857
7
7
  external_id: "857"
8
+ upstream:
9
+ repo: https://github.com/teorth/erdosproblems
10
+ data_file: data/problems.yaml
11
+ number: "857"
8
12
  status:
9
- site_status: "open"
10
- repo_status: "active"
11
- cluster: "sunflower"
13
+ site_status: open
14
+ site_badge: OPEN
15
+ repo_status: active
16
+ upstream_status: open
17
+ upstream_last_update: 2025-08-31
18
+ cluster: sunflower
19
+ prize:
20
+ display: no
12
21
  related_problems:
13
22
  - "20"
14
23
  - "536"
15
24
  - "856"
16
25
  family_tags:
17
- - "sunflower"
18
- - "extremal-set-theory"
26
+ - sunflower
27
+ - extremal-set-theory
28
+ harness:
29
+ depth: deep
30
+ statement:
31
+ short: Bound the weak sunflower number m(n,k) by C(k)^n and sharpen the current
32
+ active route toward asymptotic closure.
33
+ normalized_md_path: STATEMENT.md
34
+ references_path: REFERENCES.md
35
+ evidence_path: EVIDENCE.md
36
+ formalization_path: FORMALIZATION.md
37
+ formalization:
38
+ status: active
39
+ upstream_state: no
40
+ upstream_last_update: 2025-08-31
19
41
  research_state:
20
42
  open_problem: true
21
- active_route: "anchored_selector_linearization"
43
+ active_route: anchored_selector_linearization
22
44
  route_breakthrough: true
23
45
  problem_solved: false
@@ -0,0 +1,4 @@
1
+ # Problem 89 Evidence
2
+
3
+ - Atlas-only entry for now.
4
+ - No local computational or formalization artifacts are attached yet.
@@ -0,0 +1,5 @@
1
+ # Problem 89 Formalization
2
+
3
+ - Site reports: `Formalised statement? Yes`
4
+ - Local repo status: dossier only
5
+ - Next step: capture the formal statement link and relevant lower-bound references.
@@ -0,0 +1,6 @@
1
+ # Problem 89 References
2
+
3
+ - Source page: <https://www.erdosproblems.com/89>
4
+ - Site badge: `OPEN`
5
+ - Tags on source: `geometry`, `distances`
6
+ - Source notes the Guth-Katz bound `>> n / log n` as the current near-solution benchmark.
@@ -0,0 +1,10 @@
1
+ # Problem 89 Statement
2
+
3
+ Source: <https://www.erdosproblems.com/89>
4
+
5
+ Normalized focus:
6
+ - distinct distances in the plane
7
+ - near-optimal lower bounds
8
+
9
+ Core question:
10
+ - Does every set of `n` distinct points in `R^2` determine at least a constant multiple of `n / sqrt(log n)` distinct distances?
@@ -0,0 +1,37 @@
1
+ problem_id: "89"
2
+ display_name: "Erdos Problem #89"
3
+ title: Distinct Distances Lower Bound
4
+ source:
5
+ site: erdosproblems.com
6
+ url: https://www.erdosproblems.com/89
7
+ external_id: "89"
8
+ upstream:
9
+ repo: https://github.com/teorth/erdosproblems
10
+ data_file: data/problems.yaml
11
+ number: "89"
12
+ status:
13
+ site_status: open
14
+ site_badge: OPEN
15
+ repo_status: cataloged
16
+ upstream_status: open
17
+ upstream_last_update: 2025-08-31
18
+ cluster: geometry
19
+ prize:
20
+ display: $500
21
+ related_problems: []
22
+ family_tags:
23
+ - geometry
24
+ - distances
25
+ harness:
26
+ depth: dossier
27
+ statement:
28
+ short: Ask whether every n-point set in the plane determines at least a constant
29
+ multiple of n/sqrt(log n) distinct distances.
30
+ normalized_md_path: STATEMENT.md
31
+ references_path: REFERENCES.md
32
+ evidence_path: EVIDENCE.md
33
+ formalization_path: FORMALIZATION.md
34
+ formalization:
35
+ status: statement-formalized
36
+ upstream_state: yes
37
+ upstream_last_update: 2025-10-24
@@ -1,81 +1,88 @@
1
- export const catalog = [
2
- {
3
- problemId: '20',
4
- displayName: 'Erdos Problem #20',
5
- title: 'Strong Sunflower Problem',
6
- siteStatus: 'open',
7
- repoStatus: 'active',
8
- cluster: 'sunflower',
9
- familyTags: ['sunflower', 'uniform-families'],
10
- relatedProblems: ['857'],
11
- sourceUrl: 'https://www.erdosproblems.com/20',
12
- shortStatement:
13
- 'Determine the strong sunflower threshold for k-uniform set systems, with the k=3 lane as the immediate active frontier.',
14
- harnessDepth: 'deep',
15
- formalizationStatus: 'active',
16
- },
17
- {
18
- problemId: '536',
19
- displayName: 'Erdos Problem #536',
20
- title: 'LCM Sunflower Analogue',
21
- siteStatus: 'open',
22
- repoStatus: 'cataloged',
23
- cluster: 'sunflower',
24
- familyTags: ['sunflower-analogue', 'number-theory'],
25
- relatedProblems: ['857'],
26
- sourceUrl: 'https://www.erdosproblems.com/536',
27
- shortStatement:
28
- 'Number-theoretic analogue of the sunflower problem framed through least common multiples.',
29
- harnessDepth: 'dossier',
30
- formalizationStatus: 'planned',
31
- },
32
- {
33
- problemId: '856',
34
- displayName: 'Erdos Problem #856',
35
- title: 'Harmonic LCM Sunflower Analogue',
36
- siteStatus: 'open',
37
- repoStatus: 'cataloged',
38
- cluster: 'sunflower',
39
- familyTags: ['sunflower-analogue', 'number-theory'],
40
- relatedProblems: ['857'],
41
- sourceUrl: 'https://www.erdosproblems.com/856',
42
- shortStatement:
43
- 'A harmonic or density-shaped LCM analogue whose exponents are explicitly linked to progress on the weak sunflower problem.',
44
- harnessDepth: 'dossier',
45
- formalizationStatus: 'planned',
46
- },
47
- {
48
- problemId: '857',
49
- displayName: 'Erdos Problem #857',
50
- title: 'Sunflower Conjecture',
51
- siteStatus: 'open',
52
- repoStatus: 'active',
53
- cluster: 'sunflower',
54
- familyTags: ['sunflower', 'extremal-set-theory'],
55
- relatedProblems: ['20', '536', '856'],
56
- sourceUrl: 'https://www.erdosproblems.com/857',
57
- shortStatement:
58
- 'Bound the weak sunflower number m(n,k) by C(k)^n and sharpen the current active route toward asymptotic closure.',
59
- harnessDepth: 'deep',
60
- formalizationStatus: 'active',
61
- researchState: {
62
- openProblem: true,
63
- activeRoute: 'anchored_selector_linearization',
64
- routeBreakthrough: true,
65
- problemSolved: false,
66
- },
67
- },
68
- ];
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parse } from 'yaml';
4
+ import { getProblemDir, repoRoot } from '../runtime/paths.js';
5
+
6
+ let cachedProblems = null;
7
+
8
+ function readProblemRecord(problemId) {
9
+ const problemDir = getProblemDir(problemId);
10
+ const yamlPath = path.join(problemDir, 'problem.yaml');
11
+ const record = parse(fs.readFileSync(yamlPath, 'utf8'));
12
+ return { problemDir, record };
13
+ }
14
+
15
+ function toCatalogProblem(problemDir, record) {
16
+ const statementRelative = record.statement?.normalized_md_path ?? 'STATEMENT.md';
17
+ const referencesRelative = record.references_path ?? 'REFERENCES.md';
18
+ const evidenceRelative = record.evidence_path ?? 'EVIDENCE.md';
19
+ const formalizationRelative = record.formalization_path ?? 'FORMALIZATION.md';
20
+
21
+ return {
22
+ problemId: String(record.problem_id),
23
+ displayName: record.display_name,
24
+ title: record.title,
25
+ sourceUrl: record.source?.url,
26
+ sourceSite: record.source?.site,
27
+ siteStatus: record.status?.site_status ?? 'unknown',
28
+ siteBadge: record.status?.site_badge ?? String(record.status?.site_status ?? 'unknown').toUpperCase(),
29
+ repoStatus: record.status?.repo_status ?? 'cataloged',
30
+ upstreamStatus: record.status?.upstream_status ?? null,
31
+ upstreamLastUpdate: record.status?.upstream_last_update ?? null,
32
+ cluster: record.cluster ?? 'uncategorized',
33
+ prize: record.prize?.display ?? null,
34
+ familyTags: record.family_tags ?? [],
35
+ relatedProblems: record.related_problems ?? [],
36
+ harnessDepth: record.harness?.depth ?? 'dossier',
37
+ shortStatement: record.statement?.short ?? '',
38
+ formalizationStatus: record.formalization?.status ?? 'unstarted',
39
+ upstreamFormalizedState: record.formalization?.upstream_state ?? null,
40
+ upstreamFormalizedLastUpdate: record.formalization?.upstream_last_update ?? null,
41
+ researchState: record.research_state ?? null,
42
+ upstream: record.upstream ?? null,
43
+ statementPath: path.join(problemDir, statementRelative),
44
+ referencesPath: path.join(problemDir, referencesRelative),
45
+ evidencePath: path.join(problemDir, evidenceRelative),
46
+ formalizationPath: path.join(problemDir, formalizationRelative),
47
+ problemDir,
48
+ problemYamlPath: path.join(problemDir, 'problem.yaml'),
49
+ record,
50
+ };
51
+ }
52
+
53
+ export function loadLocalProblems() {
54
+ if (cachedProblems) {
55
+ return cachedProblems;
56
+ }
57
+ const problemsRoot = path.join(repoRoot, 'problems');
58
+ const directories = fs
59
+ .readdirSync(problemsRoot, { withFileTypes: true })
60
+ .filter((entry) => entry.isDirectory())
61
+ .map((entry) => entry.name)
62
+ .sort((a, b) => Number(a) - Number(b));
63
+
64
+ cachedProblems = directories.map((problemId) => {
65
+ const { problemDir, record } = readProblemRecord(problemId);
66
+ return toCatalogProblem(problemDir, record);
67
+ });
68
+ return cachedProblems;
69
+ }
69
70
 
70
71
  export function listProblems(filters = {}) {
71
72
  const cluster = filters.cluster ? String(filters.cluster).toLowerCase() : null;
72
- return [...catalog]
73
+ const repoStatus = filters.repoStatus ? String(filters.repoStatus).toLowerCase() : null;
74
+ const harnessDepth = filters.harnessDepth ? String(filters.harnessDepth).toLowerCase() : null;
75
+ const siteStatus = filters.siteStatus ? String(filters.siteStatus).toLowerCase() : null;
76
+
77
+ return loadLocalProblems()
73
78
  .filter((entry) => (cluster ? entry.cluster === cluster : true))
74
- .sort((a, b) => Number(a.problemId) - Number(b.problemId));
79
+ .filter((entry) => (repoStatus ? entry.repoStatus === repoStatus : true))
80
+ .filter((entry) => (harnessDepth ? entry.harnessDepth === harnessDepth : true))
81
+ .filter((entry) => (siteStatus ? entry.siteStatus === siteStatus : true));
75
82
  }
76
83
 
77
84
  export function getProblem(problemId) {
78
- return catalog.find((entry) => entry.problemId === String(problemId));
85
+ return loadLocalProblems().find((entry) => entry.problemId === String(problemId));
79
86
  }
80
87
 
81
88
  export function getCluster(clusterName) {
@@ -93,6 +100,10 @@ export function getCluster(clusterName) {
93
100
  }
94
101
 
95
102
  export function listClusters() {
96
- const names = [...new Set(catalog.map((entry) => entry.cluster))].sort();
103
+ const names = [...new Set(loadLocalProblems().map((entry) => entry.cluster))].sort();
97
104
  return names.map((name) => getCluster(name));
98
105
  }
106
+
107
+ export function clearCatalogCache() {
108
+ cachedProblems = null;
109
+ }
package/src/cli/index.js CHANGED
@@ -1,20 +1,29 @@
1
+ import { runBootstrapCommand } from '../commands/bootstrap.js';
1
2
  import { runClusterCommand } from '../commands/cluster.js';
2
3
  import { runDossierCommand } from '../commands/dossier.js';
3
4
  import { runProblemCommand } from '../commands/problem.js';
5
+ import { runScaffoldCommand } from '../commands/scaffold.js';
6
+ import { runUpstreamCommand } from '../commands/upstream.js';
4
7
  import { runWorkspaceCommand } from '../commands/workspace.js';
5
8
 
6
9
  function printUsage() {
7
10
  console.log('erdos-problems CLI');
8
11
  console.log('');
9
12
  console.log('Usage:');
10
- console.log(' erdos problem list [--cluster <name>]');
13
+ console.log(' erdos problem list [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--site-status <status>]');
11
14
  console.log(' erdos problem show <id>');
12
15
  console.log(' erdos problem use <id>');
13
16
  console.log(' erdos problem current');
17
+ console.log(' erdos problem artifacts [<id>] [--json]');
14
18
  console.log(' erdos cluster list');
15
19
  console.log(' erdos cluster show <name>');
16
20
  console.log(' erdos workspace show');
17
21
  console.log(' erdos dossier show <id>');
22
+ console.log(' erdos upstream show');
23
+ console.log(' erdos upstream sync [--write-package-snapshot]');
24
+ console.log(' erdos upstream diff [--write-package-report]');
25
+ console.log(' erdos scaffold problem <id> [--dest <path>]');
26
+ console.log(' erdos bootstrap problem <id> [--dest <path>] [--sync-upstream]');
18
27
  }
19
28
 
20
29
  const args = process.argv.slice(2);
@@ -32,6 +41,12 @@ if (!command || command === 'help' || command === '--help') {
32
41
  exitCode = runWorkspaceCommand(rest);
33
42
  } else if (command === 'dossier') {
34
43
  exitCode = runDossierCommand(rest);
44
+ } else if (command === 'upstream') {
45
+ exitCode = await runUpstreamCommand(rest);
46
+ } else if (command === 'scaffold') {
47
+ exitCode = runScaffoldCommand(rest);
48
+ } else if (command === 'bootstrap') {
49
+ exitCode = await runBootstrapCommand(rest);
35
50
  } else {
36
51
  console.error(`Unknown command: ${command}`);
37
52
  printUsage();
@@ -0,0 +1,81 @@
1
+ import path from 'node:path';
2
+ import { getProblem } from '../atlas/catalog.js';
3
+ import { scaffoldProblem } from '../runtime/problem-artifacts.js';
4
+ import { getWorkspaceProblemScaffoldDir } from '../runtime/paths.js';
5
+ import { setCurrentProblem } from '../runtime/workspace.js';
6
+ import { syncUpstream } from '../upstream/sync.js';
7
+
8
+ function parseBootstrapArgs(args) {
9
+ const [kind, value, ...rest] = args;
10
+ if (kind !== 'problem') {
11
+ return { error: 'Only `erdos bootstrap problem <id>` is supported right now.' };
12
+ }
13
+
14
+ let destination = null;
15
+ let syncUpstreamSnapshot = false;
16
+
17
+ for (let index = 0; index < rest.length; index += 1) {
18
+ const token = rest[index];
19
+ if (token === '--dest') {
20
+ destination = rest[index + 1];
21
+ if (!destination) {
22
+ return { error: 'Missing destination path after --dest.' };
23
+ }
24
+ index += 1;
25
+ continue;
26
+ }
27
+ if (token === '--sync-upstream') {
28
+ syncUpstreamSnapshot = true;
29
+ continue;
30
+ }
31
+ return { error: `Unknown bootstrap option: ${token}` };
32
+ }
33
+
34
+ return {
35
+ problemId: value,
36
+ destination,
37
+ syncUpstreamSnapshot,
38
+ };
39
+ }
40
+
41
+ export async function runBootstrapCommand(args) {
42
+ if (args.length === 0 || args[0] === 'help' || args[0] === '--help') {
43
+ console.log('Usage:');
44
+ console.log(' erdos bootstrap problem <id> [--dest <path>] [--sync-upstream]');
45
+ return 0;
46
+ }
47
+
48
+ const parsed = parseBootstrapArgs(args);
49
+ if (parsed.error) {
50
+ console.error(parsed.error);
51
+ return 1;
52
+ }
53
+ if (!parsed.problemId) {
54
+ console.error('Missing problem id.');
55
+ return 1;
56
+ }
57
+
58
+ const problem = getProblem(parsed.problemId);
59
+ if (!problem) {
60
+ console.error(`Unknown problem: ${parsed.problemId}`);
61
+ return 1;
62
+ }
63
+
64
+ if (parsed.syncUpstreamSnapshot) {
65
+ const syncResult = await syncUpstream();
66
+ console.log(`Workspace upstream snapshot refreshed: ${syncResult.workspacePaths.manifestPath}`);
67
+ }
68
+
69
+ setCurrentProblem(problem.problemId);
70
+ const destination = parsed.destination
71
+ ? path.resolve(parsed.destination)
72
+ : getWorkspaceProblemScaffoldDir(problem.problemId);
73
+ const result = scaffoldProblem(problem, destination);
74
+
75
+ console.log(`Bootstrapped problem ${problem.problemId} (${problem.title})`);
76
+ console.log(`Active problem: ${problem.problemId}`);
77
+ console.log(`Scaffold dir: ${result.destination}`);
78
+ console.log(`Artifacts copied: ${result.copiedArtifacts.length}`);
79
+ console.log(`Upstream record included: ${result.inventory.upstreamRecordIncluded ? 'yes' : 'no'}`);
80
+ return 0;
81
+ }
@@ -1,10 +1,14 @@
1
1
  import fs from 'node:fs';
2
- import path from 'node:path';
3
2
  import { getProblem } from '../atlas/catalog.js';
4
- import { getProblemDir } from '../runtime/paths.js';
5
3
  import { readCurrentProblem } from '../runtime/workspace.js';
6
4
 
7
- const sections = ['STATEMENT.md', 'REFERENCES.md', 'EVIDENCE.md', 'FORMALIZATION.md'];
5
+ const sections = [
6
+ ['problem.yaml', 'problemYamlPath'],
7
+ ['STATEMENT.md', 'statementPath'],
8
+ ['REFERENCES.md', 'referencesPath'],
9
+ ['EVIDENCE.md', 'evidencePath'],
10
+ ['FORMALIZATION.md', 'formalizationPath'],
11
+ ];
8
12
 
9
13
  export function runDossierCommand(args) {
10
14
  const [subcommand, value] = args;
@@ -32,20 +36,24 @@ export function runDossierCommand(args) {
32
36
  return 1;
33
37
  }
34
38
 
35
- const problemDir = getProblemDir(problem.problemId);
36
39
  console.log(`${problem.displayName} dossier`);
37
- console.log(`Directory: ${problemDir}`);
40
+ console.log(`Directory: ${problem.problemDir}`);
38
41
  console.log('Sections:');
39
- for (const section of sections) {
40
- const filePath = path.join(problemDir, section);
42
+ for (const [label, key] of sections) {
43
+ const filePath = problem[key];
41
44
  const exists = fs.existsSync(filePath);
42
- console.log(`- ${section}: ${exists ? 'present' : 'missing'}`);
45
+ console.log(`- ${label}: ${exists ? 'present' : 'missing'}`);
43
46
  }
44
- const statementPath = path.join(problemDir, 'STATEMENT.md');
45
- if (fs.existsSync(statementPath)) {
47
+ console.log('');
48
+ console.log('Canonical metadata:');
49
+ console.log(`- cluster: ${problem.cluster}`);
50
+ console.log(`- repo status: ${problem.repoStatus}`);
51
+ console.log(`- harness depth: ${problem.harnessDepth}`);
52
+ console.log(`- upstream number: ${problem.upstream?.number ?? '(unset)'}`);
53
+ if (fs.existsSync(problem.statementPath)) {
46
54
  console.log('');
47
55
  console.log('Statement preview:');
48
- console.log(fs.readFileSync(statementPath, 'utf8').trim());
56
+ console.log(fs.readFileSync(problem.statementPath, 'utf8').trim());
49
57
  }
50
58
  return 0;
51
59
  }
@@ -1,4 +1,5 @@
1
1
  import { getProblem, listProblems } from '../atlas/catalog.js';
2
+ import { getProblemArtifactInventory } from '../runtime/problem-artifacts.js';
2
3
  import { readCurrentProblem, setCurrentProblem } from '../runtime/workspace.js';
3
4
 
4
5
  function parseListFilters(args) {
@@ -14,20 +15,70 @@ function parseListFilters(args) {
14
15
  index += 1;
15
16
  continue;
16
17
  }
18
+ if (token === '--repo-status') {
19
+ const repoStatus = args[index + 1];
20
+ if (!repoStatus) {
21
+ return { error: 'Missing repo status after --repo-status.' };
22
+ }
23
+ filters.repoStatus = repoStatus;
24
+ index += 1;
25
+ continue;
26
+ }
27
+ if (token === '--harness-depth') {
28
+ const harnessDepth = args[index + 1];
29
+ if (!harnessDepth) {
30
+ return { error: 'Missing harness depth after --harness-depth.' };
31
+ }
32
+ filters.harnessDepth = harnessDepth;
33
+ index += 1;
34
+ continue;
35
+ }
36
+ if (token === '--site-status') {
37
+ const siteStatus = args[index + 1];
38
+ if (!siteStatus) {
39
+ return { error: 'Missing site status after --site-status.' };
40
+ }
41
+ filters.siteStatus = siteStatus;
42
+ index += 1;
43
+ continue;
44
+ }
17
45
  return { error: `Unknown list option: ${token}` };
18
46
  }
19
47
  return { filters };
20
48
  }
21
49
 
50
+ function parseArtifactArgs(args) {
51
+ const parsed = {
52
+ problemId: null,
53
+ asJson: false,
54
+ };
55
+
56
+ for (let index = 0; index < args.length; index += 1) {
57
+ const token = args[index];
58
+ if (token === '--json') {
59
+ parsed.asJson = true;
60
+ continue;
61
+ }
62
+ if (!parsed.problemId) {
63
+ parsed.problemId = token;
64
+ continue;
65
+ }
66
+ return { error: `Unknown artifact option: ${token}` };
67
+ }
68
+
69
+ return parsed;
70
+ }
71
+
22
72
  function printProblemTable(rows, activeProblem) {
23
- console.log('ID Site Repo Cluster Active Title');
73
+ console.log('ID Site Repo Cluster Depth Active Title');
24
74
  for (const row of rows) {
25
75
  const id = row.problemId.padEnd(4, ' ');
26
76
  const site = row.siteStatus.padEnd(6, ' ');
27
77
  const repo = row.repoStatus.padEnd(10, ' ');
28
- const cluster = row.cluster.padEnd(11, ' ');
78
+ const cluster = row.cluster.padEnd(15, ' ');
79
+ const depth = row.harnessDepth.padEnd(8, ' ');
29
80
  const active = (row.problemId === activeProblem ? '*' : '-').padEnd(7, ' ');
30
- console.log(`${id} ${site} ${repo} ${cluster} ${active} ${row.title}`);
81
+ console.log(`${id} ${site} ${repo} ${cluster} ${depth} ${active} ${row.title}`);
31
82
  }
32
83
  }
33
84
 
@@ -36,31 +87,68 @@ function printProblem(problem) {
36
87
  console.log(`Title: ${problem.title}`);
37
88
  console.log(`Source: ${problem.sourceUrl}`);
38
89
  console.log(`Site status: ${problem.siteStatus}`);
90
+ console.log(`Site badge: ${problem.siteBadge ?? problem.siteStatus}`);
39
91
  console.log(`Repo status: ${problem.repoStatus}`);
40
92
  console.log(`Cluster: ${problem.cluster}`);
41
93
  console.log(`Harness depth: ${problem.harnessDepth}`);
94
+ console.log(`Prize: ${problem.prize ?? '(none)'}`);
42
95
  console.log(`Formalization: ${problem.formalizationStatus}`);
96
+ console.log(`Upstream formalized: ${problem.upstreamFormalizedState ?? '(unknown)'}`);
97
+ console.log(`Upstream last update: ${problem.upstreamLastUpdate ?? '(unknown)'}`);
43
98
  console.log(`Related: ${problem.relatedProblems.join(', ') || '(none)'}`);
44
99
  console.log(`Tags: ${problem.familyTags.join(', ') || '(none)'}`);
45
100
  console.log(`Statement: ${problem.shortStatement}`);
46
101
  if (problem.researchState) {
47
102
  console.log('Research state:');
48
- console.log(` open problem: ${problem.researchState.openProblem ? 'yes' : 'no'}`);
49
- console.log(` active route: ${problem.researchState.activeRoute}`);
50
- console.log(` route breakthrough: ${problem.researchState.routeBreakthrough ? 'yes' : 'no'}`);
51
- console.log(` problem solved: ${problem.researchState.problemSolved ? 'yes' : 'no'}`);
103
+ console.log(` open problem: ${problem.researchState.open_problem ? 'yes' : 'no'}`);
104
+ console.log(` active route: ${problem.researchState.active_route}`);
105
+ console.log(` route breakthrough: ${problem.researchState.route_breakthrough ? 'yes' : 'no'}`);
106
+ console.log(` problem solved: ${problem.researchState.problem_solved ? 'yes' : 'no'}`);
107
+ }
108
+ if (problem.upstream) {
109
+ console.log('Upstream provenance:');
110
+ console.log(` repo: ${problem.upstream.repo}`);
111
+ console.log(` data file: ${problem.upstream.data_file}`);
112
+ console.log(` number: ${problem.upstream.number}`);
52
113
  }
53
114
  }
54
115
 
116
+ function printArtifactInventory(problem, inventory, asJson) {
117
+ if (asJson) {
118
+ console.log(JSON.stringify(inventory, null, 2));
119
+ return;
120
+ }
121
+
122
+ console.log(`${problem.displayName} canonical artifacts`);
123
+ console.log(`Problem directory: ${inventory.problemDir}`);
124
+ console.log(`Source: ${inventory.sourceUrl}`);
125
+ console.log('Canonical files:');
126
+ for (const artifact of inventory.canonicalArtifacts) {
127
+ console.log(`- ${artifact.label}: ${artifact.exists ? 'present' : 'missing'} (${artifact.path})`);
128
+ }
129
+ if (inventory.packContext) {
130
+ console.log(`- ${inventory.packContext.label}: ${inventory.packContext.exists ? 'present' : 'missing'} (${inventory.packContext.path})`);
131
+ }
132
+ if (inventory.upstreamSnapshot) {
133
+ console.log('Upstream snapshot:');
134
+ console.log(`- kind: ${inventory.upstreamSnapshot.kind}`);
135
+ console.log(`- manifest: ${inventory.upstreamSnapshot.manifestPath}`);
136
+ console.log(`- index: ${inventory.upstreamSnapshot.indexPath}`);
137
+ console.log(`- commit: ${inventory.upstreamSnapshot.upstreamCommit ?? '(unknown)'}`);
138
+ }
139
+ console.log(`Upstream record available: ${inventory.upstreamRecordIncluded ? 'yes' : 'no'}`);
140
+ }
141
+
55
142
  export function runProblemCommand(args) {
56
143
  const [subcommand, value, ...rest] = args;
57
144
 
58
145
  if (!subcommand || subcommand === 'help' || subcommand === '--help') {
59
146
  console.log('Usage:');
60
- console.log(' erdos problem list [--cluster <name>]');
147
+ console.log(' erdos problem list [--cluster <name>] [--repo-status <status>] [--harness-depth <depth>] [--site-status <status>]');
61
148
  console.log(' erdos problem show <id>');
62
149
  console.log(' erdos problem use <id>');
63
150
  console.log(' erdos problem current');
151
+ console.log(' erdos problem artifacts [<id>] [--json]');
64
152
  return 0;
65
153
  }
66
154
 
@@ -119,6 +207,27 @@ export function runProblemCommand(args) {
119
207
  return 0;
120
208
  }
121
209
 
210
+ if (subcommand === 'artifacts') {
211
+ const parsed = parseArtifactArgs([value, ...rest].filter(Boolean));
212
+ if (parsed.error) {
213
+ console.error(parsed.error);
214
+ return 1;
215
+ }
216
+ const problemId = parsed.problemId ?? readCurrentProblem();
217
+ if (!problemId) {
218
+ console.error('Missing problem id and no active problem is selected.');
219
+ return 1;
220
+ }
221
+ const problem = getProblem(problemId);
222
+ if (!problem) {
223
+ console.error(`Unknown problem: ${problemId}`);
224
+ return 1;
225
+ }
226
+ const inventory = getProblemArtifactInventory(problem);
227
+ printArtifactInventory(problem, inventory, parsed.asJson);
228
+ return 0;
229
+ }
230
+
122
231
  console.error(`Unknown problem subcommand: ${subcommand}`);
123
232
  return 1;
124
233
  }