@mcptoolshop/research-os 0.3.3 → 0.4.0
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/CHANGELOG.md +76 -0
- package/README.es.md +3 -1
- package/README.fr.md +7 -5
- package/README.hi.md +7 -5
- package/README.it.md +3 -1
- package/README.ja.md +7 -5
- package/README.md +3 -1
- package/README.pt-BR.md +3 -1
- package/README.zh.md +3 -1
- package/dist/cli.js +1824 -1247
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +131 -4
- package/dist/index.js +1487 -1163
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -998,17 +998,244 @@ var init_schema3 = __esm({
|
|
|
998
998
|
scope: z3.string().nullable(),
|
|
999
999
|
not: z3.string().nullable(),
|
|
1000
1000
|
extracted_by: ExtractorNameSchema,
|
|
1001
|
-
extracted_at: z3.string()
|
|
1001
|
+
extracted_at: z3.string(),
|
|
1002
|
+
/** Rule that classified the URL. Added by Component B (v0.4). Optional for back-compat with pre-v0.4 cards. */
|
|
1003
|
+
rule_hint: z3.string().optional(),
|
|
1004
|
+
/** Precedence level of the rule that fired (2–6). Optional for back-compat with pre-v0.4 cards. */
|
|
1005
|
+
precedence_level: z3.union([z3.literal(2), z3.literal(3), z3.literal(4), z3.literal(5), z3.literal(6)]).optional()
|
|
1002
1006
|
});
|
|
1003
1007
|
}
|
|
1004
1008
|
});
|
|
1005
1009
|
|
|
1010
|
+
// src/sources/canonical-vendors.ts
|
|
1011
|
+
var CANONICAL_VENDORS;
|
|
1012
|
+
var init_canonical_vendors = __esm({
|
|
1013
|
+
"src/sources/canonical-vendors.ts"() {
|
|
1014
|
+
"use strict";
|
|
1015
|
+
CANONICAL_VENDORS = [
|
|
1016
|
+
// ── Protocol-foundation vendors → primary ────────────────────────────────
|
|
1017
|
+
{
|
|
1018
|
+
slug: "xrpl-foundation",
|
|
1019
|
+
hosts: ["xrpl.org", "xls.xrpl.org"],
|
|
1020
|
+
github_orgs: ["XRPLF"],
|
|
1021
|
+
source_type: "primary",
|
|
1022
|
+
description: "XRPL Foundation \u2014 canonical authority for the XRPL protocol + XLS standards."
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
slug: "ipfs-foundation",
|
|
1026
|
+
hosts: ["docs.ipfs.tech", "ipfs.tech"],
|
|
1027
|
+
source_type: "primary",
|
|
1028
|
+
description: "IPFS protocol canonical surface."
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
slug: "arweave",
|
|
1032
|
+
hosts: ["docs.arweave.org", "arweave.org"],
|
|
1033
|
+
source_type: "primary",
|
|
1034
|
+
description: "Arweave protocol canonical surface."
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
slug: "anthropic",
|
|
1038
|
+
hosts: ["docs.anthropic.com", "anthropic.com", "claude.com"],
|
|
1039
|
+
source_type: "primary",
|
|
1040
|
+
description: "Claude API + Anthropic models \u2014 vendor owns the API surface. Resolves F-47: docs.anthropic.com is primary, not docs."
|
|
1041
|
+
},
|
|
1042
|
+
// ── Platform/engine vendors → docs ───────────────────────────────────────
|
|
1043
|
+
{
|
|
1044
|
+
slug: "godot-foundation",
|
|
1045
|
+
hosts: ["docs.godotengine.org", "godotengine.org"],
|
|
1046
|
+
github_orgs: ["godotengine"],
|
|
1047
|
+
source_type: "docs",
|
|
1048
|
+
description: "Godot engine docs. NB: api.github.com/repos/godotengine/godot/releases matches L4 as canonical-releases \u2192 primary."
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
slug: "apple",
|
|
1052
|
+
hosts: ["developer.apple.com", "docs.developer.apple.com"],
|
|
1053
|
+
source_type: "docs",
|
|
1054
|
+
description: "Apple platform docs. developer.apple.com/documentation/* is JS-shell (F-42); docs.developer.apple.com/tutorials/data/*.md is the text-stable alternate."
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
slug: "microsoft",
|
|
1058
|
+
hosts: ["learn.microsoft.com"],
|
|
1059
|
+
github_orgs: ["MicrosoftDocs"],
|
|
1060
|
+
source_type: "docs",
|
|
1061
|
+
description: "MS Learn + MicrosoftDocs raw GitHub."
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
slug: "mozilla",
|
|
1065
|
+
hosts: ["developer.mozilla.org"],
|
|
1066
|
+
source_type: "docs",
|
|
1067
|
+
description: "MDN web platform docs."
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
slug: "google-android",
|
|
1071
|
+
hosts: ["developer.android.com"],
|
|
1072
|
+
source_type: "docs",
|
|
1073
|
+
description: "Android platform docs."
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
slug: "google-play",
|
|
1077
|
+
hosts: [],
|
|
1078
|
+
hostPaths: ["support.google.com/googleplay"],
|
|
1079
|
+
source_type: "docs",
|
|
1080
|
+
description: "Play Store policy/help docs. Matched on host+path-prefix to avoid classifying all of support.google.com as docs."
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
slug: "itch-io",
|
|
1084
|
+
hosts: [],
|
|
1085
|
+
hostPaths: ["itch.io/docs"],
|
|
1086
|
+
source_type: "docs",
|
|
1087
|
+
description: "itch.io creator docs (not storefront chrome). Matched on host+path-prefix so itch.io/<creator>/<game> does not match L3."
|
|
1088
|
+
}
|
|
1089
|
+
];
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
// src/sources/source-type-classifier.ts
|
|
1094
|
+
import { URL as URL4 } from "url";
|
|
1095
|
+
function classifySourceType(input) {
|
|
1096
|
+
let parsed;
|
|
1097
|
+
try {
|
|
1098
|
+
parsed = new URL4(input.url);
|
|
1099
|
+
} catch {
|
|
1100
|
+
return { source_type: "unknown", rule_hint: "no-rule-match", precedence_level: 6 };
|
|
1101
|
+
}
|
|
1102
|
+
const host = parsed.hostname.toLowerCase();
|
|
1103
|
+
const pathname = parsed.pathname;
|
|
1104
|
+
if (host === "github.com" && /^\/[^/]+\/[^/]+/.test(pathname)) {
|
|
1105
|
+
return { source_type: "unknown", rule_hint: "flagged:github-ui-html", precedence_level: 2 };
|
|
1106
|
+
}
|
|
1107
|
+
for (const vendor of CANONICAL_VENDORS) {
|
|
1108
|
+
if (vendor.hosts.includes(host)) {
|
|
1109
|
+
return {
|
|
1110
|
+
source_type: vendor.source_type,
|
|
1111
|
+
rule_hint: `canonical-vendor:${vendor.slug}`,
|
|
1112
|
+
precedence_level: 3
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
for (const hp of vendor.hostPaths ?? []) {
|
|
1116
|
+
const slashIdx = hp.indexOf("/");
|
|
1117
|
+
const hpHost = slashIdx === -1 ? hp : hp.slice(0, slashIdx);
|
|
1118
|
+
const hpPath = slashIdx === -1 ? "" : "/" + hp.slice(slashIdx + 1);
|
|
1119
|
+
if (host === hpHost && (hpPath === "" || pathname === hpPath || pathname.startsWith(hpPath + "/"))) {
|
|
1120
|
+
return {
|
|
1121
|
+
source_type: vendor.source_type,
|
|
1122
|
+
rule_hint: `canonical-vendor:${vendor.slug}`,
|
|
1123
|
+
precedence_level: 3
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (host === "api.github.com") {
|
|
1129
|
+
if (pathname.startsWith("/search/issues")) {
|
|
1130
|
+
return { source_type: "forum", rule_hint: "github-api:search-issues", precedence_level: 4 };
|
|
1131
|
+
}
|
|
1132
|
+
const releasesMatch = pathname.match(/^\/repos\/([^/]+)\/([^/]+)\/releases(?:\/|$|\?)/);
|
|
1133
|
+
if (releasesMatch) {
|
|
1134
|
+
const org = releasesMatch[1].toLowerCase();
|
|
1135
|
+
if (CANONICAL_GITHUB_ORGS.has(org)) {
|
|
1136
|
+
return {
|
|
1137
|
+
source_type: "primary",
|
|
1138
|
+
rule_hint: "github-api:canonical-releases",
|
|
1139
|
+
precedence_level: 4
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
return {
|
|
1143
|
+
source_type: "secondary",
|
|
1144
|
+
rule_hint: "github-api:releases-noncanonical",
|
|
1145
|
+
precedence_level: 4
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
if (pathname.match(/^\/repos\/[^/]+\/[^/]+\/issues\/\d+(?:\/|$|\?)/)) {
|
|
1149
|
+
return { source_type: "forum", rule_hint: "github-api:issue-thread", precedence_level: 4 };
|
|
1150
|
+
}
|
|
1151
|
+
if (pathname.match(/^\/repos\/[^/]+\/[^/]+\/issues(?:\/|$|\?)/)) {
|
|
1152
|
+
return { source_type: "forum", rule_hint: "github-api:issues-list", precedence_level: 4 };
|
|
1153
|
+
}
|
|
1154
|
+
if (pathname.match(/^\/repos\/[^/]+\/[^/]+\/tags(?:\/|$|\?)/)) {
|
|
1155
|
+
return { source_type: "secondary", rule_hint: "github-api:tags", precedence_level: 4 };
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
if (host === "raw.githubusercontent.com") {
|
|
1159
|
+
const rawMatch = pathname.match(/^\/([^/]+)\/([^/]+)\/([^/]+)\/(.*)/);
|
|
1160
|
+
if (rawMatch) {
|
|
1161
|
+
const [, org, repo, , filePath] = rawMatch;
|
|
1162
|
+
const isCanonicalOrg = CANONICAL_GITHUB_ORGS.has(org.toLowerCase());
|
|
1163
|
+
const isDocsShapedRepo = /-docs$/.test(repo) || /-documentation$/.test(repo);
|
|
1164
|
+
const isDocFile = /\.(rst|md)$/i.test(filePath);
|
|
1165
|
+
if (isDocFile && (isCanonicalOrg || isDocsShapedRepo)) {
|
|
1166
|
+
return {
|
|
1167
|
+
source_type: "docs",
|
|
1168
|
+
rule_hint: "raw-github:project-docs",
|
|
1169
|
+
precedence_level: 5
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
if (/(?:^|\/)README\.md$/i.test(filePath)) {
|
|
1173
|
+
return {
|
|
1174
|
+
source_type: "secondary",
|
|
1175
|
+
rule_hint: "raw-github:plugin-readme",
|
|
1176
|
+
precedence_level: 5
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
return { source_type: "secondary", rule_hint: "raw-github:other", precedence_level: 5 };
|
|
1180
|
+
}
|
|
1181
|
+
return { source_type: "secondary", rule_hint: "raw-github:other", precedence_level: 5 };
|
|
1182
|
+
}
|
|
1183
|
+
if (host.startsWith("docs.")) {
|
|
1184
|
+
return {
|
|
1185
|
+
source_type: "docs",
|
|
1186
|
+
rule_hint: "fallback:docs-host-prefix",
|
|
1187
|
+
precedence_level: 6
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
const KNOWN_FORUM_HOSTS = ["stackoverflow.com", "reddit.com", "news.ycombinator.com"];
|
|
1191
|
+
if (KNOWN_FORUM_HOSTS.some((fh) => host === fh || host.endsWith("." + fh))) {
|
|
1192
|
+
return { source_type: "forum", rule_hint: "fallback:known-forum", precedence_level: 6 };
|
|
1193
|
+
}
|
|
1194
|
+
return { source_type: "unknown", rule_hint: "no-rule-match", precedence_level: 6 };
|
|
1195
|
+
}
|
|
1196
|
+
var CANONICAL_GITHUB_ORGS;
|
|
1197
|
+
var init_source_type_classifier = __esm({
|
|
1198
|
+
"src/sources/source-type-classifier.ts"() {
|
|
1199
|
+
"use strict";
|
|
1200
|
+
init_canonical_vendors();
|
|
1201
|
+
CANONICAL_GITHUB_ORGS = new Set(
|
|
1202
|
+
CANONICAL_VENDORS.flatMap((v) => (v.github_orgs ?? []).map((o) => o.toLowerCase()))
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
// src/sources/effective-card.ts
|
|
1208
|
+
function getEffectiveSourceType(card, overrides) {
|
|
1209
|
+
const matched = overrides.filter((o) => o.source_id === card.source_id && o.new_source_type != null).sort((a, b) => a.created_at > b.created_at ? -1 : a.created_at < b.created_at ? 1 : 0);
|
|
1210
|
+
return matched.length > 0 ? matched[0].new_source_type : card.source_type;
|
|
1211
|
+
}
|
|
1212
|
+
function getEffectivePublisher(card, overrides) {
|
|
1213
|
+
const matched = overrides.filter((o) => o.source_id === card.source_id && o.new_publisher !== void 0).sort((a, b) => a.created_at > b.created_at ? -1 : a.created_at < b.created_at ? 1 : 0);
|
|
1214
|
+
if (matched.length === 0) return card.publisher;
|
|
1215
|
+
return matched[0].new_publisher ?? null;
|
|
1216
|
+
}
|
|
1217
|
+
var init_effective_card = __esm({
|
|
1218
|
+
"src/sources/effective-card.ts"() {
|
|
1219
|
+
"use strict";
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1006
1223
|
// src/sources/cards.ts
|
|
1007
1224
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
1008
1225
|
import { existsSync as existsSync3 } from "fs";
|
|
1009
1226
|
import { join as join4 } from "path";
|
|
1010
1227
|
function buildCard(args) {
|
|
1011
1228
|
const { receipt, extraction, extractedBy } = args;
|
|
1229
|
+
const overrides = args.overrides ?? [];
|
|
1230
|
+
const classification = classifySourceType({ url: receipt.requested_url });
|
|
1231
|
+
const classifiedSourceType = classification.rule_hint === "no-rule-match" ? extraction.source_type : classification.source_type;
|
|
1232
|
+
const baseForEffective = {
|
|
1233
|
+
source_id: receipt.source_id,
|
|
1234
|
+
source_type: classifiedSourceType,
|
|
1235
|
+
publisher: extraction.publisher
|
|
1236
|
+
};
|
|
1237
|
+
const resolvedSourceType = getEffectiveSourceType(baseForEffective, overrides);
|
|
1238
|
+
const resolvedPublisher = getEffectivePublisher(baseForEffective, overrides);
|
|
1012
1239
|
const card = SourceCardSchema.parse({
|
|
1013
1240
|
source_id: receipt.source_id,
|
|
1014
1241
|
receipt_id: receipt.receipt_id,
|
|
@@ -1016,10 +1243,10 @@ function buildCard(args) {
|
|
|
1016
1243
|
url: receipt.requested_url,
|
|
1017
1244
|
final_url: receipt.final_url,
|
|
1018
1245
|
fetched_at: receipt.fetched_at,
|
|
1019
|
-
publisher:
|
|
1246
|
+
publisher: resolvedPublisher,
|
|
1020
1247
|
published_at: extraction.published_at,
|
|
1021
1248
|
title: extraction.title,
|
|
1022
|
-
source_type:
|
|
1249
|
+
source_type: resolvedSourceType,
|
|
1023
1250
|
relevance: extraction.relevance,
|
|
1024
1251
|
key_points: extraction.key_points,
|
|
1025
1252
|
limitations: extraction.limitations,
|
|
@@ -1027,7 +1254,9 @@ function buildCard(args) {
|
|
|
1027
1254
|
scope: extraction.scope,
|
|
1028
1255
|
not: extraction.not,
|
|
1029
1256
|
extracted_by: extractedBy,
|
|
1030
|
-
extracted_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1257
|
+
extracted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1258
|
+
rule_hint: classification.rule_hint,
|
|
1259
|
+
precedence_level: classification.precedence_level
|
|
1031
1260
|
});
|
|
1032
1261
|
return card;
|
|
1033
1262
|
}
|
|
@@ -1064,21 +1293,106 @@ var init_cards = __esm({
|
|
|
1064
1293
|
"src/sources/cards.ts"() {
|
|
1065
1294
|
"use strict";
|
|
1066
1295
|
init_schema3();
|
|
1296
|
+
init_source_type_classifier();
|
|
1297
|
+
init_effective_card();
|
|
1067
1298
|
}
|
|
1068
1299
|
});
|
|
1069
1300
|
|
|
1070
|
-
// src/sources/
|
|
1301
|
+
// src/sources/source-card-overrides-schema.ts
|
|
1302
|
+
import { z as z4 } from "zod";
|
|
1303
|
+
function validateSourceCardOverride(input) {
|
|
1304
|
+
return SourceCardOverrideSchema.parse(input);
|
|
1305
|
+
}
|
|
1306
|
+
var SourceCardOverrideSchema;
|
|
1307
|
+
var init_source_card_overrides_schema = __esm({
|
|
1308
|
+
"src/sources/source-card-overrides-schema.ts"() {
|
|
1309
|
+
"use strict";
|
|
1310
|
+
init_schema3();
|
|
1311
|
+
SourceCardOverrideSchema = z4.object({
|
|
1312
|
+
source_id: z4.string().min(1, "source_id must be non-empty"),
|
|
1313
|
+
url: z4.string().min(1, "url must be non-empty"),
|
|
1314
|
+
previous_source_type: z4.string().nullable().optional(),
|
|
1315
|
+
new_source_type: SourceTypeSchema.nullable().optional(),
|
|
1316
|
+
previous_publisher: z4.string().nullable().optional(),
|
|
1317
|
+
new_publisher: z4.string().nullable().optional(),
|
|
1318
|
+
reason: z4.string().refine((v) => v.trim().length > 0, { message: "reason must be non-empty after trim" }),
|
|
1319
|
+
operator: z4.string().min(1, "operator must be non-empty"),
|
|
1320
|
+
created_at: z4.string().refine((v) => isFinite(Date.parse(v)), {
|
|
1321
|
+
message: "created_at must be a valid ISO 8601 timestamp"
|
|
1322
|
+
}),
|
|
1323
|
+
rule_hint: z4.string().nullable().optional(),
|
|
1324
|
+
pack_version: z4.string().min(1, "pack_version must be non-empty")
|
|
1325
|
+
}).refine(
|
|
1326
|
+
(obj) => obj.new_source_type != null && obj.new_source_type !== void 0 || obj.new_publisher != null && obj.new_publisher !== void 0,
|
|
1327
|
+
{
|
|
1328
|
+
message: "At least one of new_source_type or new_publisher must be present and non-null"
|
|
1329
|
+
}
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
// src/sources/source-card-overrides.ts
|
|
1335
|
+
import { readFile as readFile5, appendFile as appendFile2, mkdir as mkdir5 } from "fs/promises";
|
|
1071
1336
|
import { existsSync as existsSync4 } from "fs";
|
|
1072
|
-
import { join as join5,
|
|
1337
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
1338
|
+
function overridesPath(packPath) {
|
|
1339
|
+
return join5(packPath, "evidence", OVERRIDES_FILE);
|
|
1340
|
+
}
|
|
1341
|
+
async function readOverrides(packPath) {
|
|
1342
|
+
const filePath = overridesPath(packPath);
|
|
1343
|
+
if (!existsSync4(filePath)) return [];
|
|
1344
|
+
const content = await readFile5(filePath, "utf8");
|
|
1345
|
+
const lines = content.split(/\r?\n/).filter(Boolean);
|
|
1346
|
+
return lines.map((line, idx) => {
|
|
1347
|
+
const lineNum = idx + 1;
|
|
1348
|
+
let parsed;
|
|
1349
|
+
try {
|
|
1350
|
+
parsed = JSON.parse(line);
|
|
1351
|
+
} catch {
|
|
1352
|
+
throw new Error(
|
|
1353
|
+
`source-card-overrides.jsonl: malformed JSON at line ${lineNum}: ${line.slice(0, 120)}`
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
try {
|
|
1357
|
+
return validateSourceCardOverride(parsed);
|
|
1358
|
+
} catch (err) {
|
|
1359
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1360
|
+
throw new Error(
|
|
1361
|
+
`source-card-overrides.jsonl: invalid override at line ${lineNum}: ${msg}
|
|
1362
|
+
Content: ${line.slice(0, 120)}`,
|
|
1363
|
+
{ cause: err }
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
async function appendOverride(packPath, override) {
|
|
1369
|
+
validateSourceCardOverride(override);
|
|
1370
|
+
const filePath = overridesPath(packPath);
|
|
1371
|
+
await mkdir5(dirname2(filePath), { recursive: true });
|
|
1372
|
+
await appendFile2(filePath, JSON.stringify(override) + "\n", "utf8");
|
|
1373
|
+
}
|
|
1374
|
+
var OVERRIDES_FILE;
|
|
1375
|
+
var init_source_card_overrides = __esm({
|
|
1376
|
+
"src/sources/source-card-overrides.ts"() {
|
|
1377
|
+
"use strict";
|
|
1378
|
+
init_source_card_overrides_schema();
|
|
1379
|
+
OVERRIDES_FILE = "source-card-overrides.jsonl";
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
// src/sources/gather.ts
|
|
1384
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1385
|
+
import { join as join6, resolve as resolve3 } from "path";
|
|
1073
1386
|
async function gather(options) {
|
|
1074
1387
|
const packPath = options.packPath ? resolve3(options.packPath) : process.cwd();
|
|
1075
|
-
if (!
|
|
1076
|
-
if (!
|
|
1388
|
+
if (!existsSync5(join6(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
1389
|
+
if (!existsSync5(join6(packPath, "sections", options.sectionId)))
|
|
1077
1390
|
throw new SectionNotFoundError(options.sectionId);
|
|
1078
1391
|
const { urls } = await collectUrls({ urls: options.urls, urlsFile: options.urlsFile });
|
|
1079
1392
|
if (urls.length === 0) throw new NoUrlsProvidedError();
|
|
1080
1393
|
const extractorList = options.extractors ?? defaultExtractors();
|
|
1081
1394
|
const extractor = await pickExtractor(extractorList);
|
|
1395
|
+
const overrides = await readOverrides(packPath);
|
|
1082
1396
|
const summary = {
|
|
1083
1397
|
sectionId: options.sectionId,
|
|
1084
1398
|
attempted: urls.length,
|
|
@@ -1113,7 +1427,7 @@ async function gather(options) {
|
|
|
1113
1427
|
extraction_extractor: extractor.name,
|
|
1114
1428
|
extraction_error: null
|
|
1115
1429
|
};
|
|
1116
|
-
const card = buildCard({ receipt: receiptToWrite, extraction: result, extractedBy: extractor.name });
|
|
1430
|
+
const card = buildCard({ receipt: receiptToWrite, extraction: result, extractedBy: extractor.name, overrides });
|
|
1117
1431
|
await writeSourceCard(packPath, card);
|
|
1118
1432
|
await appendSectionSourceId(packPath, options.sectionId, card.source_id);
|
|
1119
1433
|
summary.cardsWritten += 1;
|
|
@@ -1143,27 +1457,28 @@ var init_gather = __esm({
|
|
|
1143
1457
|
init_url_input();
|
|
1144
1458
|
init_extractors();
|
|
1145
1459
|
init_cards();
|
|
1460
|
+
init_source_card_overrides();
|
|
1146
1461
|
}
|
|
1147
1462
|
});
|
|
1148
1463
|
|
|
1149
1464
|
// src/sources/excerpts/schema.ts
|
|
1150
|
-
import { z as
|
|
1465
|
+
import { z as z5 } from "zod";
|
|
1151
1466
|
var EXCERPT_ID_PATTERN, ExcerptOriginSchema, ExcerptSchema;
|
|
1152
1467
|
var init_schema4 = __esm({
|
|
1153
1468
|
"src/sources/excerpts/schema.ts"() {
|
|
1154
1469
|
"use strict";
|
|
1155
1470
|
EXCERPT_ID_PATTERN = /^ex_[a-f0-9]{12}_\d{3,}$/;
|
|
1156
|
-
ExcerptOriginSchema =
|
|
1157
|
-
ExcerptSchema =
|
|
1158
|
-
excerpt_id:
|
|
1159
|
-
source_id:
|
|
1160
|
-
source_hash:
|
|
1161
|
-
text:
|
|
1162
|
-
location_hint:
|
|
1163
|
-
char_start:
|
|
1164
|
-
char_end:
|
|
1471
|
+
ExcerptOriginSchema = z5.enum(["raw_text", "key_point"]);
|
|
1472
|
+
ExcerptSchema = z5.object({
|
|
1473
|
+
excerpt_id: z5.string().regex(EXCERPT_ID_PATTERN),
|
|
1474
|
+
source_id: z5.string().regex(/^src_[a-f0-9]{12}$/),
|
|
1475
|
+
source_hash: z5.string().regex(/^[a-f0-9]{64}$/).nullable(),
|
|
1476
|
+
text: z5.string().min(1),
|
|
1477
|
+
location_hint: z5.string().nullable(),
|
|
1478
|
+
char_start: z5.number().int().nonnegative(),
|
|
1479
|
+
char_end: z5.number().int().nonnegative(),
|
|
1165
1480
|
origin: ExcerptOriginSchema,
|
|
1166
|
-
created_at:
|
|
1481
|
+
created_at: z5.string()
|
|
1167
1482
|
});
|
|
1168
1483
|
}
|
|
1169
1484
|
});
|
|
@@ -1292,11 +1607,11 @@ var init_chunk = __esm({
|
|
|
1292
1607
|
});
|
|
1293
1608
|
|
|
1294
1609
|
// src/sources/excerpts/ledger.ts
|
|
1295
|
-
import { existsSync as
|
|
1296
|
-
import { mkdir as
|
|
1297
|
-
import { dirname as
|
|
1610
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1611
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
1612
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
1298
1613
|
function ledgerPathFor(packPath, sourceId) {
|
|
1299
|
-
return
|
|
1614
|
+
return join7(packPath, "evidence", "excerpts", `${sourceId}.jsonl`);
|
|
1300
1615
|
}
|
|
1301
1616
|
function makeExcerptId(sourceId, index) {
|
|
1302
1617
|
const idx = String(index).padStart(3, "0");
|
|
@@ -1304,7 +1619,7 @@ function makeExcerptId(sourceId, index) {
|
|
|
1304
1619
|
return `ex_${hex}_${idx}`;
|
|
1305
1620
|
}
|
|
1306
1621
|
async function readLedger(path) {
|
|
1307
|
-
const text = await
|
|
1622
|
+
const text = await readFile6(path, "utf8");
|
|
1308
1623
|
const out = [];
|
|
1309
1624
|
for (const line of text.split(/\r?\n/)) {
|
|
1310
1625
|
if (!line.trim()) continue;
|
|
@@ -1316,7 +1631,7 @@ async function readLedger(path) {
|
|
|
1316
1631
|
return out;
|
|
1317
1632
|
}
|
|
1318
1633
|
async function writeLedger(path, excerpts) {
|
|
1319
|
-
await
|
|
1634
|
+
await mkdir6(dirname3(path), { recursive: true });
|
|
1320
1635
|
const body = excerpts.map((e) => JSON.stringify(e)).join("\n") + (excerpts.length > 0 ? "\n" : "");
|
|
1321
1636
|
await writeFile5(path, body, "utf8");
|
|
1322
1637
|
}
|
|
@@ -1324,7 +1639,7 @@ async function loadOrBuildLedger(args) {
|
|
|
1324
1639
|
const { packPath, sourceCard, sourceHash, rawText } = args;
|
|
1325
1640
|
const now = args.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
1326
1641
|
const path = ledgerPathFor(packPath, sourceCard.source_id);
|
|
1327
|
-
if (
|
|
1642
|
+
if (existsSync6(path)) {
|
|
1328
1643
|
const excerpts2 = await readLedger(path);
|
|
1329
1644
|
if (excerpts2.length > 0) {
|
|
1330
1645
|
const origins = new Set(excerpts2.map((e) => e.origin));
|
|
@@ -1393,6 +1708,9 @@ var init_sources = __esm({
|
|
|
1393
1708
|
init_extractors();
|
|
1394
1709
|
init_ollama_intern();
|
|
1395
1710
|
init_cards();
|
|
1711
|
+
init_source_card_overrides();
|
|
1712
|
+
init_source_card_overrides_schema();
|
|
1713
|
+
init_effective_card();
|
|
1396
1714
|
init_schema3();
|
|
1397
1715
|
init_excerpts();
|
|
1398
1716
|
}
|
|
@@ -1703,54 +2021,54 @@ var init_extractors2 = __esm({
|
|
|
1703
2021
|
});
|
|
1704
2022
|
|
|
1705
2023
|
// src/claims/schema.ts
|
|
1706
|
-
import { z as
|
|
2024
|
+
import { z as z6 } from "zod";
|
|
1707
2025
|
var ConfidenceSchema, ClaimExtractorSchema, ReviewStateSchema, ClaimSchema;
|
|
1708
2026
|
var init_schema5 = __esm({
|
|
1709
2027
|
"src/claims/schema.ts"() {
|
|
1710
2028
|
"use strict";
|
|
1711
2029
|
init_schema4();
|
|
1712
|
-
ConfidenceSchema =
|
|
1713
|
-
ClaimExtractorSchema =
|
|
1714
|
-
ReviewStateSchema =
|
|
2030
|
+
ConfidenceSchema = z6.enum(["low", "medium", "high"]);
|
|
2031
|
+
ClaimExtractorSchema = z6.enum(["heuristic", "ollama-intern"]);
|
|
2032
|
+
ReviewStateSchema = z6.enum([
|
|
1715
2033
|
"candidate",
|
|
1716
2034
|
"gated",
|
|
1717
2035
|
"reviewed",
|
|
1718
2036
|
"rejected",
|
|
1719
2037
|
"accepted"
|
|
1720
2038
|
]);
|
|
1721
|
-
ClaimSchema =
|
|
1722
|
-
claim_id:
|
|
1723
|
-
section_id:
|
|
1724
|
-
source_ids:
|
|
1725
|
-
source_hashes:
|
|
1726
|
-
asserts:
|
|
1727
|
-
scope:
|
|
1728
|
-
not:
|
|
2039
|
+
ClaimSchema = z6.object({
|
|
2040
|
+
claim_id: z6.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
|
|
2041
|
+
section_id: z6.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2042
|
+
source_ids: z6.array(z6.string().regex(/^src_[a-f0-9]{12}$/)).min(1, "every claim must reference at least one source_id"),
|
|
2043
|
+
source_hashes: z6.array(z6.string().regex(/^[a-f0-9]{64}$/)),
|
|
2044
|
+
asserts: z6.string().min(1),
|
|
2045
|
+
scope: z6.string().nullable(),
|
|
2046
|
+
not: z6.string().nullable(),
|
|
1729
2047
|
// Span-first extraction: the model picks excerpt IDs from the deterministic
|
|
1730
2048
|
// ledger; research-os copies the literal text into evidence_excerpt. Models
|
|
1731
2049
|
// may interpret source spans; they may not author evidence spans.
|
|
1732
2050
|
// Allowed empty for legacy claims that pre-date span-first extraction —
|
|
1733
2051
|
// those should be re-extracted; new writes always populate at least one ID.
|
|
1734
|
-
evidence_excerpt_ids:
|
|
1735
|
-
evidence_excerpt:
|
|
1736
|
-
evidence_location:
|
|
2052
|
+
evidence_excerpt_ids: z6.array(z6.string().regex(EXCERPT_ID_PATTERN)).default([]),
|
|
2053
|
+
evidence_excerpt: z6.string().min(1),
|
|
2054
|
+
evidence_location: z6.string().nullable(),
|
|
1737
2055
|
confidence: ConfidenceSchema,
|
|
1738
2056
|
extractor: ClaimExtractorSchema,
|
|
1739
|
-
extraction_method:
|
|
1740
|
-
created_at:
|
|
2057
|
+
extraction_method: z6.string().min(1),
|
|
2058
|
+
created_at: z6.string(),
|
|
1741
2059
|
review_state: ReviewStateSchema
|
|
1742
2060
|
});
|
|
1743
2061
|
}
|
|
1744
2062
|
});
|
|
1745
2063
|
|
|
1746
2064
|
// src/claims/extract.ts
|
|
1747
|
-
import { existsSync as
|
|
1748
|
-
import { appendFile as
|
|
1749
|
-
import { join as
|
|
2065
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2066
|
+
import { appendFile as appendFile3, mkdir as mkdir7, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
2067
|
+
import { join as join8, resolve as resolve4 } from "path";
|
|
1750
2068
|
async function readSectionSourceIds(packPath, sectionId) {
|
|
1751
|
-
const path =
|
|
1752
|
-
if (!
|
|
1753
|
-
const text = await
|
|
2069
|
+
const path = join8(packPath, "sections", sectionId, "sources.jsonl");
|
|
2070
|
+
if (!existsSync7(path)) return [];
|
|
2071
|
+
const text = await readFile7(path, "utf8");
|
|
1754
2072
|
const ids = [];
|
|
1755
2073
|
for (const line of text.split(/\r?\n/)) {
|
|
1756
2074
|
if (!line.trim()) continue;
|
|
@@ -1763,15 +2081,15 @@ async function readSectionSourceIds(packPath, sectionId) {
|
|
|
1763
2081
|
return ids;
|
|
1764
2082
|
}
|
|
1765
2083
|
async function readSourceCard(packPath, sourceId) {
|
|
1766
|
-
const path =
|
|
1767
|
-
if (!
|
|
1768
|
-
const text = await
|
|
2084
|
+
const path = join8(packPath, "evidence", "source-cards", `${sourceId}.json`);
|
|
2085
|
+
if (!existsSync7(path)) return null;
|
|
2086
|
+
const text = await readFile7(path, "utf8");
|
|
1769
2087
|
return SourceCardSchema.parse(JSON.parse(text));
|
|
1770
2088
|
}
|
|
1771
2089
|
async function findLatestReceipt(packPath, sourceId) {
|
|
1772
|
-
const path =
|
|
1773
|
-
if (!
|
|
1774
|
-
const text = await
|
|
2090
|
+
const path = join8(packPath, "evidence", "fetch-log.jsonl");
|
|
2091
|
+
if (!existsSync7(path)) return null;
|
|
2092
|
+
const text = await readFile7(path, "utf8");
|
|
1775
2093
|
let latest = null;
|
|
1776
2094
|
for (const line of text.split(/\r?\n/)) {
|
|
1777
2095
|
if (!line.trim()) continue;
|
|
@@ -1786,9 +2104,9 @@ async function findLatestReceipt(packPath, sourceId) {
|
|
|
1786
2104
|
return latest;
|
|
1787
2105
|
}
|
|
1788
2106
|
async function readExistingClaimIds(packPath, sectionId) {
|
|
1789
|
-
const path =
|
|
1790
|
-
if (!
|
|
1791
|
-
const text = await
|
|
2107
|
+
const path = join8(packPath, "sections", sectionId, "claims.jsonl");
|
|
2108
|
+
if (!existsSync7(path)) return /* @__PURE__ */ new Set();
|
|
2109
|
+
const text = await readFile7(path, "utf8");
|
|
1792
2110
|
const set = /* @__PURE__ */ new Set();
|
|
1793
2111
|
for (const line of text.split(/\r?\n/)) {
|
|
1794
2112
|
if (!line.trim()) continue;
|
|
@@ -1863,14 +2181,14 @@ function buildClaim(args) {
|
|
|
1863
2181
|
}
|
|
1864
2182
|
async function extract(options) {
|
|
1865
2183
|
const packPath = options.packPath ? resolve4(options.packPath) : process.cwd();
|
|
1866
|
-
if (!
|
|
1867
|
-
if (!
|
|
2184
|
+
if (!existsSync7(join8(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
2185
|
+
if (!existsSync7(join8(packPath, "sections", options.sectionId)))
|
|
1868
2186
|
throw new SectionNotFoundError(options.sectionId);
|
|
1869
2187
|
const sourceIds = await readSectionSourceIds(packPath, options.sectionId);
|
|
1870
2188
|
if (sourceIds.length === 0) throw new NoSourcesGatheredError(options.sectionId);
|
|
1871
2189
|
const adapters = options.extractors ?? defaultClaimExtractors();
|
|
1872
2190
|
const extractor = await pickClaimExtractor(adapters);
|
|
1873
|
-
const claimsPath =
|
|
2191
|
+
const claimsPath = join8(packPath, "sections", options.sectionId, "claims.jsonl");
|
|
1874
2192
|
const existingIds = await readExistingClaimIds(packPath, options.sectionId);
|
|
1875
2193
|
const summary = {
|
|
1876
2194
|
sectionId: options.sectionId,
|
|
@@ -1899,9 +2217,9 @@ async function extract(options) {
|
|
|
1899
2217
|
const receipt2 = await findLatestReceipt(packPath, sourceId);
|
|
1900
2218
|
let rawText = null;
|
|
1901
2219
|
if (receipt2?.raw_text_path) {
|
|
1902
|
-
const raw =
|
|
1903
|
-
if (
|
|
1904
|
-
rawText = await
|
|
2220
|
+
const raw = join8(packPath, receipt2.raw_text_path);
|
|
2221
|
+
if (existsSync7(raw)) {
|
|
2222
|
+
rawText = await readFile7(raw, "utf8");
|
|
1905
2223
|
}
|
|
1906
2224
|
}
|
|
1907
2225
|
const ledger = await loadOrBuildLedger({
|
|
@@ -1957,7 +2275,7 @@ async function extract(options) {
|
|
|
1957
2275
|
summary.claimsDeduped += 1;
|
|
1958
2276
|
continue;
|
|
1959
2277
|
}
|
|
1960
|
-
await
|
|
2278
|
+
await appendFile3(claimsPath, JSON.stringify(claim) + "\n", "utf8");
|
|
1961
2279
|
existingIds.add(claim.claim_id);
|
|
1962
2280
|
summary.claimsAdded += 1;
|
|
1963
2281
|
summary.claimIds.push(claim.claim_id);
|
|
@@ -1986,10 +2304,10 @@ async function extract(options) {
|
|
|
1986
2304
|
kind: /not valid JSON/i.test(f.reason) ? "extractor_invalid_json" : /aborted|timeout/i.test(f.reason) ? "extractor_timeout" : /HTTP \d{3}/i.test(f.reason) ? "extractor_http_error" : "extractor_other"
|
|
1987
2305
|
}))
|
|
1988
2306
|
};
|
|
1989
|
-
const auditsDir =
|
|
1990
|
-
await
|
|
2307
|
+
const auditsDir = join8(packPath, "audits");
|
|
2308
|
+
await mkdir7(auditsDir, { recursive: true });
|
|
1991
2309
|
await writeFile6(
|
|
1992
|
-
|
|
2310
|
+
join8(auditsDir, `${options.sectionId}-claim-extract.json`),
|
|
1993
2311
|
JSON.stringify(receipt, null, 2),
|
|
1994
2312
|
"utf8"
|
|
1995
2313
|
);
|
|
@@ -2015,61 +2333,61 @@ var init_extract = __esm({
|
|
|
2015
2333
|
});
|
|
2016
2334
|
|
|
2017
2335
|
// src/claims/density/schema.ts
|
|
2018
|
-
import { z as
|
|
2336
|
+
import { z as z7 } from "zod";
|
|
2019
2337
|
var PerSourceDensitySchema, NearDuplicateClusterSchema, DensityFlagSchema, ClaimDensityAuditSchema;
|
|
2020
2338
|
var init_schema6 = __esm({
|
|
2021
2339
|
"src/claims/density/schema.ts"() {
|
|
2022
2340
|
"use strict";
|
|
2023
|
-
PerSourceDensitySchema =
|
|
2024
|
-
source_id:
|
|
2025
|
-
publisher:
|
|
2026
|
-
source_word_count:
|
|
2027
|
-
claim_count:
|
|
2028
|
-
claims_per_1k_words:
|
|
2029
|
-
share_of_section:
|
|
2341
|
+
PerSourceDensitySchema = z7.object({
|
|
2342
|
+
source_id: z7.string().regex(/^src_[a-f0-9]{12}$/),
|
|
2343
|
+
publisher: z7.string().nullable(),
|
|
2344
|
+
source_word_count: z7.number().int().nonnegative(),
|
|
2345
|
+
claim_count: z7.number().int().nonnegative(),
|
|
2346
|
+
claims_per_1k_words: z7.number(),
|
|
2347
|
+
share_of_section: z7.number(),
|
|
2030
2348
|
// 0..1
|
|
2031
|
-
weak_scope_count:
|
|
2032
|
-
generic_scope_count:
|
|
2349
|
+
weak_scope_count: z7.number().int().nonnegative(),
|
|
2350
|
+
generic_scope_count: z7.number().int().nonnegative()
|
|
2033
2351
|
});
|
|
2034
|
-
NearDuplicateClusterSchema =
|
|
2035
|
-
representative_assert:
|
|
2036
|
-
member_count:
|
|
2037
|
-
claim_ids:
|
|
2352
|
+
NearDuplicateClusterSchema = z7.object({
|
|
2353
|
+
representative_assert: z7.string(),
|
|
2354
|
+
member_count: z7.number().int().nonnegative(),
|
|
2355
|
+
claim_ids: z7.array(z7.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/))
|
|
2038
2356
|
});
|
|
2039
|
-
DensityFlagSchema =
|
|
2040
|
-
type:
|
|
2357
|
+
DensityFlagSchema = z7.object({
|
|
2358
|
+
type: z7.enum([
|
|
2041
2359
|
"source_dominance",
|
|
2042
2360
|
"high_per_word_density",
|
|
2043
2361
|
"large_near_duplicate_cluster",
|
|
2044
2362
|
"weak_scope_majority"
|
|
2045
2363
|
]),
|
|
2046
|
-
severity:
|
|
2047
|
-
message:
|
|
2048
|
-
affects_source_ids:
|
|
2049
|
-
affects_claim_ids:
|
|
2364
|
+
severity: z7.enum(["info", "warn", "block"]),
|
|
2365
|
+
message: z7.string(),
|
|
2366
|
+
affects_source_ids: z7.array(z7.string().regex(/^src_[a-f0-9]{12}$/)).default([]),
|
|
2367
|
+
affects_claim_ids: z7.array(z7.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)).default([])
|
|
2050
2368
|
});
|
|
2051
|
-
ClaimDensityAuditSchema =
|
|
2052
|
-
audit_id:
|
|
2053
|
-
section_id:
|
|
2054
|
-
audited_at:
|
|
2055
|
-
research_os_version:
|
|
2056
|
-
candidate_claim_count:
|
|
2057
|
-
source_count:
|
|
2058
|
-
total_source_word_count:
|
|
2059
|
-
claims_per_1k_words:
|
|
2060
|
-
weak_scope_count:
|
|
2061
|
-
generic_scope_count:
|
|
2062
|
-
per_source:
|
|
2063
|
-
near_duplicate_clusters:
|
|
2064
|
-
flags:
|
|
2369
|
+
ClaimDensityAuditSchema = z7.object({
|
|
2370
|
+
audit_id: z7.string().regex(/^cda_[0-9]+_[a-z0-9-]+$/),
|
|
2371
|
+
section_id: z7.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2372
|
+
audited_at: z7.string(),
|
|
2373
|
+
research_os_version: z7.string(),
|
|
2374
|
+
candidate_claim_count: z7.number().int().nonnegative(),
|
|
2375
|
+
source_count: z7.number().int().nonnegative(),
|
|
2376
|
+
total_source_word_count: z7.number().int().nonnegative(),
|
|
2377
|
+
claims_per_1k_words: z7.number(),
|
|
2378
|
+
weak_scope_count: z7.number().int().nonnegative(),
|
|
2379
|
+
generic_scope_count: z7.number().int().nonnegative(),
|
|
2380
|
+
per_source: z7.array(PerSourceDensitySchema),
|
|
2381
|
+
near_duplicate_clusters: z7.array(NearDuplicateClusterSchema),
|
|
2382
|
+
flags: z7.array(DensityFlagSchema)
|
|
2065
2383
|
});
|
|
2066
2384
|
}
|
|
2067
2385
|
});
|
|
2068
2386
|
|
|
2069
2387
|
// src/claims/density/run.ts
|
|
2070
|
-
import { existsSync as
|
|
2071
|
-
import { mkdir as
|
|
2072
|
-
import { join as
|
|
2388
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2389
|
+
import { mkdir as mkdir8, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
2390
|
+
import { join as join9, resolve as resolve5 } from "path";
|
|
2073
2391
|
function normalize2(s) {
|
|
2074
2392
|
return s.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").toLowerCase().replace(/[^a-z0-9 ]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2075
2393
|
}
|
|
@@ -2078,9 +2396,9 @@ function countWords(text) {
|
|
|
2078
2396
|
return normalize2(text).split(" ").filter((w) => w.length > 0).length;
|
|
2079
2397
|
}
|
|
2080
2398
|
async function readClaims(packPath, sectionId) {
|
|
2081
|
-
const path =
|
|
2082
|
-
if (!
|
|
2083
|
-
const text = await
|
|
2399
|
+
const path = join9(packPath, "sections", sectionId, "claims.jsonl");
|
|
2400
|
+
if (!existsSync8(path)) return [];
|
|
2401
|
+
const text = await readFile8(path, "utf8");
|
|
2084
2402
|
const out = [];
|
|
2085
2403
|
for (const line of text.split(/\r?\n/)) {
|
|
2086
2404
|
if (!line.trim()) continue;
|
|
@@ -2094,10 +2412,10 @@ async function readClaims(packPath, sectionId) {
|
|
|
2094
2412
|
async function readSourceCards(packPath, sourceIds) {
|
|
2095
2413
|
const out = /* @__PURE__ */ new Map();
|
|
2096
2414
|
for (const sid of sourceIds) {
|
|
2097
|
-
const p =
|
|
2098
|
-
if (!
|
|
2415
|
+
const p = join9(packPath, "evidence", "source-cards", `${sid}.json`);
|
|
2416
|
+
if (!existsSync8(p)) continue;
|
|
2099
2417
|
try {
|
|
2100
|
-
out.set(sid, SourceCardSchema.parse(JSON.parse(await
|
|
2418
|
+
out.set(sid, SourceCardSchema.parse(JSON.parse(await readFile8(p, "utf8"))));
|
|
2101
2419
|
} catch {
|
|
2102
2420
|
}
|
|
2103
2421
|
}
|
|
@@ -2105,9 +2423,9 @@ async function readSourceCards(packPath, sourceIds) {
|
|
|
2105
2423
|
}
|
|
2106
2424
|
async function readLatestReceipts(packPath) {
|
|
2107
2425
|
const out = /* @__PURE__ */ new Map();
|
|
2108
|
-
const path =
|
|
2109
|
-
if (!
|
|
2110
|
-
const text = await
|
|
2426
|
+
const path = join9(packPath, "evidence", "fetch-log.jsonl");
|
|
2427
|
+
if (!existsSync8(path)) return out;
|
|
2428
|
+
const text = await readFile8(path, "utf8");
|
|
2111
2429
|
for (const line of text.split(/\r?\n/)) {
|
|
2112
2430
|
if (!line.trim()) continue;
|
|
2113
2431
|
try {
|
|
@@ -2122,9 +2440,9 @@ async function readLatestReceipts(packPath) {
|
|
|
2122
2440
|
}
|
|
2123
2441
|
async function readRawText(packPath, receipt) {
|
|
2124
2442
|
if (!receipt.raw_text_path) return null;
|
|
2125
|
-
const p =
|
|
2126
|
-
if (!
|
|
2127
|
-
return await
|
|
2443
|
+
const p = join9(packPath, receipt.raw_text_path);
|
|
2444
|
+
if (!existsSync8(p)) return null;
|
|
2445
|
+
return await readFile8(p, "utf8");
|
|
2128
2446
|
}
|
|
2129
2447
|
function isWeakScope(claim) {
|
|
2130
2448
|
return claim.scope === null && claim.not === null && claim.asserts.length > 40;
|
|
@@ -2257,8 +2575,8 @@ function buildMarkdown(audit2) {
|
|
|
2257
2575
|
}
|
|
2258
2576
|
async function auditDensity(options) {
|
|
2259
2577
|
const packPath = options.packPath ? resolve5(options.packPath) : process.cwd();
|
|
2260
|
-
if (!
|
|
2261
|
-
if (!
|
|
2578
|
+
if (!existsSync8(join9(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
2579
|
+
if (!existsSync8(join9(packPath, "sections", options.sectionId)))
|
|
2262
2580
|
throw new SectionNotFoundError(options.sectionId);
|
|
2263
2581
|
const claims = await readClaims(packPath, options.sectionId);
|
|
2264
2582
|
const allSourceIds = Array.from(
|
|
@@ -2331,10 +2649,10 @@ async function auditDensity(options) {
|
|
|
2331
2649
|
}));
|
|
2332
2650
|
const flags = [...buildFlags(partial), ...clusterFlags];
|
|
2333
2651
|
const audit2 = ClaimDensityAuditSchema.parse({ ...partial, flags });
|
|
2334
|
-
const auditsDir =
|
|
2335
|
-
await
|
|
2336
|
-
const jsonPath =
|
|
2337
|
-
const markdownPath =
|
|
2652
|
+
const auditsDir = join9(packPath, "audits");
|
|
2653
|
+
await mkdir8(auditsDir, { recursive: true });
|
|
2654
|
+
const jsonPath = join9(auditsDir, `${options.sectionId}-claim-density.json`);
|
|
2655
|
+
const markdownPath = join9(auditsDir, `${options.sectionId}-claim-density.md`);
|
|
2338
2656
|
await writeFile7(jsonPath, JSON.stringify(audit2, null, 2), "utf8");
|
|
2339
2657
|
await writeFile7(markdownPath, buildMarkdown(audit2), "utf8");
|
|
2340
2658
|
return { audit: audit2, jsonPath, markdownPath };
|
|
@@ -2378,12 +2696,12 @@ var init_claims = __esm({
|
|
|
2378
2696
|
});
|
|
2379
2697
|
|
|
2380
2698
|
// src/contradictions/schema.ts
|
|
2381
|
-
import { z as
|
|
2699
|
+
import { z as z8 } from "zod";
|
|
2382
2700
|
var ContradictionTypeSchema, SeveritySchema, OverlapAssessmentSchema, ContradictionDetectorSchema, ContradictionStatusSchema, ContradictionConfidenceSchema, ContradictionSchema;
|
|
2383
2701
|
var init_schema7 = __esm({
|
|
2384
2702
|
"src/contradictions/schema.ts"() {
|
|
2385
2703
|
"use strict";
|
|
2386
|
-
ContradictionTypeSchema =
|
|
2704
|
+
ContradictionTypeSchema = z8.enum([
|
|
2387
2705
|
"direct_conflict",
|
|
2388
2706
|
"scope_conflict",
|
|
2389
2707
|
"temporal_conflict",
|
|
@@ -2391,37 +2709,37 @@ var init_schema7 = __esm({
|
|
|
2391
2709
|
"evidence_conflict",
|
|
2392
2710
|
"overgeneralization_risk"
|
|
2393
2711
|
]);
|
|
2394
|
-
SeveritySchema =
|
|
2395
|
-
OverlapAssessmentSchema =
|
|
2712
|
+
SeveritySchema = z8.enum(["low", "medium", "high", "blocking"]);
|
|
2713
|
+
OverlapAssessmentSchema = z8.enum([
|
|
2396
2714
|
"fully_overlapping",
|
|
2397
2715
|
"partially_overlapping",
|
|
2398
2716
|
"non_overlapping",
|
|
2399
2717
|
"unknown"
|
|
2400
2718
|
]);
|
|
2401
|
-
ContradictionDetectorSchema =
|
|
2402
|
-
ContradictionStatusSchema =
|
|
2719
|
+
ContradictionDetectorSchema = z8.enum(["heuristic", "ollama-intern"]);
|
|
2720
|
+
ContradictionStatusSchema = z8.enum([
|
|
2403
2721
|
"unresolved",
|
|
2404
2722
|
"reconciled",
|
|
2405
2723
|
"preserved_deliberately",
|
|
2406
2724
|
"rejected"
|
|
2407
2725
|
]);
|
|
2408
|
-
ContradictionConfidenceSchema =
|
|
2409
|
-
ContradictionSchema =
|
|
2410
|
-
contradiction_id:
|
|
2411
|
-
section_id:
|
|
2412
|
-
claim_ids:
|
|
2413
|
-
source_ids:
|
|
2726
|
+
ContradictionConfidenceSchema = z8.enum(["low", "medium", "high"]);
|
|
2727
|
+
ContradictionSchema = z8.object({
|
|
2728
|
+
contradiction_id: z8.string().regex(/^cnt_[a-f0-9]{12}_(heuristic|ollama_intern)$/),
|
|
2729
|
+
section_id: z8.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2730
|
+
claim_ids: z8.array(z8.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)).length(2, "contradictions are pair-wise in v0.1"),
|
|
2731
|
+
source_ids: z8.array(z8.string().regex(/^src_[a-f0-9]{12}$/)).min(1),
|
|
2414
2732
|
type: ContradictionTypeSchema,
|
|
2415
|
-
summary:
|
|
2416
|
-
scope_analysis:
|
|
2733
|
+
summary: z8.string().min(1),
|
|
2734
|
+
scope_analysis: z8.string(),
|
|
2417
2735
|
overlap_assessment: OverlapAssessmentSchema,
|
|
2418
2736
|
severity: SeveritySchema,
|
|
2419
2737
|
confidence: ContradictionConfidenceSchema,
|
|
2420
2738
|
detector: ContradictionDetectorSchema,
|
|
2421
|
-
detection_method:
|
|
2422
|
-
evidence:
|
|
2739
|
+
detection_method: z8.string().min(1),
|
|
2740
|
+
evidence: z8.string(),
|
|
2423
2741
|
status: ContradictionStatusSchema,
|
|
2424
|
-
created_at:
|
|
2742
|
+
created_at: z8.string()
|
|
2425
2743
|
});
|
|
2426
2744
|
}
|
|
2427
2745
|
});
|
|
@@ -2863,12 +3181,12 @@ var init_markdown = __esm({
|
|
|
2863
3181
|
});
|
|
2864
3182
|
|
|
2865
3183
|
// src/triage/schema.ts
|
|
2866
|
-
import { z as
|
|
3184
|
+
import { z as z9 } from "zod";
|
|
2867
3185
|
var TriageDecisionSchema, ClaimTriageSchema, TriageSummarySchema;
|
|
2868
3186
|
var init_schema8 = __esm({
|
|
2869
3187
|
"src/triage/schema.ts"() {
|
|
2870
3188
|
"use strict";
|
|
2871
|
-
TriageDecisionSchema =
|
|
3189
|
+
TriageDecisionSchema = z9.enum([
|
|
2872
3190
|
// Passes triage and is forwarded to review.
|
|
2873
3191
|
"selected_for_review",
|
|
2874
3192
|
// Parked: kept on the canonical ledger as research truth, but not advanced.
|
|
@@ -2880,36 +3198,36 @@ var init_schema8 = __esm({
|
|
|
2880
3198
|
"needs_scope_repair",
|
|
2881
3199
|
"needs_human_review"
|
|
2882
3200
|
]);
|
|
2883
|
-
ClaimTriageSchema =
|
|
2884
|
-
triage_id:
|
|
2885
|
-
claim_id:
|
|
2886
|
-
section_id:
|
|
3201
|
+
ClaimTriageSchema = z9.object({
|
|
3202
|
+
triage_id: z9.string().regex(/^tri_[a-f0-9]{12}$/),
|
|
3203
|
+
claim_id: z9.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
|
|
3204
|
+
section_id: z9.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2887
3205
|
decision: TriageDecisionSchema,
|
|
2888
|
-
reason:
|
|
3206
|
+
reason: z9.string().min(1),
|
|
2889
3207
|
// Rank among selected_for_review claims (1 = highest priority). null for
|
|
2890
3208
|
// any non-selected decision.
|
|
2891
|
-
rank:
|
|
3209
|
+
rank: z9.number().int().positive().nullable(),
|
|
2892
3210
|
// Quality score [0..1] used to sort. Stable. Higher = better.
|
|
2893
|
-
quality_score:
|
|
2894
|
-
triage_method:
|
|
2895
|
-
created_at:
|
|
2896
|
-
});
|
|
2897
|
-
TriageSummarySchema =
|
|
2898
|
-
summary_id:
|
|
2899
|
-
section_id:
|
|
2900
|
-
triaged_at:
|
|
2901
|
-
research_os_version:
|
|
2902
|
-
triage_method:
|
|
2903
|
-
candidate_claims:
|
|
2904
|
-
decisions:
|
|
2905
|
-
per_source_cap:
|
|
2906
|
-
duplicate_clusters_collapsed:
|
|
2907
|
-
selected_count:
|
|
2908
|
-
selected_per_source:
|
|
2909
|
-
|
|
2910
|
-
source_id:
|
|
2911
|
-
selected:
|
|
2912
|
-
total:
|
|
3211
|
+
quality_score: z9.number().min(0).max(1),
|
|
3212
|
+
triage_method: z9.string().min(1),
|
|
3213
|
+
created_at: z9.string()
|
|
3214
|
+
});
|
|
3215
|
+
TriageSummarySchema = z9.object({
|
|
3216
|
+
summary_id: z9.string().regex(/^tris_[0-9]+_[a-z0-9-]+$/),
|
|
3217
|
+
section_id: z9.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
3218
|
+
triaged_at: z9.string(),
|
|
3219
|
+
research_os_version: z9.string(),
|
|
3220
|
+
triage_method: z9.string(),
|
|
3221
|
+
candidate_claims: z9.number().int().nonnegative(),
|
|
3222
|
+
decisions: z9.record(TriageDecisionSchema, z9.number().int().nonnegative()),
|
|
3223
|
+
per_source_cap: z9.number().int().positive(),
|
|
3224
|
+
duplicate_clusters_collapsed: z9.number().int().nonnegative(),
|
|
3225
|
+
selected_count: z9.number().int().nonnegative(),
|
|
3226
|
+
selected_per_source: z9.array(
|
|
3227
|
+
z9.object({
|
|
3228
|
+
source_id: z9.string().regex(/^src_[a-f0-9]{12}$/),
|
|
3229
|
+
selected: z9.number().int().nonnegative(),
|
|
3230
|
+
total: z9.number().int().nonnegative()
|
|
2913
3231
|
})
|
|
2914
3232
|
)
|
|
2915
3233
|
});
|
|
@@ -2922,10 +3240,10 @@ __export(run_exports, {
|
|
|
2922
3240
|
readTriagedClaimIds: () => readTriagedClaimIds,
|
|
2923
3241
|
triage: () => triage
|
|
2924
3242
|
});
|
|
2925
|
-
import { existsSync as
|
|
3243
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2926
3244
|
import { createHash as createHash2 } from "crypto";
|
|
2927
|
-
import { mkdir as
|
|
2928
|
-
import { join as
|
|
3245
|
+
import { mkdir as mkdir9, readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
|
|
3246
|
+
import { join as join10, resolve as resolve6 } from "path";
|
|
2929
3247
|
function normaliseAssert(s) {
|
|
2930
3248
|
return s.toLowerCase().replace(/[^a-z0-9 ]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2931
3249
|
}
|
|
@@ -2945,9 +3263,9 @@ function makeTriageId(claimId) {
|
|
|
2945
3263
|
return "tri_" + createHash2("sha256").update(claimId).digest("hex").slice(0, 12);
|
|
2946
3264
|
}
|
|
2947
3265
|
async function readClaims2(packPath, sectionId) {
|
|
2948
|
-
const path =
|
|
2949
|
-
if (!
|
|
2950
|
-
const text = await
|
|
3266
|
+
const path = join10(packPath, "sections", sectionId, "claims.jsonl");
|
|
3267
|
+
if (!existsSync9(path)) return [];
|
|
3268
|
+
const text = await readFile9(path, "utf8");
|
|
2951
3269
|
const out = [];
|
|
2952
3270
|
for (const line of text.split(/\r?\n/)) {
|
|
2953
3271
|
if (!line.trim()) continue;
|
|
@@ -2960,8 +3278,8 @@ async function readClaims2(packPath, sectionId) {
|
|
|
2960
3278
|
}
|
|
2961
3279
|
async function triage(options) {
|
|
2962
3280
|
const packPath = options.packPath ? resolve6(options.packPath) : process.cwd();
|
|
2963
|
-
if (!
|
|
2964
|
-
if (!
|
|
3281
|
+
if (!existsSync9(join10(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
3282
|
+
if (!existsSync9(join10(packPath, "sections", options.sectionId)))
|
|
2965
3283
|
throw new SectionNotFoundError(options.sectionId);
|
|
2966
3284
|
const perSourceCap = options.perSourceCap ?? DEFAULT_PER_SOURCE_CAP;
|
|
2967
3285
|
const minAssertChars = options.minAssertChars ?? DEFAULT_MIN_ASSERT_CHARS;
|
|
@@ -3095,13 +3413,13 @@ async function triage(options) {
|
|
|
3095
3413
|
total
|
|
3096
3414
|
})).sort((a, b) => b.total - a.total)
|
|
3097
3415
|
});
|
|
3098
|
-
const sectionDir =
|
|
3099
|
-
const auditsDir =
|
|
3100
|
-
await
|
|
3101
|
-
await
|
|
3102
|
-
const triageJsonlPath =
|
|
3103
|
-
const triageMarkdownPath =
|
|
3104
|
-
const summaryJsonPath =
|
|
3416
|
+
const sectionDir = join10(packPath, "sections", options.sectionId);
|
|
3417
|
+
const auditsDir = join10(packPath, "audits");
|
|
3418
|
+
await mkdir9(sectionDir, { recursive: true });
|
|
3419
|
+
await mkdir9(auditsDir, { recursive: true });
|
|
3420
|
+
const triageJsonlPath = join10(sectionDir, "claim-triage.jsonl");
|
|
3421
|
+
const triageMarkdownPath = join10(sectionDir, "claim-triage.md");
|
|
3422
|
+
const summaryJsonPath = join10(auditsDir, `${options.sectionId}-claim-triage.json`);
|
|
3105
3423
|
await writeFile8(triageJsonlPath, records.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf8");
|
|
3106
3424
|
await writeFile8(triageMarkdownPath, buildMarkdown2(records, summary), "utf8");
|
|
3107
3425
|
await writeFile8(summaryJsonPath, JSON.stringify(summary, null, 2), "utf8");
|
|
@@ -3121,10 +3439,10 @@ async function triage(options) {
|
|
|
3121
3439
|
};
|
|
3122
3440
|
}
|
|
3123
3441
|
async function readTriagedClaimIds(packPath, sectionId) {
|
|
3124
|
-
const path =
|
|
3442
|
+
const path = join10(packPath, "sections", sectionId, "claim-triage.jsonl");
|
|
3125
3443
|
const out = /* @__PURE__ */ new Set();
|
|
3126
|
-
if (!
|
|
3127
|
-
const text = await
|
|
3444
|
+
if (!existsSync9(path)) return out;
|
|
3445
|
+
const text = await readFile9(path, "utf8");
|
|
3128
3446
|
for (const line of text.split(/\r?\n/)) {
|
|
3129
3447
|
if (!line.trim()) continue;
|
|
3130
3448
|
try {
|
|
@@ -3198,13 +3516,13 @@ var init_run2 = __esm({
|
|
|
3198
3516
|
|
|
3199
3517
|
// src/contradictions/map.ts
|
|
3200
3518
|
import { createHash as createHash3 } from "crypto";
|
|
3201
|
-
import { existsSync as
|
|
3202
|
-
import { appendFile as
|
|
3203
|
-
import { join as
|
|
3519
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3520
|
+
import { appendFile as appendFile4, readFile as readFile10, writeFile as writeFile9 } from "fs/promises";
|
|
3521
|
+
import { join as join11, resolve as resolve7 } from "path";
|
|
3204
3522
|
async function readCandidateClaims(packPath, sectionId) {
|
|
3205
|
-
const path =
|
|
3206
|
-
if (!
|
|
3207
|
-
const text = await
|
|
3523
|
+
const path = join11(packPath, "sections", sectionId, "claims.jsonl");
|
|
3524
|
+
if (!existsSync10(path)) return [];
|
|
3525
|
+
const text = await readFile10(path, "utf8");
|
|
3208
3526
|
const claims = [];
|
|
3209
3527
|
for (const line of text.split(/\r?\n/)) {
|
|
3210
3528
|
if (!line.trim()) continue;
|
|
@@ -3215,9 +3533,9 @@ async function readCandidateClaims(packPath, sectionId) {
|
|
|
3215
3533
|
return claims;
|
|
3216
3534
|
}
|
|
3217
3535
|
async function readExistingContradictions(packPath, sectionId) {
|
|
3218
|
-
const path =
|
|
3219
|
-
if (!
|
|
3220
|
-
const text = await
|
|
3536
|
+
const path = join11(packPath, "sections", sectionId, "contradictions.jsonl");
|
|
3537
|
+
if (!existsSync10(path)) return [];
|
|
3538
|
+
const text = await readFile10(path, "utf8");
|
|
3221
3539
|
const list = [];
|
|
3222
3540
|
for (const line of text.split(/\r?\n/)) {
|
|
3223
3541
|
if (!line.trim()) continue;
|
|
@@ -3302,9 +3620,9 @@ async function resolveDetector(options) {
|
|
|
3302
3620
|
}
|
|
3303
3621
|
async function map(options) {
|
|
3304
3622
|
const packPath = options.packPath ? resolve7(options.packPath) : process.cwd();
|
|
3305
|
-
if (!
|
|
3306
|
-
const sectionDir =
|
|
3307
|
-
if (!
|
|
3623
|
+
if (!existsSync10(join11(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
3624
|
+
const sectionDir = join11(packPath, "sections", options.sectionId);
|
|
3625
|
+
if (!existsSync10(sectionDir)) throw new SectionNotFoundError(options.sectionId);
|
|
3308
3626
|
let candidateClaims = await readCandidateClaims(packPath, options.sectionId);
|
|
3309
3627
|
if (options.triagedOnly) {
|
|
3310
3628
|
const { readTriagedClaimIds: readTriagedClaimIds2 } = await Promise.resolve().then(() => (init_run2(), run_exports));
|
|
@@ -3324,8 +3642,8 @@ async function map(options) {
|
|
|
3324
3642
|
detectorError: null,
|
|
3325
3643
|
detectorAnnouncement: announcement
|
|
3326
3644
|
};
|
|
3327
|
-
const ledgerPath =
|
|
3328
|
-
const mdPath =
|
|
3645
|
+
const ledgerPath = join11(sectionDir, "contradictions.jsonl");
|
|
3646
|
+
const mdPath = join11(sectionDir, "contradictions.md");
|
|
3329
3647
|
const existingContradictions = await readExistingContradictions(
|
|
3330
3648
|
packPath,
|
|
3331
3649
|
options.sectionId
|
|
@@ -3370,7 +3688,7 @@ async function map(options) {
|
|
|
3370
3688
|
summary.contradictionsDeduped += 1;
|
|
3371
3689
|
continue;
|
|
3372
3690
|
}
|
|
3373
|
-
await
|
|
3691
|
+
await appendFile4(ledgerPath, JSON.stringify(c) + "\n", "utf8");
|
|
3374
3692
|
existingIds.add(c.contradiction_id);
|
|
3375
3693
|
existingContradictions.push(c);
|
|
3376
3694
|
summary.contradictionsAdded += 1;
|
|
@@ -3406,31 +3724,31 @@ var init_map = __esm({
|
|
|
3406
3724
|
});
|
|
3407
3725
|
|
|
3408
3726
|
// src/contradictions/resolution-schema.ts
|
|
3409
|
-
import { z as
|
|
3727
|
+
import { z as z10 } from "zod";
|
|
3410
3728
|
var ResolutionStatusSchema, ContradictionResolutionSchema;
|
|
3411
3729
|
var init_resolution_schema = __esm({
|
|
3412
3730
|
"src/contradictions/resolution-schema.ts"() {
|
|
3413
3731
|
"use strict";
|
|
3414
|
-
ResolutionStatusSchema =
|
|
3732
|
+
ResolutionStatusSchema = z10.enum([
|
|
3415
3733
|
"unresolved",
|
|
3416
3734
|
"resolved",
|
|
3417
3735
|
"preserved",
|
|
3418
3736
|
"rejected"
|
|
3419
3737
|
]);
|
|
3420
|
-
ContradictionResolutionSchema =
|
|
3421
|
-
contradiction_id:
|
|
3738
|
+
ContradictionResolutionSchema = z10.object({
|
|
3739
|
+
contradiction_id: z10.string().min(1),
|
|
3422
3740
|
status: ResolutionStatusSchema,
|
|
3423
|
-
reason:
|
|
3424
|
-
resolved_at:
|
|
3425
|
-
resolved_by:
|
|
3741
|
+
reason: z10.string().min(4),
|
|
3742
|
+
resolved_at: z10.string().min(1),
|
|
3743
|
+
resolved_by: z10.string().min(1)
|
|
3426
3744
|
});
|
|
3427
3745
|
}
|
|
3428
3746
|
});
|
|
3429
3747
|
|
|
3430
3748
|
// src/contradictions/resolve.ts
|
|
3431
|
-
import { appendFile as
|
|
3432
|
-
import { existsSync as
|
|
3433
|
-
import { join as
|
|
3749
|
+
import { appendFile as appendFile5, readFile as readFile11 } from "fs/promises";
|
|
3750
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3751
|
+
import { join as join12 } from "path";
|
|
3434
3752
|
function latestEffectiveStatuses(ledgerPath, text) {
|
|
3435
3753
|
const entries = [];
|
|
3436
3754
|
for (const line of text.split(/\r?\n/)) {
|
|
@@ -3457,15 +3775,15 @@ async function resolve8(options) {
|
|
|
3457
3775
|
if (reason.length < 4) {
|
|
3458
3776
|
throw new Error("reason must be at least 4 characters");
|
|
3459
3777
|
}
|
|
3460
|
-
const sectionDir =
|
|
3461
|
-
if (!
|
|
3778
|
+
const sectionDir = join12(packPath, "sections", sectionId);
|
|
3779
|
+
if (!existsSync11(sectionDir)) {
|
|
3462
3780
|
throw new SectionNotFoundError(sectionId);
|
|
3463
3781
|
}
|
|
3464
|
-
const candidatesPath2 =
|
|
3465
|
-
const ledgerPath =
|
|
3782
|
+
const candidatesPath2 = join12(sectionDir, "contradictions.jsonl");
|
|
3783
|
+
const ledgerPath = join12(sectionDir, "contradiction-resolutions.jsonl");
|
|
3466
3784
|
const candidates = [];
|
|
3467
|
-
if (
|
|
3468
|
-
const text = await
|
|
3785
|
+
if (existsSync11(candidatesPath2)) {
|
|
3786
|
+
const text = await readFile11(candidatesPath2, "utf8");
|
|
3469
3787
|
for (const line of text.split(/\r?\n/)) {
|
|
3470
3788
|
if (!line.trim()) continue;
|
|
3471
3789
|
try {
|
|
@@ -3480,7 +3798,7 @@ async function resolve8(options) {
|
|
|
3480
3798
|
}
|
|
3481
3799
|
}
|
|
3482
3800
|
}
|
|
3483
|
-
const existingResolutions =
|
|
3801
|
+
const existingResolutions = existsSync11(ledgerPath) ? latestEffectiveStatuses(ledgerPath, await readFile11(ledgerPath, "utf8")) : /* @__PURE__ */ new Map();
|
|
3484
3802
|
const knownIds = new Set(candidates.map((c) => c.contradiction_id));
|
|
3485
3803
|
let targetIds;
|
|
3486
3804
|
if (all) {
|
|
@@ -3512,7 +3830,7 @@ async function resolve8(options) {
|
|
|
3512
3830
|
applied++;
|
|
3513
3831
|
}
|
|
3514
3832
|
if (lines.length > 0) {
|
|
3515
|
-
await
|
|
3833
|
+
await appendFile5(ledgerPath, lines.join("\n") + "\n", "utf8");
|
|
3516
3834
|
}
|
|
3517
3835
|
return { sectionId, applied, skipped, ledgerPath };
|
|
3518
3836
|
}
|
|
@@ -3539,12 +3857,12 @@ var init_contradictions = __esm({
|
|
|
3539
3857
|
});
|
|
3540
3858
|
|
|
3541
3859
|
// src/review/schema.ts
|
|
3542
|
-
import { z as
|
|
3860
|
+
import { z as z11 } from "zod";
|
|
3543
3861
|
var FindingCategorySchema, FindingSeveritySchema, ReviewerNameSchema, ReviewDecisionSchema, ReviewConfidenceSchema, ReviewFindingSchema, ClaimReviewSchema, ReviewSnapshotSchema;
|
|
3544
3862
|
var init_schema9 = __esm({
|
|
3545
3863
|
"src/review/schema.ts"() {
|
|
3546
3864
|
"use strict";
|
|
3547
|
-
FindingCategorySchema =
|
|
3865
|
+
FindingCategorySchema = z11.enum([
|
|
3548
3866
|
"unsupported_claim",
|
|
3549
3867
|
"ungrounded_excerpt",
|
|
3550
3868
|
"stale_claim",
|
|
@@ -3571,9 +3889,9 @@ var init_schema9 = __esm({
|
|
|
3571
3889
|
// a low-value claim can appear in a perfectly-sized section.
|
|
3572
3890
|
"valid_but_low_value"
|
|
3573
3891
|
]);
|
|
3574
|
-
FindingSeveritySchema =
|
|
3575
|
-
ReviewerNameSchema =
|
|
3576
|
-
ReviewDecisionSchema =
|
|
3892
|
+
FindingSeveritySchema = z11.enum(["info", "warn", "block"]);
|
|
3893
|
+
ReviewerNameSchema = z11.enum(["heuristic", "ollama-intern"]);
|
|
3894
|
+
ReviewDecisionSchema = z11.enum([
|
|
3577
3895
|
"accepted_for_synthesis",
|
|
3578
3896
|
"rejected",
|
|
3579
3897
|
"needs_scope_repair",
|
|
@@ -3581,43 +3899,43 @@ var init_schema9 = __esm({
|
|
|
3581
3899
|
"needs_contradiction_mapping",
|
|
3582
3900
|
"needs_human_review"
|
|
3583
3901
|
]);
|
|
3584
|
-
ReviewConfidenceSchema =
|
|
3585
|
-
ReviewFindingSchema =
|
|
3586
|
-
finding_id:
|
|
3587
|
-
section_id:
|
|
3588
|
-
claim_ids:
|
|
3589
|
-
source_ids:
|
|
3902
|
+
ReviewConfidenceSchema = z11.enum(["low", "medium", "high"]);
|
|
3903
|
+
ReviewFindingSchema = z11.object({
|
|
3904
|
+
finding_id: z11.string().regex(/^fnd_[a-f0-9]{12}$/),
|
|
3905
|
+
section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
3906
|
+
claim_ids: z11.array(z11.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)),
|
|
3907
|
+
source_ids: z11.array(z11.string().regex(/^src_[a-f0-9]{12}$/)),
|
|
3590
3908
|
category: FindingCategorySchema,
|
|
3591
3909
|
severity: FindingSeveritySchema,
|
|
3592
|
-
summary:
|
|
3593
|
-
evidence:
|
|
3594
|
-
required_action:
|
|
3910
|
+
summary: z11.string().min(1),
|
|
3911
|
+
evidence: z11.string(),
|
|
3912
|
+
required_action: z11.string(),
|
|
3595
3913
|
reviewer: ReviewerNameSchema,
|
|
3596
|
-
review_method:
|
|
3914
|
+
review_method: z11.string().min(1),
|
|
3597
3915
|
confidence: ReviewConfidenceSchema,
|
|
3598
|
-
created_at:
|
|
3916
|
+
created_at: z11.string()
|
|
3599
3917
|
});
|
|
3600
|
-
ClaimReviewSchema =
|
|
3601
|
-
claim_id:
|
|
3918
|
+
ClaimReviewSchema = z11.object({
|
|
3919
|
+
claim_id: z11.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
|
|
3602
3920
|
decision: ReviewDecisionSchema,
|
|
3603
|
-
reason:
|
|
3604
|
-
finding_ids:
|
|
3921
|
+
reason: z11.string().min(1),
|
|
3922
|
+
finding_ids: z11.array(z11.string()),
|
|
3605
3923
|
reviewer: ReviewerNameSchema,
|
|
3606
|
-
review_method:
|
|
3607
|
-
created_at:
|
|
3924
|
+
review_method: z11.string().min(1),
|
|
3925
|
+
created_at: z11.string()
|
|
3608
3926
|
});
|
|
3609
|
-
ReviewSnapshotSchema =
|
|
3610
|
-
section_id:
|
|
3927
|
+
ReviewSnapshotSchema = z11.object({
|
|
3928
|
+
section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
3611
3929
|
reviewer: ReviewerNameSchema,
|
|
3612
|
-
review_method:
|
|
3613
|
-
reviewed_at:
|
|
3614
|
-
candidate_claims:
|
|
3615
|
-
findings:
|
|
3616
|
-
claim_reviews:
|
|
3617
|
-
decision_counts:
|
|
3618
|
-
severity_counts:
|
|
3619
|
-
llm_findings_rejected_ungrounded:
|
|
3620
|
-
promoted_to_reviewed:
|
|
3930
|
+
review_method: z11.string(),
|
|
3931
|
+
reviewed_at: z11.string(),
|
|
3932
|
+
candidate_claims: z11.number().int().nonnegative(),
|
|
3933
|
+
findings: z11.array(ReviewFindingSchema),
|
|
3934
|
+
claim_reviews: z11.array(ClaimReviewSchema),
|
|
3935
|
+
decision_counts: z11.record(ReviewDecisionSchema, z11.number().int().nonnegative()),
|
|
3936
|
+
severity_counts: z11.record(FindingSeveritySchema, z11.number().int().nonnegative()),
|
|
3937
|
+
llm_findings_rejected_ungrounded: z11.number().int().nonnegative(),
|
|
3938
|
+
promoted_to_reviewed: z11.boolean()
|
|
3621
3939
|
});
|
|
3622
3940
|
}
|
|
3623
3941
|
});
|
|
@@ -4505,12 +4823,12 @@ var init_markdown2 = __esm({
|
|
|
4505
4823
|
});
|
|
4506
4824
|
|
|
4507
4825
|
// src/gates/schema.ts
|
|
4508
|
-
import { z as
|
|
4826
|
+
import { z as z12 } from "zod";
|
|
4509
4827
|
var GateFamilySchema, GateCheckStatusSchema, VerdictSchema, GateCheckResultSchema, WaiverApplicationSchema, SectionGateResultSchema;
|
|
4510
4828
|
var init_schema10 = __esm({
|
|
4511
4829
|
"src/gates/schema.ts"() {
|
|
4512
4830
|
"use strict";
|
|
4513
|
-
GateFamilySchema =
|
|
4831
|
+
GateFamilySchema = z12.enum([
|
|
4514
4832
|
"source_floor",
|
|
4515
4833
|
"claim_integrity",
|
|
4516
4834
|
"scope_integrity",
|
|
@@ -4520,98 +4838,98 @@ var init_schema10 = __esm({
|
|
|
4520
4838
|
"waivers",
|
|
4521
4839
|
"accepted_claim_floor"
|
|
4522
4840
|
]);
|
|
4523
|
-
GateCheckStatusSchema =
|
|
4841
|
+
GateCheckStatusSchema = z12.enum([
|
|
4524
4842
|
"pass",
|
|
4525
4843
|
"warn",
|
|
4526
4844
|
"fail",
|
|
4527
4845
|
"pass_with_waiver",
|
|
4528
4846
|
"warn_with_waiver"
|
|
4529
4847
|
]);
|
|
4530
|
-
VerdictSchema =
|
|
4531
|
-
GateCheckResultSchema =
|
|
4848
|
+
VerdictSchema = z12.enum(["pass", "warn", "fail", "blocked"]);
|
|
4849
|
+
GateCheckResultSchema = z12.object({
|
|
4532
4850
|
family: GateFamilySchema,
|
|
4533
|
-
check:
|
|
4851
|
+
check: z12.string().min(1),
|
|
4534
4852
|
status: GateCheckStatusSchema,
|
|
4535
|
-
detail:
|
|
4536
|
-
evidence:
|
|
4537
|
-
blocks_synthesis:
|
|
4853
|
+
detail: z12.string(),
|
|
4854
|
+
evidence: z12.array(z12.string()),
|
|
4855
|
+
blocks_synthesis: z12.boolean()
|
|
4538
4856
|
});
|
|
4539
|
-
WaiverApplicationSchema =
|
|
4857
|
+
WaiverApplicationSchema = z12.object({
|
|
4540
4858
|
family: GateFamilySchema,
|
|
4541
|
-
check:
|
|
4542
|
-
reason:
|
|
4543
|
-
compensating_controls:
|
|
4859
|
+
check: z12.string().min(1),
|
|
4860
|
+
reason: z12.string().min(1),
|
|
4861
|
+
compensating_controls: z12.array(z12.string()),
|
|
4544
4862
|
original_status: GateCheckStatusSchema,
|
|
4545
4863
|
new_status: GateCheckStatusSchema
|
|
4546
4864
|
});
|
|
4547
|
-
SectionGateResultSchema =
|
|
4548
|
-
section_id:
|
|
4865
|
+
SectionGateResultSchema = z12.object({
|
|
4866
|
+
section_id: z12.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
4549
4867
|
verdict: VerdictSchema,
|
|
4550
|
-
summary:
|
|
4551
|
-
checked_at:
|
|
4552
|
-
synthesis_eligible:
|
|
4553
|
-
gate_results:
|
|
4554
|
-
failures:
|
|
4555
|
-
warnings:
|
|
4556
|
-
waivers_applied:
|
|
4557
|
-
blocking_reasons:
|
|
4558
|
-
claim_counts:
|
|
4559
|
-
total:
|
|
4560
|
-
candidate:
|
|
4561
|
-
with_evidence_excerpt:
|
|
4562
|
-
with_source_hashes:
|
|
4563
|
-
with_scope:
|
|
4564
|
-
with_not:
|
|
4565
|
-
universal_scope_null:
|
|
4566
|
-
orphans:
|
|
4868
|
+
summary: z12.string(),
|
|
4869
|
+
checked_at: z12.string(),
|
|
4870
|
+
synthesis_eligible: z12.boolean(),
|
|
4871
|
+
gate_results: z12.array(GateCheckResultSchema),
|
|
4872
|
+
failures: z12.array(GateCheckResultSchema),
|
|
4873
|
+
warnings: z12.array(GateCheckResultSchema),
|
|
4874
|
+
waivers_applied: z12.array(WaiverApplicationSchema),
|
|
4875
|
+
blocking_reasons: z12.array(z12.string()),
|
|
4876
|
+
claim_counts: z12.object({
|
|
4877
|
+
total: z12.number().int().nonnegative(),
|
|
4878
|
+
candidate: z12.number().int().nonnegative(),
|
|
4879
|
+
with_evidence_excerpt: z12.number().int().nonnegative(),
|
|
4880
|
+
with_source_hashes: z12.number().int().nonnegative(),
|
|
4881
|
+
with_scope: z12.number().int().nonnegative(),
|
|
4882
|
+
with_not: z12.number().int().nonnegative(),
|
|
4883
|
+
universal_scope_null: z12.number().int().nonnegative(),
|
|
4884
|
+
orphans: z12.number().int().nonnegative()
|
|
4567
4885
|
}),
|
|
4568
|
-
source_counts:
|
|
4569
|
-
total:
|
|
4570
|
-
primary:
|
|
4571
|
-
secondary:
|
|
4572
|
-
forum:
|
|
4573
|
-
benchmark:
|
|
4574
|
-
docs:
|
|
4575
|
-
unknown:
|
|
4576
|
-
independent_publishers:
|
|
4577
|
-
failed_fetches:
|
|
4578
|
-
section_primary:
|
|
4579
|
-
section_independent_publishers:
|
|
4886
|
+
source_counts: z12.object({
|
|
4887
|
+
total: z12.number().int().nonnegative(),
|
|
4888
|
+
primary: z12.number().int().nonnegative(),
|
|
4889
|
+
secondary: z12.number().int().nonnegative(),
|
|
4890
|
+
forum: z12.number().int().nonnegative(),
|
|
4891
|
+
benchmark: z12.number().int().nonnegative(),
|
|
4892
|
+
docs: z12.number().int().nonnegative(),
|
|
4893
|
+
unknown: z12.number().int().nonnegative(),
|
|
4894
|
+
independent_publishers: z12.number().int().nonnegative(),
|
|
4895
|
+
failed_fetches: z12.number().int().nonnegative(),
|
|
4896
|
+
section_primary: z12.number().int().nonnegative(),
|
|
4897
|
+
section_independent_publishers: z12.number().int().nonnegative()
|
|
4580
4898
|
}),
|
|
4581
|
-
contradiction_counts:
|
|
4582
|
-
total:
|
|
4583
|
-
unresolved:
|
|
4584
|
-
blocking:
|
|
4585
|
-
by_type:
|
|
4899
|
+
contradiction_counts: z12.object({
|
|
4900
|
+
total: z12.number().int().nonnegative(),
|
|
4901
|
+
unresolved: z12.number().int().nonnegative(),
|
|
4902
|
+
blocking: z12.number().int().nonnegative(),
|
|
4903
|
+
by_type: z12.record(z12.string(), z12.number().int().nonnegative())
|
|
4586
4904
|
}),
|
|
4587
|
-
freshness_summary:
|
|
4588
|
-
policy_required:
|
|
4589
|
-
max_source_age_months:
|
|
4590
|
-
stale_source_policy:
|
|
4591
|
-
stale_count:
|
|
4592
|
-
unknown_date_count:
|
|
4905
|
+
freshness_summary: z12.object({
|
|
4906
|
+
policy_required: z12.boolean(),
|
|
4907
|
+
max_source_age_months: z12.number().int().nullable(),
|
|
4908
|
+
stale_source_policy: z12.enum(["warn", "fail"]),
|
|
4909
|
+
stale_count: z12.number().int().nonnegative(),
|
|
4910
|
+
unknown_date_count: z12.number().int().nonnegative()
|
|
4593
4911
|
}),
|
|
4594
|
-
scope_integrity_summary:
|
|
4595
|
-
universal_claims:
|
|
4596
|
-
scoped_claims:
|
|
4597
|
-
with_not_constraint:
|
|
4598
|
-
overgen_risks_total:
|
|
4599
|
-
overgen_risks_blocking:
|
|
4912
|
+
scope_integrity_summary: z12.object({
|
|
4913
|
+
universal_claims: z12.number().int().nonnegative(),
|
|
4914
|
+
scoped_claims: z12.number().int().nonnegative(),
|
|
4915
|
+
with_not_constraint: z12.number().int().nonnegative(),
|
|
4916
|
+
overgen_risks_total: z12.number().int().nonnegative(),
|
|
4917
|
+
overgen_risks_blocking: z12.number().int().nonnegative()
|
|
4600
4918
|
}),
|
|
4601
|
-
next_actions:
|
|
4919
|
+
next_actions: z12.array(z12.string())
|
|
4602
4920
|
});
|
|
4603
4921
|
}
|
|
4604
4922
|
});
|
|
4605
4923
|
|
|
4606
4924
|
// src/gates/run.ts
|
|
4607
|
-
import { existsSync as
|
|
4608
|
-
import { mkdir as
|
|
4609
|
-
import { join as
|
|
4925
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4926
|
+
import { mkdir as mkdir10, readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
|
|
4927
|
+
import { join as join13, resolve as resolve9 } from "path";
|
|
4610
4928
|
import { parse as yamlParse2, stringify as yamlStringify3 } from "yaml";
|
|
4611
4929
|
async function readJsonl(packPath, rel, parse) {
|
|
4612
|
-
const path =
|
|
4613
|
-
if (!
|
|
4614
|
-
const text = await
|
|
4930
|
+
const path = join13(packPath, rel);
|
|
4931
|
+
if (!existsSync12(path)) return [];
|
|
4932
|
+
const text = await readFile12(path, "utf8");
|
|
4615
4933
|
const out = [];
|
|
4616
4934
|
for (const line of text.split(/\r?\n/)) {
|
|
4617
4935
|
if (!line.trim()) continue;
|
|
@@ -4620,14 +4938,14 @@ async function readJsonl(packPath, rel, parse) {
|
|
|
4620
4938
|
return out;
|
|
4621
4939
|
}
|
|
4622
4940
|
async function readSourceCards2(packPath) {
|
|
4623
|
-
const dir =
|
|
4624
|
-
if (!
|
|
4625
|
-
const { readdir:
|
|
4626
|
-
const entries = await
|
|
4941
|
+
const dir = join13(packPath, "evidence", "source-cards");
|
|
4942
|
+
if (!existsSync12(dir)) return [];
|
|
4943
|
+
const { readdir: readdir5 } = await import("fs/promises");
|
|
4944
|
+
const entries = await readdir5(dir);
|
|
4627
4945
|
const cards = [];
|
|
4628
4946
|
for (const entry of entries) {
|
|
4629
4947
|
if (!entry.endsWith(".json")) continue;
|
|
4630
|
-
const text = await
|
|
4948
|
+
const text = await readFile12(join13(dir, entry), "utf8");
|
|
4631
4949
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
4632
4950
|
}
|
|
4633
4951
|
return cards;
|
|
@@ -4802,14 +5120,14 @@ function buildNextActions(results) {
|
|
|
4802
5120
|
return Array.from(new Set(actions));
|
|
4803
5121
|
}
|
|
4804
5122
|
async function loadResearchYaml2(packPath) {
|
|
4805
|
-
const yamlPath =
|
|
4806
|
-
if (!
|
|
4807
|
-
const text = await
|
|
5123
|
+
const yamlPath = join13(packPath, "research.yaml");
|
|
5124
|
+
if (!existsSync12(yamlPath)) throw new PackNotFoundError(packPath);
|
|
5125
|
+
const text = await readFile12(yamlPath, "utf8");
|
|
4808
5126
|
return ResearchYamlSchema.parse(yamlParse2(text));
|
|
4809
5127
|
}
|
|
4810
5128
|
async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
|
|
4811
|
-
const yamlPath =
|
|
4812
|
-
const text = await
|
|
5129
|
+
const yamlPath = join13(packPath, "research.yaml");
|
|
5130
|
+
const text = await readFile12(yamlPath, "utf8");
|
|
4813
5131
|
const research = ResearchYamlSchema.parse(yamlParse2(text));
|
|
4814
5132
|
const idx = research.sections.findIndex((s) => s.id === sectionId);
|
|
4815
5133
|
if (idx < 0) return;
|
|
@@ -4826,9 +5144,9 @@ async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
|
|
|
4826
5144
|
}
|
|
4827
5145
|
async function gate(options) {
|
|
4828
5146
|
const packPath = options.packPath ? resolve9(options.packPath) : process.cwd();
|
|
4829
|
-
if (!
|
|
4830
|
-
const sectionDir =
|
|
4831
|
-
if (!
|
|
5147
|
+
if (!existsSync12(join13(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
5148
|
+
const sectionDir = join13(packPath, "sections", options.sectionId);
|
|
5149
|
+
if (!existsSync12(sectionDir)) throw new SectionNotFoundError(options.sectionId);
|
|
4832
5150
|
const research = await loadResearchYaml2(packPath);
|
|
4833
5151
|
const section = research.sections.find((s) => s.id === options.sectionId);
|
|
4834
5152
|
if (!section) throw new SectionNotFoundError(options.sectionId);
|
|
@@ -4882,15 +5200,15 @@ async function gate(options) {
|
|
|
4882
5200
|
scope_integrity_summary: summarizeScopeIntegrity(input),
|
|
4883
5201
|
next_actions: buildNextActions(finalResults)
|
|
4884
5202
|
});
|
|
4885
|
-
const auditsDir =
|
|
4886
|
-
await
|
|
5203
|
+
const auditsDir = join13(packPath, "audits");
|
|
5204
|
+
await mkdir10(auditsDir, { recursive: true });
|
|
4887
5205
|
await writeFile10(
|
|
4888
|
-
|
|
5206
|
+
join13(auditsDir, `${options.sectionId}-gate.json`),
|
|
4889
5207
|
JSON.stringify(result, null, 2),
|
|
4890
5208
|
"utf8"
|
|
4891
5209
|
);
|
|
4892
5210
|
await writeFile10(
|
|
4893
|
-
|
|
5211
|
+
join13(auditsDir, `${options.sectionId}-gate.md`),
|
|
4894
5212
|
renderGateMarkdown(result),
|
|
4895
5213
|
"utf8"
|
|
4896
5214
|
);
|
|
@@ -4932,21 +5250,21 @@ var init_gates = __esm({
|
|
|
4932
5250
|
});
|
|
4933
5251
|
|
|
4934
5252
|
// src/review/profiles.ts
|
|
4935
|
-
import { existsSync as
|
|
4936
|
-
import { mkdir as
|
|
4937
|
-
import { join as
|
|
4938
|
-
import { z as
|
|
5253
|
+
import { existsSync as existsSync13 } from "fs";
|
|
5254
|
+
import { mkdir as mkdir11, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
|
|
5255
|
+
import { join as join14 } from "path";
|
|
5256
|
+
import { z as z13 } from "zod";
|
|
4939
5257
|
function reviewActivePath(packPath, sectionId) {
|
|
4940
|
-
return
|
|
5258
|
+
return join14(packPath, "sections", sectionId, "review-active.json");
|
|
4941
5259
|
}
|
|
4942
5260
|
function profileDir(packPath, sectionId, profile) {
|
|
4943
|
-
return
|
|
5261
|
+
return join14(packPath, "sections", sectionId, "reviews", profile);
|
|
4944
5262
|
}
|
|
4945
5263
|
async function readActiveProfile(packPath, sectionId) {
|
|
4946
5264
|
const path = reviewActivePath(packPath, sectionId);
|
|
4947
|
-
if (!
|
|
5265
|
+
if (!existsSync13(path)) return DEFAULT_PROFILE;
|
|
4948
5266
|
try {
|
|
4949
|
-
const parsed = ReviewActiveSchema.parse(JSON.parse(await
|
|
5267
|
+
const parsed = ReviewActiveSchema.parse(JSON.parse(await readFile13(path, "utf8")));
|
|
4950
5268
|
return parsed.active_profile;
|
|
4951
5269
|
} catch {
|
|
4952
5270
|
return DEFAULT_PROFILE;
|
|
@@ -4954,7 +5272,7 @@ async function readActiveProfile(packPath, sectionId) {
|
|
|
4954
5272
|
}
|
|
4955
5273
|
async function writeActiveProfile(packPath, sectionId, active) {
|
|
4956
5274
|
const path = reviewActivePath(packPath, sectionId);
|
|
4957
|
-
await
|
|
5275
|
+
await mkdir11(join14(packPath, "sections", sectionId), { recursive: true });
|
|
4958
5276
|
await writeFile11(path, JSON.stringify(ReviewActiveSchema.parse(active), null, 2), "utf8");
|
|
4959
5277
|
}
|
|
4960
5278
|
function isValidProfileName(name) {
|
|
@@ -4965,22 +5283,22 @@ var init_profiles = __esm({
|
|
|
4965
5283
|
"src/review/profiles.ts"() {
|
|
4966
5284
|
"use strict";
|
|
4967
5285
|
DEFAULT_PROFILE = "default";
|
|
4968
|
-
PromotionCalibrationSummarySchema =
|
|
4969
|
-
fixture:
|
|
4970
|
-
good_false_positive_rate:
|
|
4971
|
-
bad_any_flag_recall:
|
|
4972
|
-
strict_category_recall:
|
|
4973
|
-
unsupported_claim_recall:
|
|
4974
|
-
notes:
|
|
4975
|
-
});
|
|
4976
|
-
ReviewActiveSchema =
|
|
4977
|
-
active_profile:
|
|
4978
|
-
promoted_at:
|
|
4979
|
-
promoted_method:
|
|
4980
|
-
promoted_reviewer:
|
|
5286
|
+
PromotionCalibrationSummarySchema = z13.object({
|
|
5287
|
+
fixture: z13.string().nullable().default(null),
|
|
5288
|
+
good_false_positive_rate: z13.string().nullable().default(null),
|
|
5289
|
+
bad_any_flag_recall: z13.string().nullable().default(null),
|
|
5290
|
+
strict_category_recall: z13.string().nullable().default(null),
|
|
5291
|
+
unsupported_claim_recall: z13.string().nullable().default(null),
|
|
5292
|
+
notes: z13.string().nullable().default(null)
|
|
5293
|
+
});
|
|
5294
|
+
ReviewActiveSchema = z13.object({
|
|
5295
|
+
active_profile: z13.string().min(1),
|
|
5296
|
+
promoted_at: z13.string(),
|
|
5297
|
+
promoted_method: z13.string(),
|
|
5298
|
+
promoted_reviewer: z13.string(),
|
|
4981
5299
|
// Free-text reason the profile was promoted. Recorded once at promotion
|
|
4982
5300
|
// time; not derived from artifacts. Required.
|
|
4983
|
-
promotion_reason:
|
|
5301
|
+
promotion_reason: z13.string().min(8).default("promoted via review-promote without an explicit reason"),
|
|
4984
5302
|
// Optional calibration evidence captured at promotion time so downstream
|
|
4985
5303
|
// consumers can see WHY the reviewer was trusted.
|
|
4986
5304
|
calibration_summary: PromotionCalibrationSummarySchema.nullable().default(null)
|
|
@@ -5801,14 +6119,14 @@ var init_markdown3 = __esm({
|
|
|
5801
6119
|
|
|
5802
6120
|
// src/review/run.ts
|
|
5803
6121
|
import { createHash as createHash4 } from "crypto";
|
|
5804
|
-
import { existsSync as
|
|
5805
|
-
import { appendFile as
|
|
5806
|
-
import { join as
|
|
6122
|
+
import { existsSync as existsSync14 } from "fs";
|
|
6123
|
+
import { appendFile as appendFile6, mkdir as mkdir12, readFile as readFile14, writeFile as writeFile12 } from "fs/promises";
|
|
6124
|
+
import { join as join15, resolve as resolve10 } from "path";
|
|
5807
6125
|
import { parse as yamlParse3, stringify as yamlStringify4 } from "yaml";
|
|
5808
6126
|
async function readJsonl2(packPath, rel, parse) {
|
|
5809
|
-
const path =
|
|
5810
|
-
if (!
|
|
5811
|
-
const text = await
|
|
6127
|
+
const path = join15(packPath, rel);
|
|
6128
|
+
if (!existsSync14(path)) return [];
|
|
6129
|
+
const text = await readFile14(path, "utf8");
|
|
5812
6130
|
const out = [];
|
|
5813
6131
|
for (const line of text.split(/\r?\n/)) {
|
|
5814
6132
|
if (!line.trim()) continue;
|
|
@@ -5817,37 +6135,37 @@ async function readJsonl2(packPath, rel, parse) {
|
|
|
5817
6135
|
return out;
|
|
5818
6136
|
}
|
|
5819
6137
|
async function readSourceCards3(packPath) {
|
|
5820
|
-
const dir =
|
|
5821
|
-
if (!
|
|
5822
|
-
const { readdir:
|
|
5823
|
-
const entries = await
|
|
6138
|
+
const dir = join15(packPath, "evidence", "source-cards");
|
|
6139
|
+
if (!existsSync14(dir)) return [];
|
|
6140
|
+
const { readdir: readdir5 } = await import("fs/promises");
|
|
6141
|
+
const entries = await readdir5(dir);
|
|
5824
6142
|
const cards = [];
|
|
5825
6143
|
for (const entry of entries) {
|
|
5826
6144
|
if (!entry.endsWith(".json")) continue;
|
|
5827
|
-
const text = await
|
|
6145
|
+
const text = await readFile14(join15(dir, entry), "utf8");
|
|
5828
6146
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
5829
6147
|
}
|
|
5830
6148
|
return cards;
|
|
5831
6149
|
}
|
|
5832
6150
|
async function readGateResult(packPath, sectionId) {
|
|
5833
|
-
const path =
|
|
5834
|
-
if (!
|
|
5835
|
-
const text = await
|
|
6151
|
+
const path = join15(packPath, "audits", `${sectionId}-gate.json`);
|
|
6152
|
+
if (!existsSync14(path)) return null;
|
|
6153
|
+
const text = await readFile14(path, "utf8");
|
|
5836
6154
|
return SectionGateResultSchema.parse(JSON.parse(text));
|
|
5837
6155
|
}
|
|
5838
6156
|
async function readBriefText(packPath, sectionId) {
|
|
5839
|
-
const path =
|
|
5840
|
-
if (!
|
|
5841
|
-
return
|
|
6157
|
+
const path = join15(packPath, "sections", sectionId, "brief.md");
|
|
6158
|
+
if (!existsSync14(path)) return null;
|
|
6159
|
+
return readFile14(path, "utf8");
|
|
5842
6160
|
}
|
|
5843
6161
|
async function readRawTextBySource(packPath, receipts) {
|
|
5844
6162
|
const map2 = /* @__PURE__ */ new Map();
|
|
5845
6163
|
for (const r of receipts) {
|
|
5846
6164
|
if (r.fetch_outcome !== "ok" || !r.raw_text_path) continue;
|
|
5847
|
-
const path =
|
|
5848
|
-
if (!
|
|
6165
|
+
const path = join15(packPath, r.raw_text_path);
|
|
6166
|
+
if (!existsSync14(path)) continue;
|
|
5849
6167
|
if (map2.has(r.source_id)) continue;
|
|
5850
|
-
map2.set(r.source_id, await
|
|
6168
|
+
map2.set(r.source_id, await readFile14(path, "utf8"));
|
|
5851
6169
|
}
|
|
5852
6170
|
return map2;
|
|
5853
6171
|
}
|
|
@@ -5857,8 +6175,8 @@ async function readExcerptsBySource(packPath, claims) {
|
|
|
5857
6175
|
for (const c of claims) for (const sid of c.source_ids) sourceIds.add(sid);
|
|
5858
6176
|
for (const sid of sourceIds) {
|
|
5859
6177
|
const path = ledgerPathFor(packPath, sid);
|
|
5860
|
-
if (!
|
|
5861
|
-
const text = await
|
|
6178
|
+
if (!existsSync14(path)) continue;
|
|
6179
|
+
const text = await readFile14(path, "utf8");
|
|
5862
6180
|
const index = /* @__PURE__ */ new Map();
|
|
5863
6181
|
for (const line of text.split(/\r?\n/)) {
|
|
5864
6182
|
if (!line.trim()) continue;
|
|
@@ -5911,15 +6229,15 @@ function buildFinding(args) {
|
|
|
5911
6229
|
});
|
|
5912
6230
|
}
|
|
5913
6231
|
async function loadResearchYaml3(packPath) {
|
|
5914
|
-
const yamlPath =
|
|
5915
|
-
if (!
|
|
5916
|
-
const text = await
|
|
6232
|
+
const yamlPath = join15(packPath, "research.yaml");
|
|
6233
|
+
if (!existsSync14(yamlPath)) throw new PackNotFoundError(packPath);
|
|
6234
|
+
const text = await readFile14(yamlPath, "utf8");
|
|
5917
6235
|
return ResearchYamlSchema.parse(yamlParse3(text));
|
|
5918
6236
|
}
|
|
5919
6237
|
async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
|
|
5920
6238
|
if (!allAccepted) return false;
|
|
5921
|
-
const yamlPath =
|
|
5922
|
-
const text = await
|
|
6239
|
+
const yamlPath = join15(packPath, "research.yaml");
|
|
6240
|
+
const text = await readFile14(yamlPath, "utf8");
|
|
5923
6241
|
const research = ResearchYamlSchema.parse(yamlParse3(text));
|
|
5924
6242
|
const idx = research.sections.findIndex((s) => s.id === sectionId);
|
|
5925
6243
|
if (idx < 0) return false;
|
|
@@ -5931,9 +6249,9 @@ async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
|
|
|
5931
6249
|
}
|
|
5932
6250
|
async function review(options) {
|
|
5933
6251
|
const packPath = options.packPath ? resolve10(options.packPath) : process.cwd();
|
|
5934
|
-
if (!
|
|
5935
|
-
const sectionDir =
|
|
5936
|
-
if (!
|
|
6252
|
+
if (!existsSync14(join15(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
6253
|
+
const sectionDir = join15(packPath, "sections", options.sectionId);
|
|
6254
|
+
if (!existsSync14(sectionDir)) throw new SectionNotFoundError(options.sectionId);
|
|
5937
6255
|
const research = await loadResearchYaml3(packPath);
|
|
5938
6256
|
const section = research.sections.find((s) => s.id === options.sectionId);
|
|
5939
6257
|
if (!section) throw new SectionNotFoundError(options.sectionId);
|
|
@@ -6183,39 +6501,39 @@ async function finalizeReview(args) {
|
|
|
6183
6501
|
promoted_to_reviewed: promoted
|
|
6184
6502
|
});
|
|
6185
6503
|
const profDir = profileDir(args.packPath, args.sectionId, args.profile);
|
|
6186
|
-
await
|
|
6187
|
-
await writeFile12(
|
|
6188
|
-
await writeFile12(
|
|
6189
|
-
const profileFindingsPath =
|
|
6504
|
+
await mkdir12(profDir, { recursive: true });
|
|
6505
|
+
await writeFile12(join15(profDir, "review.json"), JSON.stringify(snapshot, null, 2), "utf8");
|
|
6506
|
+
await writeFile12(join15(profDir, "review.md"), renderReviewMarkdown(snapshot), "utf8");
|
|
6507
|
+
const profileFindingsPath = join15(profDir, "findings.jsonl");
|
|
6190
6508
|
for (const f of dedupedFindings) {
|
|
6191
|
-
await
|
|
6509
|
+
await appendFile6(profileFindingsPath, JSON.stringify(f) + "\n", "utf8");
|
|
6192
6510
|
}
|
|
6193
|
-
const profileReviewsPath =
|
|
6511
|
+
const profileReviewsPath = join15(profDir, "claim-reviews.jsonl");
|
|
6194
6512
|
for (const r of claimReviews) {
|
|
6195
|
-
await
|
|
6513
|
+
await appendFile6(profileReviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
|
|
6196
6514
|
}
|
|
6197
6515
|
const activeProfile = await readActiveProfile(args.packPath, args.sectionId);
|
|
6198
6516
|
const isActive = args.profile === activeProfile || activeProfile === DEFAULT_PROFILE && args.profile === DEFAULT_PROFILE;
|
|
6199
6517
|
if (isActive) {
|
|
6200
|
-
const auditsDir =
|
|
6201
|
-
await
|
|
6518
|
+
const auditsDir = join15(args.packPath, "audits");
|
|
6519
|
+
await mkdir12(auditsDir, { recursive: true });
|
|
6202
6520
|
await writeFile12(
|
|
6203
|
-
|
|
6521
|
+
join15(auditsDir, `${args.sectionId}-review.json`),
|
|
6204
6522
|
JSON.stringify(snapshot, null, 2),
|
|
6205
6523
|
"utf8"
|
|
6206
6524
|
);
|
|
6207
6525
|
await writeFile12(
|
|
6208
|
-
|
|
6526
|
+
join15(auditsDir, `${args.sectionId}-review.md`),
|
|
6209
6527
|
renderReviewMarkdown(snapshot),
|
|
6210
6528
|
"utf8"
|
|
6211
6529
|
);
|
|
6212
|
-
const findingsPath =
|
|
6530
|
+
const findingsPath = join15(auditsDir, `${args.sectionId}-findings.jsonl`);
|
|
6213
6531
|
for (const f of dedupedFindings) {
|
|
6214
|
-
await
|
|
6532
|
+
await appendFile6(findingsPath, JSON.stringify(f) + "\n", "utf8");
|
|
6215
6533
|
}
|
|
6216
|
-
const reviewsPath =
|
|
6534
|
+
const reviewsPath = join15(args.packPath, "sections", args.sectionId, "claim-reviews.jsonl");
|
|
6217
6535
|
for (const r of claimReviews) {
|
|
6218
|
-
await
|
|
6536
|
+
await appendFile6(reviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
|
|
6219
6537
|
}
|
|
6220
6538
|
}
|
|
6221
6539
|
return {
|
|
@@ -6252,14 +6570,14 @@ var init_run4 = __esm({
|
|
|
6252
6570
|
});
|
|
6253
6571
|
|
|
6254
6572
|
// src/review/promote.ts
|
|
6255
|
-
import { existsSync as
|
|
6256
|
-
import { copyFile, mkdir as
|
|
6257
|
-
import { join as
|
|
6573
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6574
|
+
import { copyFile, mkdir as mkdir13, readFile as readFile15, writeFile as writeFile13, appendFile as appendFile7 } from "fs/promises";
|
|
6575
|
+
import { join as join16, resolve as resolve11 } from "path";
|
|
6258
6576
|
import { parse as yamlParse4, stringify as yamlStringify5 } from "yaml";
|
|
6259
6577
|
async function promote(options) {
|
|
6260
6578
|
const packPath = options.packPath ? resolve11(options.packPath) : process.cwd();
|
|
6261
|
-
if (!
|
|
6262
|
-
if (!
|
|
6579
|
+
if (!existsSync15(join16(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
6580
|
+
if (!existsSync15(join16(packPath, "sections", options.sectionId)))
|
|
6263
6581
|
throw new SectionNotFoundError(options.sectionId);
|
|
6264
6582
|
if (!isValidProfileName(options.profile)) {
|
|
6265
6583
|
throw new Error(
|
|
@@ -6267,39 +6585,39 @@ async function promote(options) {
|
|
|
6267
6585
|
);
|
|
6268
6586
|
}
|
|
6269
6587
|
const dir = profileDir(packPath, options.sectionId, options.profile);
|
|
6270
|
-
const reviewJsonPath =
|
|
6271
|
-
if (!
|
|
6588
|
+
const reviewJsonPath = join16(dir, "review.json");
|
|
6589
|
+
if (!existsSync15(reviewJsonPath)) {
|
|
6272
6590
|
throw new Error(
|
|
6273
6591
|
`Profile "${options.profile}" not found at ${dir}. Run \`research-os review --profile ${options.profile}\` first.`
|
|
6274
6592
|
);
|
|
6275
6593
|
}
|
|
6276
6594
|
const snapshot = ReviewSnapshotSchema.parse(
|
|
6277
|
-
JSON.parse(await
|
|
6595
|
+
JSON.parse(await readFile15(reviewJsonPath, "utf8"))
|
|
6278
6596
|
);
|
|
6279
|
-
const auditsDir =
|
|
6280
|
-
await
|
|
6281
|
-
const canonicalReviewJson =
|
|
6282
|
-
const canonicalReviewMd =
|
|
6597
|
+
const auditsDir = join16(packPath, "audits");
|
|
6598
|
+
await mkdir13(auditsDir, { recursive: true });
|
|
6599
|
+
const canonicalReviewJson = join16(auditsDir, `${options.sectionId}-review.json`);
|
|
6600
|
+
const canonicalReviewMd = join16(auditsDir, `${options.sectionId}-review.md`);
|
|
6283
6601
|
await copyFile(reviewJsonPath, canonicalReviewJson);
|
|
6284
6602
|
await writeFile13(canonicalReviewMd, renderReviewMarkdown(snapshot), "utf8");
|
|
6285
|
-
const canonicalFindings =
|
|
6286
|
-
const profileFindings =
|
|
6603
|
+
const canonicalFindings = join16(auditsDir, `${options.sectionId}-findings.jsonl`);
|
|
6604
|
+
const profileFindings = join16(dir, "findings.jsonl");
|
|
6287
6605
|
const writtenFindings = [];
|
|
6288
|
-
if (
|
|
6289
|
-
const text = await
|
|
6606
|
+
if (existsSync15(profileFindings)) {
|
|
6607
|
+
const text = await readFile15(profileFindings, "utf8");
|
|
6290
6608
|
for (const line of text.split(/\r?\n/)) {
|
|
6291
6609
|
if (!line.trim()) continue;
|
|
6292
|
-
await
|
|
6610
|
+
await appendFile7(canonicalFindings, line + "\n", "utf8");
|
|
6293
6611
|
writtenFindings.push(line);
|
|
6294
6612
|
}
|
|
6295
6613
|
}
|
|
6296
|
-
const canonicalReviews =
|
|
6297
|
-
const profileReviews =
|
|
6298
|
-
if (
|
|
6299
|
-
const text = await
|
|
6614
|
+
const canonicalReviews = join16(packPath, "sections", options.sectionId, "claim-reviews.jsonl");
|
|
6615
|
+
const profileReviews = join16(dir, "claim-reviews.jsonl");
|
|
6616
|
+
if (existsSync15(profileReviews)) {
|
|
6617
|
+
const text = await readFile15(profileReviews, "utf8");
|
|
6300
6618
|
for (const line of text.split(/\r?\n/)) {
|
|
6301
6619
|
if (!line.trim()) continue;
|
|
6302
|
-
await
|
|
6620
|
+
await appendFile7(canonicalReviews, line + "\n", "utf8");
|
|
6303
6621
|
}
|
|
6304
6622
|
}
|
|
6305
6623
|
const stamp = (options.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
@@ -6313,8 +6631,8 @@ async function promote(options) {
|
|
|
6313
6631
|
});
|
|
6314
6632
|
let sectionStatusBumped = false;
|
|
6315
6633
|
if (options.promoteSectionStatus) {
|
|
6316
|
-
const yamlPath =
|
|
6317
|
-
const research = ResearchYamlSchema.parse(yamlParse4(await
|
|
6634
|
+
const yamlPath = join16(packPath, "research.yaml");
|
|
6635
|
+
const research = ResearchYamlSchema.parse(yamlParse4(await readFile15(yamlPath, "utf8")));
|
|
6318
6636
|
const idx = research.sections.findIndex((s) => s.id === options.sectionId);
|
|
6319
6637
|
if (idx >= 0 && research.sections[idx].status === "gated") {
|
|
6320
6638
|
const allAccepted = snapshot.candidate_claims > 0 && snapshot.claim_reviews.every((r) => r.decision === "accepted_for_synthesis");
|
|
@@ -6519,16 +6837,16 @@ var init_schema11 = __esm({
|
|
|
6519
6837
|
});
|
|
6520
6838
|
|
|
6521
6839
|
// src/indexer/db.ts
|
|
6522
|
-
import { mkdirSync, writeFileSync, existsSync as
|
|
6523
|
-
import { dirname as
|
|
6840
|
+
import { mkdirSync, writeFileSync, existsSync as existsSync16 } from "fs";
|
|
6841
|
+
import { dirname as dirname4, join as join17 } from "path";
|
|
6524
6842
|
import Database from "better-sqlite3";
|
|
6525
6843
|
function indexDbPath(packPath) {
|
|
6526
|
-
return
|
|
6844
|
+
return join17(packPath, ".research-os", "index.sqlite");
|
|
6527
6845
|
}
|
|
6528
6846
|
function ensureGitIgnore(packPath) {
|
|
6529
|
-
const dir =
|
|
6530
|
-
const gi =
|
|
6531
|
-
if (!
|
|
6847
|
+
const dir = join17(packPath, ".research-os");
|
|
6848
|
+
const gi = join17(dir, ".gitignore");
|
|
6849
|
+
if (!existsSync16(gi)) {
|
|
6532
6850
|
mkdirSync(dir, { recursive: true });
|
|
6533
6851
|
writeFileSync(gi, "*\n", "utf8");
|
|
6534
6852
|
}
|
|
@@ -6536,7 +6854,7 @@ function ensureGitIgnore(packPath) {
|
|
|
6536
6854
|
function openIndexDb(opts) {
|
|
6537
6855
|
const dbPath = indexDbPath(opts.packPath);
|
|
6538
6856
|
if (!opts.readonly) {
|
|
6539
|
-
mkdirSync(
|
|
6857
|
+
mkdirSync(dirname4(dbPath), { recursive: true });
|
|
6540
6858
|
ensureGitIgnore(opts.packPath);
|
|
6541
6859
|
}
|
|
6542
6860
|
const db = new Database(dbPath, { readonly: opts.readonly ?? false, fileMustExist: opts.readonly ?? false });
|
|
@@ -6551,18 +6869,18 @@ var init_db = __esm({
|
|
|
6551
6869
|
});
|
|
6552
6870
|
|
|
6553
6871
|
// src/indexer/build.ts
|
|
6554
|
-
import { existsSync as
|
|
6555
|
-
import { readFile as
|
|
6556
|
-
import { join as
|
|
6872
|
+
import { existsSync as existsSync17 } from "fs";
|
|
6873
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
6874
|
+
import { join as join18, resolve as resolve12, relative as relative2 } from "path";
|
|
6557
6875
|
import { createHash as createHash5 } from "crypto";
|
|
6558
6876
|
import { parse as yamlParse5 } from "yaml";
|
|
6559
6877
|
function relPath(packPath, abs) {
|
|
6560
6878
|
return relative2(packPath, abs).split("\\").join("/");
|
|
6561
6879
|
}
|
|
6562
6880
|
async function tryReadJsonl(packPath, rel, parse) {
|
|
6563
|
-
const path =
|
|
6564
|
-
if (!
|
|
6565
|
-
const text = await
|
|
6881
|
+
const path = join18(packPath, rel);
|
|
6882
|
+
if (!existsSync17(path)) return [];
|
|
6883
|
+
const text = await readFile16(path, "utf8");
|
|
6566
6884
|
const out = [];
|
|
6567
6885
|
for (const line of text.split(/\r?\n/)) {
|
|
6568
6886
|
if (!line.trim()) continue;
|
|
@@ -6571,22 +6889,22 @@ async function tryReadJsonl(packPath, rel, parse) {
|
|
|
6571
6889
|
return out;
|
|
6572
6890
|
}
|
|
6573
6891
|
async function readSourceCards4(packPath) {
|
|
6574
|
-
const dir =
|
|
6575
|
-
if (!
|
|
6576
|
-
const { readdir:
|
|
6577
|
-
const entries = await
|
|
6892
|
+
const dir = join18(packPath, "evidence", "source-cards");
|
|
6893
|
+
if (!existsSync17(dir)) return [];
|
|
6894
|
+
const { readdir: readdir5 } = await import("fs/promises");
|
|
6895
|
+
const entries = await readdir5(dir);
|
|
6578
6896
|
const cards = [];
|
|
6579
6897
|
for (const entry of entries) {
|
|
6580
6898
|
if (!entry.endsWith(".json")) continue;
|
|
6581
|
-
const text = await
|
|
6899
|
+
const text = await readFile16(join18(dir, entry), "utf8");
|
|
6582
6900
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
6583
6901
|
}
|
|
6584
6902
|
return cards;
|
|
6585
6903
|
}
|
|
6586
6904
|
async function readGateResult2(packPath, sectionId) {
|
|
6587
|
-
const path =
|
|
6588
|
-
if (!
|
|
6589
|
-
const text = await
|
|
6905
|
+
const path = join18(packPath, "audits", `${sectionId}-gate.json`);
|
|
6906
|
+
if (!existsSync17(path)) return null;
|
|
6907
|
+
const text = await readFile16(path, "utf8");
|
|
6590
6908
|
return SectionGateResultSchema.parse(JSON.parse(text));
|
|
6591
6909
|
}
|
|
6592
6910
|
function fileSha256(text) {
|
|
@@ -6596,8 +6914,8 @@ async function indexSection(args) {
|
|
|
6596
6914
|
const { db, packPath, research, sectionId, now, counts } = args;
|
|
6597
6915
|
const section = research.sections.find((s) => s.id === sectionId);
|
|
6598
6916
|
if (!section) throw new SectionNotFoundError(sectionId);
|
|
6599
|
-
const sectionDir =
|
|
6600
|
-
if (!
|
|
6917
|
+
const sectionDir = join18(packPath, "sections", sectionId);
|
|
6918
|
+
if (!existsSync17(sectionDir)) throw new SectionNotFoundError(sectionId);
|
|
6601
6919
|
db.prepare("DELETE FROM sources WHERE section_id = ?").run(sectionId);
|
|
6602
6920
|
db.prepare("DELETE FROM claims WHERE section_id = ?").run(sectionId);
|
|
6603
6921
|
db.prepare("DELETE FROM contradictions WHERE section_id = ?").run(sectionId);
|
|
@@ -6636,9 +6954,9 @@ async function indexSection(args) {
|
|
|
6636
6954
|
};
|
|
6637
6955
|
const allCards = await readSourceCards4(packPath);
|
|
6638
6956
|
const sectionSourceIds = /* @__PURE__ */ new Set();
|
|
6639
|
-
const sourcesJsonlPath =
|
|
6640
|
-
if (
|
|
6641
|
-
const text = await
|
|
6957
|
+
const sourcesJsonlPath = join18(sectionDir, "sources.jsonl");
|
|
6958
|
+
if (existsSync17(sourcesJsonlPath)) {
|
|
6959
|
+
const text = await readFile16(sourcesJsonlPath, "utf8");
|
|
6642
6960
|
recordArtifact("sources_jsonl", relPath(packPath, sourcesJsonlPath), text);
|
|
6643
6961
|
for (const line of text.split(/\r?\n/)) {
|
|
6644
6962
|
if (!line.trim()) continue;
|
|
@@ -6648,7 +6966,7 @@ async function indexSection(args) {
|
|
|
6648
6966
|
}
|
|
6649
6967
|
for (const card of allCards) {
|
|
6650
6968
|
if (!sectionSourceIds.has(card.source_id) && card.section_id !== sectionId) continue;
|
|
6651
|
-
const cardPath = relPath(packPath,
|
|
6969
|
+
const cardPath = relPath(packPath, join18(packPath, "evidence", "source-cards", `${card.source_id}.json`));
|
|
6652
6970
|
db.prepare(
|
|
6653
6971
|
`INSERT OR REPLACE INTO sources(
|
|
6654
6972
|
source_id, section_id, url, publisher, source_type, relevance,
|
|
@@ -6682,9 +7000,9 @@ ${card.key_points.join("\n")}`;
|
|
|
6682
7000
|
`sections/${sectionId}/claims.jsonl`,
|
|
6683
7001
|
(r) => ClaimSchema.parse(r)
|
|
6684
7002
|
);
|
|
6685
|
-
const claimsArtifact = relPath(packPath,
|
|
6686
|
-
if (
|
|
6687
|
-
const text = await
|
|
7003
|
+
const claimsArtifact = relPath(packPath, join18(sectionDir, "claims.jsonl"));
|
|
7004
|
+
if (existsSync17(join18(sectionDir, "claims.jsonl"))) {
|
|
7005
|
+
const text = await readFile16(join18(sectionDir, "claims.jsonl"), "utf8");
|
|
6688
7006
|
recordArtifact("claims_jsonl", claimsArtifact, text);
|
|
6689
7007
|
}
|
|
6690
7008
|
for (const claim of claims) {
|
|
@@ -6721,9 +7039,9 @@ ${claim.evidence_excerpt}`;
|
|
|
6721
7039
|
`sections/${sectionId}/contradictions.jsonl`,
|
|
6722
7040
|
(r) => ContradictionSchema.parse(r)
|
|
6723
7041
|
);
|
|
6724
|
-
const contradictionsArtifact = relPath(packPath,
|
|
6725
|
-
if (
|
|
6726
|
-
const text = await
|
|
7042
|
+
const contradictionsArtifact = relPath(packPath, join18(sectionDir, "contradictions.jsonl"));
|
|
7043
|
+
if (existsSync17(join18(sectionDir, "contradictions.jsonl"))) {
|
|
7044
|
+
const text = await readFile16(join18(sectionDir, "contradictions.jsonl"), "utf8");
|
|
6727
7045
|
recordArtifact("contradictions_jsonl", contradictionsArtifact, text);
|
|
6728
7046
|
}
|
|
6729
7047
|
for (const c of contradictions) {
|
|
@@ -6759,10 +7077,10 @@ ${c.severity}`;
|
|
|
6759
7077
|
`audits/${sectionId}-findings.jsonl`,
|
|
6760
7078
|
(r) => ReviewFindingSchema.parse(r)
|
|
6761
7079
|
);
|
|
6762
|
-
const findingsArtifact = relPath(packPath,
|
|
6763
|
-
const findingsAbs =
|
|
6764
|
-
if (
|
|
6765
|
-
const text = await
|
|
7080
|
+
const findingsArtifact = relPath(packPath, join18(packPath, "audits", `${sectionId}-findings.jsonl`));
|
|
7081
|
+
const findingsAbs = join18(packPath, "audits", `${sectionId}-findings.jsonl`);
|
|
7082
|
+
if (existsSync17(findingsAbs)) {
|
|
7083
|
+
const text = await readFile16(findingsAbs, "utf8");
|
|
6766
7084
|
recordArtifact("findings_jsonl", findingsArtifact, text);
|
|
6767
7085
|
}
|
|
6768
7086
|
const findingById = /* @__PURE__ */ new Map();
|
|
@@ -6799,10 +7117,10 @@ ${f.evidence}`;
|
|
|
6799
7117
|
`sections/${sectionId}/claim-reviews.jsonl`,
|
|
6800
7118
|
(r) => ClaimReviewSchema.parse(r)
|
|
6801
7119
|
);
|
|
6802
|
-
const reviewsArtifact = relPath(packPath,
|
|
6803
|
-
const reviewsAbs =
|
|
6804
|
-
if (
|
|
6805
|
-
const text = await
|
|
7120
|
+
const reviewsArtifact = relPath(packPath, join18(sectionDir, "claim-reviews.jsonl"));
|
|
7121
|
+
const reviewsAbs = join18(sectionDir, "claim-reviews.jsonl");
|
|
7122
|
+
if (existsSync17(reviewsAbs)) {
|
|
7123
|
+
const text = await readFile16(reviewsAbs, "utf8");
|
|
6806
7124
|
recordArtifact("claim_reviews_jsonl", reviewsArtifact, text);
|
|
6807
7125
|
}
|
|
6808
7126
|
for (const r of reviews) {
|
|
@@ -6827,8 +7145,8 @@ ${r.reason}`;
|
|
|
6827
7145
|
}
|
|
6828
7146
|
const gate2 = await readGateResult2(packPath, sectionId);
|
|
6829
7147
|
if (gate2) {
|
|
6830
|
-
const gateArtifact = relPath(packPath,
|
|
6831
|
-
const gateText = await
|
|
7148
|
+
const gateArtifact = relPath(packPath, join18(packPath, "audits", `${sectionId}-gate.json`));
|
|
7149
|
+
const gateText = await readFile16(join18(packPath, "audits", `${sectionId}-gate.json`), "utf8");
|
|
6832
7150
|
recordArtifact("gate_json", gateArtifact, gateText);
|
|
6833
7151
|
db.prepare(
|
|
6834
7152
|
`INSERT OR REPLACE INTO gate_results(
|
|
@@ -6861,9 +7179,9 @@ ${gate2.next_actions.join("\n")}`;
|
|
|
6861
7179
|
"evidence/fetch-log.jsonl",
|
|
6862
7180
|
(r) => FetchReceiptSchema.parse(r)
|
|
6863
7181
|
);
|
|
6864
|
-
const fetchLogAbs =
|
|
6865
|
-
if (
|
|
6866
|
-
const text = await
|
|
7182
|
+
const fetchLogAbs = join18(packPath, "evidence", "fetch-log.jsonl");
|
|
7183
|
+
if (existsSync17(fetchLogAbs)) {
|
|
7184
|
+
const text = await readFile16(fetchLogAbs, "utf8");
|
|
6867
7185
|
recordArtifact("fetch_log_jsonl", relPath(packPath, fetchLogAbs), text);
|
|
6868
7186
|
}
|
|
6869
7187
|
for (const receipt of allReceipts.filter((r) => r.section_id === sectionId)) {
|
|
@@ -6899,9 +7217,9 @@ async function indexPackAuditRollups(db, packPath, _now) {
|
|
|
6899
7217
|
db.prepare(`DELETE FROM facts_fts WHERE record_type = ? AND record_id = ?`).run(f.recordType, f.recordId);
|
|
6900
7218
|
}
|
|
6901
7219
|
for (const f of ROLLUP_FILES) {
|
|
6902
|
-
const abs =
|
|
6903
|
-
if (!
|
|
6904
|
-
const text = await
|
|
7220
|
+
const abs = join18(packPath, "audits", f.filename);
|
|
7221
|
+
if (!existsSync17(abs)) continue;
|
|
7222
|
+
const text = await readFile16(abs, "utf8");
|
|
6905
7223
|
insertFts.run(
|
|
6906
7224
|
f.recordType,
|
|
6907
7225
|
f.recordId,
|
|
@@ -6913,8 +7231,8 @@ async function indexPackAuditRollups(db, packPath, _now) {
|
|
|
6913
7231
|
}
|
|
6914
7232
|
async function build(options) {
|
|
6915
7233
|
const packPath = options.packPath ? resolve12(options.packPath) : process.cwd();
|
|
6916
|
-
if (!
|
|
6917
|
-
const research = ResearchYamlSchema.parse(yamlParse5(await
|
|
7234
|
+
if (!existsSync17(join18(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
7235
|
+
const research = ResearchYamlSchema.parse(yamlParse5(await readFile16(join18(packPath, "research.yaml"), "utf8")));
|
|
6918
7236
|
const targets = options.sectionId ? [options.sectionId] : options.all ? research.sections.map((s) => s.id) : research.sections.map((s) => s.id);
|
|
6919
7237
|
if (targets.length === 0) {
|
|
6920
7238
|
throw new Error("No sections to index. Add at least one section to the pack.");
|
|
@@ -6971,7 +7289,7 @@ var init_build = __esm({
|
|
|
6971
7289
|
});
|
|
6972
7290
|
|
|
6973
7291
|
// src/indexer/query.ts
|
|
6974
|
-
import { existsSync as
|
|
7292
|
+
import { existsSync as existsSync18 } from "fs";
|
|
6975
7293
|
import { resolve as resolve13 } from "path";
|
|
6976
7294
|
function escapeFtsTerm(term) {
|
|
6977
7295
|
const trimmed = term.trim();
|
|
@@ -6982,8 +7300,8 @@ function escapeFtsTerm(term) {
|
|
|
6982
7300
|
function query(options) {
|
|
6983
7301
|
const packPath = options.packPath ? resolve13(options.packPath) : process.cwd();
|
|
6984
7302
|
const dbPath = indexDbPath(packPath);
|
|
6985
|
-
if (!
|
|
6986
|
-
if (!
|
|
7303
|
+
if (!existsSync18(packPath)) throw new PackNotFoundError(packPath);
|
|
7304
|
+
if (!existsSync18(dbPath)) throw new IndexNotBuiltError(dbPath);
|
|
6987
7305
|
const db = openIndexDb({ packPath, readonly: true });
|
|
6988
7306
|
const limit = options.limit ?? 25;
|
|
6989
7307
|
const ftsTerm = escapeFtsTerm(options.term);
|
|
@@ -7057,16 +7375,16 @@ var init_query = __esm({
|
|
|
7057
7375
|
});
|
|
7058
7376
|
|
|
7059
7377
|
// src/indexer/export.ts
|
|
7060
|
-
import { existsSync as
|
|
7061
|
-
import { mkdir as
|
|
7062
|
-
import { dirname as
|
|
7378
|
+
import { existsSync as existsSync19 } from "fs";
|
|
7379
|
+
import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
|
|
7380
|
+
import { dirname as dirname5, join as join19, resolve as resolve14 } from "path";
|
|
7063
7381
|
async function exportRepoKnowledge(options) {
|
|
7064
7382
|
const packPath = options.packPath ? resolve14(options.packPath) : process.cwd();
|
|
7065
|
-
if (!
|
|
7383
|
+
if (!existsSync19(packPath)) throw new PackNotFoundError(packPath);
|
|
7066
7384
|
const dbPath = indexDbPath(packPath);
|
|
7067
|
-
if (!
|
|
7068
|
-
const outPath = options.outPath ? resolve14(options.outPath) :
|
|
7069
|
-
await
|
|
7385
|
+
if (!existsSync19(dbPath)) throw new IndexNotBuiltError(dbPath);
|
|
7386
|
+
const outPath = options.outPath ? resolve14(options.outPath) : join19(packPath, "evidence", "repo-knowledge", "research-os-facts.jsonl");
|
|
7387
|
+
await mkdir14(dirname5(outPath), { recursive: true });
|
|
7070
7388
|
const db = openIndexDb({ packPath, readonly: true });
|
|
7071
7389
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7072
7390
|
const facts = [];
|
|
@@ -7225,8 +7543,8 @@ async function syncRepoKnowledge(options) {
|
|
|
7225
7543
|
};
|
|
7226
7544
|
}
|
|
7227
7545
|
const exportResult = await exportRepoKnowledge({ packPath });
|
|
7228
|
-
const { readFile:
|
|
7229
|
-
const text = await
|
|
7546
|
+
const { readFile: readFile26 } = await import("fs/promises");
|
|
7547
|
+
const text = await readFile26(exportResult.outPath, "utf8");
|
|
7230
7548
|
const facts = text.split(/\r?\n/).filter((l) => l.trim().length > 0).map((l) => JSON.parse(l));
|
|
7231
7549
|
try {
|
|
7232
7550
|
const r = await rk.ingestFacts({ facts, namespace: "research-os" });
|
|
@@ -7266,26 +7584,26 @@ var init_indexer = __esm({
|
|
|
7266
7584
|
});
|
|
7267
7585
|
|
|
7268
7586
|
// src/dispositions/schema.ts
|
|
7269
|
-
import { z as
|
|
7587
|
+
import { z as z14 } from "zod";
|
|
7270
7588
|
var ClaimSynthesisDispositionStatusSchema, ClaimSynthesisDispositionSchema;
|
|
7271
7589
|
var init_schema12 = __esm({
|
|
7272
7590
|
"src/dispositions/schema.ts"() {
|
|
7273
7591
|
"use strict";
|
|
7274
|
-
ClaimSynthesisDispositionStatusSchema =
|
|
7592
|
+
ClaimSynthesisDispositionStatusSchema = z14.enum([
|
|
7275
7593
|
"parked_not_for_synthesis",
|
|
7276
7594
|
"preserved_for_human_note",
|
|
7277
7595
|
"needs_human_review_excluded",
|
|
7278
7596
|
"out_of_bounds_regression_fixture"
|
|
7279
7597
|
]);
|
|
7280
|
-
ClaimSynthesisDispositionSchema =
|
|
7281
|
-
claim_id:
|
|
7282
|
-
section_id:
|
|
7598
|
+
ClaimSynthesisDispositionSchema = z14.object({
|
|
7599
|
+
claim_id: z14.string().min(1),
|
|
7600
|
+
section_id: z14.string().min(1),
|
|
7283
7601
|
status: ClaimSynthesisDispositionStatusSchema,
|
|
7284
|
-
reason:
|
|
7285
|
-
decided_by:
|
|
7286
|
-
authorized_by:
|
|
7287
|
-
source:
|
|
7288
|
-
created_at:
|
|
7602
|
+
reason: z14.string().min(4),
|
|
7603
|
+
decided_by: z14.string().min(1),
|
|
7604
|
+
authorized_by: z14.string().min(1),
|
|
7605
|
+
source: z14.string().min(1),
|
|
7606
|
+
created_at: z14.string().min(1)
|
|
7289
7607
|
});
|
|
7290
7608
|
}
|
|
7291
7609
|
});
|
|
@@ -7616,91 +7934,91 @@ var init_derive = __esm({
|
|
|
7616
7934
|
});
|
|
7617
7935
|
|
|
7618
7936
|
// src/cowork/schema.ts
|
|
7619
|
-
import { z as
|
|
7937
|
+
import { z as z15 } from "zod";
|
|
7620
7938
|
var HandoffModeSchema, IndexStatusSchema, ProvenanceSummarySchema, SectionStateSchema, WaiverEntrySchema, GateVerdictEntrySchema, ReviewDecisionCountSchema, CoworkHandoffPayloadSchema;
|
|
7621
7939
|
var init_schema13 = __esm({
|
|
7622
7940
|
"src/cowork/schema.ts"() {
|
|
7623
7941
|
"use strict";
|
|
7624
|
-
HandoffModeSchema =
|
|
7942
|
+
HandoffModeSchema = z15.enum([
|
|
7625
7943
|
"repair_required",
|
|
7626
7944
|
"synthesis_ready",
|
|
7627
7945
|
"human_review_required"
|
|
7628
7946
|
]);
|
|
7629
|
-
IndexStatusSchema =
|
|
7630
|
-
ProvenanceSummarySchema =
|
|
7631
|
-
accepted_count:
|
|
7632
|
-
rejected_count:
|
|
7633
|
-
triage_parked_count:
|
|
7634
|
-
needs_review_undispositioned_count:
|
|
7635
|
-
dispositioned_count:
|
|
7636
|
-
dispositioned_breakdown:
|
|
7637
|
-
parked_not_for_synthesis:
|
|
7638
|
-
preserved_for_human_note:
|
|
7639
|
-
needs_human_review_excluded:
|
|
7640
|
-
out_of_bounds_regression_fixture:
|
|
7947
|
+
IndexStatusSchema = z15.enum(["present", "missing"]);
|
|
7948
|
+
ProvenanceSummarySchema = z15.object({
|
|
7949
|
+
accepted_count: z15.number().int().nonnegative(),
|
|
7950
|
+
rejected_count: z15.number().int().nonnegative(),
|
|
7951
|
+
triage_parked_count: z15.number().int().nonnegative(),
|
|
7952
|
+
needs_review_undispositioned_count: z15.number().int().nonnegative(),
|
|
7953
|
+
dispositioned_count: z15.number().int().nonnegative(),
|
|
7954
|
+
dispositioned_breakdown: z15.object({
|
|
7955
|
+
parked_not_for_synthesis: z15.number().int().nonnegative(),
|
|
7956
|
+
preserved_for_human_note: z15.number().int().nonnegative(),
|
|
7957
|
+
needs_human_review_excluded: z15.number().int().nonnegative(),
|
|
7958
|
+
out_of_bounds_regression_fixture: z15.number().int().nonnegative()
|
|
7641
7959
|
}),
|
|
7642
|
-
active_repair_blockers:
|
|
7643
|
-
active_unresolved_contradictions:
|
|
7644
|
-
waivers_active:
|
|
7645
|
-
overrides_applied_count:
|
|
7646
|
-
});
|
|
7647
|
-
SectionStateSchema =
|
|
7648
|
-
section_id:
|
|
7649
|
-
purpose:
|
|
7650
|
-
status:
|
|
7651
|
-
has_gate_run:
|
|
7652
|
-
has_review_run:
|
|
7653
|
-
gate_verdict:
|
|
7654
|
-
synthesis_eligible:
|
|
7655
|
-
accepted_claim_ids:
|
|
7656
|
-
repair_claim_ids:
|
|
7657
|
-
rejected_claim_ids:
|
|
7658
|
-
dispositioned_claim_ids:
|
|
7659
|
-
candidate_claims_total:
|
|
7660
|
-
unresolved_contradiction_ids:
|
|
7661
|
-
blocking_reasons:
|
|
7662
|
-
active_blockers:
|
|
7663
|
-
blocking_contradictions_unresolved:
|
|
7960
|
+
active_repair_blockers: z15.number().int().nonnegative(),
|
|
7961
|
+
active_unresolved_contradictions: z15.number().int().nonnegative(),
|
|
7962
|
+
waivers_active: z15.array(z15.string()),
|
|
7963
|
+
overrides_applied_count: z15.number().int().nonnegative()
|
|
7964
|
+
});
|
|
7965
|
+
SectionStateSchema = z15.object({
|
|
7966
|
+
section_id: z15.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
7967
|
+
purpose: z15.string(),
|
|
7968
|
+
status: z15.string(),
|
|
7969
|
+
has_gate_run: z15.boolean(),
|
|
7970
|
+
has_review_run: z15.boolean(),
|
|
7971
|
+
gate_verdict: z15.string().nullable(),
|
|
7972
|
+
synthesis_eligible: z15.boolean(),
|
|
7973
|
+
accepted_claim_ids: z15.array(z15.string()),
|
|
7974
|
+
repair_claim_ids: z15.array(z15.string()),
|
|
7975
|
+
rejected_claim_ids: z15.array(z15.string()),
|
|
7976
|
+
dispositioned_claim_ids: z15.array(z15.string()).default([]),
|
|
7977
|
+
candidate_claims_total: z15.number().int().nonnegative(),
|
|
7978
|
+
unresolved_contradiction_ids: z15.array(z15.string()),
|
|
7979
|
+
blocking_reasons: z15.array(z15.string()),
|
|
7980
|
+
active_blockers: z15.array(z15.string()).default([]),
|
|
7981
|
+
blocking_contradictions_unresolved: z15.number().int().nonnegative(),
|
|
7664
7982
|
provenance_summary: ProvenanceSummarySchema.optional()
|
|
7665
7983
|
});
|
|
7666
|
-
WaiverEntrySchema =
|
|
7667
|
-
scope:
|
|
7668
|
-
family:
|
|
7669
|
-
reason:
|
|
7670
|
-
compensating_controls:
|
|
7671
|
-
applied_to:
|
|
7672
|
-
});
|
|
7673
|
-
GateVerdictEntrySchema =
|
|
7674
|
-
section_id:
|
|
7675
|
-
verdict:
|
|
7676
|
-
synthesis_eligible:
|
|
7677
|
-
});
|
|
7678
|
-
ReviewDecisionCountSchema =
|
|
7679
|
-
section_id:
|
|
7680
|
-
decision:
|
|
7681
|
-
count:
|
|
7682
|
-
});
|
|
7683
|
-
CoworkHandoffPayloadSchema =
|
|
7684
|
-
pack_id:
|
|
7685
|
-
pack_topic:
|
|
7686
|
-
generated_at:
|
|
7984
|
+
WaiverEntrySchema = z15.object({
|
|
7985
|
+
scope: z15.enum(["pack", "gate"]),
|
|
7986
|
+
family: z15.string(),
|
|
7987
|
+
reason: z15.string(),
|
|
7988
|
+
compensating_controls: z15.array(z15.string()),
|
|
7989
|
+
applied_to: z15.string()
|
|
7990
|
+
});
|
|
7991
|
+
GateVerdictEntrySchema = z15.object({
|
|
7992
|
+
section_id: z15.string(),
|
|
7993
|
+
verdict: z15.string(),
|
|
7994
|
+
synthesis_eligible: z15.boolean()
|
|
7995
|
+
});
|
|
7996
|
+
ReviewDecisionCountSchema = z15.object({
|
|
7997
|
+
section_id: z15.string(),
|
|
7998
|
+
decision: z15.string(),
|
|
7999
|
+
count: z15.number().int().nonnegative()
|
|
8000
|
+
});
|
|
8001
|
+
CoworkHandoffPayloadSchema = z15.object({
|
|
8002
|
+
pack_id: z15.string(),
|
|
8003
|
+
pack_topic: z15.string(),
|
|
8004
|
+
generated_at: z15.string(),
|
|
7687
8005
|
mode: HandoffModeSchema,
|
|
7688
|
-
synthesis_allowed:
|
|
7689
|
-
summary:
|
|
7690
|
-
sections:
|
|
7691
|
-
accepted_claim_ids:
|
|
7692
|
-
repair_claim_ids:
|
|
7693
|
-
blocked_claim_ids:
|
|
7694
|
-
dispositioned_claim_ids:
|
|
7695
|
-
unresolved_contradiction_ids:
|
|
7696
|
-
waivers:
|
|
7697
|
-
gate_verdicts:
|
|
7698
|
-
review_decisions:
|
|
7699
|
-
recommended_next_actions:
|
|
7700
|
-
allowed_write_paths:
|
|
7701
|
-
forbidden_actions:
|
|
8006
|
+
synthesis_allowed: z15.boolean(),
|
|
8007
|
+
summary: z15.string(),
|
|
8008
|
+
sections: z15.array(SectionStateSchema),
|
|
8009
|
+
accepted_claim_ids: z15.array(z15.string()),
|
|
8010
|
+
repair_claim_ids: z15.array(z15.string()),
|
|
8011
|
+
blocked_claim_ids: z15.array(z15.string()),
|
|
8012
|
+
dispositioned_claim_ids: z15.array(z15.string()).default([]),
|
|
8013
|
+
unresolved_contradiction_ids: z15.array(z15.string()),
|
|
8014
|
+
waivers: z15.array(WaiverEntrySchema),
|
|
8015
|
+
gate_verdicts: z15.array(GateVerdictEntrySchema),
|
|
8016
|
+
review_decisions: z15.array(ReviewDecisionCountSchema),
|
|
8017
|
+
recommended_next_actions: z15.array(z15.string()),
|
|
8018
|
+
allowed_write_paths: z15.array(z15.string()),
|
|
8019
|
+
forbidden_actions: z15.array(z15.string()),
|
|
7702
8020
|
index_status: IndexStatusSchema,
|
|
7703
|
-
warnings:
|
|
8021
|
+
warnings: z15.array(z15.string())
|
|
7704
8022
|
});
|
|
7705
8023
|
}
|
|
7706
8024
|
});
|
|
@@ -7879,19 +8197,19 @@ var init_markdown4 = __esm({
|
|
|
7879
8197
|
});
|
|
7880
8198
|
|
|
7881
8199
|
// src/cowork/run.ts
|
|
7882
|
-
import { existsSync as
|
|
7883
|
-
import { mkdir as
|
|
7884
|
-
import { join as
|
|
8200
|
+
import { existsSync as existsSync20 } from "fs";
|
|
8201
|
+
import { mkdir as mkdir15, readFile as readFile17, writeFile as writeFile15 } from "fs/promises";
|
|
8202
|
+
import { join as join20, resolve as resolve16 } from "path";
|
|
7885
8203
|
import { parse as yamlParse6 } from "yaml";
|
|
7886
8204
|
async function loadResearchYaml4(packPath) {
|
|
7887
|
-
const yamlPath =
|
|
7888
|
-
if (!
|
|
7889
|
-
const text = await
|
|
8205
|
+
const yamlPath = join20(packPath, "research.yaml");
|
|
8206
|
+
if (!existsSync20(yamlPath)) throw new PackNotFoundError(packPath);
|
|
8207
|
+
const text = await readFile17(yamlPath, "utf8");
|
|
7890
8208
|
return ResearchYamlSchema.parse(yamlParse6(text));
|
|
7891
8209
|
}
|
|
7892
8210
|
async function readJsonl3(path, parse, warnings) {
|
|
7893
|
-
if (!
|
|
7894
|
-
const text = await
|
|
8211
|
+
if (!existsSync20(path)) return [];
|
|
8212
|
+
const text = await readFile17(path, "utf8");
|
|
7895
8213
|
const out = [];
|
|
7896
8214
|
for (const line of text.split(/\r?\n/)) {
|
|
7897
8215
|
if (!line.trim()) continue;
|
|
@@ -7905,10 +8223,10 @@ async function readJsonl3(path, parse, warnings) {
|
|
|
7905
8223
|
return out;
|
|
7906
8224
|
}
|
|
7907
8225
|
async function readGate(packPath, sectionId, warnings) {
|
|
7908
|
-
const path =
|
|
7909
|
-
if (!
|
|
8226
|
+
const path = join20(packPath, "audits", `${sectionId}-gate.json`);
|
|
8227
|
+
if (!existsSync20(path)) return null;
|
|
7910
8228
|
try {
|
|
7911
|
-
const text = await
|
|
8229
|
+
const text = await readFile17(path, "utf8");
|
|
7912
8230
|
return SectionGateResultSchema.parse(JSON.parse(text));
|
|
7913
8231
|
} catch (err) {
|
|
7914
8232
|
warnings.push(`malformed gate result at audits/${sectionId}-gate.json: ${err instanceof Error ? err.message : err}`);
|
|
@@ -7923,35 +8241,35 @@ async function handoff(options) {
|
|
|
7923
8241
|
for (const section of research.sections) {
|
|
7924
8242
|
const sid = section.id;
|
|
7925
8243
|
const claims = await readJsonl3(
|
|
7926
|
-
|
|
8244
|
+
join20(packPath, "sections", sid, "claims.jsonl"),
|
|
7927
8245
|
(r) => ClaimSchema.parse(r),
|
|
7928
8246
|
warnings
|
|
7929
8247
|
);
|
|
7930
8248
|
const candidateClaims = claims.filter((c) => c.review_state === "candidate");
|
|
7931
8249
|
const claimReviews = await readJsonl3(
|
|
7932
|
-
|
|
8250
|
+
join20(packPath, "sections", sid, "claim-reviews.jsonl"),
|
|
7933
8251
|
(r) => ClaimReviewSchema.parse(r),
|
|
7934
8252
|
warnings
|
|
7935
8253
|
);
|
|
7936
8254
|
const contradictions = await readJsonl3(
|
|
7937
|
-
|
|
8255
|
+
join20(packPath, "sections", sid, "contradictions.jsonl"),
|
|
7938
8256
|
(r) => ContradictionSchema.parse(r),
|
|
7939
8257
|
warnings
|
|
7940
8258
|
);
|
|
7941
8259
|
const resolutions = await readJsonl3(
|
|
7942
|
-
|
|
8260
|
+
join20(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
|
|
7943
8261
|
(r) => ContradictionResolutionSchema.parse(r),
|
|
7944
8262
|
warnings
|
|
7945
8263
|
);
|
|
7946
8264
|
const dispositions = await readJsonl3(
|
|
7947
|
-
|
|
8265
|
+
join20(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
|
|
7948
8266
|
(r) => ClaimSynthesisDispositionSchema.parse(r),
|
|
7949
8267
|
warnings
|
|
7950
8268
|
);
|
|
7951
8269
|
const gate2 = await readGate(packPath, sid, warnings);
|
|
7952
8270
|
perSection.set(sid, { gate: gate2, candidateClaims, claimReviews, contradictions, resolutions, dispositions });
|
|
7953
8271
|
}
|
|
7954
|
-
const indexExists =
|
|
8272
|
+
const indexExists = existsSync20(indexDbPath(packPath));
|
|
7955
8273
|
if (!indexExists) {
|
|
7956
8274
|
warnings.push(
|
|
7957
8275
|
"Pack-local index missing at .research-os/index.sqlite \u2014 Cowork can still operate from canonical artifacts, but `research-os query` will refuse until you run `research-os index build --all`."
|
|
@@ -7967,10 +8285,10 @@ async function handoff(options) {
|
|
|
7967
8285
|
warnings
|
|
7968
8286
|
})
|
|
7969
8287
|
);
|
|
7970
|
-
const handoffsDir =
|
|
7971
|
-
await
|
|
7972
|
-
const jsonPath =
|
|
7973
|
-
const mdPath =
|
|
8288
|
+
const handoffsDir = join20(packPath, "handoffs");
|
|
8289
|
+
await mkdir15(handoffsDir, { recursive: true });
|
|
8290
|
+
const jsonPath = join20(handoffsDir, "cowork-handoff.json");
|
|
8291
|
+
const mdPath = join20(handoffsDir, "cowork-master.md");
|
|
7974
8292
|
await writeFile15(jsonPath, JSON.stringify(payload, null, 2), "utf8");
|
|
7975
8293
|
await writeFile15(mdPath, renderCoworkMaster(payload), "utf8");
|
|
7976
8294
|
return {
|
|
@@ -8250,86 +8568,86 @@ var init_derive2 = __esm({
|
|
|
8250
8568
|
});
|
|
8251
8569
|
|
|
8252
8570
|
// src/synth/schema.ts
|
|
8253
|
-
import { z as
|
|
8571
|
+
import { z as z16 } from "zod";
|
|
8254
8572
|
var SectionAcceptedSummarySchema, ClaimClusterSchema, SharedSourceSchema, ScopeOverlapSchema, CrossSectionContradictionRefSchema, WaiverDependencySchema, AllowedSynthesisInputSchema, ForbiddenInputSchema, CrossSectionMapSchema;
|
|
8255
8573
|
var init_schema14 = __esm({
|
|
8256
8574
|
"src/synth/schema.ts"() {
|
|
8257
8575
|
"use strict";
|
|
8258
|
-
SectionAcceptedSummarySchema =
|
|
8259
|
-
section_id:
|
|
8260
|
-
purpose:
|
|
8261
|
-
status:
|
|
8262
|
-
accepted_claim_ids:
|
|
8263
|
-
excluded_reason:
|
|
8264
|
-
});
|
|
8265
|
-
ClaimClusterSchema = z15.object({
|
|
8266
|
-
cluster_id: z15.string(),
|
|
8267
|
-
shared_source_ids: z15.array(z15.string()),
|
|
8268
|
-
member_claim_ids: z15.array(z15.string()).min(1),
|
|
8269
|
-
spans_sections: z15.array(z15.string())
|
|
8270
|
-
});
|
|
8271
|
-
SharedSourceSchema = z15.object({
|
|
8272
|
-
source_id: z15.string(),
|
|
8273
|
-
publisher: z15.string().nullable(),
|
|
8274
|
-
source_type: z15.string(),
|
|
8275
|
-
used_by_claim_ids: z15.array(z15.string()),
|
|
8276
|
-
spans_sections: z15.array(z15.string())
|
|
8277
|
-
});
|
|
8278
|
-
ScopeOverlapSchema = z15.object({
|
|
8279
|
-
claim_a: z15.string(),
|
|
8280
|
-
claim_b: z15.string(),
|
|
8281
|
-
scope_a: z15.string().nullable(),
|
|
8282
|
-
scope_b: z15.string().nullable(),
|
|
8283
|
-
jaccard: z15.number().min(0).max(1),
|
|
8284
|
-
cross_section: z15.boolean(),
|
|
8285
|
-
warning: z15.string()
|
|
8286
|
-
});
|
|
8287
|
-
CrossSectionContradictionRefSchema = z15.object({
|
|
8288
|
-
contradiction_id: z15.string(),
|
|
8289
|
-
claim_ids: z15.array(z15.string()),
|
|
8290
|
-
sections: z15.array(z15.string()),
|
|
8291
|
-
type: z15.string(),
|
|
8292
|
-
severity: z15.string(),
|
|
8293
|
-
status: z15.string()
|
|
8294
|
-
});
|
|
8295
|
-
WaiverDependencySchema = z15.object({
|
|
8296
|
-
scope: z15.enum(["pack", "gate"]),
|
|
8297
|
-
family: z15.string(),
|
|
8298
|
-
reason: z15.string(),
|
|
8299
|
-
compensating_controls: z15.array(z15.string()),
|
|
8300
|
-
applied_to: z15.string(),
|
|
8301
|
-
must_disclose_in: z15.enum(["decision-brief.md", "final-report.md", "both"])
|
|
8576
|
+
SectionAcceptedSummarySchema = z16.object({
|
|
8577
|
+
section_id: z16.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
8578
|
+
purpose: z16.string(),
|
|
8579
|
+
status: z16.string(),
|
|
8580
|
+
accepted_claim_ids: z16.array(z16.string()),
|
|
8581
|
+
excluded_reason: z16.string().nullable()
|
|
8302
8582
|
});
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
scope: z15.string().nullable(),
|
|
8309
|
-
not: z15.string().nullable(),
|
|
8310
|
-
source_ids: z15.array(z15.string())
|
|
8311
|
-
});
|
|
8312
|
-
ForbiddenInputSchema = z15.object({
|
|
8313
|
-
claim_id: z15.string(),
|
|
8314
|
-
section_id: z15.string(),
|
|
8315
|
-
decision: z15.string(),
|
|
8316
|
-
reason: z15.string()
|
|
8583
|
+
ClaimClusterSchema = z16.object({
|
|
8584
|
+
cluster_id: z16.string(),
|
|
8585
|
+
shared_source_ids: z16.array(z16.string()),
|
|
8586
|
+
member_claim_ids: z16.array(z16.string()).min(1),
|
|
8587
|
+
spans_sections: z16.array(z16.string())
|
|
8317
8588
|
});
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8589
|
+
SharedSourceSchema = z16.object({
|
|
8590
|
+
source_id: z16.string(),
|
|
8591
|
+
publisher: z16.string().nullable(),
|
|
8592
|
+
source_type: z16.string(),
|
|
8593
|
+
used_by_claim_ids: z16.array(z16.string()),
|
|
8594
|
+
spans_sections: z16.array(z16.string())
|
|
8595
|
+
});
|
|
8596
|
+
ScopeOverlapSchema = z16.object({
|
|
8597
|
+
claim_a: z16.string(),
|
|
8598
|
+
claim_b: z16.string(),
|
|
8599
|
+
scope_a: z16.string().nullable(),
|
|
8600
|
+
scope_b: z16.string().nullable(),
|
|
8601
|
+
jaccard: z16.number().min(0).max(1),
|
|
8602
|
+
cross_section: z16.boolean(),
|
|
8603
|
+
warning: z16.string()
|
|
8604
|
+
});
|
|
8605
|
+
CrossSectionContradictionRefSchema = z16.object({
|
|
8606
|
+
contradiction_id: z16.string(),
|
|
8607
|
+
claim_ids: z16.array(z16.string()),
|
|
8608
|
+
sections: z16.array(z16.string()),
|
|
8609
|
+
type: z16.string(),
|
|
8610
|
+
severity: z16.string(),
|
|
8611
|
+
status: z16.string()
|
|
8612
|
+
});
|
|
8613
|
+
WaiverDependencySchema = z16.object({
|
|
8614
|
+
scope: z16.enum(["pack", "gate"]),
|
|
8615
|
+
family: z16.string(),
|
|
8616
|
+
reason: z16.string(),
|
|
8617
|
+
compensating_controls: z16.array(z16.string()),
|
|
8618
|
+
applied_to: z16.string(),
|
|
8619
|
+
must_disclose_in: z16.enum(["decision-brief.md", "final-report.md", "both"])
|
|
8620
|
+
});
|
|
8621
|
+
AllowedSynthesisInputSchema = z16.object({
|
|
8622
|
+
claim_id: z16.string(),
|
|
8623
|
+
section_id: z16.string(),
|
|
8624
|
+
artifact_path: z16.string(),
|
|
8625
|
+
asserts: z16.string(),
|
|
8626
|
+
scope: z16.string().nullable(),
|
|
8627
|
+
not: z16.string().nullable(),
|
|
8628
|
+
source_ids: z16.array(z16.string())
|
|
8629
|
+
});
|
|
8630
|
+
ForbiddenInputSchema = z16.object({
|
|
8631
|
+
claim_id: z16.string(),
|
|
8632
|
+
section_id: z16.string(),
|
|
8633
|
+
decision: z16.string(),
|
|
8634
|
+
reason: z16.string()
|
|
8635
|
+
});
|
|
8636
|
+
CrossSectionMapSchema = z16.object({
|
|
8637
|
+
pack_id: z16.string(),
|
|
8638
|
+
pack_topic: z16.string(),
|
|
8639
|
+
pack_decision: z16.string(),
|
|
8640
|
+
generated_at: z16.string(),
|
|
8641
|
+
accepted_claim_ids: z16.array(z16.string()),
|
|
8642
|
+
sections: z16.array(SectionAcceptedSummarySchema),
|
|
8643
|
+
claim_clusters: z16.array(ClaimClusterSchema),
|
|
8644
|
+
shared_sources: z16.array(SharedSourceSchema),
|
|
8645
|
+
scope_overlaps: z16.array(ScopeOverlapSchema),
|
|
8646
|
+
cross_section_contradictions: z16.array(CrossSectionContradictionRefSchema),
|
|
8647
|
+
waiver_dependencies: z16.array(WaiverDependencySchema),
|
|
8648
|
+
open_questions: z16.array(z16.string()),
|
|
8649
|
+
allowed_synthesis_inputs: z16.array(AllowedSynthesisInputSchema),
|
|
8650
|
+
forbidden_inputs: z16.array(ForbiddenInputSchema)
|
|
8333
8651
|
});
|
|
8334
8652
|
}
|
|
8335
8653
|
});
|
|
@@ -8589,13 +8907,13 @@ var init_markdown5 = __esm({
|
|
|
8589
8907
|
});
|
|
8590
8908
|
|
|
8591
8909
|
// src/synth/run.ts
|
|
8592
|
-
import { existsSync as
|
|
8593
|
-
import { mkdir as
|
|
8594
|
-
import { join as
|
|
8910
|
+
import { existsSync as existsSync21 } from "fs";
|
|
8911
|
+
import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile16 } from "fs/promises";
|
|
8912
|
+
import { join as join21, resolve as resolve17 } from "path";
|
|
8595
8913
|
import { parse as yamlParse7 } from "yaml";
|
|
8596
8914
|
async function readJsonl4(path, parse) {
|
|
8597
|
-
if (!
|
|
8598
|
-
const text = await
|
|
8915
|
+
if (!existsSync21(path)) return [];
|
|
8916
|
+
const text = await readFile18(path, "utf8");
|
|
8599
8917
|
const out = [];
|
|
8600
8918
|
for (const line of text.split(/\r?\n/)) {
|
|
8601
8919
|
if (!line.trim()) continue;
|
|
@@ -8604,27 +8922,27 @@ async function readJsonl4(path, parse) {
|
|
|
8604
8922
|
return out;
|
|
8605
8923
|
}
|
|
8606
8924
|
async function readSourceCards5(packPath) {
|
|
8607
|
-
const dir =
|
|
8608
|
-
if (!
|
|
8609
|
-
const { readdir:
|
|
8610
|
-
const entries = await
|
|
8925
|
+
const dir = join21(packPath, "evidence", "source-cards");
|
|
8926
|
+
if (!existsSync21(dir)) return [];
|
|
8927
|
+
const { readdir: readdir5 } = await import("fs/promises");
|
|
8928
|
+
const entries = await readdir5(dir);
|
|
8611
8929
|
const cards = [];
|
|
8612
8930
|
for (const entry of entries) {
|
|
8613
8931
|
if (!entry.endsWith(".json")) continue;
|
|
8614
|
-
const text = await
|
|
8932
|
+
const text = await readFile18(join21(dir, entry), "utf8");
|
|
8615
8933
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
8616
8934
|
}
|
|
8617
8935
|
return cards;
|
|
8618
8936
|
}
|
|
8619
8937
|
async function workspace(options) {
|
|
8620
8938
|
const packPath = options.packPath ? resolve17(options.packPath) : process.cwd();
|
|
8621
|
-
const yamlPath =
|
|
8622
|
-
if (!
|
|
8623
|
-
const research = ResearchYamlSchema.parse(yamlParse7(await
|
|
8624
|
-
const handoffPath =
|
|
8625
|
-
if (!
|
|
8939
|
+
const yamlPath = join21(packPath, "research.yaml");
|
|
8940
|
+
if (!existsSync21(yamlPath)) throw new PackNotFoundError(packPath);
|
|
8941
|
+
const research = ResearchYamlSchema.parse(yamlParse7(await readFile18(yamlPath, "utf8")));
|
|
8942
|
+
const handoffPath = join21(packPath, "handoffs", "cowork-handoff.json");
|
|
8943
|
+
if (!existsSync21(handoffPath)) throw new HandoffNotFoundError();
|
|
8626
8944
|
const handoff2 = CoworkHandoffPayloadSchema.parse(
|
|
8627
|
-
JSON.parse(await
|
|
8945
|
+
JSON.parse(await readFile18(handoffPath, "utf8"))
|
|
8628
8946
|
);
|
|
8629
8947
|
if (handoff2.mode !== "synthesis_ready") {
|
|
8630
8948
|
return {
|
|
@@ -8646,21 +8964,21 @@ async function workspace(options) {
|
|
|
8646
8964
|
claimsBySection.set(
|
|
8647
8965
|
section.id,
|
|
8648
8966
|
await readJsonl4(
|
|
8649
|
-
|
|
8967
|
+
join21(packPath, "sections", section.id, "claims.jsonl"),
|
|
8650
8968
|
(r) => ClaimSchema.parse(r)
|
|
8651
8969
|
)
|
|
8652
8970
|
);
|
|
8653
8971
|
reviewsBySection.set(
|
|
8654
8972
|
section.id,
|
|
8655
8973
|
await readJsonl4(
|
|
8656
|
-
|
|
8974
|
+
join21(packPath, "sections", section.id, "claim-reviews.jsonl"),
|
|
8657
8975
|
(r) => ClaimReviewSchema.parse(r)
|
|
8658
8976
|
)
|
|
8659
8977
|
);
|
|
8660
8978
|
contradictionsBySection.set(
|
|
8661
8979
|
section.id,
|
|
8662
8980
|
await readJsonl4(
|
|
8663
|
-
|
|
8981
|
+
join21(packPath, "sections", section.id, "contradictions.jsonl"),
|
|
8664
8982
|
(r) => ContradictionSchema.parse(r)
|
|
8665
8983
|
)
|
|
8666
8984
|
);
|
|
@@ -8677,17 +8995,17 @@ async function workspace(options) {
|
|
|
8677
8995
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8678
8996
|
})
|
|
8679
8997
|
);
|
|
8680
|
-
const synthDir =
|
|
8681
|
-
await
|
|
8998
|
+
const synthDir = join21(packPath, "synthesis");
|
|
8999
|
+
await mkdir16(synthDir, { recursive: true });
|
|
8682
9000
|
const filesWritten = [];
|
|
8683
9001
|
const writeIfAbsent = async (relPath2, content) => {
|
|
8684
|
-
const abs =
|
|
8685
|
-
if (
|
|
9002
|
+
const abs = join21(synthDir, relPath2);
|
|
9003
|
+
if (existsSync21(abs)) return;
|
|
8686
9004
|
await writeFile16(abs, content, "utf8");
|
|
8687
9005
|
filesWritten.push(abs);
|
|
8688
9006
|
};
|
|
8689
9007
|
const writeAlways = async (relPath2, content) => {
|
|
8690
|
-
const abs =
|
|
9008
|
+
const abs = join21(synthDir, relPath2);
|
|
8691
9009
|
await writeFile16(abs, content, "utf8");
|
|
8692
9010
|
filesWritten.push(abs);
|
|
8693
9011
|
};
|
|
@@ -9497,179 +9815,179 @@ var init_aggregate = __esm({
|
|
|
9497
9815
|
});
|
|
9498
9816
|
|
|
9499
9817
|
// src/audit/schema.ts
|
|
9500
|
-
import { z as
|
|
9818
|
+
import { z as z17 } from "zod";
|
|
9501
9819
|
var PackVerdictSchema, HandoffModeSchema2, OrphanClaimRowSchema, StaleSourceRowSchema, WeakSourceRowSchema, UnresolvedContradictionRowSchema, ScopeWideningRiskRowSchema, SourceDiversityGapRowSchema, SynthesisReadinessRowSchema, PackAuditPayloadSchema;
|
|
9502
9820
|
var init_schema15 = __esm({
|
|
9503
9821
|
"src/audit/schema.ts"() {
|
|
9504
9822
|
"use strict";
|
|
9505
|
-
PackVerdictSchema =
|
|
9823
|
+
PackVerdictSchema = z17.enum([
|
|
9506
9824
|
"ready_for_synthesis",
|
|
9507
9825
|
"repair_required",
|
|
9508
9826
|
"human_review_required",
|
|
9509
9827
|
"blocked"
|
|
9510
9828
|
]);
|
|
9511
|
-
HandoffModeSchema2 =
|
|
9829
|
+
HandoffModeSchema2 = z17.enum([
|
|
9512
9830
|
"repair_required",
|
|
9513
9831
|
"synthesis_ready",
|
|
9514
9832
|
"human_review_required",
|
|
9515
9833
|
"unknown"
|
|
9516
9834
|
]);
|
|
9517
|
-
OrphanClaimRowSchema =
|
|
9518
|
-
claim_id:
|
|
9519
|
-
section_id:
|
|
9520
|
-
reason:
|
|
9835
|
+
OrphanClaimRowSchema = z17.object({
|
|
9836
|
+
claim_id: z17.string(),
|
|
9837
|
+
section_id: z17.string(),
|
|
9838
|
+
reason: z17.enum([
|
|
9521
9839
|
"missing_source_card",
|
|
9522
9840
|
"missing_source_hash",
|
|
9523
9841
|
"missing_evidence_excerpt",
|
|
9524
9842
|
"unresolvable_source_id"
|
|
9525
9843
|
]),
|
|
9526
|
-
details:
|
|
9527
|
-
artifact_path:
|
|
9528
|
-
});
|
|
9529
|
-
StaleSourceRowSchema =
|
|
9530
|
-
source_id:
|
|
9531
|
-
section_id:
|
|
9532
|
-
publisher:
|
|
9533
|
-
reason:
|
|
9534
|
-
details:
|
|
9535
|
-
artifact_path:
|
|
9536
|
-
policy:
|
|
9537
|
-
required:
|
|
9538
|
-
max_source_age_months:
|
|
9539
|
-
stale_source_policy:
|
|
9844
|
+
details: z17.string(),
|
|
9845
|
+
artifact_path: z17.string()
|
|
9846
|
+
});
|
|
9847
|
+
StaleSourceRowSchema = z17.object({
|
|
9848
|
+
source_id: z17.string(),
|
|
9849
|
+
section_id: z17.string(),
|
|
9850
|
+
publisher: z17.string().nullable(),
|
|
9851
|
+
reason: z17.enum(["too_old", "missing_date", "unparseable_date"]),
|
|
9852
|
+
details: z17.string(),
|
|
9853
|
+
artifact_path: z17.string(),
|
|
9854
|
+
policy: z17.object({
|
|
9855
|
+
required: z17.boolean(),
|
|
9856
|
+
max_source_age_months: z17.number().int().nullable(),
|
|
9857
|
+
stale_source_policy: z17.enum(["warn", "fail"])
|
|
9540
9858
|
})
|
|
9541
9859
|
});
|
|
9542
|
-
WeakSourceRowSchema =
|
|
9543
|
-
reason:
|
|
9860
|
+
WeakSourceRowSchema = z17.object({
|
|
9861
|
+
reason: z17.enum([
|
|
9544
9862
|
"source_cluster_monopoly",
|
|
9545
9863
|
"low_independent_publishers",
|
|
9546
9864
|
"missing_primary_source",
|
|
9547
9865
|
"excessive_type_imbalance",
|
|
9548
9866
|
"failed_fetches_reducing_floor"
|
|
9549
9867
|
]),
|
|
9550
|
-
section_id:
|
|
9551
|
-
details:
|
|
9552
|
-
evidence_ids:
|
|
9553
|
-
artifact_path:
|
|
9554
|
-
});
|
|
9555
|
-
UnresolvedContradictionRowSchema =
|
|
9556
|
-
contradiction_id:
|
|
9557
|
-
section_id:
|
|
9558
|
-
type:
|
|
9559
|
-
severity:
|
|
9560
|
-
status:
|
|
9561
|
-
claim_ids:
|
|
9562
|
-
artifact_path:
|
|
9563
|
-
});
|
|
9564
|
-
ScopeWideningRiskRowSchema =
|
|
9565
|
-
reason:
|
|
9868
|
+
section_id: z17.string(),
|
|
9869
|
+
details: z17.string(),
|
|
9870
|
+
evidence_ids: z17.array(z17.string()),
|
|
9871
|
+
artifact_path: z17.string()
|
|
9872
|
+
});
|
|
9873
|
+
UnresolvedContradictionRowSchema = z17.object({
|
|
9874
|
+
contradiction_id: z17.string(),
|
|
9875
|
+
section_id: z17.string(),
|
|
9876
|
+
type: z17.string(),
|
|
9877
|
+
severity: z17.string(),
|
|
9878
|
+
status: z17.string(),
|
|
9879
|
+
claim_ids: z17.array(z17.string()),
|
|
9880
|
+
artifact_path: z17.string()
|
|
9881
|
+
});
|
|
9882
|
+
ScopeWideningRiskRowSchema = z17.object({
|
|
9883
|
+
reason: z17.enum([
|
|
9566
9884
|
"overgeneralization_finding",
|
|
9567
9885
|
"scope_null_in_use",
|
|
9568
9886
|
"missing_not_flagged",
|
|
9569
9887
|
"contextual_to_universal_risk"
|
|
9570
9888
|
]),
|
|
9571
|
-
claim_id:
|
|
9572
|
-
section_id:
|
|
9573
|
-
details:
|
|
9574
|
-
artifact_path:
|
|
9889
|
+
claim_id: z17.string(),
|
|
9890
|
+
section_id: z17.string(),
|
|
9891
|
+
details: z17.string(),
|
|
9892
|
+
artifact_path: z17.string()
|
|
9575
9893
|
});
|
|
9576
|
-
SourceDiversityGapRowSchema =
|
|
9577
|
-
reason:
|
|
9894
|
+
SourceDiversityGapRowSchema = z17.object({
|
|
9895
|
+
reason: z17.enum([
|
|
9578
9896
|
"section_publisher_monopoly",
|
|
9579
9897
|
"low_section_publisher_count",
|
|
9580
9898
|
"cross_section_publisher_overlap",
|
|
9581
9899
|
"section_has_no_sources"
|
|
9582
9900
|
]),
|
|
9583
|
-
section_id:
|
|
9584
|
-
details:
|
|
9585
|
-
evidence_ids:
|
|
9586
|
-
});
|
|
9587
|
-
SynthesisReadinessRowSchema =
|
|
9588
|
-
section_id:
|
|
9589
|
-
purpose:
|
|
9590
|
-
status:
|
|
9591
|
-
has_gate_run:
|
|
9592
|
-
has_review_run:
|
|
9593
|
-
gate_verdict:
|
|
9594
|
-
synthesis_eligible:
|
|
9595
|
-
candidate_claims:
|
|
9596
|
-
accepted_claims:
|
|
9597
|
-
repair_claims:
|
|
9598
|
-
rejected_claims:
|
|
9599
|
-
dispositioned_claims:
|
|
9600
|
-
blocking_reasons:
|
|
9901
|
+
section_id: z17.string(),
|
|
9902
|
+
details: z17.string(),
|
|
9903
|
+
evidence_ids: z17.array(z17.string())
|
|
9904
|
+
});
|
|
9905
|
+
SynthesisReadinessRowSchema = z17.object({
|
|
9906
|
+
section_id: z17.string(),
|
|
9907
|
+
purpose: z17.string(),
|
|
9908
|
+
status: z17.string(),
|
|
9909
|
+
has_gate_run: z17.boolean(),
|
|
9910
|
+
has_review_run: z17.boolean(),
|
|
9911
|
+
gate_verdict: z17.string().nullable(),
|
|
9912
|
+
synthesis_eligible: z17.boolean(),
|
|
9913
|
+
candidate_claims: z17.number().int().nonnegative(),
|
|
9914
|
+
accepted_claims: z17.number().int().nonnegative(),
|
|
9915
|
+
repair_claims: z17.number().int().nonnegative(),
|
|
9916
|
+
rejected_claims: z17.number().int().nonnegative(),
|
|
9917
|
+
dispositioned_claims: z17.number().int().nonnegative(),
|
|
9918
|
+
blocking_reasons: z17.array(z17.string()),
|
|
9601
9919
|
cowork_handoff_mode: HandoffModeSchema2,
|
|
9602
|
-
workspace_allowed:
|
|
9920
|
+
workspace_allowed: z17.boolean()
|
|
9603
9921
|
});
|
|
9604
|
-
PackAuditPayloadSchema =
|
|
9605
|
-
pack_id:
|
|
9606
|
-
pack_topic:
|
|
9607
|
-
generated_at:
|
|
9922
|
+
PackAuditPayloadSchema = z17.object({
|
|
9923
|
+
pack_id: z17.string(),
|
|
9924
|
+
pack_topic: z17.string(),
|
|
9925
|
+
generated_at: z17.string(),
|
|
9608
9926
|
verdict: PackVerdictSchema,
|
|
9609
|
-
synthesis_allowed:
|
|
9610
|
-
section_summaries:
|
|
9611
|
-
claim_summary:
|
|
9612
|
-
total:
|
|
9613
|
-
candidate:
|
|
9614
|
-
accepted_for_synthesis:
|
|
9615
|
-
rejected:
|
|
9616
|
-
needs_repair:
|
|
9617
|
-
dispositioned:
|
|
9618
|
-
no_review:
|
|
9619
|
-
with_evidence_excerpt:
|
|
9620
|
-
with_source_hashes:
|
|
9621
|
-
scope_null:
|
|
9622
|
-
not_null:
|
|
9623
|
-
orphans:
|
|
9927
|
+
synthesis_allowed: z17.boolean(),
|
|
9928
|
+
section_summaries: z17.array(SynthesisReadinessRowSchema),
|
|
9929
|
+
claim_summary: z17.object({
|
|
9930
|
+
total: z17.number().int().nonnegative(),
|
|
9931
|
+
candidate: z17.number().int().nonnegative(),
|
|
9932
|
+
accepted_for_synthesis: z17.number().int().nonnegative(),
|
|
9933
|
+
rejected: z17.number().int().nonnegative(),
|
|
9934
|
+
needs_repair: z17.number().int().nonnegative(),
|
|
9935
|
+
dispositioned: z17.number().int().nonnegative(),
|
|
9936
|
+
no_review: z17.number().int().nonnegative(),
|
|
9937
|
+
with_evidence_excerpt: z17.number().int().nonnegative(),
|
|
9938
|
+
with_source_hashes: z17.number().int().nonnegative(),
|
|
9939
|
+
scope_null: z17.number().int().nonnegative(),
|
|
9940
|
+
not_null: z17.number().int().nonnegative(),
|
|
9941
|
+
orphans: z17.number().int().nonnegative()
|
|
9624
9942
|
}),
|
|
9625
|
-
source_summary:
|
|
9626
|
-
total:
|
|
9627
|
-
primary:
|
|
9628
|
-
secondary:
|
|
9629
|
-
forum:
|
|
9630
|
-
benchmark:
|
|
9631
|
-
docs:
|
|
9632
|
-
unknown:
|
|
9633
|
-
independent_publishers:
|
|
9634
|
-
failed_fetches:
|
|
9635
|
-
sections_with_sources:
|
|
9636
|
-
sections_without_sources:
|
|
9943
|
+
source_summary: z17.object({
|
|
9944
|
+
total: z17.number().int().nonnegative(),
|
|
9945
|
+
primary: z17.number().int().nonnegative(),
|
|
9946
|
+
secondary: z17.number().int().nonnegative(),
|
|
9947
|
+
forum: z17.number().int().nonnegative(),
|
|
9948
|
+
benchmark: z17.number().int().nonnegative(),
|
|
9949
|
+
docs: z17.number().int().nonnegative(),
|
|
9950
|
+
unknown: z17.number().int().nonnegative(),
|
|
9951
|
+
independent_publishers: z17.number().int().nonnegative(),
|
|
9952
|
+
failed_fetches: z17.number().int().nonnegative(),
|
|
9953
|
+
sections_with_sources: z17.number().int().nonnegative(),
|
|
9954
|
+
sections_without_sources: z17.number().int().nonnegative()
|
|
9637
9955
|
}),
|
|
9638
|
-
contradiction_summary:
|
|
9639
|
-
total:
|
|
9640
|
-
unresolved:
|
|
9641
|
-
blocking:
|
|
9642
|
-
reconciled:
|
|
9643
|
-
preserved_deliberately:
|
|
9644
|
-
rejected:
|
|
9645
|
-
by_type:
|
|
9646
|
-
sections_with_clean_ledger:
|
|
9956
|
+
contradiction_summary: z17.object({
|
|
9957
|
+
total: z17.number().int().nonnegative(),
|
|
9958
|
+
unresolved: z17.number().int().nonnegative(),
|
|
9959
|
+
blocking: z17.number().int().nonnegative(),
|
|
9960
|
+
reconciled: z17.number().int().nonnegative(),
|
|
9961
|
+
preserved_deliberately: z17.number().int().nonnegative(),
|
|
9962
|
+
rejected: z17.number().int().nonnegative(),
|
|
9963
|
+
by_type: z17.record(z17.string(), z17.number().int().nonnegative()),
|
|
9964
|
+
sections_with_clean_ledger: z17.number().int().nonnegative()
|
|
9647
9965
|
}),
|
|
9648
|
-
review_summary:
|
|
9649
|
-
sections_with_review_run:
|
|
9650
|
-
sections_without_review_run:
|
|
9651
|
-
decision_counts:
|
|
9652
|
-
blocking_findings:
|
|
9966
|
+
review_summary: z17.object({
|
|
9967
|
+
sections_with_review_run: z17.number().int().nonnegative(),
|
|
9968
|
+
sections_without_review_run: z17.number().int().nonnegative(),
|
|
9969
|
+
decision_counts: z17.record(z17.string(), z17.number().int().nonnegative()),
|
|
9970
|
+
blocking_findings: z17.number().int().nonnegative()
|
|
9653
9971
|
}),
|
|
9654
|
-
waiver_summary:
|
|
9655
|
-
total:
|
|
9656
|
-
invalid:
|
|
9657
|
-
by_family:
|
|
9972
|
+
waiver_summary: z17.object({
|
|
9973
|
+
total: z17.number().int().nonnegative(),
|
|
9974
|
+
invalid: z17.number().int().nonnegative(),
|
|
9975
|
+
by_family: z17.record(z17.string(), z17.number().int().nonnegative())
|
|
9658
9976
|
}),
|
|
9659
|
-
readiness_summary:
|
|
9660
|
-
total_sections:
|
|
9661
|
-
ready_sections:
|
|
9662
|
-
repair_sections:
|
|
9663
|
-
blocked_sections:
|
|
9664
|
-
no_gate_sections:
|
|
9665
|
-
no_review_sections:
|
|
9977
|
+
readiness_summary: z17.object({
|
|
9978
|
+
total_sections: z17.number().int().nonnegative(),
|
|
9979
|
+
ready_sections: z17.number().int().nonnegative(),
|
|
9980
|
+
repair_sections: z17.number().int().nonnegative(),
|
|
9981
|
+
blocked_sections: z17.number().int().nonnegative(),
|
|
9982
|
+
no_gate_sections: z17.number().int().nonnegative(),
|
|
9983
|
+
no_review_sections: z17.number().int().nonnegative(),
|
|
9666
9984
|
cowork_handoff_mode: HandoffModeSchema2,
|
|
9667
|
-
workspace_allowed:
|
|
9985
|
+
workspace_allowed: z17.boolean()
|
|
9668
9986
|
}),
|
|
9669
|
-
audit_files:
|
|
9670
|
-
blocking_reasons:
|
|
9671
|
-
warnings:
|
|
9672
|
-
next_actions:
|
|
9987
|
+
audit_files: z17.array(z17.string()),
|
|
9988
|
+
blocking_reasons: z17.array(z17.string()),
|
|
9989
|
+
warnings: z17.array(z17.string()),
|
|
9990
|
+
next_actions: z17.array(z17.string())
|
|
9673
9991
|
});
|
|
9674
9992
|
}
|
|
9675
9993
|
});
|
|
@@ -9891,13 +10209,13 @@ var init_markdown6 = __esm({
|
|
|
9891
10209
|
});
|
|
9892
10210
|
|
|
9893
10211
|
// src/audit/run.ts
|
|
9894
|
-
import { existsSync as
|
|
9895
|
-
import { mkdir as
|
|
9896
|
-
import { join as
|
|
10212
|
+
import { existsSync as existsSync22 } from "fs";
|
|
10213
|
+
import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile17 } from "fs/promises";
|
|
10214
|
+
import { join as join22, resolve as resolve18 } from "path";
|
|
9897
10215
|
import { parse as yamlParse8 } from "yaml";
|
|
9898
10216
|
async function readJsonl5(path, parse, warnings) {
|
|
9899
|
-
if (!
|
|
9900
|
-
const text = await
|
|
10217
|
+
if (!existsSync22(path)) return [];
|
|
10218
|
+
const text = await readFile19(path, "utf8");
|
|
9901
10219
|
const out = [];
|
|
9902
10220
|
for (const line of text.split(/\r?\n/)) {
|
|
9903
10221
|
if (!line.trim()) continue;
|
|
@@ -9910,15 +10228,15 @@ async function readJsonl5(path, parse, warnings) {
|
|
|
9910
10228
|
return out;
|
|
9911
10229
|
}
|
|
9912
10230
|
async function readSourceCards6(packPath, warnings) {
|
|
9913
|
-
const dir =
|
|
9914
|
-
if (!
|
|
9915
|
-
const { readdir:
|
|
9916
|
-
const entries = await
|
|
10231
|
+
const dir = join22(packPath, "evidence", "source-cards");
|
|
10232
|
+
if (!existsSync22(dir)) return [];
|
|
10233
|
+
const { readdir: readdir5 } = await import("fs/promises");
|
|
10234
|
+
const entries = await readdir5(dir);
|
|
9917
10235
|
const cards = [];
|
|
9918
10236
|
for (const entry of entries) {
|
|
9919
10237
|
if (!entry.endsWith(".json")) continue;
|
|
9920
10238
|
try {
|
|
9921
|
-
const text = await
|
|
10239
|
+
const text = await readFile19(join22(dir, entry), "utf8");
|
|
9922
10240
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
9923
10241
|
} catch (err) {
|
|
9924
10242
|
warnings.push(`malformed source card ${entry}: ${err instanceof Error ? err.message : "parse error"}`);
|
|
@@ -9927,29 +10245,29 @@ async function readSourceCards6(packPath, warnings) {
|
|
|
9927
10245
|
return cards;
|
|
9928
10246
|
}
|
|
9929
10247
|
async function readGate2(packPath, sectionId, warnings) {
|
|
9930
|
-
const path =
|
|
9931
|
-
if (!
|
|
10248
|
+
const path = join22(packPath, "audits", `${sectionId}-gate.json`);
|
|
10249
|
+
if (!existsSync22(path)) return null;
|
|
9932
10250
|
try {
|
|
9933
|
-
return SectionGateResultSchema.parse(JSON.parse(await
|
|
10251
|
+
return SectionGateResultSchema.parse(JSON.parse(await readFile19(path, "utf8")));
|
|
9934
10252
|
} catch (err) {
|
|
9935
10253
|
warnings.push(`malformed gate result for ${sectionId}: ${err instanceof Error ? err.message : "parse error"}`);
|
|
9936
10254
|
return null;
|
|
9937
10255
|
}
|
|
9938
10256
|
}
|
|
9939
10257
|
async function readHandoff(packPath, warnings) {
|
|
9940
|
-
const path =
|
|
9941
|
-
if (!
|
|
10258
|
+
const path = join22(packPath, "handoffs", "cowork-handoff.json");
|
|
10259
|
+
if (!existsSync22(path)) return null;
|
|
9942
10260
|
try {
|
|
9943
|
-
return CoworkHandoffPayloadSchema.parse(JSON.parse(await
|
|
10261
|
+
return CoworkHandoffPayloadSchema.parse(JSON.parse(await readFile19(path, "utf8")));
|
|
9944
10262
|
} catch (err) {
|
|
9945
10263
|
warnings.push(`malformed handoff: ${err instanceof Error ? err.message : "parse error"}`);
|
|
9946
10264
|
return null;
|
|
9947
10265
|
}
|
|
9948
10266
|
}
|
|
9949
10267
|
async function readSourceIdsForSection(packPath, sectionId) {
|
|
9950
|
-
const path =
|
|
9951
|
-
if (!
|
|
9952
|
-
const text = await
|
|
10268
|
+
const path = join22(packPath, "sections", sectionId, "sources.jsonl");
|
|
10269
|
+
if (!existsSync22(path)) return [];
|
|
10270
|
+
const text = await readFile19(path, "utf8");
|
|
9953
10271
|
const ids = [];
|
|
9954
10272
|
for (const line of text.split(/\r?\n/)) {
|
|
9955
10273
|
if (!line.trim()) continue;
|
|
@@ -9963,41 +10281,41 @@ async function readSourceIdsForSection(packPath, sectionId) {
|
|
|
9963
10281
|
}
|
|
9964
10282
|
async function audit(options) {
|
|
9965
10283
|
const packPath = options.packPath ? resolve18(options.packPath) : process.cwd();
|
|
9966
|
-
const yamlPath =
|
|
9967
|
-
if (!
|
|
9968
|
-
const research = ResearchYamlSchema.parse(yamlParse8(await
|
|
10284
|
+
const yamlPath = join22(packPath, "research.yaml");
|
|
10285
|
+
if (!existsSync22(yamlPath)) throw new PackNotFoundError(packPath);
|
|
10286
|
+
const research = ResearchYamlSchema.parse(yamlParse8(await readFile19(yamlPath, "utf8")));
|
|
9969
10287
|
const warnings = [];
|
|
9970
10288
|
const perSection = /* @__PURE__ */ new Map();
|
|
9971
10289
|
for (const section of research.sections) {
|
|
9972
10290
|
const sid = section.id;
|
|
9973
10291
|
const claims = await readJsonl5(
|
|
9974
|
-
|
|
10292
|
+
join22(packPath, "sections", sid, "claims.jsonl"),
|
|
9975
10293
|
(r) => ClaimSchema.parse(r),
|
|
9976
10294
|
warnings
|
|
9977
10295
|
);
|
|
9978
10296
|
const candidateClaims = claims.filter((c) => c.review_state === "candidate");
|
|
9979
10297
|
const claimReviews = await readJsonl5(
|
|
9980
|
-
|
|
10298
|
+
join22(packPath, "sections", sid, "claim-reviews.jsonl"),
|
|
9981
10299
|
(r) => ClaimReviewSchema.parse(r),
|
|
9982
10300
|
warnings
|
|
9983
10301
|
);
|
|
9984
10302
|
const contradictions = await readJsonl5(
|
|
9985
|
-
|
|
10303
|
+
join22(packPath, "sections", sid, "contradictions.jsonl"),
|
|
9986
10304
|
(r) => ContradictionSchema.parse(r),
|
|
9987
10305
|
warnings
|
|
9988
10306
|
);
|
|
9989
10307
|
const resolutions = await readJsonl5(
|
|
9990
|
-
|
|
10308
|
+
join22(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
|
|
9991
10309
|
(r) => ContradictionResolutionSchema.parse(r),
|
|
9992
10310
|
warnings
|
|
9993
10311
|
);
|
|
9994
10312
|
const dispositions = await readJsonl5(
|
|
9995
|
-
|
|
10313
|
+
join22(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
|
|
9996
10314
|
(r) => ClaimSynthesisDispositionSchema.parse(r),
|
|
9997
10315
|
warnings
|
|
9998
10316
|
);
|
|
9999
10317
|
const findings = await readJsonl5(
|
|
10000
|
-
|
|
10318
|
+
join22(packPath, "audits", `${sid}-findings.jsonl`),
|
|
10001
10319
|
(r) => ReviewFindingSchema.parse(r),
|
|
10002
10320
|
warnings
|
|
10003
10321
|
);
|
|
@@ -10017,7 +10335,7 @@ async function audit(options) {
|
|
|
10017
10335
|
}
|
|
10018
10336
|
const sources = await readSourceCards6(packPath, warnings);
|
|
10019
10337
|
const receipts = await readJsonl5(
|
|
10020
|
-
|
|
10338
|
+
join22(packPath, "evidence", "fetch-log.jsonl"),
|
|
10021
10339
|
(r) => FetchReceiptSchema.parse(r),
|
|
10022
10340
|
warnings
|
|
10023
10341
|
);
|
|
@@ -10033,11 +10351,11 @@ async function audit(options) {
|
|
|
10033
10351
|
warnings
|
|
10034
10352
|
});
|
|
10035
10353
|
const payload = PackAuditPayloadSchema.parse(result.payload);
|
|
10036
|
-
const auditsDir =
|
|
10037
|
-
await
|
|
10354
|
+
const auditsDir = join22(packPath, "audits");
|
|
10355
|
+
await mkdir17(auditsDir, { recursive: true });
|
|
10038
10356
|
const filesWritten = [];
|
|
10039
10357
|
const writeFileAndTrack = async (rel, content) => {
|
|
10040
|
-
const abs =
|
|
10358
|
+
const abs = join22(packPath, rel);
|
|
10041
10359
|
await writeFile17(abs, content, "utf8");
|
|
10042
10360
|
filesWritten.push(abs);
|
|
10043
10361
|
};
|
|
@@ -10121,18 +10439,18 @@ var init_audit = __esm({
|
|
|
10121
10439
|
});
|
|
10122
10440
|
|
|
10123
10441
|
// src/freeze/checks.ts
|
|
10124
|
-
import { existsSync as
|
|
10125
|
-
import { readFile as
|
|
10442
|
+
import { existsSync as existsSync23 } from "fs";
|
|
10443
|
+
import { readFile as readFile20, stat as stat2 } from "fs/promises";
|
|
10126
10444
|
import { createHash as createHash8 } from "crypto";
|
|
10127
|
-
import { join as
|
|
10445
|
+
import { join as join23 } from "path";
|
|
10128
10446
|
async function fileSha2562(absPath) {
|
|
10129
|
-
const buf = await
|
|
10447
|
+
const buf = await readFile20(absPath);
|
|
10130
10448
|
const sha = createHash8("sha256").update(buf).digest("hex");
|
|
10131
10449
|
return { path: absPath, sha256: sha, bytes: buf.byteLength };
|
|
10132
10450
|
}
|
|
10133
10451
|
async function hashArtifact(ctx, rel) {
|
|
10134
|
-
const abs =
|
|
10135
|
-
if (!
|
|
10452
|
+
const abs = join23(ctx.packPath, rel);
|
|
10453
|
+
if (!existsSync23(abs)) {
|
|
10136
10454
|
ctx.missingArtifacts.push(rel);
|
|
10137
10455
|
return null;
|
|
10138
10456
|
}
|
|
@@ -10336,88 +10654,88 @@ var init_markdown7 = __esm({
|
|
|
10336
10654
|
});
|
|
10337
10655
|
|
|
10338
10656
|
// src/freeze/schema.ts
|
|
10339
|
-
import { z as
|
|
10657
|
+
import { z as z18 } from "zod";
|
|
10340
10658
|
var ArtifactHashSchema, IntegrityCheckSchema, FreezeReceiptPayloadSchema, FreezeRefusalPayloadSchema;
|
|
10341
10659
|
var init_schema16 = __esm({
|
|
10342
10660
|
"src/freeze/schema.ts"() {
|
|
10343
10661
|
"use strict";
|
|
10344
|
-
ArtifactHashSchema =
|
|
10345
|
-
path:
|
|
10346
|
-
sha256:
|
|
10347
|
-
bytes:
|
|
10348
|
-
});
|
|
10349
|
-
IntegrityCheckSchema =
|
|
10350
|
-
name:
|
|
10351
|
-
passed:
|
|
10352
|
-
detail:
|
|
10353
|
-
});
|
|
10354
|
-
FreezeReceiptPayloadSchema =
|
|
10355
|
-
pack_id:
|
|
10356
|
-
pack_topic:
|
|
10357
|
-
frozen_at:
|
|
10358
|
-
verdict:
|
|
10359
|
-
pack_audit_hash:
|
|
10360
|
-
handoff_hash:
|
|
10361
|
-
synthesis_hashes:
|
|
10362
|
-
canonical_artifact_hashes:
|
|
10363
|
-
accepted_claim_ids:
|
|
10364
|
-
cited_claim_ids:
|
|
10365
|
-
uncited_accepted_claim_ids:
|
|
10366
|
-
unresolved_contradictions:
|
|
10367
|
-
|
|
10368
|
-
contradiction_id:
|
|
10369
|
-
section_id:
|
|
10370
|
-
type:
|
|
10371
|
-
severity:
|
|
10372
|
-
status:
|
|
10373
|
-
disclosed_in:
|
|
10662
|
+
ArtifactHashSchema = z18.object({
|
|
10663
|
+
path: z18.string(),
|
|
10664
|
+
sha256: z18.string().regex(/^[a-f0-9]{64}$/),
|
|
10665
|
+
bytes: z18.number().int().nonnegative()
|
|
10666
|
+
});
|
|
10667
|
+
IntegrityCheckSchema = z18.object({
|
|
10668
|
+
name: z18.string().min(1),
|
|
10669
|
+
passed: z18.boolean(),
|
|
10670
|
+
detail: z18.string()
|
|
10671
|
+
});
|
|
10672
|
+
FreezeReceiptPayloadSchema = z18.object({
|
|
10673
|
+
pack_id: z18.string(),
|
|
10674
|
+
pack_topic: z18.string(),
|
|
10675
|
+
frozen_at: z18.string(),
|
|
10676
|
+
verdict: z18.literal("frozen"),
|
|
10677
|
+
pack_audit_hash: z18.string().regex(/^[a-f0-9]{64}$/),
|
|
10678
|
+
handoff_hash: z18.string().regex(/^[a-f0-9]{64}$/),
|
|
10679
|
+
synthesis_hashes: z18.array(ArtifactHashSchema),
|
|
10680
|
+
canonical_artifact_hashes: z18.array(ArtifactHashSchema),
|
|
10681
|
+
accepted_claim_ids: z18.array(z18.string()),
|
|
10682
|
+
cited_claim_ids: z18.array(z18.string()),
|
|
10683
|
+
uncited_accepted_claim_ids: z18.array(z18.string()),
|
|
10684
|
+
unresolved_contradictions: z18.array(
|
|
10685
|
+
z18.object({
|
|
10686
|
+
contradiction_id: z18.string(),
|
|
10687
|
+
section_id: z18.string(),
|
|
10688
|
+
type: z18.string(),
|
|
10689
|
+
severity: z18.string(),
|
|
10690
|
+
status: z18.string(),
|
|
10691
|
+
disclosed_in: z18.array(z18.string())
|
|
10374
10692
|
})
|
|
10375
10693
|
),
|
|
10376
|
-
waivers_disclosed:
|
|
10377
|
-
|
|
10378
|
-
family:
|
|
10379
|
-
applied_to:
|
|
10380
|
-
reason:
|
|
10381
|
-
compensating_controls:
|
|
10382
|
-
disclosed_in:
|
|
10694
|
+
waivers_disclosed: z18.array(
|
|
10695
|
+
z18.object({
|
|
10696
|
+
family: z18.string(),
|
|
10697
|
+
applied_to: z18.string(),
|
|
10698
|
+
reason: z18.string(),
|
|
10699
|
+
compensating_controls: z18.array(z18.string()),
|
|
10700
|
+
disclosed_in: z18.array(z18.string())
|
|
10383
10701
|
})
|
|
10384
10702
|
),
|
|
10385
|
-
sections:
|
|
10386
|
-
|
|
10387
|
-
section_id:
|
|
10388
|
-
status:
|
|
10389
|
-
accepted_claims:
|
|
10390
|
-
sources:
|
|
10391
|
-
contradictions:
|
|
10703
|
+
sections: z18.array(
|
|
10704
|
+
z18.object({
|
|
10705
|
+
section_id: z18.string(),
|
|
10706
|
+
status: z18.string(),
|
|
10707
|
+
accepted_claims: z18.number().int().nonnegative(),
|
|
10708
|
+
sources: z18.number().int().nonnegative(),
|
|
10709
|
+
contradictions: z18.number().int().nonnegative()
|
|
10392
10710
|
})
|
|
10393
10711
|
),
|
|
10394
|
-
source_count:
|
|
10395
|
-
claim_count:
|
|
10396
|
-
contradiction_count:
|
|
10397
|
-
review_finding_count:
|
|
10398
|
-
gate_result_count:
|
|
10399
|
-
integrity_checks:
|
|
10400
|
-
});
|
|
10401
|
-
FreezeRefusalPayloadSchema =
|
|
10402
|
-
pack_id:
|
|
10403
|
-
pack_topic:
|
|
10404
|
-
checked_at:
|
|
10405
|
-
verdict:
|
|
10406
|
-
reasons:
|
|
10407
|
-
blocking_reasons:
|
|
10408
|
-
missing_artifacts:
|
|
10409
|
-
invalid_artifacts:
|
|
10410
|
-
next_actions:
|
|
10411
|
-
would_freeze:
|
|
10712
|
+
source_count: z18.number().int().nonnegative(),
|
|
10713
|
+
claim_count: z18.number().int().nonnegative(),
|
|
10714
|
+
contradiction_count: z18.number().int().nonnegative(),
|
|
10715
|
+
review_finding_count: z18.number().int().nonnegative(),
|
|
10716
|
+
gate_result_count: z18.number().int().nonnegative(),
|
|
10717
|
+
integrity_checks: z18.array(IntegrityCheckSchema)
|
|
10718
|
+
});
|
|
10719
|
+
FreezeRefusalPayloadSchema = z18.object({
|
|
10720
|
+
pack_id: z18.string(),
|
|
10721
|
+
pack_topic: z18.string(),
|
|
10722
|
+
checked_at: z18.string(),
|
|
10723
|
+
verdict: z18.literal("refused"),
|
|
10724
|
+
reasons: z18.array(z18.string()),
|
|
10725
|
+
blocking_reasons: z18.array(z18.string()),
|
|
10726
|
+
missing_artifacts: z18.array(z18.string()),
|
|
10727
|
+
invalid_artifacts: z18.array(z18.object({ path: z18.string(), error: z18.string() })),
|
|
10728
|
+
next_actions: z18.array(z18.string()),
|
|
10729
|
+
would_freeze: z18.literal(false)
|
|
10412
10730
|
});
|
|
10413
10731
|
}
|
|
10414
10732
|
});
|
|
10415
10733
|
|
|
10416
10734
|
// src/freeze/run.ts
|
|
10417
|
-
import { existsSync as
|
|
10418
|
-
import { mkdir as
|
|
10735
|
+
import { existsSync as existsSync24 } from "fs";
|
|
10736
|
+
import { mkdir as mkdir18, readFile as readFile21, readdir as readdir2, unlink, writeFile as writeFile18 } from "fs/promises";
|
|
10419
10737
|
import { createHash as createHash9 } from "crypto";
|
|
10420
|
-
import { join as
|
|
10738
|
+
import { join as join24, resolve as resolve19 } from "path";
|
|
10421
10739
|
import { parse as yamlParse9, stringify as yamlStringify6 } from "yaml";
|
|
10422
10740
|
function packId3(research) {
|
|
10423
10741
|
const fingerprint = `${research.topic}|${research.created_at}`;
|
|
@@ -10436,10 +10754,10 @@ function noteRefusal(ctx, reason, blocking = true) {
|
|
|
10436
10754
|
if (blocking) ctx.blockingReasons.push(reason);
|
|
10437
10755
|
}
|
|
10438
10756
|
async function tryReadJson(packPath, rel, parse, invalid) {
|
|
10439
|
-
const abs =
|
|
10440
|
-
if (!
|
|
10757
|
+
const abs = join24(packPath, rel);
|
|
10758
|
+
if (!existsSync24(abs)) return null;
|
|
10441
10759
|
try {
|
|
10442
|
-
return parse(JSON.parse(await
|
|
10760
|
+
return parse(JSON.parse(await readFile21(abs, "utf8")));
|
|
10443
10761
|
} catch (err) {
|
|
10444
10762
|
invalid.push({ path: rel, error: err instanceof Error ? err.message : "parse error" });
|
|
10445
10763
|
return null;
|
|
@@ -10447,8 +10765,8 @@ async function tryReadJson(packPath, rel, parse, invalid) {
|
|
|
10447
10765
|
}
|
|
10448
10766
|
async function freeze(options) {
|
|
10449
10767
|
const packPath = options.packPath ? resolve19(options.packPath) : process.cwd();
|
|
10450
|
-
const yamlPath =
|
|
10451
|
-
if (!
|
|
10768
|
+
const yamlPath = join24(packPath, "research.yaml");
|
|
10769
|
+
if (!existsSync24(yamlPath)) throw new PackNotFoundError(packPath);
|
|
10452
10770
|
const refusal = {
|
|
10453
10771
|
reasons: [],
|
|
10454
10772
|
blockingReasons: [],
|
|
@@ -10457,7 +10775,7 @@ async function freeze(options) {
|
|
|
10457
10775
|
};
|
|
10458
10776
|
let research;
|
|
10459
10777
|
try {
|
|
10460
|
-
research = ResearchYamlSchema.parse(yamlParse9(await
|
|
10778
|
+
research = ResearchYamlSchema.parse(yamlParse9(await readFile21(yamlPath, "utf8")));
|
|
10461
10779
|
} catch (err) {
|
|
10462
10780
|
refusal.invalidArtifacts.push({ path: "research.yaml", error: err instanceof Error ? err.message : "parse error" });
|
|
10463
10781
|
return writeRefusal({
|
|
@@ -10469,13 +10787,13 @@ async function freeze(options) {
|
|
|
10469
10787
|
}
|
|
10470
10788
|
const pid = packId3(research);
|
|
10471
10789
|
for (const rel of REQUIRED_PACK_ARTIFACTS) {
|
|
10472
|
-
if (!
|
|
10790
|
+
if (!existsSync24(join24(packPath, rel))) {
|
|
10473
10791
|
refusal.missingArtifacts.push(rel);
|
|
10474
10792
|
noteRefusal(refusal, `Required artifact missing: ${rel}.`);
|
|
10475
10793
|
}
|
|
10476
10794
|
}
|
|
10477
10795
|
for (const rel of SYNTHESIS_FILES) {
|
|
10478
|
-
if (!
|
|
10796
|
+
if (!existsSync24(join24(packPath, rel))) {
|
|
10479
10797
|
refusal.missingArtifacts.push(rel);
|
|
10480
10798
|
noteRefusal(refusal, `Synthesis artifact missing: ${rel}. Run \`research-os synth workspace\` after the pack reaches synthesis_ready.`);
|
|
10481
10799
|
}
|
|
@@ -10509,22 +10827,22 @@ async function freeze(options) {
|
|
|
10509
10827
|
if (handoff2 && handoff2.mode !== "synthesis_ready") {
|
|
10510
10828
|
noteRefusal(refusal, `Cowork handoff mode is "${handoff2.mode}", not "synthesis_ready".`);
|
|
10511
10829
|
}
|
|
10512
|
-
const finalReportAbs =
|
|
10513
|
-
const decisionBriefAbs =
|
|
10514
|
-
const workingReportAbs =
|
|
10830
|
+
const finalReportAbs = join24(packPath, "synthesis/final-report.md");
|
|
10831
|
+
const decisionBriefAbs = join24(packPath, "synthesis/decision-brief.md");
|
|
10832
|
+
const workingReportAbs = join24(packPath, "synthesis/working-report.md");
|
|
10515
10833
|
let finalReportText = "";
|
|
10516
10834
|
let decisionBriefText = "";
|
|
10517
10835
|
let workingReportText = "";
|
|
10518
|
-
if (
|
|
10519
|
-
if (
|
|
10520
|
-
if (
|
|
10836
|
+
if (existsSync24(finalReportAbs)) finalReportText = await readFile21(finalReportAbs, "utf8");
|
|
10837
|
+
if (existsSync24(decisionBriefAbs)) decisionBriefText = await readFile21(decisionBriefAbs, "utf8");
|
|
10838
|
+
if (existsSync24(workingReportAbs)) workingReportText = await readFile21(workingReportAbs, "utf8");
|
|
10521
10839
|
const livePackClaimIds = /* @__PURE__ */ new Set();
|
|
10522
10840
|
const liveLatestDecisionByClaim = /* @__PURE__ */ new Map();
|
|
10523
10841
|
const liveLatestCreatedAtByClaim = /* @__PURE__ */ new Map();
|
|
10524
10842
|
for (const section of research.sections) {
|
|
10525
|
-
const claimsFile =
|
|
10526
|
-
if (
|
|
10527
|
-
const text = await
|
|
10843
|
+
const claimsFile = join24(packPath, "sections", section.id, "claims.jsonl");
|
|
10844
|
+
if (existsSync24(claimsFile)) {
|
|
10845
|
+
const text = await readFile21(claimsFile, "utf8");
|
|
10528
10846
|
for (const line of text.split(/\r?\n/)) {
|
|
10529
10847
|
if (!line.trim()) continue;
|
|
10530
10848
|
try {
|
|
@@ -10538,9 +10856,9 @@ async function freeze(options) {
|
|
|
10538
10856
|
}
|
|
10539
10857
|
}
|
|
10540
10858
|
}
|
|
10541
|
-
const reviewsFile =
|
|
10542
|
-
if (
|
|
10543
|
-
const text = await
|
|
10859
|
+
const reviewsFile = join24(packPath, "sections", section.id, "claim-reviews.jsonl");
|
|
10860
|
+
if (existsSync24(reviewsFile)) {
|
|
10861
|
+
const text = await readFile21(reviewsFile, "utf8");
|
|
10544
10862
|
const reviews = [];
|
|
10545
10863
|
for (const line of text.split(/\r?\n/)) {
|
|
10546
10864
|
if (!line.trim()) continue;
|
|
@@ -10582,7 +10900,7 @@ async function freeze(options) {
|
|
|
10582
10900
|
const unknownCitations = allCited.filter((c) => isWellFormedClaimId(c) && !allClaimIds.has(c));
|
|
10583
10901
|
const repairCitations = allCited.filter((c) => repairOrRejected.has(c));
|
|
10584
10902
|
const uncitedAccepted = acceptedClaimIds.filter((c) => !allCitedSet.has(c));
|
|
10585
|
-
if (
|
|
10903
|
+
if (existsSync24(finalReportAbs) && finalReportText.trim().length > 0) {
|
|
10586
10904
|
if (citationsInFinal.length === 0 && acceptedClaimIds.length > 0) {
|
|
10587
10905
|
noteRefusal(refusal, "final-report.md contains no [claim:...] citations even though accepted claims exist.");
|
|
10588
10906
|
}
|
|
@@ -10634,7 +10952,7 @@ async function freeze(options) {
|
|
|
10634
10952
|
}
|
|
10635
10953
|
}
|
|
10636
10954
|
for (const section of research.sections) {
|
|
10637
|
-
if (!
|
|
10955
|
+
if (!existsSync24(join24(packPath, "audits", `${section.id}-gate.json`))) {
|
|
10638
10956
|
noteRefusal(refusal, `Section ${section.id} has no gate result on file.`);
|
|
10639
10957
|
refusal.missingArtifacts.push(`audits/${section.id}-gate.json`);
|
|
10640
10958
|
}
|
|
@@ -10674,16 +10992,16 @@ async function freeze(options) {
|
|
|
10674
10992
|
}
|
|
10675
10993
|
canonicalPaths.push("evidence/fetch-log.jsonl");
|
|
10676
10994
|
canonicalPaths.push("evidence/citation-ledger.jsonl");
|
|
10677
|
-
if (
|
|
10678
|
-
const entries = await readdir2(
|
|
10995
|
+
if (existsSync24(join24(packPath, "evidence", "source-cards"))) {
|
|
10996
|
+
const entries = await readdir2(join24(packPath, "evidence", "source-cards"));
|
|
10679
10997
|
for (const e of entries) {
|
|
10680
10998
|
if (e.endsWith(".json")) canonicalPaths.push(`evidence/source-cards/${e}`);
|
|
10681
10999
|
}
|
|
10682
11000
|
}
|
|
10683
11001
|
const canonicalHashes = [];
|
|
10684
11002
|
for (const rel of canonicalPaths) {
|
|
10685
|
-
const abs =
|
|
10686
|
-
if (!
|
|
11003
|
+
const abs = join24(packPath, rel);
|
|
11004
|
+
if (!existsSync24(abs)) continue;
|
|
10687
11005
|
const h = await hashArtifact(ctx, rel);
|
|
10688
11006
|
if (h) canonicalHashes.push(h);
|
|
10689
11007
|
}
|
|
@@ -10737,7 +11055,7 @@ async function freeze(options) {
|
|
|
10737
11055
|
claim_count: acceptedClaimIds.length,
|
|
10738
11056
|
contradiction_count: unresolvedContradictionRefs.length,
|
|
10739
11057
|
review_finding_count: 0,
|
|
10740
|
-
gate_result_count: research.sections.filter((s) =>
|
|
11058
|
+
gate_result_count: research.sections.filter((s) => existsSync24(join24(packPath, "audits", `${s.id}-gate.json`))).length,
|
|
10741
11059
|
integrity_checks: checks
|
|
10742
11060
|
});
|
|
10743
11061
|
const frozenResearch = {
|
|
@@ -10746,14 +11064,14 @@ async function freeze(options) {
|
|
|
10746
11064
|
sections: research.sections.map((s) => ({ ...s, status: "frozen" }))
|
|
10747
11065
|
};
|
|
10748
11066
|
await writeFile18(yamlPath, yamlStringify6(frozenResearch, { lineWidth: 0 }), "utf8");
|
|
10749
|
-
const auditsDir =
|
|
10750
|
-
await
|
|
11067
|
+
const auditsDir = join24(packPath, "audits");
|
|
11068
|
+
await mkdir18(auditsDir, { recursive: true });
|
|
10751
11069
|
for (const stale of ["freeze-refusal.json", "freeze-refusal.md"]) {
|
|
10752
|
-
const p =
|
|
10753
|
-
if (
|
|
11070
|
+
const p = join24(auditsDir, stale);
|
|
11071
|
+
if (existsSync24(p)) await unlink(p);
|
|
10754
11072
|
}
|
|
10755
|
-
const jsonPath =
|
|
10756
|
-
const mdPath =
|
|
11073
|
+
const jsonPath = join24(auditsDir, "freeze-receipt.json");
|
|
11074
|
+
const mdPath = join24(auditsDir, "freeze-receipt.md");
|
|
10757
11075
|
await writeFile18(jsonPath, JSON.stringify(receipt, null, 2), "utf8");
|
|
10758
11076
|
await writeFile18(mdPath, renderFreezeReceiptMarkdown(receipt), "utf8");
|
|
10759
11077
|
return {
|
|
@@ -10783,14 +11101,14 @@ async function writeRefusal(args) {
|
|
|
10783
11101
|
next_actions: nextActions,
|
|
10784
11102
|
would_freeze: false
|
|
10785
11103
|
});
|
|
10786
|
-
const auditsDir =
|
|
10787
|
-
await
|
|
11104
|
+
const auditsDir = join24(args.packPath, "audits");
|
|
11105
|
+
await mkdir18(auditsDir, { recursive: true });
|
|
10788
11106
|
for (const stale of ["freeze-receipt.json", "freeze-receipt.md"]) {
|
|
10789
|
-
const p =
|
|
10790
|
-
if (
|
|
11107
|
+
const p = join24(auditsDir, stale);
|
|
11108
|
+
if (existsSync24(p)) await unlink(p);
|
|
10791
11109
|
}
|
|
10792
|
-
const jsonPath =
|
|
10793
|
-
const mdPath =
|
|
11110
|
+
const jsonPath = join24(auditsDir, "freeze-refusal.json");
|
|
11111
|
+
const mdPath = join24(auditsDir, "freeze-refusal.md");
|
|
10794
11112
|
await writeFile18(jsonPath, JSON.stringify(payload, null, 2), "utf8");
|
|
10795
11113
|
await writeFile18(mdPath, renderFreezeRefusalMarkdown(payload), "utf8");
|
|
10796
11114
|
return {
|
|
@@ -10882,43 +11200,43 @@ var init_freeze = __esm({
|
|
|
10882
11200
|
});
|
|
10883
11201
|
|
|
10884
11202
|
// src/invalidate/schema.ts
|
|
10885
|
-
import { z as
|
|
11203
|
+
import { z as z19 } from "zod";
|
|
10886
11204
|
var ArchivedArtifactSchema, SectionStatusChangeSchema, InvalidationReceiptSchema;
|
|
10887
11205
|
var init_schema17 = __esm({
|
|
10888
11206
|
"src/invalidate/schema.ts"() {
|
|
10889
11207
|
"use strict";
|
|
10890
|
-
ArchivedArtifactSchema =
|
|
10891
|
-
src:
|
|
11208
|
+
ArchivedArtifactSchema = z19.object({
|
|
11209
|
+
src: z19.string(),
|
|
10892
11210
|
// path relative to packPath, before archival
|
|
10893
|
-
dst:
|
|
11211
|
+
dst: z19.string()
|
|
10894
11212
|
// path relative to packPath, after archival
|
|
10895
11213
|
});
|
|
10896
|
-
SectionStatusChangeSchema =
|
|
10897
|
-
section_id:
|
|
10898
|
-
before:
|
|
10899
|
-
after:
|
|
11214
|
+
SectionStatusChangeSchema = z19.object({
|
|
11215
|
+
section_id: z19.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
11216
|
+
before: z19.string(),
|
|
11217
|
+
after: z19.string()
|
|
10900
11218
|
});
|
|
10901
|
-
InvalidationReceiptSchema =
|
|
10902
|
-
receipt_id:
|
|
10903
|
-
contract_label:
|
|
10904
|
-
superseded_contract:
|
|
10905
|
-
new_contract:
|
|
10906
|
-
reason:
|
|
10907
|
-
invalidated_at:
|
|
10908
|
-
research_os_version:
|
|
10909
|
-
affected_sections:
|
|
10910
|
-
archived_artifacts:
|
|
10911
|
-
section_status_changes:
|
|
10912
|
-
frozen_at_cleared:
|
|
10913
|
-
notes:
|
|
11219
|
+
InvalidationReceiptSchema = z19.object({
|
|
11220
|
+
receipt_id: z19.string().regex(/^inv_[0-9]+_[a-z0-9-]+$/),
|
|
11221
|
+
contract_label: z19.string().min(1),
|
|
11222
|
+
superseded_contract: z19.string().min(1).nullable(),
|
|
11223
|
+
new_contract: z19.string().min(1),
|
|
11224
|
+
reason: z19.string().min(8),
|
|
11225
|
+
invalidated_at: z19.string(),
|
|
11226
|
+
research_os_version: z19.string(),
|
|
11227
|
+
affected_sections: z19.array(z19.string().regex(/^[0-9]{2}-[a-z0-9-]+$/)),
|
|
11228
|
+
archived_artifacts: z19.array(ArchivedArtifactSchema),
|
|
11229
|
+
section_status_changes: z19.array(SectionStatusChangeSchema),
|
|
11230
|
+
frozen_at_cleared: z19.boolean(),
|
|
11231
|
+
notes: z19.string().nullable()
|
|
10914
11232
|
});
|
|
10915
11233
|
}
|
|
10916
11234
|
});
|
|
10917
11235
|
|
|
10918
11236
|
// src/invalidate/run.ts
|
|
10919
|
-
import { existsSync as
|
|
10920
|
-
import { mkdir as
|
|
10921
|
-
import { dirname as
|
|
11237
|
+
import { existsSync as existsSync25 } from "fs";
|
|
11238
|
+
import { mkdir as mkdir19, readFile as readFile22, readdir as readdir3, rename, writeFile as writeFile19 } from "fs/promises";
|
|
11239
|
+
import { dirname as dirname6, join as join25, posix, relative as relative3, resolve as resolve20, sep } from "path";
|
|
10922
11240
|
import { parse as yamlParse10, stringify as yamlStringify7 } from "yaml";
|
|
10923
11241
|
function posixify(p) {
|
|
10924
11242
|
return p.split(sep).join("/");
|
|
@@ -10936,15 +11254,15 @@ function isLegacyClaimLine(line) {
|
|
|
10936
11254
|
}
|
|
10937
11255
|
}
|
|
10938
11256
|
async function detectLegacyClaimSections(packPath) {
|
|
10939
|
-
const sectionsDir =
|
|
10940
|
-
if (!
|
|
11257
|
+
const sectionsDir = join25(packPath, "sections");
|
|
11258
|
+
if (!existsSync25(sectionsDir)) return [];
|
|
10941
11259
|
const entries = await readdir3(sectionsDir, { withFileTypes: true });
|
|
10942
11260
|
const affected = [];
|
|
10943
11261
|
for (const entry of entries) {
|
|
10944
11262
|
if (!entry.isDirectory()) continue;
|
|
10945
|
-
const claimsPath =
|
|
10946
|
-
if (!
|
|
10947
|
-
const text = await
|
|
11263
|
+
const claimsPath = join25(sectionsDir, entry.name, "claims.jsonl");
|
|
11264
|
+
if (!existsSync25(claimsPath)) continue;
|
|
11265
|
+
const text = await readFile22(claimsPath, "utf8");
|
|
10948
11266
|
let hasLegacy = false;
|
|
10949
11267
|
for (const line of text.split(/\r?\n/)) {
|
|
10950
11268
|
if (isLegacyClaimLine(line)) {
|
|
@@ -10957,16 +11275,16 @@ async function detectLegacyClaimSections(packPath) {
|
|
|
10957
11275
|
return affected.sort();
|
|
10958
11276
|
}
|
|
10959
11277
|
async function moveIfExists(packPath, archiveRel, rel) {
|
|
10960
|
-
const src =
|
|
10961
|
-
if (!
|
|
10962
|
-
const dst =
|
|
10963
|
-
await
|
|
11278
|
+
const src = join25(packPath, rel);
|
|
11279
|
+
if (!existsSync25(src)) return null;
|
|
11280
|
+
const dst = join25(packPath, archiveRel, rel);
|
|
11281
|
+
await mkdir19(dirname6(dst), { recursive: true });
|
|
10964
11282
|
await rename(src, dst);
|
|
10965
|
-
return { src: posixify(rel), dst: posixify(
|
|
11283
|
+
return { src: posixify(rel), dst: posixify(join25(archiveRel, rel)) };
|
|
10966
11284
|
}
|
|
10967
11285
|
async function moveDirContentsIfExists(packPath, archiveRel, relDir, skipPrefix) {
|
|
10968
|
-
const fullDir =
|
|
10969
|
-
if (!
|
|
11286
|
+
const fullDir = join25(packPath, relDir);
|
|
11287
|
+
if (!existsSync25(fullDir)) return [];
|
|
10970
11288
|
const entries = await readdir3(fullDir, { withFileTypes: true });
|
|
10971
11289
|
const moved = [];
|
|
10972
11290
|
for (const entry of entries) {
|
|
@@ -11044,8 +11362,8 @@ function buildReceiptMarkdown(receipt) {
|
|
|
11044
11362
|
}
|
|
11045
11363
|
async function invalidateExtraction(options) {
|
|
11046
11364
|
const packPath = options.packPath ? resolve20(options.packPath) : process.cwd();
|
|
11047
|
-
const yamlPath =
|
|
11048
|
-
if (!
|
|
11365
|
+
const yamlPath = join25(packPath, "research.yaml");
|
|
11366
|
+
if (!existsSync25(yamlPath)) throw new PackNotFoundError(packPath);
|
|
11049
11367
|
const reason = options.reason.trim();
|
|
11050
11368
|
if (reason.length < 8) {
|
|
11051
11369
|
throw new Error("invalidation reason must be at least 8 characters");
|
|
@@ -11063,13 +11381,13 @@ async function invalidateExtraction(options) {
|
|
|
11063
11381
|
const stampPath = stampIso.replace(/[:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
11064
11382
|
const archiveRel = posix.join("audits", "legacy", label, stampPath);
|
|
11065
11383
|
const affectedSections = await detectLegacyClaimSections(packPath);
|
|
11066
|
-
const research = ResearchYamlSchema.parse(yamlParse10(await
|
|
11384
|
+
const research = ResearchYamlSchema.parse(yamlParse10(await readFile22(yamlPath, "utf8")));
|
|
11067
11385
|
const packLevelDirs = ["handoffs", "synthesis", "audits"];
|
|
11068
11386
|
let probeArtifactsExist = affectedSections.length > 0;
|
|
11069
11387
|
if (!probeArtifactsExist) {
|
|
11070
11388
|
for (const d of packLevelDirs) {
|
|
11071
|
-
const full =
|
|
11072
|
-
if (!
|
|
11389
|
+
const full = join25(packPath, d);
|
|
11390
|
+
if (!existsSync25(full)) continue;
|
|
11073
11391
|
const entries = await readdir3(full, { withFileTypes: true });
|
|
11074
11392
|
const meaningful = entries.some(
|
|
11075
11393
|
(e) => !(d === "audits" && e.isDirectory() && e.name === "legacy")
|
|
@@ -11091,7 +11409,7 @@ async function invalidateExtraction(options) {
|
|
|
11091
11409
|
message: "No legacy artifacts found. Pack is on the current extraction contract."
|
|
11092
11410
|
};
|
|
11093
11411
|
}
|
|
11094
|
-
await
|
|
11412
|
+
await mkdir19(join25(packPath, archiveRel), { recursive: true });
|
|
11095
11413
|
const archived = [];
|
|
11096
11414
|
for (const sectionId of affectedSections) {
|
|
11097
11415
|
for (const filename of [
|
|
@@ -11140,14 +11458,14 @@ async function invalidateExtraction(options) {
|
|
|
11140
11458
|
frozen_at_cleared: frozenAtCleared,
|
|
11141
11459
|
notes: options.notes ?? null
|
|
11142
11460
|
});
|
|
11143
|
-
const receiptDir =
|
|
11461
|
+
const receiptDir = join25(packPath, archiveRel);
|
|
11144
11462
|
await writeFile19(
|
|
11145
|
-
|
|
11463
|
+
join25(receiptDir, "invalidation.json"),
|
|
11146
11464
|
JSON.stringify(receipt, null, 2),
|
|
11147
11465
|
"utf8"
|
|
11148
11466
|
);
|
|
11149
11467
|
await writeFile19(
|
|
11150
|
-
|
|
11468
|
+
join25(receiptDir, "invalidation.md"),
|
|
11151
11469
|
buildReceiptMarkdown(receipt),
|
|
11152
11470
|
"utf8"
|
|
11153
11471
|
);
|
|
@@ -11173,20 +11491,20 @@ var init_run9 = __esm({
|
|
|
11173
11491
|
});
|
|
11174
11492
|
|
|
11175
11493
|
// src/invalidate/review.ts
|
|
11176
|
-
import { existsSync as
|
|
11177
|
-
import { mkdir as
|
|
11178
|
-
import { dirname as
|
|
11179
|
-
import { z as
|
|
11494
|
+
import { existsSync as existsSync26 } from "fs";
|
|
11495
|
+
import { mkdir as mkdir20, rename as rename2, writeFile as writeFile20 } from "fs/promises";
|
|
11496
|
+
import { dirname as dirname7, join as join26, posix as posix2, relative as relative4, resolve as resolve21, sep as sep2 } from "path";
|
|
11497
|
+
import { z as z20 } from "zod";
|
|
11180
11498
|
function posixify2(p) {
|
|
11181
11499
|
return p.split(sep2).join("/");
|
|
11182
11500
|
}
|
|
11183
11501
|
async function moveIfExists2(packPath, archiveRel, rel) {
|
|
11184
|
-
const src =
|
|
11185
|
-
if (!
|
|
11186
|
-
const dst =
|
|
11187
|
-
await
|
|
11502
|
+
const src = join26(packPath, rel);
|
|
11503
|
+
if (!existsSync26(src)) return null;
|
|
11504
|
+
const dst = join26(packPath, archiveRel, rel);
|
|
11505
|
+
await mkdir20(dirname7(dst), { recursive: true });
|
|
11188
11506
|
await rename2(src, dst);
|
|
11189
|
-
return { src: posixify2(rel), dst: posixify2(
|
|
11507
|
+
return { src: posixify2(rel), dst: posixify2(join26(archiveRel, rel)) };
|
|
11190
11508
|
}
|
|
11191
11509
|
function buildReceiptMarkdown2(receipt) {
|
|
11192
11510
|
const lines = [];
|
|
@@ -11228,8 +11546,8 @@ function buildReceiptMarkdown2(receipt) {
|
|
|
11228
11546
|
}
|
|
11229
11547
|
async function invalidateReview(options) {
|
|
11230
11548
|
const packPath = options.packPath ? resolve21(options.packPath) : process.cwd();
|
|
11231
|
-
if (!
|
|
11232
|
-
if (!
|
|
11549
|
+
if (!existsSync26(join26(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
11550
|
+
if (!existsSync26(join26(packPath, "sections", options.sectionId)))
|
|
11233
11551
|
throw new SectionNotFoundError(options.sectionId);
|
|
11234
11552
|
const reason = options.reason.trim();
|
|
11235
11553
|
if (reason.length < 8) {
|
|
@@ -11252,7 +11570,7 @@ async function invalidateReview(options) {
|
|
|
11252
11570
|
// Also clear review-active.json so the next review run is unambiguous.
|
|
11253
11571
|
posix2.join("sections", options.sectionId, "review-active.json")
|
|
11254
11572
|
];
|
|
11255
|
-
const present = candidatePaths.filter((rel) =>
|
|
11573
|
+
const present = candidatePaths.filter((rel) => existsSync26(join26(packPath, rel)));
|
|
11256
11574
|
if (present.length === 0) {
|
|
11257
11575
|
return {
|
|
11258
11576
|
performed: false,
|
|
@@ -11264,7 +11582,7 @@ async function invalidateReview(options) {
|
|
|
11264
11582
|
message: "No canonical review artifacts found to invalidate. Section has no active review state."
|
|
11265
11583
|
};
|
|
11266
11584
|
}
|
|
11267
|
-
await
|
|
11585
|
+
await mkdir20(join26(packPath, archiveRel), { recursive: true });
|
|
11268
11586
|
const archived = [];
|
|
11269
11587
|
for (const rel of present) {
|
|
11270
11588
|
const a = await moveIfExists2(packPath, archiveRel, rel);
|
|
@@ -11280,14 +11598,14 @@ async function invalidateReview(options) {
|
|
|
11280
11598
|
archived_artifacts: archived,
|
|
11281
11599
|
notes: options.notes ?? null
|
|
11282
11600
|
});
|
|
11283
|
-
const receiptDir =
|
|
11601
|
+
const receiptDir = join26(packPath, archiveRel);
|
|
11284
11602
|
await writeFile20(
|
|
11285
|
-
|
|
11603
|
+
join26(receiptDir, "invalidation.json"),
|
|
11286
11604
|
JSON.stringify(receipt, null, 2),
|
|
11287
11605
|
"utf8"
|
|
11288
11606
|
);
|
|
11289
11607
|
await writeFile20(
|
|
11290
|
-
|
|
11608
|
+
join26(receiptDir, "invalidation.md"),
|
|
11291
11609
|
buildReceiptMarkdown2(receipt),
|
|
11292
11610
|
"utf8"
|
|
11293
11611
|
);
|
|
@@ -11308,15 +11626,15 @@ var init_review2 = __esm({
|
|
|
11308
11626
|
init_errors();
|
|
11309
11627
|
init_src();
|
|
11310
11628
|
init_schema17();
|
|
11311
|
-
ReviewInvalidationReceiptSchema =
|
|
11312
|
-
receipt_id:
|
|
11313
|
-
section_id:
|
|
11314
|
-
contract_label:
|
|
11315
|
-
reason:
|
|
11316
|
-
invalidated_at:
|
|
11317
|
-
research_os_version:
|
|
11318
|
-
archived_artifacts:
|
|
11319
|
-
notes:
|
|
11629
|
+
ReviewInvalidationReceiptSchema = z20.object({
|
|
11630
|
+
receipt_id: z20.string().regex(/^invr_[0-9]+_[a-z0-9-]+$/),
|
|
11631
|
+
section_id: z20.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
11632
|
+
contract_label: z20.string().min(1),
|
|
11633
|
+
reason: z20.string().min(8),
|
|
11634
|
+
invalidated_at: z20.string(),
|
|
11635
|
+
research_os_version: z20.string(),
|
|
11636
|
+
archived_artifacts: z20.array(ArchivedArtifactSchema),
|
|
11637
|
+
notes: z20.string().nullable()
|
|
11320
11638
|
});
|
|
11321
11639
|
}
|
|
11322
11640
|
});
|
|
@@ -11341,17 +11659,17 @@ var init_triage = __esm({
|
|
|
11341
11659
|
});
|
|
11342
11660
|
|
|
11343
11661
|
// src/discover/schema.ts
|
|
11344
|
-
import { z as
|
|
11662
|
+
import { z as z21 } from "zod";
|
|
11345
11663
|
var DiscoveryCandidateStatusSchema, SourceTypeGuessSchema, DiscoveryCandidateSchema, DiscoverySummarySchema;
|
|
11346
11664
|
var init_schema18 = __esm({
|
|
11347
11665
|
"src/discover/schema.ts"() {
|
|
11348
11666
|
"use strict";
|
|
11349
|
-
DiscoveryCandidateStatusSchema =
|
|
11667
|
+
DiscoveryCandidateStatusSchema = z21.enum([
|
|
11350
11668
|
"candidate",
|
|
11351
11669
|
"approved",
|
|
11352
11670
|
"rejected"
|
|
11353
11671
|
]);
|
|
11354
|
-
SourceTypeGuessSchema =
|
|
11672
|
+
SourceTypeGuessSchema = z21.enum([
|
|
11355
11673
|
"primary",
|
|
11356
11674
|
"docs",
|
|
11357
11675
|
"paper",
|
|
@@ -11361,36 +11679,36 @@ var init_schema18 = __esm({
|
|
|
11361
11679
|
"benchmark",
|
|
11362
11680
|
"unknown"
|
|
11363
11681
|
]);
|
|
11364
|
-
DiscoveryCandidateSchema =
|
|
11365
|
-
candidate_id:
|
|
11366
|
-
section_id:
|
|
11367
|
-
url:
|
|
11368
|
-
title:
|
|
11369
|
-
publisher:
|
|
11682
|
+
DiscoveryCandidateSchema = z21.object({
|
|
11683
|
+
candidate_id: z21.string().regex(/^disc_[a-f0-9]{12}$/),
|
|
11684
|
+
section_id: z21.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
11685
|
+
url: z21.string().url(),
|
|
11686
|
+
title: z21.string().min(1),
|
|
11687
|
+
publisher: z21.string().nullable(),
|
|
11370
11688
|
source_type_guess: SourceTypeGuessSchema,
|
|
11371
|
-
why_relevant:
|
|
11689
|
+
why_relevant: z21.string().min(1),
|
|
11372
11690
|
// The free-text query that produced this candidate (for traceability).
|
|
11373
|
-
query:
|
|
11691
|
+
query: z21.string().min(1),
|
|
11374
11692
|
// Lower rank = more central. Stable per-(query, provider) ordering.
|
|
11375
|
-
rank:
|
|
11376
|
-
discovered_at:
|
|
11693
|
+
rank: z21.number().int().positive(),
|
|
11694
|
+
discovered_at: z21.string(),
|
|
11377
11695
|
// 'candidate' | 'approved' | 'rejected'. Append-only: new entries with
|
|
11378
11696
|
// same candidate_id supersede older ones; the latest entry's status wins.
|
|
11379
11697
|
status: DiscoveryCandidateStatusSchema,
|
|
11380
|
-
discovered_by:
|
|
11381
|
-
reason:
|
|
11698
|
+
discovered_by: z21.string().min(1),
|
|
11699
|
+
reason: z21.string().nullable().default(null)
|
|
11382
11700
|
});
|
|
11383
|
-
DiscoverySummarySchema =
|
|
11384
|
-
summary_id:
|
|
11385
|
-
section_id:
|
|
11386
|
-
ran_at:
|
|
11387
|
-
research_os_version:
|
|
11388
|
-
query:
|
|
11389
|
-
provider:
|
|
11390
|
-
candidates_proposed:
|
|
11391
|
-
candidates_validated:
|
|
11392
|
-
candidates_rejected_invalid_url:
|
|
11393
|
-
warnings:
|
|
11701
|
+
DiscoverySummarySchema = z21.object({
|
|
11702
|
+
summary_id: z21.string().regex(/^disum_[0-9]+_[a-z0-9-]+$/),
|
|
11703
|
+
section_id: z21.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
11704
|
+
ran_at: z21.string(),
|
|
11705
|
+
research_os_version: z21.string(),
|
|
11706
|
+
query: z21.string().min(1),
|
|
11707
|
+
provider: z21.string().min(1),
|
|
11708
|
+
candidates_proposed: z21.number().int().nonnegative(),
|
|
11709
|
+
candidates_validated: z21.number().int().nonnegative(),
|
|
11710
|
+
candidates_rejected_invalid_url: z21.number().int().nonnegative(),
|
|
11711
|
+
warnings: z21.array(z21.string())
|
|
11394
11712
|
});
|
|
11395
11713
|
}
|
|
11396
11714
|
});
|
|
@@ -11542,10 +11860,10 @@ Propose source-URL candidates for this section. Quality over quantity.`;
|
|
|
11542
11860
|
});
|
|
11543
11861
|
|
|
11544
11862
|
// src/discover/run.ts
|
|
11545
|
-
import { existsSync as
|
|
11863
|
+
import { existsSync as existsSync27 } from "fs";
|
|
11546
11864
|
import { createHash as createHash10 } from "crypto";
|
|
11547
|
-
import { mkdir as
|
|
11548
|
-
import { join as
|
|
11865
|
+
import { mkdir as mkdir21, readFile as readFile23, writeFile as writeFile21, appendFile as appendFile8 } from "fs/promises";
|
|
11866
|
+
import { join as join27, resolve as resolve22 } from "path";
|
|
11549
11867
|
import { parse as yamlParse11 } from "yaml";
|
|
11550
11868
|
function makeCandidateId(sectionId, url) {
|
|
11551
11869
|
const hex = createHash10("sha256").update(`${sectionId}|${url}`).digest("hex").slice(0, 12);
|
|
@@ -11560,21 +11878,21 @@ function isHttpsUrl(s) {
|
|
|
11560
11878
|
}
|
|
11561
11879
|
}
|
|
11562
11880
|
function candidatesPath(packPath, sectionId) {
|
|
11563
|
-
return
|
|
11881
|
+
return join27(packPath, "sections", sectionId, "discovery-candidates.jsonl");
|
|
11564
11882
|
}
|
|
11565
11883
|
function reportPath(packPath, sectionId) {
|
|
11566
|
-
return
|
|
11884
|
+
return join27(packPath, "sections", sectionId, "discovery-report.md");
|
|
11567
11885
|
}
|
|
11568
11886
|
function summaryPath(packPath, sectionId) {
|
|
11569
|
-
return
|
|
11887
|
+
return join27(packPath, "audits", `${sectionId}-discovery.json`);
|
|
11570
11888
|
}
|
|
11571
11889
|
function approvedUrlsPath(packPath, sectionId) {
|
|
11572
|
-
return
|
|
11890
|
+
return join27(packPath, "sections", sectionId, "urls.approved.txt");
|
|
11573
11891
|
}
|
|
11574
11892
|
async function readCandidates(packPath, sectionId) {
|
|
11575
11893
|
const path = candidatesPath(packPath, sectionId);
|
|
11576
|
-
if (!
|
|
11577
|
-
const text = await
|
|
11894
|
+
if (!existsSync27(path)) return [];
|
|
11895
|
+
const text = await readFile23(path, "utf8");
|
|
11578
11896
|
const out = [];
|
|
11579
11897
|
for (const line of text.split(/\r?\n/)) {
|
|
11580
11898
|
if (!line.trim()) continue;
|
|
@@ -11604,7 +11922,7 @@ async function pickProvider(providers) {
|
|
|
11604
11922
|
}
|
|
11605
11923
|
async function loadSectionPurpose(packPath, sectionId) {
|
|
11606
11924
|
const research = ResearchYamlSchema.parse(
|
|
11607
|
-
yamlParse11(await
|
|
11925
|
+
yamlParse11(await readFile23(join27(packPath, "research.yaml"), "utf8"))
|
|
11608
11926
|
);
|
|
11609
11927
|
const section = research.sections.find((s) => s.id === sectionId);
|
|
11610
11928
|
return section?.purpose ?? "";
|
|
@@ -11642,8 +11960,8 @@ function buildReportMarkdown(args) {
|
|
|
11642
11960
|
}
|
|
11643
11961
|
async function discover(options) {
|
|
11644
11962
|
const packPath = options.packPath ? resolve22(options.packPath) : process.cwd();
|
|
11645
|
-
if (!
|
|
11646
|
-
if (!
|
|
11963
|
+
if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
11964
|
+
if (!existsSync27(join27(packPath, "sections", options.sectionId)))
|
|
11647
11965
|
throw new SectionNotFoundError(options.sectionId);
|
|
11648
11966
|
const query2 = options.query.trim();
|
|
11649
11967
|
if (query2.length < 4) throw new Error("discover query must be at least 4 characters");
|
|
@@ -11703,9 +12021,9 @@ async function discover(options) {
|
|
|
11703
12021
|
newCandidates.push(candidate);
|
|
11704
12022
|
}
|
|
11705
12023
|
const ledgerPath = candidatesPath(packPath, options.sectionId);
|
|
11706
|
-
await
|
|
12024
|
+
await mkdir21(join27(packPath, "sections", options.sectionId), { recursive: true });
|
|
11707
12025
|
for (const c of newCandidates) {
|
|
11708
|
-
await
|
|
12026
|
+
await appendFile8(ledgerPath, JSON.stringify(c) + "\n", "utf8");
|
|
11709
12027
|
}
|
|
11710
12028
|
const all = await readCandidates(packPath, options.sectionId);
|
|
11711
12029
|
const latest = Array.from(latestPerCandidate(all).values());
|
|
@@ -11729,7 +12047,7 @@ async function discover(options) {
|
|
|
11729
12047
|
candidates_rejected_invalid_url: invalidCount,
|
|
11730
12048
|
warnings
|
|
11731
12049
|
});
|
|
11732
|
-
await
|
|
12050
|
+
await mkdir21(join27(packPath, "audits"), { recursive: true });
|
|
11733
12051
|
await writeFile21(summaryPath(packPath, options.sectionId), JSON.stringify(summary, null, 2), "utf8");
|
|
11734
12052
|
return {
|
|
11735
12053
|
candidatesAdded: newCandidates.length,
|
|
@@ -11749,13 +12067,13 @@ async function appendStatusUpdate(packPath, sectionId, candidate, newStatus, rea
|
|
|
11749
12067
|
reason,
|
|
11750
12068
|
discovered_at: stampIso
|
|
11751
12069
|
});
|
|
11752
|
-
await
|
|
12070
|
+
await appendFile8(candidatesPath(packPath, sectionId), JSON.stringify(updated) + "\n", "utf8");
|
|
11753
12071
|
return updated;
|
|
11754
12072
|
}
|
|
11755
12073
|
async function approve(options) {
|
|
11756
12074
|
const packPath = options.packPath ? resolve22(options.packPath) : process.cwd();
|
|
11757
|
-
if (!
|
|
11758
|
-
if (!
|
|
12075
|
+
if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12076
|
+
if (!existsSync27(join27(packPath, "sections", options.sectionId)))
|
|
11759
12077
|
throw new SectionNotFoundError(options.sectionId);
|
|
11760
12078
|
const all = await readCandidates(packPath, options.sectionId);
|
|
11761
12079
|
const latest = latestPerCandidate(all);
|
|
@@ -11788,8 +12106,8 @@ async function approve(options) {
|
|
|
11788
12106
|
}
|
|
11789
12107
|
async function reject(options) {
|
|
11790
12108
|
const packPath = options.packPath ? resolve22(options.packPath) : process.cwd();
|
|
11791
|
-
if (!
|
|
11792
|
-
if (!
|
|
12109
|
+
if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12110
|
+
if (!existsSync27(join27(packPath, "sections", options.sectionId)))
|
|
11793
12111
|
throw new SectionNotFoundError(options.sectionId);
|
|
11794
12112
|
if (options.reason.trim().length < 4) {
|
|
11795
12113
|
throw new Error("reject requires --reason of at least 4 characters");
|
|
@@ -11817,15 +12135,15 @@ async function reject(options) {
|
|
|
11817
12135
|
}
|
|
11818
12136
|
async function exportUrls(options) {
|
|
11819
12137
|
const packPath = options.packPath ? resolve22(options.packPath) : process.cwd();
|
|
11820
|
-
if (!
|
|
11821
|
-
if (!
|
|
12138
|
+
if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12139
|
+
if (!existsSync27(join27(packPath, "sections", options.sectionId)))
|
|
11822
12140
|
throw new SectionNotFoundError(options.sectionId);
|
|
11823
12141
|
const all = await readCandidates(packPath, options.sectionId);
|
|
11824
12142
|
const latest = latestPerCandidate(all);
|
|
11825
12143
|
const approved = Array.from(latest.values()).filter((c) => c.status === "approved").sort((a, b) => a.rank - b.rank);
|
|
11826
12144
|
const lines = approved.map((c) => c.url).join("\n");
|
|
11827
12145
|
const path = approvedUrlsPath(packPath, options.sectionId);
|
|
11828
|
-
await
|
|
12146
|
+
await mkdir21(join27(packPath, "sections", options.sectionId), { recursive: true });
|
|
11829
12147
|
await writeFile21(path, lines + (lines.length > 0 ? "\n" : ""), "utf8");
|
|
11830
12148
|
return { exportPath: path, approvedCount: approved.length };
|
|
11831
12149
|
}
|
|
@@ -11874,108 +12192,108 @@ var init_src = __esm({
|
|
|
11874
12192
|
init_triage();
|
|
11875
12193
|
init_discover();
|
|
11876
12194
|
init_errors();
|
|
11877
|
-
RESEARCH_OS_VERSION = "0.
|
|
12195
|
+
RESEARCH_OS_VERSION = "0.4.0";
|
|
11878
12196
|
}
|
|
11879
12197
|
});
|
|
11880
12198
|
|
|
11881
12199
|
// src/section_report/schema.ts
|
|
11882
|
-
import { z as
|
|
12200
|
+
import { z as z22 } from "zod";
|
|
11883
12201
|
var SectionReportSourcesSchema, SectionReportExtractionSchema, SectionReportContradictionsSchema, SectionReportReviewSchema, SectionReportAcceptanceSchema, SectionReportSchema;
|
|
11884
12202
|
var init_schema19 = __esm({
|
|
11885
12203
|
"src/section_report/schema.ts"() {
|
|
11886
12204
|
"use strict";
|
|
11887
|
-
SectionReportSourcesSchema =
|
|
11888
|
-
fetched_ok:
|
|
11889
|
-
source_cards:
|
|
11890
|
-
publishers:
|
|
11891
|
-
primary_source_waiver:
|
|
11892
|
-
status:
|
|
11893
|
-
reason:
|
|
11894
|
-
compensating_controls:
|
|
12205
|
+
SectionReportSourcesSchema = z22.object({
|
|
12206
|
+
fetched_ok: z22.number().int().nonnegative(),
|
|
12207
|
+
source_cards: z22.number().int().nonnegative(),
|
|
12208
|
+
publishers: z22.array(z22.string()),
|
|
12209
|
+
primary_source_waiver: z22.object({
|
|
12210
|
+
status: z22.enum(["none", "requested", "granted"]),
|
|
12211
|
+
reason: z22.string().nullable(),
|
|
12212
|
+
compensating_controls: z22.array(z22.string())
|
|
11895
12213
|
})
|
|
11896
12214
|
});
|
|
11897
|
-
SectionReportExtractionSchema =
|
|
11898
|
-
candidate_claims:
|
|
11899
|
-
claims_per_source:
|
|
11900
|
-
|
|
11901
|
-
source_id:
|
|
11902
|
-
claims:
|
|
12215
|
+
SectionReportExtractionSchema = z22.object({
|
|
12216
|
+
candidate_claims: z22.number().int().nonnegative(),
|
|
12217
|
+
claims_per_source: z22.array(
|
|
12218
|
+
z22.object({
|
|
12219
|
+
source_id: z22.string(),
|
|
12220
|
+
claims: z22.number().int().nonnegative()
|
|
11903
12221
|
})
|
|
11904
12222
|
),
|
|
11905
|
-
claims_per_1k_words:
|
|
11906
|
-
excerpt_pages_processed:
|
|
11907
|
-
excerpt_id_failures:
|
|
11908
|
-
malformed_extractor_outputs:
|
|
11909
|
-
near_duplicate_clusters:
|
|
11910
|
-
weak_scope_count:
|
|
11911
|
-
generic_scope_count:
|
|
11912
|
-
density_flags:
|
|
11913
|
-
});
|
|
11914
|
-
SectionReportContradictionsSchema =
|
|
11915
|
-
pairs_compared:
|
|
11916
|
-
contradiction_candidates:
|
|
11917
|
-
overgeneralization_risks:
|
|
11918
|
-
});
|
|
11919
|
-
SectionReportReviewSchema =
|
|
11920
|
-
reviewed:
|
|
11921
|
-
accepted_for_synthesis:
|
|
11922
|
-
needs_scope_repair:
|
|
11923
|
-
needs_source_repair:
|
|
11924
|
-
needs_contradiction_mapping:
|
|
11925
|
-
rejected:
|
|
11926
|
-
needs_human_review:
|
|
11927
|
-
rejection_or_repair_by_category:
|
|
11928
|
-
|
|
11929
|
-
category:
|
|
11930
|
-
count:
|
|
12223
|
+
claims_per_1k_words: z22.number(),
|
|
12224
|
+
excerpt_pages_processed: z22.number().int().nonnegative().nullable(),
|
|
12225
|
+
excerpt_id_failures: z22.number().int().nonnegative().nullable(),
|
|
12226
|
+
malformed_extractor_outputs: z22.number().int().nonnegative().nullable(),
|
|
12227
|
+
near_duplicate_clusters: z22.number().int().nonnegative(),
|
|
12228
|
+
weak_scope_count: z22.number().int().nonnegative(),
|
|
12229
|
+
generic_scope_count: z22.number().int().nonnegative(),
|
|
12230
|
+
density_flags: z22.number().int().nonnegative()
|
|
12231
|
+
});
|
|
12232
|
+
SectionReportContradictionsSchema = z22.object({
|
|
12233
|
+
pairs_compared: z22.number().int().nonnegative().nullable(),
|
|
12234
|
+
contradiction_candidates: z22.number().int().nonnegative(),
|
|
12235
|
+
overgeneralization_risks: z22.number().int().nonnegative()
|
|
12236
|
+
});
|
|
12237
|
+
SectionReportReviewSchema = z22.object({
|
|
12238
|
+
reviewed: z22.boolean(),
|
|
12239
|
+
accepted_for_synthesis: z22.number().int().nonnegative(),
|
|
12240
|
+
needs_scope_repair: z22.number().int().nonnegative(),
|
|
12241
|
+
needs_source_repair: z22.number().int().nonnegative(),
|
|
12242
|
+
needs_contradiction_mapping: z22.number().int().nonnegative(),
|
|
12243
|
+
rejected: z22.number().int().nonnegative(),
|
|
12244
|
+
needs_human_review: z22.number().int().nonnegative(),
|
|
12245
|
+
rejection_or_repair_by_category: z22.array(
|
|
12246
|
+
z22.object({
|
|
12247
|
+
category: z22.string(),
|
|
12248
|
+
count: z22.number().int().nonnegative()
|
|
11931
12249
|
})
|
|
11932
12250
|
)
|
|
11933
12251
|
});
|
|
11934
|
-
SectionReportAcceptanceSchema =
|
|
11935
|
-
candidate_claims:
|
|
11936
|
-
accepted_for_synthesis:
|
|
11937
|
-
acceptance_ratio:
|
|
12252
|
+
SectionReportAcceptanceSchema = z22.object({
|
|
12253
|
+
candidate_claims: z22.number().int().nonnegative(),
|
|
12254
|
+
accepted_for_synthesis: z22.number().int().nonnegative(),
|
|
12255
|
+
acceptance_ratio: z22.number(),
|
|
11938
12256
|
// 0..1
|
|
11939
|
-
accepted_per_source:
|
|
12257
|
+
accepted_per_source: z22.number(),
|
|
11940
12258
|
// accepted / source_count, 0 if no sources
|
|
11941
|
-
accepted_per_1k_words:
|
|
11942
|
-
top_rejection_category:
|
|
11943
|
-
claim_overproduction_fired:
|
|
11944
|
-
synthesis_ready:
|
|
11945
|
-
});
|
|
11946
|
-
SectionReportSchema =
|
|
11947
|
-
report_id:
|
|
11948
|
-
section_id:
|
|
11949
|
-
reported_at:
|
|
11950
|
-
research_os_version:
|
|
11951
|
-
status:
|
|
12259
|
+
accepted_per_1k_words: z22.number(),
|
|
12260
|
+
top_rejection_category: z22.string().nullable(),
|
|
12261
|
+
claim_overproduction_fired: z22.boolean(),
|
|
12262
|
+
synthesis_ready: z22.boolean()
|
|
12263
|
+
});
|
|
12264
|
+
SectionReportSchema = z22.object({
|
|
12265
|
+
report_id: z22.string().regex(/^secrep_[0-9]+_[a-z0-9-]+$/),
|
|
12266
|
+
section_id: z22.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
12267
|
+
reported_at: z22.string(),
|
|
12268
|
+
research_os_version: z22.string(),
|
|
12269
|
+
status: z22.string(),
|
|
11952
12270
|
sources: SectionReportSourcesSchema,
|
|
11953
12271
|
extraction: SectionReportExtractionSchema,
|
|
11954
12272
|
contradictions: SectionReportContradictionsSchema,
|
|
11955
12273
|
review: SectionReportReviewSchema,
|
|
11956
12274
|
acceptance: SectionReportAcceptanceSchema,
|
|
11957
|
-
gate_verdict:
|
|
11958
|
-
gate_synthesis_eligible:
|
|
12275
|
+
gate_verdict: z22.string().nullable(),
|
|
12276
|
+
gate_synthesis_eligible: z22.boolean().nullable()
|
|
11959
12277
|
});
|
|
11960
12278
|
}
|
|
11961
12279
|
});
|
|
11962
12280
|
|
|
11963
12281
|
// src/section_report/run.ts
|
|
11964
|
-
import { existsSync as
|
|
11965
|
-
import { mkdir as
|
|
11966
|
-
import { join as
|
|
12282
|
+
import { existsSync as existsSync28 } from "fs";
|
|
12283
|
+
import { mkdir as mkdir22, readFile as readFile24, writeFile as writeFile22 } from "fs/promises";
|
|
12284
|
+
import { join as join28, resolve as resolve23 } from "path";
|
|
11967
12285
|
import { parse as yamlParse12 } from "yaml";
|
|
11968
12286
|
async function readJson(path, parser) {
|
|
11969
|
-
if (!
|
|
12287
|
+
if (!existsSync28(path)) return null;
|
|
11970
12288
|
try {
|
|
11971
|
-
return parser(JSON.parse(await
|
|
12289
|
+
return parser(JSON.parse(await readFile24(path, "utf8")));
|
|
11972
12290
|
} catch {
|
|
11973
12291
|
return null;
|
|
11974
12292
|
}
|
|
11975
12293
|
}
|
|
11976
12294
|
async function readJsonl6(path, parse) {
|
|
11977
|
-
if (!
|
|
11978
|
-
const text = await
|
|
12295
|
+
if (!existsSync28(path)) return [];
|
|
12296
|
+
const text = await readFile24(path, "utf8");
|
|
11979
12297
|
const out = [];
|
|
11980
12298
|
for (const line of text.split(/\r?\n/)) {
|
|
11981
12299
|
if (!line.trim()) continue;
|
|
@@ -11992,15 +12310,15 @@ function countWords2(text) {
|
|
|
11992
12310
|
}
|
|
11993
12311
|
async function reportSection(options) {
|
|
11994
12312
|
const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
|
|
11995
|
-
if (!
|
|
11996
|
-
if (!
|
|
12313
|
+
if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12314
|
+
if (!existsSync28(join28(packPath, "sections", options.sectionId)))
|
|
11997
12315
|
throw new SectionNotFoundError(options.sectionId);
|
|
11998
12316
|
const research = ResearchYamlSchema.parse(
|
|
11999
|
-
yamlParse12(await
|
|
12317
|
+
yamlParse12(await readFile24(join28(packPath, "research.yaml"), "utf8"))
|
|
12000
12318
|
);
|
|
12001
12319
|
const section = research.sections.find((s) => s.id === options.sectionId);
|
|
12002
12320
|
const allReceipts = await readJsonl6(
|
|
12003
|
-
|
|
12321
|
+
join28(packPath, "evidence", "fetch-log.jsonl"),
|
|
12004
12322
|
(raw) => FetchReceiptSchema.parse(raw)
|
|
12005
12323
|
);
|
|
12006
12324
|
const okReceipts = /* @__PURE__ */ new Map();
|
|
@@ -12011,9 +12329,9 @@ async function reportSection(options) {
|
|
|
12011
12329
|
if (!prev || prev.fetched_at < r.fetched_at) okReceipts.set(r.source_id, r);
|
|
12012
12330
|
}
|
|
12013
12331
|
const sectionSourceIds = [];
|
|
12014
|
-
const sourcesPath =
|
|
12015
|
-
if (
|
|
12016
|
-
const text = await
|
|
12332
|
+
const sourcesPath = join28(packPath, "sections", options.sectionId, "sources.jsonl");
|
|
12333
|
+
if (existsSync28(sourcesPath)) {
|
|
12334
|
+
const text = await readFile24(sourcesPath, "utf8");
|
|
12017
12335
|
for (const line of text.split(/\r?\n/)) {
|
|
12018
12336
|
if (!line.trim()) continue;
|
|
12019
12337
|
try {
|
|
@@ -12025,10 +12343,10 @@ async function reportSection(options) {
|
|
|
12025
12343
|
}
|
|
12026
12344
|
const cards = /* @__PURE__ */ new Map();
|
|
12027
12345
|
for (const sid of sectionSourceIds) {
|
|
12028
|
-
const cardPath =
|
|
12029
|
-
if (!
|
|
12346
|
+
const cardPath = join28(packPath, "evidence", "source-cards", `${sid}.json`);
|
|
12347
|
+
if (!existsSync28(cardPath)) continue;
|
|
12030
12348
|
try {
|
|
12031
|
-
cards.set(sid, SourceCardSchema.parse(JSON.parse(await
|
|
12349
|
+
cards.set(sid, SourceCardSchema.parse(JSON.parse(await readFile24(cardPath, "utf8"))));
|
|
12032
12350
|
} catch {
|
|
12033
12351
|
}
|
|
12034
12352
|
}
|
|
@@ -12039,12 +12357,12 @@ async function reportSection(options) {
|
|
|
12039
12357
|
for (const sid of sectionSourceIds) {
|
|
12040
12358
|
const r = okReceipts.get(sid);
|
|
12041
12359
|
if (!r?.raw_text_path) continue;
|
|
12042
|
-
const p =
|
|
12043
|
-
if (!
|
|
12044
|
-
totalWords += countWords2(await
|
|
12360
|
+
const p = join28(packPath, r.raw_text_path);
|
|
12361
|
+
if (!existsSync28(p)) continue;
|
|
12362
|
+
totalWords += countWords2(await readFile24(p, "utf8"));
|
|
12045
12363
|
}
|
|
12046
12364
|
const claims = await readJsonl6(
|
|
12047
|
-
|
|
12365
|
+
join28(packPath, "sections", options.sectionId, "claims.jsonl"),
|
|
12048
12366
|
(raw) => ClaimSchema.parse(raw)
|
|
12049
12367
|
);
|
|
12050
12368
|
const claimsBySrc = /* @__PURE__ */ new Map();
|
|
@@ -12056,11 +12374,11 @@ async function reportSection(options) {
|
|
|
12056
12374
|
}
|
|
12057
12375
|
}
|
|
12058
12376
|
const density = await readJson(
|
|
12059
|
-
|
|
12377
|
+
join28(packPath, "audits", `${options.sectionId}-claim-density.json`),
|
|
12060
12378
|
(raw) => raw
|
|
12061
12379
|
);
|
|
12062
12380
|
const extractReceipt = await readJson(
|
|
12063
|
-
|
|
12381
|
+
join28(packPath, "audits", `${options.sectionId}-claim-extract.json`),
|
|
12064
12382
|
(raw) => raw
|
|
12065
12383
|
);
|
|
12066
12384
|
let excerptPagesProcessed = null;
|
|
@@ -12078,7 +12396,7 @@ async function reportSection(options) {
|
|
|
12078
12396
|
).length;
|
|
12079
12397
|
}
|
|
12080
12398
|
const contradictions = await readJsonl6(
|
|
12081
|
-
|
|
12399
|
+
join28(packPath, "sections", options.sectionId, "contradictions.jsonl"),
|
|
12082
12400
|
(raw) => ContradictionSchema.parse(raw)
|
|
12083
12401
|
);
|
|
12084
12402
|
let pairsCompared = null;
|
|
@@ -12087,7 +12405,7 @@ async function reportSection(options) {
|
|
|
12087
12405
|
(c) => c.type === "overgeneralization_risk"
|
|
12088
12406
|
).length;
|
|
12089
12407
|
const reviews = await readJsonl6(
|
|
12090
|
-
|
|
12408
|
+
join28(packPath, "sections", options.sectionId, "claim-reviews.jsonl"),
|
|
12091
12409
|
(raw) => ClaimReviewSchema.parse(raw)
|
|
12092
12410
|
);
|
|
12093
12411
|
const latestReviewByClaim = /* @__PURE__ */ new Map();
|
|
@@ -12096,7 +12414,7 @@ async function reportSection(options) {
|
|
|
12096
12414
|
if (!prev || prev.created_at < r.created_at) latestReviewByClaim.set(r.claim_id, r);
|
|
12097
12415
|
}
|
|
12098
12416
|
const findings = await readJsonl6(
|
|
12099
|
-
|
|
12417
|
+
join28(packPath, "audits", `${options.sectionId}-findings.jsonl`),
|
|
12100
12418
|
(raw) => ReviewFindingSchema.parse(raw)
|
|
12101
12419
|
);
|
|
12102
12420
|
const decisionCounts = {
|
|
@@ -12121,7 +12439,7 @@ async function reportSection(options) {
|
|
|
12121
12439
|
const topRejectionCategory = rejectionByCategory[0]?.category ?? null;
|
|
12122
12440
|
const claimOverproductionFired = (categoryCounts.get("claim_overproduction") ?? 0) > 0;
|
|
12123
12441
|
const gate2 = await readJson(
|
|
12124
|
-
|
|
12442
|
+
join28(packPath, "audits", `${options.sectionId}-gate.json`),
|
|
12125
12443
|
(raw) => raw
|
|
12126
12444
|
);
|
|
12127
12445
|
const candidate = claims.length;
|
|
@@ -12257,10 +12575,10 @@ async function reportSection(options) {
|
|
|
12257
12575
|
md.push("---");
|
|
12258
12576
|
md.push("");
|
|
12259
12577
|
md.push("_This report is read-only \u2014 it does not mutate any canonical artifact. Re-run after any pipeline step to refresh._");
|
|
12260
|
-
const auditsDir =
|
|
12261
|
-
await
|
|
12262
|
-
const jsonPath =
|
|
12263
|
-
const markdownPath =
|
|
12578
|
+
const auditsDir = join28(packPath, "audits");
|
|
12579
|
+
await mkdir22(auditsDir, { recursive: true });
|
|
12580
|
+
const jsonPath = join28(auditsDir, `${options.sectionId}-section-report.json`);
|
|
12581
|
+
const markdownPath = join28(auditsDir, `${options.sectionId}-section-report.md`);
|
|
12264
12582
|
await writeFile22(jsonPath, JSON.stringify(report, null, 2), "utf8");
|
|
12265
12583
|
await writeFile22(markdownPath, md.join("\n"), "utf8");
|
|
12266
12584
|
return { report, jsonPath, markdownPath };
|
|
@@ -12308,17 +12626,17 @@ init_invalidate();
|
|
|
12308
12626
|
import { Command, Option } from "commander";
|
|
12309
12627
|
|
|
12310
12628
|
// src/pack/publish/index.ts
|
|
12311
|
-
import { existsSync as
|
|
12312
|
-
import { join as
|
|
12629
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
12630
|
+
import { join as join32, basename, resolve as resolve24 } from "path";
|
|
12313
12631
|
|
|
12314
12632
|
// src/pack/publish/manifest.ts
|
|
12315
12633
|
init_schema();
|
|
12316
12634
|
init_schema9();
|
|
12317
12635
|
import { createHash as createHash11 } from "crypto";
|
|
12318
|
-
import { readFileSync, existsSync as
|
|
12319
|
-
import { join as
|
|
12636
|
+
import { readFileSync, existsSync as existsSync29 } from "fs";
|
|
12637
|
+
import { join as join29 } from "path";
|
|
12320
12638
|
import { parse as parseYaml } from "yaml";
|
|
12321
|
-
import { z as
|
|
12639
|
+
import { z as z24 } from "zod";
|
|
12322
12640
|
|
|
12323
12641
|
// src/closure-ledger/effective-accepted.ts
|
|
12324
12642
|
function getEffectiveDecisionMap(reviews) {
|
|
@@ -12363,37 +12681,38 @@ function findIncompatibleDecisions(reviews) {
|
|
|
12363
12681
|
}
|
|
12364
12682
|
|
|
12365
12683
|
// src/pack/publish/schema.ts
|
|
12366
|
-
import { z as
|
|
12367
|
-
var SectionSummarySchema =
|
|
12368
|
-
id:
|
|
12369
|
-
accepted_claims:
|
|
12370
|
-
gate:
|
|
12371
|
-
synthesis_eligible:
|
|
12372
|
-
});
|
|
12373
|
-
var TotalsSchema =
|
|
12374
|
-
sections:
|
|
12375
|
-
accepted_claims:
|
|
12376
|
-
dispositioned:
|
|
12377
|
-
unresolved_contradictions:
|
|
12378
|
-
preserved_contradiction_records:
|
|
12379
|
-
});
|
|
12380
|
-
var PackManifestSchema =
|
|
12381
|
-
name:
|
|
12382
|
-
topic:
|
|
12383
|
-
frozen_at:
|
|
12384
|
-
research_os_version:
|
|
12385
|
-
sections:
|
|
12684
|
+
import { z as z23 } from "zod";
|
|
12685
|
+
var SectionSummarySchema = z23.object({
|
|
12686
|
+
id: z23.string().min(1),
|
|
12687
|
+
accepted_claims: z23.number().int().min(0),
|
|
12688
|
+
gate: z23.enum(["pass", "warn", "fail", "blocked", "pass_with_waiver"]),
|
|
12689
|
+
synthesis_eligible: z23.boolean()
|
|
12690
|
+
});
|
|
12691
|
+
var TotalsSchema = z23.object({
|
|
12692
|
+
sections: z23.number().int().min(1),
|
|
12693
|
+
accepted_claims: z23.number().int().min(0),
|
|
12694
|
+
dispositioned: z23.number().int().min(0),
|
|
12695
|
+
unresolved_contradictions: z23.number().int().min(0),
|
|
12696
|
+
preserved_contradiction_records: z23.number().int().min(0).optional()
|
|
12697
|
+
});
|
|
12698
|
+
var PackManifestSchema = z23.object({
|
|
12699
|
+
name: z23.string().min(1),
|
|
12700
|
+
topic: z23.string().min(1),
|
|
12701
|
+
frozen_at: z23.string().datetime(),
|
|
12702
|
+
research_os_version: z23.string().min(1),
|
|
12703
|
+
sections: z23.array(SectionSummarySchema).min(1),
|
|
12386
12704
|
totals: TotalsSchema,
|
|
12387
|
-
freeze_receipt_sha256:
|
|
12388
|
-
operator_notes:
|
|
12705
|
+
freeze_receipt_sha256: z23.string().regex(/^[a-f0-9]{64}$/, "Must be a 64-char hex sha256"),
|
|
12706
|
+
operator_notes: z23.string().default("")
|
|
12389
12707
|
});
|
|
12390
12708
|
|
|
12391
12709
|
// src/pack/publish/manifest.ts
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12710
|
+
init_src();
|
|
12711
|
+
var GateResultMinimalSchema = z24.object({
|
|
12712
|
+
verdict: z24.enum(["pass", "warn", "fail", "blocked"]),
|
|
12713
|
+
synthesis_eligible: z24.boolean()
|
|
12395
12714
|
});
|
|
12396
|
-
var ClaimIdOnlySchema =
|
|
12715
|
+
var ClaimIdOnlySchema = z24.object({ claim_id: z24.string() });
|
|
12397
12716
|
function sha256Bytes(buf) {
|
|
12398
12717
|
return createHash11("sha256").update(buf).digest("hex");
|
|
12399
12718
|
}
|
|
@@ -12401,11 +12720,11 @@ function parseJsonl(content) {
|
|
|
12401
12720
|
return content.split("\n").filter((l) => l.trim().length > 0).map((l) => JSON.parse(l));
|
|
12402
12721
|
}
|
|
12403
12722
|
function readJsonlSafe(filePath) {
|
|
12404
|
-
if (!
|
|
12723
|
+
if (!existsSync29(filePath)) return [];
|
|
12405
12724
|
return parseJsonl(readFileSync(filePath, "utf8"));
|
|
12406
12725
|
}
|
|
12407
12726
|
function readClaimReviews(filePath) {
|
|
12408
|
-
if (!
|
|
12727
|
+
if (!existsSync29(filePath)) return [];
|
|
12409
12728
|
const raw = parseJsonl(readFileSync(filePath, "utf8"));
|
|
12410
12729
|
const valid = [];
|
|
12411
12730
|
for (const r of raw) {
|
|
@@ -12427,22 +12746,22 @@ function latestDispositionStatuses(dispositions) {
|
|
|
12427
12746
|
return m;
|
|
12428
12747
|
}
|
|
12429
12748
|
function deriveManifest(packDir, packageName, operatorNotes = "", warnings = []) {
|
|
12430
|
-
const yamlPath =
|
|
12431
|
-
if (!
|
|
12749
|
+
const yamlPath = join29(packDir, "research.yaml");
|
|
12750
|
+
if (!existsSync29(yamlPath)) throw new Error(`research.yaml not found in ${packDir}`);
|
|
12432
12751
|
const research = ResearchYamlSchema.parse(parseYaml(readFileSync(yamlPath, "utf8")));
|
|
12433
12752
|
if (!research.frozen_at) {
|
|
12434
12753
|
throw new Error(`Pack is not frozen: research.yaml.frozen_at is null \u2014 run research-os freeze first`);
|
|
12435
12754
|
}
|
|
12436
|
-
const receiptPath =
|
|
12437
|
-
if (!
|
|
12755
|
+
const receiptPath = join29(packDir, "audits/freeze-receipt.json");
|
|
12756
|
+
if (!existsSync29(receiptPath)) {
|
|
12438
12757
|
throw new Error(`audits/freeze-receipt.json not found \u2014 pack is not frozen`);
|
|
12439
12758
|
}
|
|
12440
12759
|
const receiptBytes = readFileSync(receiptPath);
|
|
12441
12760
|
const freeze_receipt_sha256 = sha256Bytes(receiptBytes);
|
|
12442
12761
|
const receipt = JSON.parse(receiptBytes.toString("utf8"));
|
|
12443
12762
|
const frozen_at = receipt.frozen_at ?? research.frozen_at;
|
|
12444
|
-
const packAuditPath =
|
|
12445
|
-
if (!
|
|
12763
|
+
const packAuditPath = join29(packDir, "audits/pack-audit.json");
|
|
12764
|
+
if (!existsSync29(packAuditPath)) throw new Error(`audits/pack-audit.json not found`);
|
|
12446
12765
|
const packAudit = JSON.parse(readFileSync(packAuditPath, "utf8"));
|
|
12447
12766
|
const auditSectionMap = new Map(
|
|
12448
12767
|
(packAudit.section_summaries ?? []).map((s) => [s.section_id, s.accepted_claims])
|
|
@@ -12453,8 +12772,8 @@ function deriveManifest(packDir, packageName, operatorNotes = "", warnings = [])
|
|
|
12453
12772
|
let totalDispositioned = 0;
|
|
12454
12773
|
let totalPreserved = 0;
|
|
12455
12774
|
for (const sectionId of sectionIds) {
|
|
12456
|
-
const sectionDir =
|
|
12457
|
-
const reviewsPath =
|
|
12775
|
+
const sectionDir = join29(packDir, "sections", sectionId);
|
|
12776
|
+
const reviewsPath = join29(sectionDir, "claim-reviews.jsonl");
|
|
12458
12777
|
const reviews = readClaimReviews(reviewsPath);
|
|
12459
12778
|
const conflicts = findIncompatibleDecisions(reviews);
|
|
12460
12779
|
if (conflicts.length > 0) {
|
|
@@ -12465,8 +12784,8 @@ function deriveManifest(packDir, packageName, operatorNotes = "", warnings = [])
|
|
|
12465
12784
|
}
|
|
12466
12785
|
const effectiveAccepted = getEffectiveAcceptedClaimIds(reviews);
|
|
12467
12786
|
const acceptedCount = effectiveAccepted.size;
|
|
12468
|
-
const claimsPath =
|
|
12469
|
-
if (
|
|
12787
|
+
const claimsPath = join29(sectionDir, "claims.jsonl");
|
|
12788
|
+
if (existsSync29(claimsPath)) {
|
|
12470
12789
|
const claimRows = parseJsonl(readFileSync(claimsPath, "utf8"));
|
|
12471
12790
|
const claimIds = /* @__PURE__ */ new Set();
|
|
12472
12791
|
for (const c of claimRows) {
|
|
@@ -12493,8 +12812,8 @@ function deriveManifest(packDir, packageName, operatorNotes = "", warnings = [])
|
|
|
12493
12812
|
`section ${sectionId}: legacy pack-audit.json accepted_claims (${auditAccepted}) differs from effective accepted set (${acceptedCount}). Using effective count (${acceptedCount}) in manifest. Legacy audit count preserved in pack/audits/pack-audit.json (immutable per Law 15).`
|
|
12494
12813
|
);
|
|
12495
12814
|
}
|
|
12496
|
-
const gateResultPath =
|
|
12497
|
-
if (!
|
|
12815
|
+
const gateResultPath = join29(packDir, "audits", `${sectionId}-gate.json`);
|
|
12816
|
+
if (!existsSync29(gateResultPath)) {
|
|
12498
12817
|
throw new Error(`audits/${sectionId}-gate.json not found \u2014 section not gated`);
|
|
12499
12818
|
}
|
|
12500
12819
|
const gateResult = GateResultMinimalSchema.parse(
|
|
@@ -12506,12 +12825,12 @@ function deriveManifest(packDir, packageName, operatorNotes = "", warnings = [])
|
|
|
12506
12825
|
);
|
|
12507
12826
|
}
|
|
12508
12827
|
const dispositions = readJsonlSafe(
|
|
12509
|
-
|
|
12828
|
+
join29(sectionDir, "claim-synthesis-dispositions.jsonl")
|
|
12510
12829
|
);
|
|
12511
12830
|
const dispositionMap = latestDispositionStatuses(dispositions);
|
|
12512
12831
|
totalDispositioned += dispositionMap.size;
|
|
12513
12832
|
const resolutions = readJsonlSafe(
|
|
12514
|
-
|
|
12833
|
+
join29(sectionDir, "contradiction-resolutions.jsonl")
|
|
12515
12834
|
);
|
|
12516
12835
|
const resolutionMap = latestContradictionStatuses(resolutions);
|
|
12517
12836
|
const stillUnresolved = [...resolutionMap.values()].filter((s) => s === "unresolved").length;
|
|
@@ -12539,7 +12858,7 @@ function deriveManifest(packDir, packageName, operatorNotes = "", warnings = [])
|
|
|
12539
12858
|
name: packageName,
|
|
12540
12859
|
topic: research.topic,
|
|
12541
12860
|
frozen_at,
|
|
12542
|
-
research_os_version:
|
|
12861
|
+
research_os_version: RESEARCH_OS_VERSION,
|
|
12543
12862
|
sections,
|
|
12544
12863
|
totals: totalPreserved > 0 ? { ...totalsBase, preserved_contradiction_records: totalPreserved } : totalsBase,
|
|
12545
12864
|
freeze_receipt_sha256,
|
|
@@ -12666,14 +12985,14 @@ See [\`../../docs/how-to-read-a-pack.md\`](../../docs/how-to-read-a-pack.md) for
|
|
|
12666
12985
|
|
|
12667
12986
|
// src/pack/publish/copy.ts
|
|
12668
12987
|
import { mkdirSync as mkdirSync2, copyFileSync, readdirSync } from "fs";
|
|
12669
|
-
import { join as
|
|
12988
|
+
import { join as join30 } from "path";
|
|
12670
12989
|
function copyDir(src, dst) {
|
|
12671
12990
|
mkdirSync2(dst, { recursive: true });
|
|
12672
12991
|
let count = 0;
|
|
12673
12992
|
const entries = readdirSync(src, { withFileTypes: true });
|
|
12674
12993
|
for (const entry of entries) {
|
|
12675
|
-
const srcPath =
|
|
12676
|
-
const dstPath =
|
|
12994
|
+
const srcPath = join30(src, entry.name);
|
|
12995
|
+
const dstPath = join30(dst, entry.name);
|
|
12677
12996
|
if (entry.isDirectory()) {
|
|
12678
12997
|
count += copyDir(srcPath, dstPath);
|
|
12679
12998
|
} else if (entry.isFile()) {
|
|
@@ -12686,8 +13005,8 @@ function copyDir(src, dst) {
|
|
|
12686
13005
|
|
|
12687
13006
|
// src/pack/publish/verify.ts
|
|
12688
13007
|
import { createHash as createHash12 } from "crypto";
|
|
12689
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
12690
|
-
import { join as
|
|
13008
|
+
import { readFileSync as readFileSync2, existsSync as existsSync30 } from "fs";
|
|
13009
|
+
import { join as join31 } from "path";
|
|
12691
13010
|
var REQUIRED_FILES = [
|
|
12692
13011
|
"pack/audits/freeze-receipt.json",
|
|
12693
13012
|
"synthesis/final-report.md",
|
|
@@ -12700,14 +13019,14 @@ function sha256File(filePath) {
|
|
|
12700
13019
|
}
|
|
12701
13020
|
function verifyPack(packageDir) {
|
|
12702
13021
|
for (const rel of REQUIRED_FILES) {
|
|
12703
|
-
const full =
|
|
12704
|
-
if (!
|
|
13022
|
+
const full = join31(packageDir, rel);
|
|
13023
|
+
if (!existsSync30(full)) {
|
|
12705
13024
|
return { pass: false, reason: `MISSING required file: ${rel}` };
|
|
12706
13025
|
}
|
|
12707
13026
|
}
|
|
12708
13027
|
let rawManifest;
|
|
12709
13028
|
try {
|
|
12710
|
-
rawManifest = JSON.parse(readFileSync2(
|
|
13029
|
+
rawManifest = JSON.parse(readFileSync2(join31(packageDir, "pack.manifest.json"), "utf8"));
|
|
12711
13030
|
} catch (e) {
|
|
12712
13031
|
return { pass: false, reason: `pack.manifest.json parse error: ${e.message}` };
|
|
12713
13032
|
}
|
|
@@ -12717,7 +13036,7 @@ function verifyPack(packageDir) {
|
|
|
12717
13036
|
return { pass: false, reason: `pack.manifest.json schema violation: ${issues}` };
|
|
12718
13037
|
}
|
|
12719
13038
|
const m = parsed.data;
|
|
12720
|
-
const receiptPath =
|
|
13039
|
+
const receiptPath = join31(packageDir, "pack/audits/freeze-receipt.json");
|
|
12721
13040
|
const actualReceiptHash = sha256File(receiptPath);
|
|
12722
13041
|
if (actualReceiptHash !== m.freeze_receipt_sha256) {
|
|
12723
13042
|
return {
|
|
@@ -12745,8 +13064,8 @@ function verifyPack(packageDir) {
|
|
|
12745
13064
|
let verified = 0;
|
|
12746
13065
|
const softWarnings = [];
|
|
12747
13066
|
for (const entry of allFingerprints) {
|
|
12748
|
-
const artifactPath =
|
|
12749
|
-
if (!
|
|
13067
|
+
const artifactPath = join31(packageDir, "pack", entry.path);
|
|
13068
|
+
if (!existsSync30(artifactPath)) {
|
|
12750
13069
|
return {
|
|
12751
13070
|
pass: false,
|
|
12752
13071
|
reason: `Fingerprinted artifact missing: pack/${entry.path}`,
|
|
@@ -12789,7 +13108,7 @@ async function publish(input) {
|
|
|
12789
13108
|
const packageName = basename(toDir);
|
|
12790
13109
|
const warnings = [];
|
|
12791
13110
|
for (const rel of REQUIRED_SOURCE_FILES) {
|
|
12792
|
-
if (!
|
|
13111
|
+
if (!existsSync31(join32(fromDir, rel))) {
|
|
12793
13112
|
throw new Error(
|
|
12794
13113
|
`Source pack missing required file: ${rel}
|
|
12795
13114
|
Hint: run research-os freeze before publish
|
|
@@ -12797,14 +13116,14 @@ async function publish(input) {
|
|
|
12797
13116
|
);
|
|
12798
13117
|
}
|
|
12799
13118
|
}
|
|
12800
|
-
if (
|
|
13119
|
+
if (existsSync31(join32(fromDir, "audits/freeze-refusal.json")) || existsSync31(join32(fromDir, "audits/freeze-refusal.md"))) {
|
|
12801
13120
|
throw new Error(
|
|
12802
13121
|
`Source pack has freeze-refusal artifacts \u2014 pack did not freeze cleanly.
|
|
12803
13122
|
Resolve blocking reasons then re-run research-os freeze.
|
|
12804
13123
|
Pack: ${fromDir}`
|
|
12805
13124
|
);
|
|
12806
13125
|
}
|
|
12807
|
-
if (
|
|
13126
|
+
if (existsSync31(toDir)) {
|
|
12808
13127
|
const entries = readdirSync2(toDir);
|
|
12809
13128
|
if (entries.length > 0 && !input.force) {
|
|
12810
13129
|
throw new Error(
|
|
@@ -12815,8 +13134,8 @@ async function publish(input) {
|
|
|
12815
13134
|
}
|
|
12816
13135
|
const manifest = deriveManifest(fromDir, packageName, input.operatorNotes ?? "", warnings);
|
|
12817
13136
|
if (input.dryRun) {
|
|
12818
|
-
const finalReportPath2 =
|
|
12819
|
-
const finalReport2 =
|
|
13137
|
+
const finalReportPath2 = join32(fromDir, "synthesis/final-report.md");
|
|
13138
|
+
const finalReport2 = existsSync31(finalReportPath2) ? readFileSync3(finalReportPath2, "utf8") : "";
|
|
12820
13139
|
const readme2 = generateReadme(manifest, finalReport2);
|
|
12821
13140
|
return {
|
|
12822
13141
|
packageName,
|
|
@@ -12830,12 +13149,12 @@ async function publish(input) {
|
|
|
12830
13149
|
}
|
|
12831
13150
|
mkdirSync3(toDir, { recursive: true });
|
|
12832
13151
|
const filesWritten = [];
|
|
12833
|
-
const packTarget =
|
|
13152
|
+
const packTarget = join32(toDir, "pack");
|
|
12834
13153
|
const packFileCount = copyDir(fromDir, packTarget);
|
|
12835
13154
|
filesWritten.push(`pack/ (${packFileCount} files)`);
|
|
12836
|
-
const synthSrc =
|
|
12837
|
-
if (
|
|
12838
|
-
const synthTarget =
|
|
13155
|
+
const synthSrc = join32(fromDir, "synthesis");
|
|
13156
|
+
if (existsSync31(synthSrc)) {
|
|
13157
|
+
const synthTarget = join32(toDir, "synthesis");
|
|
12839
13158
|
const synthFileCount = copyDir(synthSrc, synthTarget);
|
|
12840
13159
|
filesWritten.push(`synthesis/ (${synthFileCount} files)`);
|
|
12841
13160
|
} else {
|
|
@@ -12844,20 +13163,20 @@ async function publish(input) {
|
|
|
12844
13163
|
);
|
|
12845
13164
|
}
|
|
12846
13165
|
writeFileSync2(
|
|
12847
|
-
|
|
13166
|
+
join32(toDir, "pack.manifest.json"),
|
|
12848
13167
|
JSON.stringify(manifest, null, 2) + "\n",
|
|
12849
13168
|
"utf8"
|
|
12850
13169
|
);
|
|
12851
13170
|
filesWritten.push("pack.manifest.json");
|
|
12852
|
-
const finalReportPath =
|
|
12853
|
-
const finalReport =
|
|
13171
|
+
const finalReportPath = join32(fromDir, "synthesis/final-report.md");
|
|
13172
|
+
const finalReport = existsSync31(finalReportPath) ? readFileSync3(finalReportPath, "utf8") : "";
|
|
12854
13173
|
const readme = generateReadme(manifest, finalReport);
|
|
12855
|
-
writeFileSync2(
|
|
13174
|
+
writeFileSync2(join32(toDir, "README.md"), readme, "utf8");
|
|
12856
13175
|
filesWritten.push("README.md");
|
|
12857
|
-
const docsDir =
|
|
13176
|
+
const docsDir = join32(toDir, "docs");
|
|
12858
13177
|
mkdirSync3(docsDir, { recursive: true });
|
|
12859
|
-
const howToReadPath =
|
|
12860
|
-
if (
|
|
13178
|
+
const howToReadPath = join32(docsDir, "how-to-read-this.md");
|
|
13179
|
+
if (existsSync31(howToReadPath)) {
|
|
12861
13180
|
warnings.push(
|
|
12862
13181
|
"docs/how-to-read-this.md already exists \u2014 not overwritten (operator-authored content preserved)"
|
|
12863
13182
|
);
|
|
@@ -12884,6 +13203,206 @@ async function publish(input) {
|
|
|
12884
13203
|
};
|
|
12885
13204
|
}
|
|
12886
13205
|
|
|
13206
|
+
// src/sources/source-card-audit.ts
|
|
13207
|
+
init_schema3();
|
|
13208
|
+
init_source_card_overrides();
|
|
13209
|
+
init_source_card_overrides_schema();
|
|
13210
|
+
init_effective_card();
|
|
13211
|
+
init_source_type_classifier();
|
|
13212
|
+
init_src();
|
|
13213
|
+
import { readdir as readdir4, readFile as readFile25, writeFile as writeFile23, mkdir as mkdir23 } from "fs/promises";
|
|
13214
|
+
import { existsSync as existsSync32 } from "fs";
|
|
13215
|
+
import { join as join33 } from "path";
|
|
13216
|
+
function hasSourceTypeOverride(card, overrides) {
|
|
13217
|
+
return overrides.some((o) => o.source_id === card.source_id && o.new_source_type != null);
|
|
13218
|
+
}
|
|
13219
|
+
function hasPublisherOverride(card, overrides) {
|
|
13220
|
+
return overrides.some((o) => o.source_id === card.source_id && o.new_publisher !== void 0);
|
|
13221
|
+
}
|
|
13222
|
+
function determineFinding(card, overrides) {
|
|
13223
|
+
const classification = classifySourceType({ url: card.url });
|
|
13224
|
+
const stOverride = hasSourceTypeOverride(card, overrides);
|
|
13225
|
+
const pubOverride = hasPublisherOverride(card, overrides);
|
|
13226
|
+
if (classification.rule_hint === "flagged:github-ui-html") return "github_ui_html";
|
|
13227
|
+
if (classification.rule_hint.startsWith("flagged:")) return "classifier_flagged";
|
|
13228
|
+
if (classification.rule_hint !== "no-rule-match" && classification.source_type !== card.source_type && !stOverride) {
|
|
13229
|
+
return "source_type_mismatch";
|
|
13230
|
+
}
|
|
13231
|
+
if (card.publisher === null && !pubOverride) return "publisher_missing";
|
|
13232
|
+
if (stOverride || pubOverride) return "override_applied";
|
|
13233
|
+
return "no_action";
|
|
13234
|
+
}
|
|
13235
|
+
function buildMarkdown3(report) {
|
|
13236
|
+
const packName = report.pack_path.replace(/\\/g, "/").split("/").pop() ?? report.pack_path;
|
|
13237
|
+
const t = report.totals;
|
|
13238
|
+
const lines = [
|
|
13239
|
+
`# Source-Card Audit Report`,
|
|
13240
|
+
``,
|
|
13241
|
+
`**Pack:** ${packName}`,
|
|
13242
|
+
`**Audited at:** ${report.audited_at}`,
|
|
13243
|
+
`**research-os:** ${report.research_os_version}`,
|
|
13244
|
+
``,
|
|
13245
|
+
`## Totals`,
|
|
13246
|
+
``,
|
|
13247
|
+
`| Metric | Count |`,
|
|
13248
|
+
`|--------|-------|`,
|
|
13249
|
+
`| Cards scanned | ${t.cards_scanned} |`,
|
|
13250
|
+
`| Cards with overrides | ${t.cards_with_overrides} |`,
|
|
13251
|
+
`| Source-type mismatches | ${t.source_type_mismatches} |`,
|
|
13252
|
+
`| Publisher missing | ${t.publisher_missing} |`,
|
|
13253
|
+
`| GitHub UI HTML candidates | ${t.github_ui_html} |`,
|
|
13254
|
+
`| Classifier-flagged (other) | ${t.classifier_flagged_other} |`,
|
|
13255
|
+
`| Clean (no action) | ${t.no_action} |`
|
|
13256
|
+
];
|
|
13257
|
+
if (report.findings.length > 0) {
|
|
13258
|
+
lines.push(
|
|
13259
|
+
``,
|
|
13260
|
+
`## Findings`,
|
|
13261
|
+
``,
|
|
13262
|
+
`| source_id | URL | Kind | Raw type | Classifier type | Effective type | Override? |`,
|
|
13263
|
+
`|-----------|-----|------|----------|-----------------|----------------|-----------|`
|
|
13264
|
+
);
|
|
13265
|
+
for (const f of report.findings) {
|
|
13266
|
+
const urlShort = f.url.length > 60 ? f.url.slice(0, 57) + "..." : f.url;
|
|
13267
|
+
lines.push(
|
|
13268
|
+
`| ${f.source_id} | ${urlShort} | ${f.kind} | ${f.raw_source_type} | ${f.classifier_source_type} | ${f.effective_source_type} | ${f.override_in_effect ? "yes" : "no"} |`
|
|
13269
|
+
);
|
|
13270
|
+
}
|
|
13271
|
+
}
|
|
13272
|
+
return lines.join("\n") + "\n";
|
|
13273
|
+
}
|
|
13274
|
+
async function runSourceCardAudit(packPath) {
|
|
13275
|
+
const cardsDir = join33(packPath, "evidence", "source-cards");
|
|
13276
|
+
if (!existsSync32(cardsDir)) {
|
|
13277
|
+
throw new Error(
|
|
13278
|
+
`Pack directory does not contain evidence/source-cards/: ${packPath}`
|
|
13279
|
+
);
|
|
13280
|
+
}
|
|
13281
|
+
const entries = await readdir4(cardsDir);
|
|
13282
|
+
const cardFiles = entries.filter((f) => f.endsWith(".json"));
|
|
13283
|
+
const cards = [];
|
|
13284
|
+
for (const file of cardFiles) {
|
|
13285
|
+
const raw = await readFile25(join33(cardsDir, file), "utf8");
|
|
13286
|
+
cards.push(SourceCardSchema.parse(JSON.parse(raw)));
|
|
13287
|
+
}
|
|
13288
|
+
const overrides = await readOverrides(packPath);
|
|
13289
|
+
const totals = {
|
|
13290
|
+
cards_scanned: cards.length,
|
|
13291
|
+
cards_with_overrides: 0,
|
|
13292
|
+
source_type_mismatches: 0,
|
|
13293
|
+
publisher_missing: 0,
|
|
13294
|
+
github_ui_html: 0,
|
|
13295
|
+
classifier_flagged_other: 0,
|
|
13296
|
+
no_action: 0
|
|
13297
|
+
};
|
|
13298
|
+
const findings = [];
|
|
13299
|
+
for (const card of cards) {
|
|
13300
|
+
const classification = classifySourceType({ url: card.url });
|
|
13301
|
+
const effectiveSourceType = getEffectiveSourceType(card, overrides);
|
|
13302
|
+
const effectivePublisher = getEffectivePublisher(card, overrides);
|
|
13303
|
+
const stOverride = hasSourceTypeOverride(card, overrides);
|
|
13304
|
+
const pubOverride = hasPublisherOverride(card, overrides);
|
|
13305
|
+
const overrideInEffect = stOverride || pubOverride;
|
|
13306
|
+
if (overrideInEffect) totals.cards_with_overrides++;
|
|
13307
|
+
const kind = determineFinding(card, overrides);
|
|
13308
|
+
switch (kind) {
|
|
13309
|
+
case "source_type_mismatch":
|
|
13310
|
+
totals.source_type_mismatches++;
|
|
13311
|
+
break;
|
|
13312
|
+
case "publisher_missing":
|
|
13313
|
+
totals.publisher_missing++;
|
|
13314
|
+
break;
|
|
13315
|
+
case "github_ui_html":
|
|
13316
|
+
totals.github_ui_html++;
|
|
13317
|
+
break;
|
|
13318
|
+
case "classifier_flagged":
|
|
13319
|
+
totals.classifier_flagged_other++;
|
|
13320
|
+
break;
|
|
13321
|
+
case "override_applied":
|
|
13322
|
+
// informational — falls into no_action bucket
|
|
13323
|
+
case "no_action":
|
|
13324
|
+
totals.no_action++;
|
|
13325
|
+
break;
|
|
13326
|
+
case "publisher_mismatch":
|
|
13327
|
+
break;
|
|
13328
|
+
}
|
|
13329
|
+
findings.push({
|
|
13330
|
+
source_id: card.source_id,
|
|
13331
|
+
url: card.url,
|
|
13332
|
+
kind,
|
|
13333
|
+
raw_source_type: card.source_type,
|
|
13334
|
+
classifier_source_type: classification.source_type,
|
|
13335
|
+
effective_source_type: effectiveSourceType,
|
|
13336
|
+
raw_publisher: card.publisher,
|
|
13337
|
+
effective_publisher: effectivePublisher,
|
|
13338
|
+
classifier_rule_hint: classification.rule_hint,
|
|
13339
|
+
classifier_precedence_level: classification.precedence_level,
|
|
13340
|
+
override_in_effect: overrideInEffect
|
|
13341
|
+
});
|
|
13342
|
+
}
|
|
13343
|
+
const report = {
|
|
13344
|
+
schema_version: 1,
|
|
13345
|
+
pack_path: packPath,
|
|
13346
|
+
audited_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13347
|
+
research_os_version: RESEARCH_OS_VERSION,
|
|
13348
|
+
totals,
|
|
13349
|
+
findings
|
|
13350
|
+
};
|
|
13351
|
+
const auditsDir = join33(packPath, "audits");
|
|
13352
|
+
await mkdir23(auditsDir, { recursive: true });
|
|
13353
|
+
const jsonPath = join33(auditsDir, "source-card-audit.json");
|
|
13354
|
+
const mdPath = join33(auditsDir, "source-card-audit.md");
|
|
13355
|
+
await writeFile23(jsonPath, JSON.stringify(report, null, 2), "utf8");
|
|
13356
|
+
await writeFile23(mdPath, buildMarkdown3(report), "utf8");
|
|
13357
|
+
return { report, jsonPath, mdPath };
|
|
13358
|
+
}
|
|
13359
|
+
async function applySourceCardOverrides(packPath, fromFile) {
|
|
13360
|
+
const freezeReceipt = join33(packPath, "audits", "freeze-receipt.json");
|
|
13361
|
+
if (existsSync32(freezeReceipt)) {
|
|
13362
|
+
throw new Error(
|
|
13363
|
+
`Cannot apply overrides to a frozen pack. audits/freeze-receipt.json is present.
|
|
13364
|
+
Pack: ${packPath}
|
|
13365
|
+
Use a fresh (non-frozen) copy of the pack for operator corrections.`
|
|
13366
|
+
);
|
|
13367
|
+
}
|
|
13368
|
+
let rawContent;
|
|
13369
|
+
try {
|
|
13370
|
+
rawContent = await readFile25(fromFile, "utf8");
|
|
13371
|
+
} catch (err) {
|
|
13372
|
+
throw new Error(`Cannot read override file: ${fromFile}`, { cause: err });
|
|
13373
|
+
}
|
|
13374
|
+
let entries;
|
|
13375
|
+
try {
|
|
13376
|
+
const parsed = JSON.parse(rawContent);
|
|
13377
|
+
if (!Array.isArray(parsed)) {
|
|
13378
|
+
throw new Error("Override file must be a JSON array at the top level.");
|
|
13379
|
+
}
|
|
13380
|
+
entries = parsed;
|
|
13381
|
+
} catch (err) {
|
|
13382
|
+
throw new Error(
|
|
13383
|
+
`Override file parse error: ${err instanceof Error ? err.message : String(err)}`,
|
|
13384
|
+
{ cause: err }
|
|
13385
|
+
);
|
|
13386
|
+
}
|
|
13387
|
+
const validated = [];
|
|
13388
|
+
for (let i = 0; i < entries.length; i++) {
|
|
13389
|
+
try {
|
|
13390
|
+
validated.push(validateSourceCardOverride(entries[i]));
|
|
13391
|
+
} catch (err) {
|
|
13392
|
+
throw new Error(
|
|
13393
|
+
`Override entry ${i + 1} failed validation: ${err instanceof Error ? err.message : String(err)}`,
|
|
13394
|
+
{ cause: err }
|
|
13395
|
+
);
|
|
13396
|
+
}
|
|
13397
|
+
}
|
|
13398
|
+
for (const entry of validated) {
|
|
13399
|
+
await appendOverride(packPath, entry);
|
|
13400
|
+
}
|
|
13401
|
+
const ledgerPath = join33(packPath, "evidence", "source-card-overrides.jsonl");
|
|
13402
|
+
const distinctSourceIds = new Set(validated.map((e) => e.source_id)).size;
|
|
13403
|
+
return { applied: validated.length, ledgerPath, distinctSourceIds };
|
|
13404
|
+
}
|
|
13405
|
+
|
|
12887
13406
|
// src/cli.ts
|
|
12888
13407
|
init_errors();
|
|
12889
13408
|
init_src();
|
|
@@ -14006,5 +14525,63 @@ warnings:
|
|
|
14006
14525
|
reportError(err);
|
|
14007
14526
|
}
|
|
14008
14527
|
});
|
|
14528
|
+
var sourceCardCmd = program.command("source-card").description("Source-card inspection and operator-correction surface");
|
|
14529
|
+
sourceCardCmd.command("audit").description(
|
|
14530
|
+
"Audit source cards in a pack: classifier drift, missing publishers, GitHub UI HTML. Read-only by default; use --apply --from <file> to commit operator corrections."
|
|
14531
|
+
).option("--pack <dir>", "Path to the pack root (defaults to cwd)", process.cwd()).option("--json", "Print the JSON report to stdout in addition to writing the artifact", false).option("--apply", "Apply operator-authored overrides from --from <file>", false).option("--from <file>", "JSON array file of proposed overrides (required with --apply)").action(async (opts) => {
|
|
14532
|
+
try {
|
|
14533
|
+
const packPath = opts.pack;
|
|
14534
|
+
if (opts.apply) {
|
|
14535
|
+
const fromFile = opts.from;
|
|
14536
|
+
if (!fromFile) {
|
|
14537
|
+
process.stderr.write("research-os: --apply requires --from <file>\n");
|
|
14538
|
+
process.exit(1);
|
|
14539
|
+
}
|
|
14540
|
+
const result = await applySourceCardOverrides(packPath, fromFile);
|
|
14541
|
+
process.stdout.write(`source-card overrides applied
|
|
14542
|
+
`);
|
|
14543
|
+
process.stdout.write(` entries applied: ${result.applied}
|
|
14544
|
+
`);
|
|
14545
|
+
process.stdout.write(` source_ids touched: ${result.distinctSourceIds}
|
|
14546
|
+
`);
|
|
14547
|
+
process.stdout.write(` ledger: ${result.ledgerPath}
|
|
14548
|
+
`);
|
|
14549
|
+
return;
|
|
14550
|
+
}
|
|
14551
|
+
const { report, jsonPath, mdPath } = await runSourceCardAudit(packPath);
|
|
14552
|
+
const t = report.totals;
|
|
14553
|
+
const packName = packPath.replace(/\\/g, "/").split("/").pop() ?? packPath;
|
|
14554
|
+
process.stdout.write(`research-os source-card audit \u2014 pack: ${packName}
|
|
14555
|
+
|
|
14556
|
+
`);
|
|
14557
|
+
process.stdout.write(`Cards scanned: ${t.cards_scanned}
|
|
14558
|
+
`);
|
|
14559
|
+
process.stdout.write(`Cards with overrides applied: ${t.cards_with_overrides}
|
|
14560
|
+
`);
|
|
14561
|
+
process.stdout.write(`Source-type mismatches: ${t.source_type_mismatches}
|
|
14562
|
+
`);
|
|
14563
|
+
process.stdout.write(`Publisher missing: ${t.publisher_missing}
|
|
14564
|
+
`);
|
|
14565
|
+
process.stdout.write(`GitHub UI HTML candidates: ${t.github_ui_html}
|
|
14566
|
+
`);
|
|
14567
|
+
process.stdout.write(`Classifier-flagged (other): ${t.classifier_flagged_other}
|
|
14568
|
+
`);
|
|
14569
|
+
process.stdout.write(`Clean (no action): ${t.no_action}
|
|
14570
|
+
`);
|
|
14571
|
+
process.stdout.write(`
|
|
14572
|
+
Reports written:
|
|
14573
|
+
`);
|
|
14574
|
+
process.stdout.write(` ${jsonPath}
|
|
14575
|
+
`);
|
|
14576
|
+
process.stdout.write(` ${mdPath}
|
|
14577
|
+
`);
|
|
14578
|
+
if (opts.json) {
|
|
14579
|
+
process.stdout.write("\n");
|
|
14580
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
14581
|
+
}
|
|
14582
|
+
} catch (err) {
|
|
14583
|
+
reportError(err);
|
|
14584
|
+
}
|
|
14585
|
+
});
|
|
14009
14586
|
program.parseAsync(process.argv);
|
|
14010
14587
|
//# sourceMappingURL=cli.js.map
|