Repository navigation
Expand file tree
/
Copy pathgenerate-index.mjs
More file actions
208 lines (201 loc) · 6.63 KB
/
Copy pathgenerate-index.mjs
File metadata and controls
208 lines (201 loc) · 6.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import { promises as fs } from "node:fs";
import path from "node:path";
import { readJson, root, walkFiles } from "./lib/files.mjs";
const PACK_ROOT = path.join(root, "problem-packs");
const checkOnly = process.argv.includes("--check");
const loadPacks = async () => {
const problemJsonFiles = await walkFiles(
PACK_ROOT,
(file) => path.basename(file) === "problem.json"
);
const packs = [];
for (const file of problemJsonFiles) {
const packDir = path.dirname(file);
const relativeDir = path.relative(root, packDir);
const problem = await readJson(path.relative(root, file));
const tasks = JSON.parse(await fs.readFile(path.join(packDir, "tasks.json"), "utf8"));
const evidence = JSON.parse(
await fs.readFile(path.join(packDir, "evidence.json"), "utf8")
);
let claims = [];
try {
claims = JSON.parse(await fs.readFile(path.join(packDir, "claims.json"), "utf8"));
} catch {
// no claims.json yet
}
const scopedTasks = tasks.filter((t) => t.status === "scoped").length;
const acceptedTasks = tasks.filter((t) => t.status === "accepted").length;
const highRiskTasks = tasks.filter((t) => t.safety_risk === "high").length;
const acceptedClaims = claims.filter(
(c) => c.status === "accepted" || c.status === "field-tested"
).length;
packs.push({
id: problem.id,
title: problem.title,
status: problem.status,
domain: problem.domain.join(", "),
region: problem.region.join(", "),
safety_level: problem.safety_level,
evidence_count: evidence.length,
task_count: tasks.length,
scoped_tasks: scopedTasks,
accepted_tasks: acceptedTasks,
high_risk_tasks: highRiskTasks,
total_claims: claims.length,
accepted_claims: acceptedClaims,
has_claims: claims.length > 0,
dir: relativeDir
});
}
packs.sort((a, b) => a.id.localeCompare(b.id));
return packs;
};
const progressLabel = (pack) => {
const parts = [];
parts.push(pack.evidence_count + " ev");
parts.push(pack.task_count + " tasks");
if (pack.has_claims) parts.push(pack.total_claims + " claims");
if (pack.accepted_claims > 0) parts.push(pack.accepted_claims + " accepted");
return parts.join("; ");
};
const priorityLabel = (pack) => {
if (pack.accepted_tasks > 0 || pack.accepted_claims > 0) return "accepted work";
if (pack.has_claims) return "has claims";
if (pack.scoped_tasks > 0 && pack.evidence_count < 3) return "needs evidence";
if (pack.scoped_tasks > 0) return "ready";
return "needs triage";
};
const writeOrCheck = async (relativePath, content) => {
const target = path.join(root, relativePath);
if (checkOnly) {
let existing = "";
try {
existing = await fs.readFile(target, "utf8");
} catch {
throw new Error(relativePath + " is missing. Run pnpm build.");
}
if (existing !== content) {
throw new Error(relativePath + " is stale. Run pnpm build.");
}
return;
}
await fs.writeFile(target, content);
console.log("Generated " + relativePath);
};
const generate = async () => {
const packs = await loadPacks();
const lines = [];
lines.push("# Problem Pack Index");
lines.push("");
lines.push(
"> Auto-generated by scripts/generate-index.mjs. Do not edit directly. Run pnpm build to regenerate."
);
lines.push("");
lines.push("## Summary");
lines.push("");
lines.push("| Metric | Value |");
lines.push("|---|---|");
lines.push("| Total packs | " + packs.length + " |");
lines.push("| Packs with claims | " + packs.filter((p) => p.has_claims).length + " |");
lines.push(
"| Packs with accepted claims | " +
packs.filter((p) => p.accepted_claims > 0).length +
" |"
);
lines.push(
"| Total evidence records | " + packs.reduce((s, p) => s + p.evidence_count, 0) + " |"
);
lines.push("| Total tasks | " + packs.reduce((s, p) => s + p.task_count, 0) + " |");
lines.push(
"| Scoped tasks (ready for work) | " +
packs.reduce((s, p) => s + p.scoped_tasks, 0) +
" |"
);
lines.push(
"| High-risk tasks | " + packs.reduce((s, p) => s + p.high_risk_tasks, 0) + " |"
);
lines.push("");
lines.push("## All Problem Packs");
lines.push("");
lines.push("| ID | Title | Status | Domain | Region | Safety | Progress | Priority |");
lines.push("|---|---|---|---|---|---|---|---|");
for (const p of packs) {
const link = "[" + p.id + "](../../" + p.dir + "/problem.md)";
lines.push(
"| " +
link +
" | " +
p.title +
" | " +
p.status +
" | " +
p.domain +
" | " +
p.region +
" | " +
p.safety_level +
" | " +
progressLabel(p) +
" | " +
priorityLabel(p) +
" |"
);
}
lines.push("");
lines.push("## Priority Guide");
lines.push("");
lines.push("| Priority | Meaning | What to do |");
lines.push("|---|---|---|");
lines.push(
"| needs evidence | Has scoped tasks but fewer than 3 evidence records | Start with the source-inventory task (literature-scout role) |"
);
lines.push(
"| ready | Has scoped tasks and at least 3 evidence records | Pick a downstream task from tasks.json |"
);
lines.push(
"| has claims | Has at least one claim in claims.json | Review claims, advance to replication or acceptance |"
);
lines.push(
"| accepted work | Has accepted tasks or claims | Maintain, check for evidence decay, advance to field-tested |"
);
lines.push(
"| needs triage | No scoped tasks yet | Open a Discussion to scope the problem |"
);
lines.push("");
lines.push("## Needs Evidence (Highest Leverage)");
lines.push("");
lines.push(
"These packs have scoped tasks but fewer than 3 evidence records. The source-inventory task is the entry point."
);
lines.push("");
const needsEvidence = packs.filter((p) => p.scoped_tasks > 0 && p.evidence_count < 3);
if (needsEvidence.length === 0) {
lines.push("All packs with scoped tasks have at least 3 evidence records.");
} else {
lines.push("| ID | Evidence | Tasks | High-risk | Safety |");
lines.push("|---|---|---|---|---|");
for (const p of needsEvidence) {
const link = "[" + p.id + "](../../" + p.dir + "/problem.md)";
lines.push(
"| " +
link +
" | " +
p.evidence_count +
" | " +
p.task_count +
" | " +
p.high_risk_tasks +
" | " +
p.safety_level +
" |"
);
}
}
lines.push("");
const content = lines.join("\n") + "\n";
await writeOrCheck("docs/wiki/Index.md", content);
};
generate().catch((error) => {
console.error(error.message);
process.exit(1);
});