@mcptoolshop/research-os 0.3.2 → 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 +159 -0
- package/README.es.md +9 -5
- package/README.fr.md +8 -4
- package/README.hi.md +8 -4
- package/README.it.md +9 -5
- package/README.ja.md +8 -4
- package/README.md +6 -2
- package/README.pt-BR.md +8 -4
- package/README.zh.md +9 -5
- package/dist/cli.js +1849 -1270
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +143 -4
- package/dist/index.js +1511 -1185
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -999,17 +999,244 @@ var init_schema3 = __esm({
|
|
|
999
999
|
scope: z3.string().nullable(),
|
|
1000
1000
|
not: z3.string().nullable(),
|
|
1001
1001
|
extracted_by: ExtractorNameSchema,
|
|
1002
|
-
extracted_at: z3.string()
|
|
1002
|
+
extracted_at: z3.string(),
|
|
1003
|
+
/** Rule that classified the URL. Added by Component B (v0.4). Optional for back-compat with pre-v0.4 cards. */
|
|
1004
|
+
rule_hint: z3.string().optional(),
|
|
1005
|
+
/** Precedence level of the rule that fired (2–6). Optional for back-compat with pre-v0.4 cards. */
|
|
1006
|
+
precedence_level: z3.union([z3.literal(2), z3.literal(3), z3.literal(4), z3.literal(5), z3.literal(6)]).optional()
|
|
1003
1007
|
});
|
|
1004
1008
|
}
|
|
1005
1009
|
});
|
|
1006
1010
|
|
|
1011
|
+
// src/sources/canonical-vendors.ts
|
|
1012
|
+
var CANONICAL_VENDORS;
|
|
1013
|
+
var init_canonical_vendors = __esm({
|
|
1014
|
+
"src/sources/canonical-vendors.ts"() {
|
|
1015
|
+
"use strict";
|
|
1016
|
+
CANONICAL_VENDORS = [
|
|
1017
|
+
// ── Protocol-foundation vendors → primary ────────────────────────────────
|
|
1018
|
+
{
|
|
1019
|
+
slug: "xrpl-foundation",
|
|
1020
|
+
hosts: ["xrpl.org", "xls.xrpl.org"],
|
|
1021
|
+
github_orgs: ["XRPLF"],
|
|
1022
|
+
source_type: "primary",
|
|
1023
|
+
description: "XRPL Foundation \u2014 canonical authority for the XRPL protocol + XLS standards."
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
slug: "ipfs-foundation",
|
|
1027
|
+
hosts: ["docs.ipfs.tech", "ipfs.tech"],
|
|
1028
|
+
source_type: "primary",
|
|
1029
|
+
description: "IPFS protocol canonical surface."
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
slug: "arweave",
|
|
1033
|
+
hosts: ["docs.arweave.org", "arweave.org"],
|
|
1034
|
+
source_type: "primary",
|
|
1035
|
+
description: "Arweave protocol canonical surface."
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
slug: "anthropic",
|
|
1039
|
+
hosts: ["docs.anthropic.com", "anthropic.com", "claude.com"],
|
|
1040
|
+
source_type: "primary",
|
|
1041
|
+
description: "Claude API + Anthropic models \u2014 vendor owns the API surface. Resolves F-47: docs.anthropic.com is primary, not docs."
|
|
1042
|
+
},
|
|
1043
|
+
// ── Platform/engine vendors → docs ───────────────────────────────────────
|
|
1044
|
+
{
|
|
1045
|
+
slug: "godot-foundation",
|
|
1046
|
+
hosts: ["docs.godotengine.org", "godotengine.org"],
|
|
1047
|
+
github_orgs: ["godotengine"],
|
|
1048
|
+
source_type: "docs",
|
|
1049
|
+
description: "Godot engine docs. NB: api.github.com/repos/godotengine/godot/releases matches L4 as canonical-releases \u2192 primary."
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
slug: "apple",
|
|
1053
|
+
hosts: ["developer.apple.com", "docs.developer.apple.com"],
|
|
1054
|
+
source_type: "docs",
|
|
1055
|
+
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."
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
slug: "microsoft",
|
|
1059
|
+
hosts: ["learn.microsoft.com"],
|
|
1060
|
+
github_orgs: ["MicrosoftDocs"],
|
|
1061
|
+
source_type: "docs",
|
|
1062
|
+
description: "MS Learn + MicrosoftDocs raw GitHub."
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
slug: "mozilla",
|
|
1066
|
+
hosts: ["developer.mozilla.org"],
|
|
1067
|
+
source_type: "docs",
|
|
1068
|
+
description: "MDN web platform docs."
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
slug: "google-android",
|
|
1072
|
+
hosts: ["developer.android.com"],
|
|
1073
|
+
source_type: "docs",
|
|
1074
|
+
description: "Android platform docs."
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
slug: "google-play",
|
|
1078
|
+
hosts: [],
|
|
1079
|
+
hostPaths: ["support.google.com/googleplay"],
|
|
1080
|
+
source_type: "docs",
|
|
1081
|
+
description: "Play Store policy/help docs. Matched on host+path-prefix to avoid classifying all of support.google.com as docs."
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
slug: "itch-io",
|
|
1085
|
+
hosts: [],
|
|
1086
|
+
hostPaths: ["itch.io/docs"],
|
|
1087
|
+
source_type: "docs",
|
|
1088
|
+
description: "itch.io creator docs (not storefront chrome). Matched on host+path-prefix so itch.io/<creator>/<game> does not match L3."
|
|
1089
|
+
}
|
|
1090
|
+
];
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
// src/sources/source-type-classifier.ts
|
|
1095
|
+
import { URL as URL4 } from "url";
|
|
1096
|
+
function classifySourceType(input) {
|
|
1097
|
+
let parsed;
|
|
1098
|
+
try {
|
|
1099
|
+
parsed = new URL4(input.url);
|
|
1100
|
+
} catch {
|
|
1101
|
+
return { source_type: "unknown", rule_hint: "no-rule-match", precedence_level: 6 };
|
|
1102
|
+
}
|
|
1103
|
+
const host = parsed.hostname.toLowerCase();
|
|
1104
|
+
const pathname = parsed.pathname;
|
|
1105
|
+
if (host === "github.com" && /^\/[^/]+\/[^/]+/.test(pathname)) {
|
|
1106
|
+
return { source_type: "unknown", rule_hint: "flagged:github-ui-html", precedence_level: 2 };
|
|
1107
|
+
}
|
|
1108
|
+
for (const vendor of CANONICAL_VENDORS) {
|
|
1109
|
+
if (vendor.hosts.includes(host)) {
|
|
1110
|
+
return {
|
|
1111
|
+
source_type: vendor.source_type,
|
|
1112
|
+
rule_hint: `canonical-vendor:${vendor.slug}`,
|
|
1113
|
+
precedence_level: 3
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
for (const hp of vendor.hostPaths ?? []) {
|
|
1117
|
+
const slashIdx = hp.indexOf("/");
|
|
1118
|
+
const hpHost = slashIdx === -1 ? hp : hp.slice(0, slashIdx);
|
|
1119
|
+
const hpPath = slashIdx === -1 ? "" : "/" + hp.slice(slashIdx + 1);
|
|
1120
|
+
if (host === hpHost && (hpPath === "" || pathname === hpPath || pathname.startsWith(hpPath + "/"))) {
|
|
1121
|
+
return {
|
|
1122
|
+
source_type: vendor.source_type,
|
|
1123
|
+
rule_hint: `canonical-vendor:${vendor.slug}`,
|
|
1124
|
+
precedence_level: 3
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (host === "api.github.com") {
|
|
1130
|
+
if (pathname.startsWith("/search/issues")) {
|
|
1131
|
+
return { source_type: "forum", rule_hint: "github-api:search-issues", precedence_level: 4 };
|
|
1132
|
+
}
|
|
1133
|
+
const releasesMatch = pathname.match(/^\/repos\/([^/]+)\/([^/]+)\/releases(?:\/|$|\?)/);
|
|
1134
|
+
if (releasesMatch) {
|
|
1135
|
+
const org = releasesMatch[1].toLowerCase();
|
|
1136
|
+
if (CANONICAL_GITHUB_ORGS.has(org)) {
|
|
1137
|
+
return {
|
|
1138
|
+
source_type: "primary",
|
|
1139
|
+
rule_hint: "github-api:canonical-releases",
|
|
1140
|
+
precedence_level: 4
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
return {
|
|
1144
|
+
source_type: "secondary",
|
|
1145
|
+
rule_hint: "github-api:releases-noncanonical",
|
|
1146
|
+
precedence_level: 4
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
if (pathname.match(/^\/repos\/[^/]+\/[^/]+\/issues\/\d+(?:\/|$|\?)/)) {
|
|
1150
|
+
return { source_type: "forum", rule_hint: "github-api:issue-thread", precedence_level: 4 };
|
|
1151
|
+
}
|
|
1152
|
+
if (pathname.match(/^\/repos\/[^/]+\/[^/]+\/issues(?:\/|$|\?)/)) {
|
|
1153
|
+
return { source_type: "forum", rule_hint: "github-api:issues-list", precedence_level: 4 };
|
|
1154
|
+
}
|
|
1155
|
+
if (pathname.match(/^\/repos\/[^/]+\/[^/]+\/tags(?:\/|$|\?)/)) {
|
|
1156
|
+
return { source_type: "secondary", rule_hint: "github-api:tags", precedence_level: 4 };
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
if (host === "raw.githubusercontent.com") {
|
|
1160
|
+
const rawMatch = pathname.match(/^\/([^/]+)\/([^/]+)\/([^/]+)\/(.*)/);
|
|
1161
|
+
if (rawMatch) {
|
|
1162
|
+
const [, org, repo, , filePath] = rawMatch;
|
|
1163
|
+
const isCanonicalOrg = CANONICAL_GITHUB_ORGS.has(org.toLowerCase());
|
|
1164
|
+
const isDocsShapedRepo = /-docs$/.test(repo) || /-documentation$/.test(repo);
|
|
1165
|
+
const isDocFile = /\.(rst|md)$/i.test(filePath);
|
|
1166
|
+
if (isDocFile && (isCanonicalOrg || isDocsShapedRepo)) {
|
|
1167
|
+
return {
|
|
1168
|
+
source_type: "docs",
|
|
1169
|
+
rule_hint: "raw-github:project-docs",
|
|
1170
|
+
precedence_level: 5
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
if (/(?:^|\/)README\.md$/i.test(filePath)) {
|
|
1174
|
+
return {
|
|
1175
|
+
source_type: "secondary",
|
|
1176
|
+
rule_hint: "raw-github:plugin-readme",
|
|
1177
|
+
precedence_level: 5
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
return { source_type: "secondary", rule_hint: "raw-github:other", precedence_level: 5 };
|
|
1181
|
+
}
|
|
1182
|
+
return { source_type: "secondary", rule_hint: "raw-github:other", precedence_level: 5 };
|
|
1183
|
+
}
|
|
1184
|
+
if (host.startsWith("docs.")) {
|
|
1185
|
+
return {
|
|
1186
|
+
source_type: "docs",
|
|
1187
|
+
rule_hint: "fallback:docs-host-prefix",
|
|
1188
|
+
precedence_level: 6
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
const KNOWN_FORUM_HOSTS = ["stackoverflow.com", "reddit.com", "news.ycombinator.com"];
|
|
1192
|
+
if (KNOWN_FORUM_HOSTS.some((fh) => host === fh || host.endsWith("." + fh))) {
|
|
1193
|
+
return { source_type: "forum", rule_hint: "fallback:known-forum", precedence_level: 6 };
|
|
1194
|
+
}
|
|
1195
|
+
return { source_type: "unknown", rule_hint: "no-rule-match", precedence_level: 6 };
|
|
1196
|
+
}
|
|
1197
|
+
var CANONICAL_GITHUB_ORGS;
|
|
1198
|
+
var init_source_type_classifier = __esm({
|
|
1199
|
+
"src/sources/source-type-classifier.ts"() {
|
|
1200
|
+
"use strict";
|
|
1201
|
+
init_canonical_vendors();
|
|
1202
|
+
CANONICAL_GITHUB_ORGS = new Set(
|
|
1203
|
+
CANONICAL_VENDORS.flatMap((v) => (v.github_orgs ?? []).map((o) => o.toLowerCase()))
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
// src/sources/effective-card.ts
|
|
1209
|
+
function getEffectiveSourceType(card, overrides) {
|
|
1210
|
+
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);
|
|
1211
|
+
return matched.length > 0 ? matched[0].new_source_type : card.source_type;
|
|
1212
|
+
}
|
|
1213
|
+
function getEffectivePublisher(card, overrides) {
|
|
1214
|
+
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);
|
|
1215
|
+
if (matched.length === 0) return card.publisher;
|
|
1216
|
+
return matched[0].new_publisher ?? null;
|
|
1217
|
+
}
|
|
1218
|
+
var init_effective_card = __esm({
|
|
1219
|
+
"src/sources/effective-card.ts"() {
|
|
1220
|
+
"use strict";
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1007
1224
|
// src/sources/cards.ts
|
|
1008
1225
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
1009
1226
|
import { existsSync as existsSync3 } from "fs";
|
|
1010
1227
|
import { join as join4 } from "path";
|
|
1011
1228
|
function buildCard(args) {
|
|
1012
1229
|
const { receipt, extraction, extractedBy } = args;
|
|
1230
|
+
const overrides = args.overrides ?? [];
|
|
1231
|
+
const classification = classifySourceType({ url: receipt.requested_url });
|
|
1232
|
+
const classifiedSourceType = classification.rule_hint === "no-rule-match" ? extraction.source_type : classification.source_type;
|
|
1233
|
+
const baseForEffective = {
|
|
1234
|
+
source_id: receipt.source_id,
|
|
1235
|
+
source_type: classifiedSourceType,
|
|
1236
|
+
publisher: extraction.publisher
|
|
1237
|
+
};
|
|
1238
|
+
const resolvedSourceType = getEffectiveSourceType(baseForEffective, overrides);
|
|
1239
|
+
const resolvedPublisher = getEffectivePublisher(baseForEffective, overrides);
|
|
1013
1240
|
const card = SourceCardSchema.parse({
|
|
1014
1241
|
source_id: receipt.source_id,
|
|
1015
1242
|
receipt_id: receipt.receipt_id,
|
|
@@ -1017,10 +1244,10 @@ function buildCard(args) {
|
|
|
1017
1244
|
url: receipt.requested_url,
|
|
1018
1245
|
final_url: receipt.final_url,
|
|
1019
1246
|
fetched_at: receipt.fetched_at,
|
|
1020
|
-
publisher:
|
|
1247
|
+
publisher: resolvedPublisher,
|
|
1021
1248
|
published_at: extraction.published_at,
|
|
1022
1249
|
title: extraction.title,
|
|
1023
|
-
source_type:
|
|
1250
|
+
source_type: resolvedSourceType,
|
|
1024
1251
|
relevance: extraction.relevance,
|
|
1025
1252
|
key_points: extraction.key_points,
|
|
1026
1253
|
limitations: extraction.limitations,
|
|
@@ -1028,7 +1255,9 @@ function buildCard(args) {
|
|
|
1028
1255
|
scope: extraction.scope,
|
|
1029
1256
|
not: extraction.not,
|
|
1030
1257
|
extracted_by: extractedBy,
|
|
1031
|
-
extracted_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1258
|
+
extracted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1259
|
+
rule_hint: classification.rule_hint,
|
|
1260
|
+
precedence_level: classification.precedence_level
|
|
1032
1261
|
});
|
|
1033
1262
|
return card;
|
|
1034
1263
|
}
|
|
@@ -1065,21 +1294,106 @@ var init_cards = __esm({
|
|
|
1065
1294
|
"src/sources/cards.ts"() {
|
|
1066
1295
|
"use strict";
|
|
1067
1296
|
init_schema3();
|
|
1297
|
+
init_source_type_classifier();
|
|
1298
|
+
init_effective_card();
|
|
1068
1299
|
}
|
|
1069
1300
|
});
|
|
1070
1301
|
|
|
1071
|
-
// src/sources/
|
|
1302
|
+
// src/sources/source-card-overrides-schema.ts
|
|
1303
|
+
import { z as z4 } from "zod";
|
|
1304
|
+
function validateSourceCardOverride(input) {
|
|
1305
|
+
return SourceCardOverrideSchema.parse(input);
|
|
1306
|
+
}
|
|
1307
|
+
var SourceCardOverrideSchema;
|
|
1308
|
+
var init_source_card_overrides_schema = __esm({
|
|
1309
|
+
"src/sources/source-card-overrides-schema.ts"() {
|
|
1310
|
+
"use strict";
|
|
1311
|
+
init_schema3();
|
|
1312
|
+
SourceCardOverrideSchema = z4.object({
|
|
1313
|
+
source_id: z4.string().min(1, "source_id must be non-empty"),
|
|
1314
|
+
url: z4.string().min(1, "url must be non-empty"),
|
|
1315
|
+
previous_source_type: z4.string().nullable().optional(),
|
|
1316
|
+
new_source_type: SourceTypeSchema.nullable().optional(),
|
|
1317
|
+
previous_publisher: z4.string().nullable().optional(),
|
|
1318
|
+
new_publisher: z4.string().nullable().optional(),
|
|
1319
|
+
reason: z4.string().refine((v) => v.trim().length > 0, { message: "reason must be non-empty after trim" }),
|
|
1320
|
+
operator: z4.string().min(1, "operator must be non-empty"),
|
|
1321
|
+
created_at: z4.string().refine((v) => isFinite(Date.parse(v)), {
|
|
1322
|
+
message: "created_at must be a valid ISO 8601 timestamp"
|
|
1323
|
+
}),
|
|
1324
|
+
rule_hint: z4.string().nullable().optional(),
|
|
1325
|
+
pack_version: z4.string().min(1, "pack_version must be non-empty")
|
|
1326
|
+
}).refine(
|
|
1327
|
+
(obj) => obj.new_source_type != null && obj.new_source_type !== void 0 || obj.new_publisher != null && obj.new_publisher !== void 0,
|
|
1328
|
+
{
|
|
1329
|
+
message: "At least one of new_source_type or new_publisher must be present and non-null"
|
|
1330
|
+
}
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
// src/sources/source-card-overrides.ts
|
|
1336
|
+
import { readFile as readFile5, appendFile as appendFile2, mkdir as mkdir5 } from "fs/promises";
|
|
1072
1337
|
import { existsSync as existsSync4 } from "fs";
|
|
1073
|
-
import { join as join5,
|
|
1338
|
+
import { join as join5, dirname as dirname2 } from "path";
|
|
1339
|
+
function overridesPath(packPath) {
|
|
1340
|
+
return join5(packPath, "evidence", OVERRIDES_FILE);
|
|
1341
|
+
}
|
|
1342
|
+
async function readOverrides(packPath) {
|
|
1343
|
+
const filePath = overridesPath(packPath);
|
|
1344
|
+
if (!existsSync4(filePath)) return [];
|
|
1345
|
+
const content = await readFile5(filePath, "utf8");
|
|
1346
|
+
const lines = content.split(/\r?\n/).filter(Boolean);
|
|
1347
|
+
return lines.map((line, idx) => {
|
|
1348
|
+
const lineNum = idx + 1;
|
|
1349
|
+
let parsed;
|
|
1350
|
+
try {
|
|
1351
|
+
parsed = JSON.parse(line);
|
|
1352
|
+
} catch {
|
|
1353
|
+
throw new Error(
|
|
1354
|
+
`source-card-overrides.jsonl: malformed JSON at line ${lineNum}: ${line.slice(0, 120)}`
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
try {
|
|
1358
|
+
return validateSourceCardOverride(parsed);
|
|
1359
|
+
} catch (err) {
|
|
1360
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1361
|
+
throw new Error(
|
|
1362
|
+
`source-card-overrides.jsonl: invalid override at line ${lineNum}: ${msg}
|
|
1363
|
+
Content: ${line.slice(0, 120)}`,
|
|
1364
|
+
{ cause: err }
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
async function appendOverride(packPath, override) {
|
|
1370
|
+
validateSourceCardOverride(override);
|
|
1371
|
+
const filePath = overridesPath(packPath);
|
|
1372
|
+
await mkdir5(dirname2(filePath), { recursive: true });
|
|
1373
|
+
await appendFile2(filePath, JSON.stringify(override) + "\n", "utf8");
|
|
1374
|
+
}
|
|
1375
|
+
var OVERRIDES_FILE;
|
|
1376
|
+
var init_source_card_overrides = __esm({
|
|
1377
|
+
"src/sources/source-card-overrides.ts"() {
|
|
1378
|
+
"use strict";
|
|
1379
|
+
init_source_card_overrides_schema();
|
|
1380
|
+
OVERRIDES_FILE = "source-card-overrides.jsonl";
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
// src/sources/gather.ts
|
|
1385
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1386
|
+
import { join as join6, resolve as resolve3 } from "path";
|
|
1074
1387
|
async function gather(options) {
|
|
1075
1388
|
const packPath = options.packPath ? resolve3(options.packPath) : process.cwd();
|
|
1076
|
-
if (!
|
|
1077
|
-
if (!
|
|
1389
|
+
if (!existsSync5(join6(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
1390
|
+
if (!existsSync5(join6(packPath, "sections", options.sectionId)))
|
|
1078
1391
|
throw new SectionNotFoundError(options.sectionId);
|
|
1079
1392
|
const { urls } = await collectUrls({ urls: options.urls, urlsFile: options.urlsFile });
|
|
1080
1393
|
if (urls.length === 0) throw new NoUrlsProvidedError();
|
|
1081
1394
|
const extractorList = options.extractors ?? defaultExtractors();
|
|
1082
1395
|
const extractor = await pickExtractor(extractorList);
|
|
1396
|
+
const overrides = await readOverrides(packPath);
|
|
1083
1397
|
const summary = {
|
|
1084
1398
|
sectionId: options.sectionId,
|
|
1085
1399
|
attempted: urls.length,
|
|
@@ -1114,7 +1428,7 @@ async function gather(options) {
|
|
|
1114
1428
|
extraction_extractor: extractor.name,
|
|
1115
1429
|
extraction_error: null
|
|
1116
1430
|
};
|
|
1117
|
-
const card = buildCard({ receipt: receiptToWrite, extraction: result, extractedBy: extractor.name });
|
|
1431
|
+
const card = buildCard({ receipt: receiptToWrite, extraction: result, extractedBy: extractor.name, overrides });
|
|
1118
1432
|
await writeSourceCard(packPath, card);
|
|
1119
1433
|
await appendSectionSourceId(packPath, options.sectionId, card.source_id);
|
|
1120
1434
|
summary.cardsWritten += 1;
|
|
@@ -1144,27 +1458,28 @@ var init_gather = __esm({
|
|
|
1144
1458
|
init_url_input();
|
|
1145
1459
|
init_extractors();
|
|
1146
1460
|
init_cards();
|
|
1461
|
+
init_source_card_overrides();
|
|
1147
1462
|
}
|
|
1148
1463
|
});
|
|
1149
1464
|
|
|
1150
1465
|
// src/sources/excerpts/schema.ts
|
|
1151
|
-
import { z as
|
|
1466
|
+
import { z as z5 } from "zod";
|
|
1152
1467
|
var EXCERPT_ID_PATTERN, ExcerptOriginSchema, ExcerptSchema;
|
|
1153
1468
|
var init_schema4 = __esm({
|
|
1154
1469
|
"src/sources/excerpts/schema.ts"() {
|
|
1155
1470
|
"use strict";
|
|
1156
1471
|
EXCERPT_ID_PATTERN = /^ex_[a-f0-9]{12}_\d{3,}$/;
|
|
1157
|
-
ExcerptOriginSchema =
|
|
1158
|
-
ExcerptSchema =
|
|
1159
|
-
excerpt_id:
|
|
1160
|
-
source_id:
|
|
1161
|
-
source_hash:
|
|
1162
|
-
text:
|
|
1163
|
-
location_hint:
|
|
1164
|
-
char_start:
|
|
1165
|
-
char_end:
|
|
1472
|
+
ExcerptOriginSchema = z5.enum(["raw_text", "key_point"]);
|
|
1473
|
+
ExcerptSchema = z5.object({
|
|
1474
|
+
excerpt_id: z5.string().regex(EXCERPT_ID_PATTERN),
|
|
1475
|
+
source_id: z5.string().regex(/^src_[a-f0-9]{12}$/),
|
|
1476
|
+
source_hash: z5.string().regex(/^[a-f0-9]{64}$/).nullable(),
|
|
1477
|
+
text: z5.string().min(1),
|
|
1478
|
+
location_hint: z5.string().nullable(),
|
|
1479
|
+
char_start: z5.number().int().nonnegative(),
|
|
1480
|
+
char_end: z5.number().int().nonnegative(),
|
|
1166
1481
|
origin: ExcerptOriginSchema,
|
|
1167
|
-
created_at:
|
|
1482
|
+
created_at: z5.string()
|
|
1168
1483
|
});
|
|
1169
1484
|
}
|
|
1170
1485
|
});
|
|
@@ -1293,11 +1608,11 @@ var init_chunk = __esm({
|
|
|
1293
1608
|
});
|
|
1294
1609
|
|
|
1295
1610
|
// src/sources/excerpts/ledger.ts
|
|
1296
|
-
import { existsSync as
|
|
1297
|
-
import { mkdir as
|
|
1298
|
-
import { dirname as
|
|
1611
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1612
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
1613
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
1299
1614
|
function ledgerPathFor(packPath, sourceId) {
|
|
1300
|
-
return
|
|
1615
|
+
return join7(packPath, "evidence", "excerpts", `${sourceId}.jsonl`);
|
|
1301
1616
|
}
|
|
1302
1617
|
function makeExcerptId(sourceId, index) {
|
|
1303
1618
|
const idx = String(index).padStart(3, "0");
|
|
@@ -1305,7 +1620,7 @@ function makeExcerptId(sourceId, index) {
|
|
|
1305
1620
|
return `ex_${hex}_${idx}`;
|
|
1306
1621
|
}
|
|
1307
1622
|
async function readLedger(path) {
|
|
1308
|
-
const text = await
|
|
1623
|
+
const text = await readFile6(path, "utf8");
|
|
1309
1624
|
const out = [];
|
|
1310
1625
|
for (const line of text.split(/\r?\n/)) {
|
|
1311
1626
|
if (!line.trim()) continue;
|
|
@@ -1317,7 +1632,7 @@ async function readLedger(path) {
|
|
|
1317
1632
|
return out;
|
|
1318
1633
|
}
|
|
1319
1634
|
async function writeLedger(path, excerpts) {
|
|
1320
|
-
await
|
|
1635
|
+
await mkdir6(dirname3(path), { recursive: true });
|
|
1321
1636
|
const body = excerpts.map((e) => JSON.stringify(e)).join("\n") + (excerpts.length > 0 ? "\n" : "");
|
|
1322
1637
|
await writeFile5(path, body, "utf8");
|
|
1323
1638
|
}
|
|
@@ -1325,7 +1640,7 @@ async function loadOrBuildLedger(args) {
|
|
|
1325
1640
|
const { packPath, sourceCard, sourceHash, rawText } = args;
|
|
1326
1641
|
const now = args.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
1327
1642
|
const path = ledgerPathFor(packPath, sourceCard.source_id);
|
|
1328
|
-
if (
|
|
1643
|
+
if (existsSync6(path)) {
|
|
1329
1644
|
const excerpts2 = await readLedger(path);
|
|
1330
1645
|
if (excerpts2.length > 0) {
|
|
1331
1646
|
const origins = new Set(excerpts2.map((e) => e.origin));
|
|
@@ -1394,6 +1709,9 @@ var init_sources = __esm({
|
|
|
1394
1709
|
init_extractors();
|
|
1395
1710
|
init_ollama_intern();
|
|
1396
1711
|
init_cards();
|
|
1712
|
+
init_source_card_overrides();
|
|
1713
|
+
init_source_card_overrides_schema();
|
|
1714
|
+
init_effective_card();
|
|
1397
1715
|
init_schema3();
|
|
1398
1716
|
init_excerpts();
|
|
1399
1717
|
}
|
|
@@ -1704,50 +2022,50 @@ var init_extractors2 = __esm({
|
|
|
1704
2022
|
});
|
|
1705
2023
|
|
|
1706
2024
|
// src/claims/schema.ts
|
|
1707
|
-
import { z as
|
|
2025
|
+
import { z as z6 } from "zod";
|
|
1708
2026
|
var ConfidenceSchema, ClaimExtractorSchema, ReviewStateSchema, ClaimSchema;
|
|
1709
2027
|
var init_schema5 = __esm({
|
|
1710
2028
|
"src/claims/schema.ts"() {
|
|
1711
2029
|
"use strict";
|
|
1712
2030
|
init_schema4();
|
|
1713
|
-
ConfidenceSchema =
|
|
1714
|
-
ClaimExtractorSchema =
|
|
1715
|
-
ReviewStateSchema =
|
|
2031
|
+
ConfidenceSchema = z6.enum(["low", "medium", "high"]);
|
|
2032
|
+
ClaimExtractorSchema = z6.enum(["heuristic", "ollama-intern"]);
|
|
2033
|
+
ReviewStateSchema = z6.enum([
|
|
1716
2034
|
"candidate",
|
|
1717
2035
|
"gated",
|
|
1718
2036
|
"reviewed",
|
|
1719
2037
|
"rejected",
|
|
1720
2038
|
"accepted"
|
|
1721
2039
|
]);
|
|
1722
|
-
ClaimSchema =
|
|
1723
|
-
claim_id:
|
|
1724
|
-
section_id:
|
|
1725
|
-
source_ids:
|
|
1726
|
-
source_hashes:
|
|
1727
|
-
asserts:
|
|
1728
|
-
scope:
|
|
1729
|
-
not:
|
|
2040
|
+
ClaimSchema = z6.object({
|
|
2041
|
+
claim_id: z6.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
|
|
2042
|
+
section_id: z6.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2043
|
+
source_ids: z6.array(z6.string().regex(/^src_[a-f0-9]{12}$/)).min(1, "every claim must reference at least one source_id"),
|
|
2044
|
+
source_hashes: z6.array(z6.string().regex(/^[a-f0-9]{64}$/)),
|
|
2045
|
+
asserts: z6.string().min(1),
|
|
2046
|
+
scope: z6.string().nullable(),
|
|
2047
|
+
not: z6.string().nullable(),
|
|
1730
2048
|
// Span-first extraction: the model picks excerpt IDs from the deterministic
|
|
1731
2049
|
// ledger; research-os copies the literal text into evidence_excerpt. Models
|
|
1732
2050
|
// may interpret source spans; they may not author evidence spans.
|
|
1733
2051
|
// Allowed empty for legacy claims that pre-date span-first extraction —
|
|
1734
2052
|
// those should be re-extracted; new writes always populate at least one ID.
|
|
1735
|
-
evidence_excerpt_ids:
|
|
1736
|
-
evidence_excerpt:
|
|
1737
|
-
evidence_location:
|
|
2053
|
+
evidence_excerpt_ids: z6.array(z6.string().regex(EXCERPT_ID_PATTERN)).default([]),
|
|
2054
|
+
evidence_excerpt: z6.string().min(1),
|
|
2055
|
+
evidence_location: z6.string().nullable(),
|
|
1738
2056
|
confidence: ConfidenceSchema,
|
|
1739
2057
|
extractor: ClaimExtractorSchema,
|
|
1740
|
-
extraction_method:
|
|
1741
|
-
created_at:
|
|
2058
|
+
extraction_method: z6.string().min(1),
|
|
2059
|
+
created_at: z6.string(),
|
|
1742
2060
|
review_state: ReviewStateSchema
|
|
1743
2061
|
});
|
|
1744
2062
|
}
|
|
1745
2063
|
});
|
|
1746
2064
|
|
|
1747
2065
|
// src/claims/extract.ts
|
|
1748
|
-
import { existsSync as
|
|
1749
|
-
import { appendFile as
|
|
1750
|
-
import { join as
|
|
2066
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2067
|
+
import { appendFile as appendFile3, mkdir as mkdir7, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
2068
|
+
import { join as join8, resolve as resolve4 } from "path";
|
|
1751
2069
|
function normalize2(text) {
|
|
1752
2070
|
return text.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").toLowerCase().trim();
|
|
1753
2071
|
}
|
|
@@ -1758,9 +2076,9 @@ function evidenceGrounded(excerpt, rawText) {
|
|
|
1758
2076
|
return normalize2(rawText).includes(e);
|
|
1759
2077
|
}
|
|
1760
2078
|
async function readSectionSourceIds(packPath, sectionId) {
|
|
1761
|
-
const path =
|
|
1762
|
-
if (!
|
|
1763
|
-
const text = await
|
|
2079
|
+
const path = join8(packPath, "sections", sectionId, "sources.jsonl");
|
|
2080
|
+
if (!existsSync7(path)) return [];
|
|
2081
|
+
const text = await readFile7(path, "utf8");
|
|
1764
2082
|
const ids = [];
|
|
1765
2083
|
for (const line of text.split(/\r?\n/)) {
|
|
1766
2084
|
if (!line.trim()) continue;
|
|
@@ -1773,15 +2091,15 @@ async function readSectionSourceIds(packPath, sectionId) {
|
|
|
1773
2091
|
return ids;
|
|
1774
2092
|
}
|
|
1775
2093
|
async function readSourceCard(packPath, sourceId) {
|
|
1776
|
-
const path =
|
|
1777
|
-
if (!
|
|
1778
|
-
const text = await
|
|
2094
|
+
const path = join8(packPath, "evidence", "source-cards", `${sourceId}.json`);
|
|
2095
|
+
if (!existsSync7(path)) return null;
|
|
2096
|
+
const text = await readFile7(path, "utf8");
|
|
1779
2097
|
return SourceCardSchema.parse(JSON.parse(text));
|
|
1780
2098
|
}
|
|
1781
2099
|
async function findLatestReceipt(packPath, sourceId) {
|
|
1782
|
-
const path =
|
|
1783
|
-
if (!
|
|
1784
|
-
const text = await
|
|
2100
|
+
const path = join8(packPath, "evidence", "fetch-log.jsonl");
|
|
2101
|
+
if (!existsSync7(path)) return null;
|
|
2102
|
+
const text = await readFile7(path, "utf8");
|
|
1785
2103
|
let latest = null;
|
|
1786
2104
|
for (const line of text.split(/\r?\n/)) {
|
|
1787
2105
|
if (!line.trim()) continue;
|
|
@@ -1796,9 +2114,9 @@ async function findLatestReceipt(packPath, sourceId) {
|
|
|
1796
2114
|
return latest;
|
|
1797
2115
|
}
|
|
1798
2116
|
async function readExistingClaimIds(packPath, sectionId) {
|
|
1799
|
-
const path =
|
|
1800
|
-
if (!
|
|
1801
|
-
const text = await
|
|
2117
|
+
const path = join8(packPath, "sections", sectionId, "claims.jsonl");
|
|
2118
|
+
if (!existsSync7(path)) return /* @__PURE__ */ new Set();
|
|
2119
|
+
const text = await readFile7(path, "utf8");
|
|
1802
2120
|
const set = /* @__PURE__ */ new Set();
|
|
1803
2121
|
for (const line of text.split(/\r?\n/)) {
|
|
1804
2122
|
if (!line.trim()) continue;
|
|
@@ -1873,14 +2191,14 @@ function buildClaim(args) {
|
|
|
1873
2191
|
}
|
|
1874
2192
|
async function extract(options) {
|
|
1875
2193
|
const packPath = options.packPath ? resolve4(options.packPath) : process.cwd();
|
|
1876
|
-
if (!
|
|
1877
|
-
if (!
|
|
2194
|
+
if (!existsSync7(join8(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
2195
|
+
if (!existsSync7(join8(packPath, "sections", options.sectionId)))
|
|
1878
2196
|
throw new SectionNotFoundError(options.sectionId);
|
|
1879
2197
|
const sourceIds = await readSectionSourceIds(packPath, options.sectionId);
|
|
1880
2198
|
if (sourceIds.length === 0) throw new NoSourcesGatheredError(options.sectionId);
|
|
1881
2199
|
const adapters = options.extractors ?? defaultClaimExtractors();
|
|
1882
2200
|
const extractor = await pickClaimExtractor(adapters);
|
|
1883
|
-
const claimsPath =
|
|
2201
|
+
const claimsPath = join8(packPath, "sections", options.sectionId, "claims.jsonl");
|
|
1884
2202
|
const existingIds = await readExistingClaimIds(packPath, options.sectionId);
|
|
1885
2203
|
const summary = {
|
|
1886
2204
|
sectionId: options.sectionId,
|
|
@@ -1909,9 +2227,9 @@ async function extract(options) {
|
|
|
1909
2227
|
const receipt2 = await findLatestReceipt(packPath, sourceId);
|
|
1910
2228
|
let rawText = null;
|
|
1911
2229
|
if (receipt2?.raw_text_path) {
|
|
1912
|
-
const raw =
|
|
1913
|
-
if (
|
|
1914
|
-
rawText = await
|
|
2230
|
+
const raw = join8(packPath, receipt2.raw_text_path);
|
|
2231
|
+
if (existsSync7(raw)) {
|
|
2232
|
+
rawText = await readFile7(raw, "utf8");
|
|
1915
2233
|
}
|
|
1916
2234
|
}
|
|
1917
2235
|
const ledger = await loadOrBuildLedger({
|
|
@@ -1967,7 +2285,7 @@ async function extract(options) {
|
|
|
1967
2285
|
summary.claimsDeduped += 1;
|
|
1968
2286
|
continue;
|
|
1969
2287
|
}
|
|
1970
|
-
await
|
|
2288
|
+
await appendFile3(claimsPath, JSON.stringify(claim) + "\n", "utf8");
|
|
1971
2289
|
existingIds.add(claim.claim_id);
|
|
1972
2290
|
summary.claimsAdded += 1;
|
|
1973
2291
|
summary.claimIds.push(claim.claim_id);
|
|
@@ -1996,10 +2314,10 @@ async function extract(options) {
|
|
|
1996
2314
|
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"
|
|
1997
2315
|
}))
|
|
1998
2316
|
};
|
|
1999
|
-
const auditsDir =
|
|
2000
|
-
await
|
|
2317
|
+
const auditsDir = join8(packPath, "audits");
|
|
2318
|
+
await mkdir7(auditsDir, { recursive: true });
|
|
2001
2319
|
await writeFile6(
|
|
2002
|
-
|
|
2320
|
+
join8(auditsDir, `${options.sectionId}-claim-extract.json`),
|
|
2003
2321
|
JSON.stringify(receipt, null, 2),
|
|
2004
2322
|
"utf8"
|
|
2005
2323
|
);
|
|
@@ -2026,61 +2344,61 @@ var init_extract = __esm({
|
|
|
2026
2344
|
});
|
|
2027
2345
|
|
|
2028
2346
|
// src/claims/density/schema.ts
|
|
2029
|
-
import { z as
|
|
2347
|
+
import { z as z7 } from "zod";
|
|
2030
2348
|
var PerSourceDensitySchema, NearDuplicateClusterSchema, DensityFlagSchema, ClaimDensityAuditSchema;
|
|
2031
2349
|
var init_schema6 = __esm({
|
|
2032
2350
|
"src/claims/density/schema.ts"() {
|
|
2033
2351
|
"use strict";
|
|
2034
|
-
PerSourceDensitySchema =
|
|
2035
|
-
source_id:
|
|
2036
|
-
publisher:
|
|
2037
|
-
source_word_count:
|
|
2038
|
-
claim_count:
|
|
2039
|
-
claims_per_1k_words:
|
|
2040
|
-
share_of_section:
|
|
2352
|
+
PerSourceDensitySchema = z7.object({
|
|
2353
|
+
source_id: z7.string().regex(/^src_[a-f0-9]{12}$/),
|
|
2354
|
+
publisher: z7.string().nullable(),
|
|
2355
|
+
source_word_count: z7.number().int().nonnegative(),
|
|
2356
|
+
claim_count: z7.number().int().nonnegative(),
|
|
2357
|
+
claims_per_1k_words: z7.number(),
|
|
2358
|
+
share_of_section: z7.number(),
|
|
2041
2359
|
// 0..1
|
|
2042
|
-
weak_scope_count:
|
|
2043
|
-
generic_scope_count:
|
|
2360
|
+
weak_scope_count: z7.number().int().nonnegative(),
|
|
2361
|
+
generic_scope_count: z7.number().int().nonnegative()
|
|
2044
2362
|
});
|
|
2045
|
-
NearDuplicateClusterSchema =
|
|
2046
|
-
representative_assert:
|
|
2047
|
-
member_count:
|
|
2048
|
-
claim_ids:
|
|
2363
|
+
NearDuplicateClusterSchema = z7.object({
|
|
2364
|
+
representative_assert: z7.string(),
|
|
2365
|
+
member_count: z7.number().int().nonnegative(),
|
|
2366
|
+
claim_ids: z7.array(z7.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/))
|
|
2049
2367
|
});
|
|
2050
|
-
DensityFlagSchema =
|
|
2051
|
-
type:
|
|
2368
|
+
DensityFlagSchema = z7.object({
|
|
2369
|
+
type: z7.enum([
|
|
2052
2370
|
"source_dominance",
|
|
2053
2371
|
"high_per_word_density",
|
|
2054
2372
|
"large_near_duplicate_cluster",
|
|
2055
2373
|
"weak_scope_majority"
|
|
2056
2374
|
]),
|
|
2057
|
-
severity:
|
|
2058
|
-
message:
|
|
2059
|
-
affects_source_ids:
|
|
2060
|
-
affects_claim_ids:
|
|
2375
|
+
severity: z7.enum(["info", "warn", "block"]),
|
|
2376
|
+
message: z7.string(),
|
|
2377
|
+
affects_source_ids: z7.array(z7.string().regex(/^src_[a-f0-9]{12}$/)).default([]),
|
|
2378
|
+
affects_claim_ids: z7.array(z7.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)).default([])
|
|
2061
2379
|
});
|
|
2062
|
-
ClaimDensityAuditSchema =
|
|
2063
|
-
audit_id:
|
|
2064
|
-
section_id:
|
|
2065
|
-
audited_at:
|
|
2066
|
-
research_os_version:
|
|
2067
|
-
candidate_claim_count:
|
|
2068
|
-
source_count:
|
|
2069
|
-
total_source_word_count:
|
|
2070
|
-
claims_per_1k_words:
|
|
2071
|
-
weak_scope_count:
|
|
2072
|
-
generic_scope_count:
|
|
2073
|
-
per_source:
|
|
2074
|
-
near_duplicate_clusters:
|
|
2075
|
-
flags:
|
|
2380
|
+
ClaimDensityAuditSchema = z7.object({
|
|
2381
|
+
audit_id: z7.string().regex(/^cda_[0-9]+_[a-z0-9-]+$/),
|
|
2382
|
+
section_id: z7.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2383
|
+
audited_at: z7.string(),
|
|
2384
|
+
research_os_version: z7.string(),
|
|
2385
|
+
candidate_claim_count: z7.number().int().nonnegative(),
|
|
2386
|
+
source_count: z7.number().int().nonnegative(),
|
|
2387
|
+
total_source_word_count: z7.number().int().nonnegative(),
|
|
2388
|
+
claims_per_1k_words: z7.number(),
|
|
2389
|
+
weak_scope_count: z7.number().int().nonnegative(),
|
|
2390
|
+
generic_scope_count: z7.number().int().nonnegative(),
|
|
2391
|
+
per_source: z7.array(PerSourceDensitySchema),
|
|
2392
|
+
near_duplicate_clusters: z7.array(NearDuplicateClusterSchema),
|
|
2393
|
+
flags: z7.array(DensityFlagSchema)
|
|
2076
2394
|
});
|
|
2077
2395
|
}
|
|
2078
2396
|
});
|
|
2079
2397
|
|
|
2080
2398
|
// src/claims/density/run.ts
|
|
2081
|
-
import { existsSync as
|
|
2082
|
-
import { mkdir as
|
|
2083
|
-
import { join as
|
|
2399
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2400
|
+
import { mkdir as mkdir8, readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
|
|
2401
|
+
import { join as join9, resolve as resolve5 } from "path";
|
|
2084
2402
|
function normalize3(s) {
|
|
2085
2403
|
return s.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").toLowerCase().replace(/[^a-z0-9 ]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2086
2404
|
}
|
|
@@ -2089,9 +2407,9 @@ function countWords(text) {
|
|
|
2089
2407
|
return normalize3(text).split(" ").filter((w) => w.length > 0).length;
|
|
2090
2408
|
}
|
|
2091
2409
|
async function readClaims(packPath, sectionId) {
|
|
2092
|
-
const path =
|
|
2093
|
-
if (!
|
|
2094
|
-
const text = await
|
|
2410
|
+
const path = join9(packPath, "sections", sectionId, "claims.jsonl");
|
|
2411
|
+
if (!existsSync8(path)) return [];
|
|
2412
|
+
const text = await readFile8(path, "utf8");
|
|
2095
2413
|
const out = [];
|
|
2096
2414
|
for (const line of text.split(/\r?\n/)) {
|
|
2097
2415
|
if (!line.trim()) continue;
|
|
@@ -2105,10 +2423,10 @@ async function readClaims(packPath, sectionId) {
|
|
|
2105
2423
|
async function readSourceCards(packPath, sourceIds) {
|
|
2106
2424
|
const out = /* @__PURE__ */ new Map();
|
|
2107
2425
|
for (const sid of sourceIds) {
|
|
2108
|
-
const p =
|
|
2109
|
-
if (!
|
|
2426
|
+
const p = join9(packPath, "evidence", "source-cards", `${sid}.json`);
|
|
2427
|
+
if (!existsSync8(p)) continue;
|
|
2110
2428
|
try {
|
|
2111
|
-
out.set(sid, SourceCardSchema.parse(JSON.parse(await
|
|
2429
|
+
out.set(sid, SourceCardSchema.parse(JSON.parse(await readFile8(p, "utf8"))));
|
|
2112
2430
|
} catch {
|
|
2113
2431
|
}
|
|
2114
2432
|
}
|
|
@@ -2116,9 +2434,9 @@ async function readSourceCards(packPath, sourceIds) {
|
|
|
2116
2434
|
}
|
|
2117
2435
|
async function readLatestReceipts(packPath) {
|
|
2118
2436
|
const out = /* @__PURE__ */ new Map();
|
|
2119
|
-
const path =
|
|
2120
|
-
if (!
|
|
2121
|
-
const text = await
|
|
2437
|
+
const path = join9(packPath, "evidence", "fetch-log.jsonl");
|
|
2438
|
+
if (!existsSync8(path)) return out;
|
|
2439
|
+
const text = await readFile8(path, "utf8");
|
|
2122
2440
|
for (const line of text.split(/\r?\n/)) {
|
|
2123
2441
|
if (!line.trim()) continue;
|
|
2124
2442
|
try {
|
|
@@ -2133,9 +2451,9 @@ async function readLatestReceipts(packPath) {
|
|
|
2133
2451
|
}
|
|
2134
2452
|
async function readRawText(packPath, receipt) {
|
|
2135
2453
|
if (!receipt.raw_text_path) return null;
|
|
2136
|
-
const p =
|
|
2137
|
-
if (!
|
|
2138
|
-
return await
|
|
2454
|
+
const p = join9(packPath, receipt.raw_text_path);
|
|
2455
|
+
if (!existsSync8(p)) return null;
|
|
2456
|
+
return await readFile8(p, "utf8");
|
|
2139
2457
|
}
|
|
2140
2458
|
function isWeakScope(claim) {
|
|
2141
2459
|
return claim.scope === null && claim.not === null && claim.asserts.length > 40;
|
|
@@ -2268,8 +2586,8 @@ function buildMarkdown(audit2) {
|
|
|
2268
2586
|
}
|
|
2269
2587
|
async function auditDensity(options) {
|
|
2270
2588
|
const packPath = options.packPath ? resolve5(options.packPath) : process.cwd();
|
|
2271
|
-
if (!
|
|
2272
|
-
if (!
|
|
2589
|
+
if (!existsSync8(join9(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
2590
|
+
if (!existsSync8(join9(packPath, "sections", options.sectionId)))
|
|
2273
2591
|
throw new SectionNotFoundError(options.sectionId);
|
|
2274
2592
|
const claims = await readClaims(packPath, options.sectionId);
|
|
2275
2593
|
const allSourceIds = Array.from(
|
|
@@ -2342,10 +2660,10 @@ async function auditDensity(options) {
|
|
|
2342
2660
|
}));
|
|
2343
2661
|
const flags = [...buildFlags(partial), ...clusterFlags];
|
|
2344
2662
|
const audit2 = ClaimDensityAuditSchema.parse({ ...partial, flags });
|
|
2345
|
-
const auditsDir =
|
|
2346
|
-
await
|
|
2347
|
-
const jsonPath =
|
|
2348
|
-
const markdownPath =
|
|
2663
|
+
const auditsDir = join9(packPath, "audits");
|
|
2664
|
+
await mkdir8(auditsDir, { recursive: true });
|
|
2665
|
+
const jsonPath = join9(auditsDir, `${options.sectionId}-claim-density.json`);
|
|
2666
|
+
const markdownPath = join9(auditsDir, `${options.sectionId}-claim-density.md`);
|
|
2349
2667
|
await writeFile7(jsonPath, JSON.stringify(audit2, null, 2), "utf8");
|
|
2350
2668
|
await writeFile7(markdownPath, buildMarkdown(audit2), "utf8");
|
|
2351
2669
|
return { audit: audit2, jsonPath, markdownPath };
|
|
@@ -2389,12 +2707,12 @@ var init_claims = __esm({
|
|
|
2389
2707
|
});
|
|
2390
2708
|
|
|
2391
2709
|
// src/contradictions/schema.ts
|
|
2392
|
-
import { z as
|
|
2710
|
+
import { z as z8 } from "zod";
|
|
2393
2711
|
var ContradictionTypeSchema, SeveritySchema, OverlapAssessmentSchema, ContradictionDetectorSchema, ContradictionStatusSchema, ContradictionConfidenceSchema, ContradictionSchema;
|
|
2394
2712
|
var init_schema7 = __esm({
|
|
2395
2713
|
"src/contradictions/schema.ts"() {
|
|
2396
2714
|
"use strict";
|
|
2397
|
-
ContradictionTypeSchema =
|
|
2715
|
+
ContradictionTypeSchema = z8.enum([
|
|
2398
2716
|
"direct_conflict",
|
|
2399
2717
|
"scope_conflict",
|
|
2400
2718
|
"temporal_conflict",
|
|
@@ -2402,37 +2720,37 @@ var init_schema7 = __esm({
|
|
|
2402
2720
|
"evidence_conflict",
|
|
2403
2721
|
"overgeneralization_risk"
|
|
2404
2722
|
]);
|
|
2405
|
-
SeveritySchema =
|
|
2406
|
-
OverlapAssessmentSchema =
|
|
2723
|
+
SeveritySchema = z8.enum(["low", "medium", "high", "blocking"]);
|
|
2724
|
+
OverlapAssessmentSchema = z8.enum([
|
|
2407
2725
|
"fully_overlapping",
|
|
2408
2726
|
"partially_overlapping",
|
|
2409
2727
|
"non_overlapping",
|
|
2410
2728
|
"unknown"
|
|
2411
2729
|
]);
|
|
2412
|
-
ContradictionDetectorSchema =
|
|
2413
|
-
ContradictionStatusSchema =
|
|
2730
|
+
ContradictionDetectorSchema = z8.enum(["heuristic", "ollama-intern"]);
|
|
2731
|
+
ContradictionStatusSchema = z8.enum([
|
|
2414
2732
|
"unresolved",
|
|
2415
2733
|
"reconciled",
|
|
2416
2734
|
"preserved_deliberately",
|
|
2417
2735
|
"rejected"
|
|
2418
2736
|
]);
|
|
2419
|
-
ContradictionConfidenceSchema =
|
|
2420
|
-
ContradictionSchema =
|
|
2421
|
-
contradiction_id:
|
|
2422
|
-
section_id:
|
|
2423
|
-
claim_ids:
|
|
2424
|
-
source_ids:
|
|
2737
|
+
ContradictionConfidenceSchema = z8.enum(["low", "medium", "high"]);
|
|
2738
|
+
ContradictionSchema = z8.object({
|
|
2739
|
+
contradiction_id: z8.string().regex(/^cnt_[a-f0-9]{12}_(heuristic|ollama_intern)$/),
|
|
2740
|
+
section_id: z8.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2741
|
+
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"),
|
|
2742
|
+
source_ids: z8.array(z8.string().regex(/^src_[a-f0-9]{12}$/)).min(1),
|
|
2425
2743
|
type: ContradictionTypeSchema,
|
|
2426
|
-
summary:
|
|
2427
|
-
scope_analysis:
|
|
2744
|
+
summary: z8.string().min(1),
|
|
2745
|
+
scope_analysis: z8.string(),
|
|
2428
2746
|
overlap_assessment: OverlapAssessmentSchema,
|
|
2429
2747
|
severity: SeveritySchema,
|
|
2430
2748
|
confidence: ContradictionConfidenceSchema,
|
|
2431
2749
|
detector: ContradictionDetectorSchema,
|
|
2432
|
-
detection_method:
|
|
2433
|
-
evidence:
|
|
2750
|
+
detection_method: z8.string().min(1),
|
|
2751
|
+
evidence: z8.string(),
|
|
2434
2752
|
status: ContradictionStatusSchema,
|
|
2435
|
-
created_at:
|
|
2753
|
+
created_at: z8.string()
|
|
2436
2754
|
});
|
|
2437
2755
|
}
|
|
2438
2756
|
});
|
|
@@ -2877,12 +3195,12 @@ var init_markdown = __esm({
|
|
|
2877
3195
|
});
|
|
2878
3196
|
|
|
2879
3197
|
// src/triage/schema.ts
|
|
2880
|
-
import { z as
|
|
3198
|
+
import { z as z9 } from "zod";
|
|
2881
3199
|
var TriageDecisionSchema, ClaimTriageSchema, TriageSummarySchema;
|
|
2882
3200
|
var init_schema8 = __esm({
|
|
2883
3201
|
"src/triage/schema.ts"() {
|
|
2884
3202
|
"use strict";
|
|
2885
|
-
TriageDecisionSchema =
|
|
3203
|
+
TriageDecisionSchema = z9.enum([
|
|
2886
3204
|
// Passes triage and is forwarded to review.
|
|
2887
3205
|
"selected_for_review",
|
|
2888
3206
|
// Parked: kept on the canonical ledger as research truth, but not advanced.
|
|
@@ -2894,36 +3212,36 @@ var init_schema8 = __esm({
|
|
|
2894
3212
|
"needs_scope_repair",
|
|
2895
3213
|
"needs_human_review"
|
|
2896
3214
|
]);
|
|
2897
|
-
ClaimTriageSchema =
|
|
2898
|
-
triage_id:
|
|
2899
|
-
claim_id:
|
|
2900
|
-
section_id:
|
|
3215
|
+
ClaimTriageSchema = z9.object({
|
|
3216
|
+
triage_id: z9.string().regex(/^tri_[a-f0-9]{12}$/),
|
|
3217
|
+
claim_id: z9.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
|
|
3218
|
+
section_id: z9.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
2901
3219
|
decision: TriageDecisionSchema,
|
|
2902
|
-
reason:
|
|
3220
|
+
reason: z9.string().min(1),
|
|
2903
3221
|
// Rank among selected_for_review claims (1 = highest priority). null for
|
|
2904
3222
|
// any non-selected decision.
|
|
2905
|
-
rank:
|
|
3223
|
+
rank: z9.number().int().positive().nullable(),
|
|
2906
3224
|
// Quality score [0..1] used to sort. Stable. Higher = better.
|
|
2907
|
-
quality_score:
|
|
2908
|
-
triage_method:
|
|
2909
|
-
created_at:
|
|
2910
|
-
});
|
|
2911
|
-
TriageSummarySchema =
|
|
2912
|
-
summary_id:
|
|
2913
|
-
section_id:
|
|
2914
|
-
triaged_at:
|
|
2915
|
-
research_os_version:
|
|
2916
|
-
triage_method:
|
|
2917
|
-
candidate_claims:
|
|
2918
|
-
decisions:
|
|
2919
|
-
per_source_cap:
|
|
2920
|
-
duplicate_clusters_collapsed:
|
|
2921
|
-
selected_count:
|
|
2922
|
-
selected_per_source:
|
|
2923
|
-
|
|
2924
|
-
source_id:
|
|
2925
|
-
selected:
|
|
2926
|
-
total:
|
|
3225
|
+
quality_score: z9.number().min(0).max(1),
|
|
3226
|
+
triage_method: z9.string().min(1),
|
|
3227
|
+
created_at: z9.string()
|
|
3228
|
+
});
|
|
3229
|
+
TriageSummarySchema = z9.object({
|
|
3230
|
+
summary_id: z9.string().regex(/^tris_[0-9]+_[a-z0-9-]+$/),
|
|
3231
|
+
section_id: z9.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
3232
|
+
triaged_at: z9.string(),
|
|
3233
|
+
research_os_version: z9.string(),
|
|
3234
|
+
triage_method: z9.string(),
|
|
3235
|
+
candidate_claims: z9.number().int().nonnegative(),
|
|
3236
|
+
decisions: z9.record(TriageDecisionSchema, z9.number().int().nonnegative()),
|
|
3237
|
+
per_source_cap: z9.number().int().positive(),
|
|
3238
|
+
duplicate_clusters_collapsed: z9.number().int().nonnegative(),
|
|
3239
|
+
selected_count: z9.number().int().nonnegative(),
|
|
3240
|
+
selected_per_source: z9.array(
|
|
3241
|
+
z9.object({
|
|
3242
|
+
source_id: z9.string().regex(/^src_[a-f0-9]{12}$/),
|
|
3243
|
+
selected: z9.number().int().nonnegative(),
|
|
3244
|
+
total: z9.number().int().nonnegative()
|
|
2927
3245
|
})
|
|
2928
3246
|
)
|
|
2929
3247
|
});
|
|
@@ -2936,10 +3254,10 @@ __export(run_exports, {
|
|
|
2936
3254
|
readTriagedClaimIds: () => readTriagedClaimIds,
|
|
2937
3255
|
triage: () => triage
|
|
2938
3256
|
});
|
|
2939
|
-
import { existsSync as
|
|
3257
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2940
3258
|
import { createHash as createHash2 } from "crypto";
|
|
2941
|
-
import { mkdir as
|
|
2942
|
-
import { join as
|
|
3259
|
+
import { mkdir as mkdir9, readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
|
|
3260
|
+
import { join as join10, resolve as resolve6 } from "path";
|
|
2943
3261
|
function normaliseAssert(s) {
|
|
2944
3262
|
return s.toLowerCase().replace(/[^a-z0-9 ]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2945
3263
|
}
|
|
@@ -2959,9 +3277,9 @@ function makeTriageId(claimId) {
|
|
|
2959
3277
|
return "tri_" + createHash2("sha256").update(claimId).digest("hex").slice(0, 12);
|
|
2960
3278
|
}
|
|
2961
3279
|
async function readClaims2(packPath, sectionId) {
|
|
2962
|
-
const path =
|
|
2963
|
-
if (!
|
|
2964
|
-
const text = await
|
|
3280
|
+
const path = join10(packPath, "sections", sectionId, "claims.jsonl");
|
|
3281
|
+
if (!existsSync9(path)) return [];
|
|
3282
|
+
const text = await readFile9(path, "utf8");
|
|
2965
3283
|
const out = [];
|
|
2966
3284
|
for (const line of text.split(/\r?\n/)) {
|
|
2967
3285
|
if (!line.trim()) continue;
|
|
@@ -2974,8 +3292,8 @@ async function readClaims2(packPath, sectionId) {
|
|
|
2974
3292
|
}
|
|
2975
3293
|
async function triage(options) {
|
|
2976
3294
|
const packPath = options.packPath ? resolve6(options.packPath) : process.cwd();
|
|
2977
|
-
if (!
|
|
2978
|
-
if (!
|
|
3295
|
+
if (!existsSync9(join10(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
3296
|
+
if (!existsSync9(join10(packPath, "sections", options.sectionId)))
|
|
2979
3297
|
throw new SectionNotFoundError(options.sectionId);
|
|
2980
3298
|
const perSourceCap = options.perSourceCap ?? DEFAULT_PER_SOURCE_CAP;
|
|
2981
3299
|
const minAssertChars = options.minAssertChars ?? DEFAULT_MIN_ASSERT_CHARS;
|
|
@@ -3109,13 +3427,13 @@ async function triage(options) {
|
|
|
3109
3427
|
total
|
|
3110
3428
|
})).sort((a, b) => b.total - a.total)
|
|
3111
3429
|
});
|
|
3112
|
-
const sectionDir =
|
|
3113
|
-
const auditsDir =
|
|
3114
|
-
await
|
|
3115
|
-
await
|
|
3116
|
-
const triageJsonlPath =
|
|
3117
|
-
const triageMarkdownPath =
|
|
3118
|
-
const summaryJsonPath =
|
|
3430
|
+
const sectionDir = join10(packPath, "sections", options.sectionId);
|
|
3431
|
+
const auditsDir = join10(packPath, "audits");
|
|
3432
|
+
await mkdir9(sectionDir, { recursive: true });
|
|
3433
|
+
await mkdir9(auditsDir, { recursive: true });
|
|
3434
|
+
const triageJsonlPath = join10(sectionDir, "claim-triage.jsonl");
|
|
3435
|
+
const triageMarkdownPath = join10(sectionDir, "claim-triage.md");
|
|
3436
|
+
const summaryJsonPath = join10(auditsDir, `${options.sectionId}-claim-triage.json`);
|
|
3119
3437
|
await writeFile8(triageJsonlPath, records.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf8");
|
|
3120
3438
|
await writeFile8(triageMarkdownPath, buildMarkdown2(records, summary), "utf8");
|
|
3121
3439
|
await writeFile8(summaryJsonPath, JSON.stringify(summary, null, 2), "utf8");
|
|
@@ -3135,10 +3453,10 @@ async function triage(options) {
|
|
|
3135
3453
|
};
|
|
3136
3454
|
}
|
|
3137
3455
|
async function readTriagedClaimIds(packPath, sectionId) {
|
|
3138
|
-
const path =
|
|
3456
|
+
const path = join10(packPath, "sections", sectionId, "claim-triage.jsonl");
|
|
3139
3457
|
const out = /* @__PURE__ */ new Set();
|
|
3140
|
-
if (!
|
|
3141
|
-
const text = await
|
|
3458
|
+
if (!existsSync9(path)) return out;
|
|
3459
|
+
const text = await readFile9(path, "utf8");
|
|
3142
3460
|
for (const line of text.split(/\r?\n/)) {
|
|
3143
3461
|
if (!line.trim()) continue;
|
|
3144
3462
|
try {
|
|
@@ -3212,13 +3530,13 @@ var init_run2 = __esm({
|
|
|
3212
3530
|
|
|
3213
3531
|
// src/contradictions/map.ts
|
|
3214
3532
|
import { createHash as createHash3 } from "crypto";
|
|
3215
|
-
import { existsSync as
|
|
3216
|
-
import { appendFile as
|
|
3217
|
-
import { join as
|
|
3533
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3534
|
+
import { appendFile as appendFile4, readFile as readFile10, writeFile as writeFile9 } from "fs/promises";
|
|
3535
|
+
import { join as join11, resolve as resolve7 } from "path";
|
|
3218
3536
|
async function readCandidateClaims(packPath, sectionId) {
|
|
3219
|
-
const path =
|
|
3220
|
-
if (!
|
|
3221
|
-
const text = await
|
|
3537
|
+
const path = join11(packPath, "sections", sectionId, "claims.jsonl");
|
|
3538
|
+
if (!existsSync10(path)) return [];
|
|
3539
|
+
const text = await readFile10(path, "utf8");
|
|
3222
3540
|
const claims = [];
|
|
3223
3541
|
for (const line of text.split(/\r?\n/)) {
|
|
3224
3542
|
if (!line.trim()) continue;
|
|
@@ -3229,9 +3547,9 @@ async function readCandidateClaims(packPath, sectionId) {
|
|
|
3229
3547
|
return claims;
|
|
3230
3548
|
}
|
|
3231
3549
|
async function readExistingContradictions(packPath, sectionId) {
|
|
3232
|
-
const path =
|
|
3233
|
-
if (!
|
|
3234
|
-
const text = await
|
|
3550
|
+
const path = join11(packPath, "sections", sectionId, "contradictions.jsonl");
|
|
3551
|
+
if (!existsSync10(path)) return [];
|
|
3552
|
+
const text = await readFile10(path, "utf8");
|
|
3235
3553
|
const list = [];
|
|
3236
3554
|
for (const line of text.split(/\r?\n/)) {
|
|
3237
3555
|
if (!line.trim()) continue;
|
|
@@ -3316,9 +3634,9 @@ async function resolveDetector(options) {
|
|
|
3316
3634
|
}
|
|
3317
3635
|
async function map(options) {
|
|
3318
3636
|
const packPath = options.packPath ? resolve7(options.packPath) : process.cwd();
|
|
3319
|
-
if (!
|
|
3320
|
-
const sectionDir =
|
|
3321
|
-
if (!
|
|
3637
|
+
if (!existsSync10(join11(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
3638
|
+
const sectionDir = join11(packPath, "sections", options.sectionId);
|
|
3639
|
+
if (!existsSync10(sectionDir)) throw new SectionNotFoundError(options.sectionId);
|
|
3322
3640
|
let candidateClaims = await readCandidateClaims(packPath, options.sectionId);
|
|
3323
3641
|
if (options.triagedOnly) {
|
|
3324
3642
|
const { readTriagedClaimIds: readTriagedClaimIds2 } = await Promise.resolve().then(() => (init_run2(), run_exports));
|
|
@@ -3338,8 +3656,8 @@ async function map(options) {
|
|
|
3338
3656
|
detectorError: null,
|
|
3339
3657
|
detectorAnnouncement: announcement
|
|
3340
3658
|
};
|
|
3341
|
-
const ledgerPath =
|
|
3342
|
-
const mdPath =
|
|
3659
|
+
const ledgerPath = join11(sectionDir, "contradictions.jsonl");
|
|
3660
|
+
const mdPath = join11(sectionDir, "contradictions.md");
|
|
3343
3661
|
const existingContradictions = await readExistingContradictions(
|
|
3344
3662
|
packPath,
|
|
3345
3663
|
options.sectionId
|
|
@@ -3384,7 +3702,7 @@ async function map(options) {
|
|
|
3384
3702
|
summary.contradictionsDeduped += 1;
|
|
3385
3703
|
continue;
|
|
3386
3704
|
}
|
|
3387
|
-
await
|
|
3705
|
+
await appendFile4(ledgerPath, JSON.stringify(c) + "\n", "utf8");
|
|
3388
3706
|
existingIds.add(c.contradiction_id);
|
|
3389
3707
|
existingContradictions.push(c);
|
|
3390
3708
|
summary.contradictionsAdded += 1;
|
|
@@ -3420,31 +3738,31 @@ var init_map = __esm({
|
|
|
3420
3738
|
});
|
|
3421
3739
|
|
|
3422
3740
|
// src/contradictions/resolution-schema.ts
|
|
3423
|
-
import { z as
|
|
3741
|
+
import { z as z10 } from "zod";
|
|
3424
3742
|
var ResolutionStatusSchema, ContradictionResolutionSchema;
|
|
3425
3743
|
var init_resolution_schema = __esm({
|
|
3426
3744
|
"src/contradictions/resolution-schema.ts"() {
|
|
3427
3745
|
"use strict";
|
|
3428
|
-
ResolutionStatusSchema =
|
|
3746
|
+
ResolutionStatusSchema = z10.enum([
|
|
3429
3747
|
"unresolved",
|
|
3430
3748
|
"resolved",
|
|
3431
3749
|
"preserved",
|
|
3432
3750
|
"rejected"
|
|
3433
3751
|
]);
|
|
3434
|
-
ContradictionResolutionSchema =
|
|
3435
|
-
contradiction_id:
|
|
3752
|
+
ContradictionResolutionSchema = z10.object({
|
|
3753
|
+
contradiction_id: z10.string().min(1),
|
|
3436
3754
|
status: ResolutionStatusSchema,
|
|
3437
|
-
reason:
|
|
3438
|
-
resolved_at:
|
|
3439
|
-
resolved_by:
|
|
3755
|
+
reason: z10.string().min(4),
|
|
3756
|
+
resolved_at: z10.string().min(1),
|
|
3757
|
+
resolved_by: z10.string().min(1)
|
|
3440
3758
|
});
|
|
3441
3759
|
}
|
|
3442
3760
|
});
|
|
3443
3761
|
|
|
3444
3762
|
// src/contradictions/resolve.ts
|
|
3445
|
-
import { appendFile as
|
|
3446
|
-
import { existsSync as
|
|
3447
|
-
import { join as
|
|
3763
|
+
import { appendFile as appendFile5, readFile as readFile11 } from "fs/promises";
|
|
3764
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3765
|
+
import { join as join12 } from "path";
|
|
3448
3766
|
function latestEffectiveStatuses(ledgerPath, text) {
|
|
3449
3767
|
const entries = [];
|
|
3450
3768
|
for (const line of text.split(/\r?\n/)) {
|
|
@@ -3471,15 +3789,15 @@ async function resolve8(options) {
|
|
|
3471
3789
|
if (reason.length < 4) {
|
|
3472
3790
|
throw new Error("reason must be at least 4 characters");
|
|
3473
3791
|
}
|
|
3474
|
-
const sectionDir =
|
|
3475
|
-
if (!
|
|
3792
|
+
const sectionDir = join12(packPath, "sections", sectionId);
|
|
3793
|
+
if (!existsSync11(sectionDir)) {
|
|
3476
3794
|
throw new SectionNotFoundError(sectionId);
|
|
3477
3795
|
}
|
|
3478
|
-
const candidatesPath2 =
|
|
3479
|
-
const ledgerPath =
|
|
3796
|
+
const candidatesPath2 = join12(sectionDir, "contradictions.jsonl");
|
|
3797
|
+
const ledgerPath = join12(sectionDir, "contradiction-resolutions.jsonl");
|
|
3480
3798
|
const candidates = [];
|
|
3481
|
-
if (
|
|
3482
|
-
const text = await
|
|
3799
|
+
if (existsSync11(candidatesPath2)) {
|
|
3800
|
+
const text = await readFile11(candidatesPath2, "utf8");
|
|
3483
3801
|
for (const line of text.split(/\r?\n/)) {
|
|
3484
3802
|
if (!line.trim()) continue;
|
|
3485
3803
|
try {
|
|
@@ -3494,7 +3812,7 @@ async function resolve8(options) {
|
|
|
3494
3812
|
}
|
|
3495
3813
|
}
|
|
3496
3814
|
}
|
|
3497
|
-
const existingResolutions =
|
|
3815
|
+
const existingResolutions = existsSync11(ledgerPath) ? latestEffectiveStatuses(ledgerPath, await readFile11(ledgerPath, "utf8")) : /* @__PURE__ */ new Map();
|
|
3498
3816
|
const knownIds = new Set(candidates.map((c) => c.contradiction_id));
|
|
3499
3817
|
let targetIds;
|
|
3500
3818
|
if (all) {
|
|
@@ -3526,7 +3844,7 @@ async function resolve8(options) {
|
|
|
3526
3844
|
applied++;
|
|
3527
3845
|
}
|
|
3528
3846
|
if (lines.length > 0) {
|
|
3529
|
-
await
|
|
3847
|
+
await appendFile5(ledgerPath, lines.join("\n") + "\n", "utf8");
|
|
3530
3848
|
}
|
|
3531
3849
|
return { sectionId, applied, skipped, ledgerPath };
|
|
3532
3850
|
}
|
|
@@ -3553,12 +3871,12 @@ var init_contradictions = __esm({
|
|
|
3553
3871
|
});
|
|
3554
3872
|
|
|
3555
3873
|
// src/review/schema.ts
|
|
3556
|
-
import { z as
|
|
3874
|
+
import { z as z11 } from "zod";
|
|
3557
3875
|
var FindingCategorySchema, FindingSeveritySchema, ReviewerNameSchema, ReviewDecisionSchema, ReviewConfidenceSchema, ReviewFindingSchema, ClaimReviewSchema, ReviewSnapshotSchema;
|
|
3558
3876
|
var init_schema9 = __esm({
|
|
3559
3877
|
"src/review/schema.ts"() {
|
|
3560
3878
|
"use strict";
|
|
3561
|
-
FindingCategorySchema =
|
|
3879
|
+
FindingCategorySchema = z11.enum([
|
|
3562
3880
|
"unsupported_claim",
|
|
3563
3881
|
"ungrounded_excerpt",
|
|
3564
3882
|
"stale_claim",
|
|
@@ -3585,9 +3903,9 @@ var init_schema9 = __esm({
|
|
|
3585
3903
|
// a low-value claim can appear in a perfectly-sized section.
|
|
3586
3904
|
"valid_but_low_value"
|
|
3587
3905
|
]);
|
|
3588
|
-
FindingSeveritySchema =
|
|
3589
|
-
ReviewerNameSchema =
|
|
3590
|
-
ReviewDecisionSchema =
|
|
3906
|
+
FindingSeveritySchema = z11.enum(["info", "warn", "block"]);
|
|
3907
|
+
ReviewerNameSchema = z11.enum(["heuristic", "ollama-intern"]);
|
|
3908
|
+
ReviewDecisionSchema = z11.enum([
|
|
3591
3909
|
"accepted_for_synthesis",
|
|
3592
3910
|
"rejected",
|
|
3593
3911
|
"needs_scope_repair",
|
|
@@ -3595,43 +3913,43 @@ var init_schema9 = __esm({
|
|
|
3595
3913
|
"needs_contradiction_mapping",
|
|
3596
3914
|
"needs_human_review"
|
|
3597
3915
|
]);
|
|
3598
|
-
ReviewConfidenceSchema =
|
|
3599
|
-
ReviewFindingSchema =
|
|
3600
|
-
finding_id:
|
|
3601
|
-
section_id:
|
|
3602
|
-
claim_ids:
|
|
3603
|
-
source_ids:
|
|
3916
|
+
ReviewConfidenceSchema = z11.enum(["low", "medium", "high"]);
|
|
3917
|
+
ReviewFindingSchema = z11.object({
|
|
3918
|
+
finding_id: z11.string().regex(/^fnd_[a-f0-9]{12}$/),
|
|
3919
|
+
section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
3920
|
+
claim_ids: z11.array(z11.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)),
|
|
3921
|
+
source_ids: z11.array(z11.string().regex(/^src_[a-f0-9]{12}$/)),
|
|
3604
3922
|
category: FindingCategorySchema,
|
|
3605
3923
|
severity: FindingSeveritySchema,
|
|
3606
|
-
summary:
|
|
3607
|
-
evidence:
|
|
3608
|
-
required_action:
|
|
3924
|
+
summary: z11.string().min(1),
|
|
3925
|
+
evidence: z11.string(),
|
|
3926
|
+
required_action: z11.string(),
|
|
3609
3927
|
reviewer: ReviewerNameSchema,
|
|
3610
|
-
review_method:
|
|
3928
|
+
review_method: z11.string().min(1),
|
|
3611
3929
|
confidence: ReviewConfidenceSchema,
|
|
3612
|
-
created_at:
|
|
3930
|
+
created_at: z11.string()
|
|
3613
3931
|
});
|
|
3614
|
-
ClaimReviewSchema =
|
|
3615
|
-
claim_id:
|
|
3932
|
+
ClaimReviewSchema = z11.object({
|
|
3933
|
+
claim_id: z11.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
|
|
3616
3934
|
decision: ReviewDecisionSchema,
|
|
3617
|
-
reason:
|
|
3618
|
-
finding_ids:
|
|
3935
|
+
reason: z11.string().min(1),
|
|
3936
|
+
finding_ids: z11.array(z11.string()),
|
|
3619
3937
|
reviewer: ReviewerNameSchema,
|
|
3620
|
-
review_method:
|
|
3621
|
-
created_at:
|
|
3938
|
+
review_method: z11.string().min(1),
|
|
3939
|
+
created_at: z11.string()
|
|
3622
3940
|
});
|
|
3623
|
-
ReviewSnapshotSchema =
|
|
3624
|
-
section_id:
|
|
3941
|
+
ReviewSnapshotSchema = z11.object({
|
|
3942
|
+
section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
3625
3943
|
reviewer: ReviewerNameSchema,
|
|
3626
|
-
review_method:
|
|
3627
|
-
reviewed_at:
|
|
3628
|
-
candidate_claims:
|
|
3629
|
-
findings:
|
|
3630
|
-
claim_reviews:
|
|
3631
|
-
decision_counts:
|
|
3632
|
-
severity_counts:
|
|
3633
|
-
llm_findings_rejected_ungrounded:
|
|
3634
|
-
promoted_to_reviewed:
|
|
3944
|
+
review_method: z11.string(),
|
|
3945
|
+
reviewed_at: z11.string(),
|
|
3946
|
+
candidate_claims: z11.number().int().nonnegative(),
|
|
3947
|
+
findings: z11.array(ReviewFindingSchema),
|
|
3948
|
+
claim_reviews: z11.array(ClaimReviewSchema),
|
|
3949
|
+
decision_counts: z11.record(ReviewDecisionSchema, z11.number().int().nonnegative()),
|
|
3950
|
+
severity_counts: z11.record(FindingSeveritySchema, z11.number().int().nonnegative()),
|
|
3951
|
+
llm_findings_rejected_ungrounded: z11.number().int().nonnegative(),
|
|
3952
|
+
promoted_to_reviewed: z11.boolean()
|
|
3635
3953
|
});
|
|
3636
3954
|
}
|
|
3637
3955
|
});
|
|
@@ -3641,6 +3959,7 @@ function checkSourceFloor(input) {
|
|
|
3641
3959
|
const cfg = input.research.gates.source_floor;
|
|
3642
3960
|
const results = [];
|
|
3643
3961
|
const cards = input.sources;
|
|
3962
|
+
const sectionCards = cards.filter((c) => c.section_id === input.section.id);
|
|
3644
3963
|
const sourceCount = cards.length;
|
|
3645
3964
|
if (sourceCount < cfg.min_sources) {
|
|
3646
3965
|
results.push({
|
|
@@ -3664,12 +3983,15 @@ function checkSourceFloor(input) {
|
|
|
3664
3983
|
const publishers = new Set(
|
|
3665
3984
|
cards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
3666
3985
|
);
|
|
3986
|
+
const sectionPublishers = new Set(
|
|
3987
|
+
sectionCards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
3988
|
+
);
|
|
3667
3989
|
if (publishers.size < cfg.min_independent_publishers) {
|
|
3668
3990
|
results.push({
|
|
3669
3991
|
family: "source_floor",
|
|
3670
3992
|
check: "min_independent_publishers",
|
|
3671
3993
|
status: "fail",
|
|
3672
|
-
detail: `Found ${publishers.size} independent publisher(s); minimum ${cfg.min_independent_publishers} required
|
|
3994
|
+
detail: `Found ${publishers.size} independent publisher(s); minimum ${cfg.min_independent_publishers} required. (pack-wide=${publishers.size}, section-scoped=${sectionPublishers.size})`,
|
|
3673
3995
|
evidence: [...publishers],
|
|
3674
3996
|
blocks_synthesis: true
|
|
3675
3997
|
});
|
|
@@ -3678,18 +4000,19 @@ function checkSourceFloor(input) {
|
|
|
3678
4000
|
family: "source_floor",
|
|
3679
4001
|
check: "min_independent_publishers",
|
|
3680
4002
|
status: "pass",
|
|
3681
|
-
detail: `${publishers.size} independent publisher(s) >= minimum ${cfg.min_independent_publishers}
|
|
4003
|
+
detail: `${publishers.size} independent publisher(s) >= minimum ${cfg.min_independent_publishers}. (pack-wide=${publishers.size}, section-scoped=${sectionPublishers.size})`,
|
|
3682
4004
|
evidence: [],
|
|
3683
4005
|
blocks_synthesis: false
|
|
3684
4006
|
});
|
|
3685
4007
|
}
|
|
3686
4008
|
const primaryCount = cards.filter((c) => c.source_type === "primary").length;
|
|
4009
|
+
const sectionPrimaryCount = sectionCards.filter((c) => c.source_type === "primary").length;
|
|
3687
4010
|
if (primaryCount < cfg.primary_sources_required) {
|
|
3688
4011
|
results.push({
|
|
3689
4012
|
family: "source_floor",
|
|
3690
4013
|
check: "primary_sources_required",
|
|
3691
4014
|
status: "fail",
|
|
3692
|
-
detail: `Found ${primaryCount} primary source(s); minimum ${cfg.primary_sources_required} required. Pre-waiver
|
|
4015
|
+
detail: `Found ${primaryCount} primary source(s); minimum ${cfg.primary_sources_required} required. Pre-waiver. (pack-wide=${primaryCount}, section-scoped=${sectionPrimaryCount})`,
|
|
3693
4016
|
evidence: cards.filter((c) => c.source_type === "primary").map((c) => c.source_id),
|
|
3694
4017
|
blocks_synthesis: true
|
|
3695
4018
|
});
|
|
@@ -3698,7 +4021,7 @@ function checkSourceFloor(input) {
|
|
|
3698
4021
|
family: "source_floor",
|
|
3699
4022
|
check: "primary_sources_required",
|
|
3700
4023
|
status: "pass",
|
|
3701
|
-
detail: `${primaryCount} primary source(s) >= minimum ${cfg.primary_sources_required}
|
|
4024
|
+
detail: `${primaryCount} primary source(s) >= minimum ${cfg.primary_sources_required}. (pack-wide=${primaryCount}, section-scoped=${sectionPrimaryCount})`,
|
|
3702
4025
|
evidence: [],
|
|
3703
4026
|
blocks_synthesis: false
|
|
3704
4027
|
});
|
|
@@ -3852,25 +4175,14 @@ function checkClaimIntegrity(input) {
|
|
|
3852
4175
|
monoClaims.push(claim.claim_id);
|
|
3853
4176
|
}
|
|
3854
4177
|
}
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
});
|
|
3864
|
-
} else {
|
|
3865
|
-
results.push({
|
|
3866
|
-
family: "claim_integrity",
|
|
3867
|
-
check: "no_source_cluster_monopoly",
|
|
3868
|
-
status: "pass",
|
|
3869
|
-
detail: `${monoClaims.length}/${claims.length} claim(s) source from a single publisher (within tolerance).`,
|
|
3870
|
-
evidence: [],
|
|
3871
|
-
blocks_synthesis: false
|
|
3872
|
-
});
|
|
3873
|
-
}
|
|
4178
|
+
results.push({
|
|
4179
|
+
family: "claim_integrity",
|
|
4180
|
+
check: "no_source_cluster_monopoly",
|
|
4181
|
+
status: "pass",
|
|
4182
|
+
detail: `${monoClaims.length}/${claims.length} claim(s) are single-source by architecture (each claim references exactly one source). Publisher diversity is enforced at source-card level via min_independent_publishers.`,
|
|
4183
|
+
evidence: [],
|
|
4184
|
+
blocks_synthesis: false
|
|
4185
|
+
});
|
|
3874
4186
|
}
|
|
3875
4187
|
return results;
|
|
3876
4188
|
}
|
|
@@ -4525,12 +4837,12 @@ var init_markdown2 = __esm({
|
|
|
4525
4837
|
});
|
|
4526
4838
|
|
|
4527
4839
|
// src/gates/schema.ts
|
|
4528
|
-
import { z as
|
|
4840
|
+
import { z as z12 } from "zod";
|
|
4529
4841
|
var GateFamilySchema, GateCheckStatusSchema, VerdictSchema, GateCheckResultSchema, WaiverApplicationSchema, SectionGateResultSchema;
|
|
4530
4842
|
var init_schema10 = __esm({
|
|
4531
4843
|
"src/gates/schema.ts"() {
|
|
4532
4844
|
"use strict";
|
|
4533
|
-
GateFamilySchema =
|
|
4845
|
+
GateFamilySchema = z12.enum([
|
|
4534
4846
|
"source_floor",
|
|
4535
4847
|
"claim_integrity",
|
|
4536
4848
|
"scope_integrity",
|
|
@@ -4540,96 +4852,98 @@ var init_schema10 = __esm({
|
|
|
4540
4852
|
"waivers",
|
|
4541
4853
|
"accepted_claim_floor"
|
|
4542
4854
|
]);
|
|
4543
|
-
GateCheckStatusSchema =
|
|
4855
|
+
GateCheckStatusSchema = z12.enum([
|
|
4544
4856
|
"pass",
|
|
4545
4857
|
"warn",
|
|
4546
4858
|
"fail",
|
|
4547
4859
|
"pass_with_waiver",
|
|
4548
4860
|
"warn_with_waiver"
|
|
4549
4861
|
]);
|
|
4550
|
-
VerdictSchema =
|
|
4551
|
-
GateCheckResultSchema =
|
|
4862
|
+
VerdictSchema = z12.enum(["pass", "warn", "fail", "blocked"]);
|
|
4863
|
+
GateCheckResultSchema = z12.object({
|
|
4552
4864
|
family: GateFamilySchema,
|
|
4553
|
-
check:
|
|
4865
|
+
check: z12.string().min(1),
|
|
4554
4866
|
status: GateCheckStatusSchema,
|
|
4555
|
-
detail:
|
|
4556
|
-
evidence:
|
|
4557
|
-
blocks_synthesis:
|
|
4867
|
+
detail: z12.string(),
|
|
4868
|
+
evidence: z12.array(z12.string()),
|
|
4869
|
+
blocks_synthesis: z12.boolean()
|
|
4558
4870
|
});
|
|
4559
|
-
WaiverApplicationSchema =
|
|
4871
|
+
WaiverApplicationSchema = z12.object({
|
|
4560
4872
|
family: GateFamilySchema,
|
|
4561
|
-
check:
|
|
4562
|
-
reason:
|
|
4563
|
-
compensating_controls:
|
|
4873
|
+
check: z12.string().min(1),
|
|
4874
|
+
reason: z12.string().min(1),
|
|
4875
|
+
compensating_controls: z12.array(z12.string()),
|
|
4564
4876
|
original_status: GateCheckStatusSchema,
|
|
4565
4877
|
new_status: GateCheckStatusSchema
|
|
4566
4878
|
});
|
|
4567
|
-
SectionGateResultSchema =
|
|
4568
|
-
section_id:
|
|
4879
|
+
SectionGateResultSchema = z12.object({
|
|
4880
|
+
section_id: z12.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
4569
4881
|
verdict: VerdictSchema,
|
|
4570
|
-
summary:
|
|
4571
|
-
checked_at:
|
|
4572
|
-
synthesis_eligible:
|
|
4573
|
-
gate_results:
|
|
4574
|
-
failures:
|
|
4575
|
-
warnings:
|
|
4576
|
-
waivers_applied:
|
|
4577
|
-
blocking_reasons:
|
|
4578
|
-
claim_counts:
|
|
4579
|
-
total:
|
|
4580
|
-
candidate:
|
|
4581
|
-
with_evidence_excerpt:
|
|
4582
|
-
with_source_hashes:
|
|
4583
|
-
with_scope:
|
|
4584
|
-
with_not:
|
|
4585
|
-
universal_scope_null:
|
|
4586
|
-
orphans:
|
|
4882
|
+
summary: z12.string(),
|
|
4883
|
+
checked_at: z12.string(),
|
|
4884
|
+
synthesis_eligible: z12.boolean(),
|
|
4885
|
+
gate_results: z12.array(GateCheckResultSchema),
|
|
4886
|
+
failures: z12.array(GateCheckResultSchema),
|
|
4887
|
+
warnings: z12.array(GateCheckResultSchema),
|
|
4888
|
+
waivers_applied: z12.array(WaiverApplicationSchema),
|
|
4889
|
+
blocking_reasons: z12.array(z12.string()),
|
|
4890
|
+
claim_counts: z12.object({
|
|
4891
|
+
total: z12.number().int().nonnegative(),
|
|
4892
|
+
candidate: z12.number().int().nonnegative(),
|
|
4893
|
+
with_evidence_excerpt: z12.number().int().nonnegative(),
|
|
4894
|
+
with_source_hashes: z12.number().int().nonnegative(),
|
|
4895
|
+
with_scope: z12.number().int().nonnegative(),
|
|
4896
|
+
with_not: z12.number().int().nonnegative(),
|
|
4897
|
+
universal_scope_null: z12.number().int().nonnegative(),
|
|
4898
|
+
orphans: z12.number().int().nonnegative()
|
|
4587
4899
|
}),
|
|
4588
|
-
source_counts:
|
|
4589
|
-
total:
|
|
4590
|
-
primary:
|
|
4591
|
-
secondary:
|
|
4592
|
-
forum:
|
|
4593
|
-
benchmark:
|
|
4594
|
-
docs:
|
|
4595
|
-
unknown:
|
|
4596
|
-
independent_publishers:
|
|
4597
|
-
failed_fetches:
|
|
4900
|
+
source_counts: z12.object({
|
|
4901
|
+
total: z12.number().int().nonnegative(),
|
|
4902
|
+
primary: z12.number().int().nonnegative(),
|
|
4903
|
+
secondary: z12.number().int().nonnegative(),
|
|
4904
|
+
forum: z12.number().int().nonnegative(),
|
|
4905
|
+
benchmark: z12.number().int().nonnegative(),
|
|
4906
|
+
docs: z12.number().int().nonnegative(),
|
|
4907
|
+
unknown: z12.number().int().nonnegative(),
|
|
4908
|
+
independent_publishers: z12.number().int().nonnegative(),
|
|
4909
|
+
failed_fetches: z12.number().int().nonnegative(),
|
|
4910
|
+
section_primary: z12.number().int().nonnegative(),
|
|
4911
|
+
section_independent_publishers: z12.number().int().nonnegative()
|
|
4598
4912
|
}),
|
|
4599
|
-
contradiction_counts:
|
|
4600
|
-
total:
|
|
4601
|
-
unresolved:
|
|
4602
|
-
blocking:
|
|
4603
|
-
by_type:
|
|
4913
|
+
contradiction_counts: z12.object({
|
|
4914
|
+
total: z12.number().int().nonnegative(),
|
|
4915
|
+
unresolved: z12.number().int().nonnegative(),
|
|
4916
|
+
blocking: z12.number().int().nonnegative(),
|
|
4917
|
+
by_type: z12.record(z12.string(), z12.number().int().nonnegative())
|
|
4604
4918
|
}),
|
|
4605
|
-
freshness_summary:
|
|
4606
|
-
policy_required:
|
|
4607
|
-
max_source_age_months:
|
|
4608
|
-
stale_source_policy:
|
|
4609
|
-
stale_count:
|
|
4610
|
-
unknown_date_count:
|
|
4919
|
+
freshness_summary: z12.object({
|
|
4920
|
+
policy_required: z12.boolean(),
|
|
4921
|
+
max_source_age_months: z12.number().int().nullable(),
|
|
4922
|
+
stale_source_policy: z12.enum(["warn", "fail"]),
|
|
4923
|
+
stale_count: z12.number().int().nonnegative(),
|
|
4924
|
+
unknown_date_count: z12.number().int().nonnegative()
|
|
4611
4925
|
}),
|
|
4612
|
-
scope_integrity_summary:
|
|
4613
|
-
universal_claims:
|
|
4614
|
-
scoped_claims:
|
|
4615
|
-
with_not_constraint:
|
|
4616
|
-
overgen_risks_total:
|
|
4617
|
-
overgen_risks_blocking:
|
|
4926
|
+
scope_integrity_summary: z12.object({
|
|
4927
|
+
universal_claims: z12.number().int().nonnegative(),
|
|
4928
|
+
scoped_claims: z12.number().int().nonnegative(),
|
|
4929
|
+
with_not_constraint: z12.number().int().nonnegative(),
|
|
4930
|
+
overgen_risks_total: z12.number().int().nonnegative(),
|
|
4931
|
+
overgen_risks_blocking: z12.number().int().nonnegative()
|
|
4618
4932
|
}),
|
|
4619
|
-
next_actions:
|
|
4933
|
+
next_actions: z12.array(z12.string())
|
|
4620
4934
|
});
|
|
4621
4935
|
}
|
|
4622
4936
|
});
|
|
4623
4937
|
|
|
4624
4938
|
// src/gates/run.ts
|
|
4625
|
-
import { existsSync as
|
|
4626
|
-
import { mkdir as
|
|
4627
|
-
import { join as
|
|
4939
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4940
|
+
import { mkdir as mkdir10, readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
|
|
4941
|
+
import { join as join13, resolve as resolve9 } from "path";
|
|
4628
4942
|
import { parse as yamlParse2, stringify as yamlStringify3 } from "yaml";
|
|
4629
4943
|
async function readJsonl(packPath, rel, parse) {
|
|
4630
|
-
const path =
|
|
4631
|
-
if (!
|
|
4632
|
-
const text = await
|
|
4944
|
+
const path = join13(packPath, rel);
|
|
4945
|
+
if (!existsSync12(path)) return [];
|
|
4946
|
+
const text = await readFile12(path, "utf8");
|
|
4633
4947
|
const out = [];
|
|
4634
4948
|
for (const line of text.split(/\r?\n/)) {
|
|
4635
4949
|
if (!line.trim()) continue;
|
|
@@ -4638,14 +4952,14 @@ async function readJsonl(packPath, rel, parse) {
|
|
|
4638
4952
|
return out;
|
|
4639
4953
|
}
|
|
4640
4954
|
async function readSourceCards2(packPath) {
|
|
4641
|
-
const dir =
|
|
4642
|
-
if (!
|
|
4955
|
+
const dir = join13(packPath, "evidence", "source-cards");
|
|
4956
|
+
if (!existsSync12(dir)) return [];
|
|
4643
4957
|
const { readdir: readdir4 } = await import("fs/promises");
|
|
4644
4958
|
const entries = await readdir4(dir);
|
|
4645
4959
|
const cards = [];
|
|
4646
4960
|
for (const entry of entries) {
|
|
4647
4961
|
if (!entry.endsWith(".json")) continue;
|
|
4648
|
-
const text = await
|
|
4962
|
+
const text = await readFile12(join13(dir, entry), "utf8");
|
|
4649
4963
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
4650
4964
|
}
|
|
4651
4965
|
return cards;
|
|
@@ -4667,10 +4981,14 @@ function summarizeClaimCounts(input) {
|
|
|
4667
4981
|
}
|
|
4668
4982
|
function summarizeSourceCounts(input) {
|
|
4669
4983
|
const cards = input.sources;
|
|
4984
|
+
const sectionCards = cards.filter((c) => c.section_id === input.section.id);
|
|
4670
4985
|
const failed = input.receipts.filter((r) => r.fetch_outcome !== "ok").length;
|
|
4671
4986
|
const publishers = new Set(
|
|
4672
4987
|
cards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
4673
4988
|
);
|
|
4989
|
+
const sectionPublishers = new Set(
|
|
4990
|
+
sectionCards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
4991
|
+
);
|
|
4674
4992
|
return {
|
|
4675
4993
|
total: cards.length,
|
|
4676
4994
|
primary: cards.filter((c) => c.source_type === "primary").length,
|
|
@@ -4680,7 +4998,9 @@ function summarizeSourceCounts(input) {
|
|
|
4680
4998
|
docs: cards.filter((c) => c.source_type === "docs").length,
|
|
4681
4999
|
unknown: cards.filter((c) => c.source_type === "unknown").length,
|
|
4682
5000
|
independent_publishers: publishers.size,
|
|
4683
|
-
failed_fetches: failed
|
|
5001
|
+
failed_fetches: failed,
|
|
5002
|
+
section_primary: sectionCards.filter((c) => c.source_type === "primary").length,
|
|
5003
|
+
section_independent_publishers: sectionPublishers.size
|
|
4684
5004
|
};
|
|
4685
5005
|
}
|
|
4686
5006
|
function buildEffectiveStatuses3(resolutions) {
|
|
@@ -4814,14 +5134,14 @@ function buildNextActions(results) {
|
|
|
4814
5134
|
return Array.from(new Set(actions));
|
|
4815
5135
|
}
|
|
4816
5136
|
async function loadResearchYaml2(packPath) {
|
|
4817
|
-
const yamlPath =
|
|
4818
|
-
if (!
|
|
4819
|
-
const text = await
|
|
5137
|
+
const yamlPath = join13(packPath, "research.yaml");
|
|
5138
|
+
if (!existsSync12(yamlPath)) throw new PackNotFoundError(packPath);
|
|
5139
|
+
const text = await readFile12(yamlPath, "utf8");
|
|
4820
5140
|
return ResearchYamlSchema.parse(yamlParse2(text));
|
|
4821
5141
|
}
|
|
4822
5142
|
async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
|
|
4823
|
-
const yamlPath =
|
|
4824
|
-
const text = await
|
|
5143
|
+
const yamlPath = join13(packPath, "research.yaml");
|
|
5144
|
+
const text = await readFile12(yamlPath, "utf8");
|
|
4825
5145
|
const research = ResearchYamlSchema.parse(yamlParse2(text));
|
|
4826
5146
|
const idx = research.sections.findIndex((s) => s.id === sectionId);
|
|
4827
5147
|
if (idx < 0) return;
|
|
@@ -4838,9 +5158,9 @@ async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
|
|
|
4838
5158
|
}
|
|
4839
5159
|
async function gate(options) {
|
|
4840
5160
|
const packPath = options.packPath ? resolve9(options.packPath) : process.cwd();
|
|
4841
|
-
if (!
|
|
4842
|
-
const sectionDir =
|
|
4843
|
-
if (!
|
|
5161
|
+
if (!existsSync12(join13(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
5162
|
+
const sectionDir = join13(packPath, "sections", options.sectionId);
|
|
5163
|
+
if (!existsSync12(sectionDir)) throw new SectionNotFoundError(options.sectionId);
|
|
4844
5164
|
const research = await loadResearchYaml2(packPath);
|
|
4845
5165
|
const section = research.sections.find((s) => s.id === options.sectionId);
|
|
4846
5166
|
if (!section) throw new SectionNotFoundError(options.sectionId);
|
|
@@ -4894,15 +5214,15 @@ async function gate(options) {
|
|
|
4894
5214
|
scope_integrity_summary: summarizeScopeIntegrity(input),
|
|
4895
5215
|
next_actions: buildNextActions(finalResults)
|
|
4896
5216
|
});
|
|
4897
|
-
const auditsDir =
|
|
4898
|
-
await
|
|
5217
|
+
const auditsDir = join13(packPath, "audits");
|
|
5218
|
+
await mkdir10(auditsDir, { recursive: true });
|
|
4899
5219
|
await writeFile10(
|
|
4900
|
-
|
|
5220
|
+
join13(auditsDir, `${options.sectionId}-gate.json`),
|
|
4901
5221
|
JSON.stringify(result, null, 2),
|
|
4902
5222
|
"utf8"
|
|
4903
5223
|
);
|
|
4904
5224
|
await writeFile10(
|
|
4905
|
-
|
|
5225
|
+
join13(auditsDir, `${options.sectionId}-gate.md`),
|
|
4906
5226
|
renderGateMarkdown(result),
|
|
4907
5227
|
"utf8"
|
|
4908
5228
|
);
|
|
@@ -4944,21 +5264,21 @@ var init_gates = __esm({
|
|
|
4944
5264
|
});
|
|
4945
5265
|
|
|
4946
5266
|
// src/review/profiles.ts
|
|
4947
|
-
import { existsSync as
|
|
4948
|
-
import { mkdir as
|
|
4949
|
-
import { join as
|
|
4950
|
-
import { z as
|
|
5267
|
+
import { existsSync as existsSync13 } from "fs";
|
|
5268
|
+
import { mkdir as mkdir11, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
|
|
5269
|
+
import { join as join14 } from "path";
|
|
5270
|
+
import { z as z13 } from "zod";
|
|
4951
5271
|
function reviewActivePath(packPath, sectionId) {
|
|
4952
|
-
return
|
|
5272
|
+
return join14(packPath, "sections", sectionId, "review-active.json");
|
|
4953
5273
|
}
|
|
4954
5274
|
function profileDir(packPath, sectionId, profile) {
|
|
4955
|
-
return
|
|
5275
|
+
return join14(packPath, "sections", sectionId, "reviews", profile);
|
|
4956
5276
|
}
|
|
4957
5277
|
async function readActiveProfile(packPath, sectionId) {
|
|
4958
5278
|
const path = reviewActivePath(packPath, sectionId);
|
|
4959
|
-
if (!
|
|
5279
|
+
if (!existsSync13(path)) return DEFAULT_PROFILE;
|
|
4960
5280
|
try {
|
|
4961
|
-
const parsed = ReviewActiveSchema.parse(JSON.parse(await
|
|
5281
|
+
const parsed = ReviewActiveSchema.parse(JSON.parse(await readFile13(path, "utf8")));
|
|
4962
5282
|
return parsed.active_profile;
|
|
4963
5283
|
} catch {
|
|
4964
5284
|
return DEFAULT_PROFILE;
|
|
@@ -4966,7 +5286,7 @@ async function readActiveProfile(packPath, sectionId) {
|
|
|
4966
5286
|
}
|
|
4967
5287
|
async function writeActiveProfile(packPath, sectionId, active) {
|
|
4968
5288
|
const path = reviewActivePath(packPath, sectionId);
|
|
4969
|
-
await
|
|
5289
|
+
await mkdir11(join14(packPath, "sections", sectionId), { recursive: true });
|
|
4970
5290
|
await writeFile11(path, JSON.stringify(ReviewActiveSchema.parse(active), null, 2), "utf8");
|
|
4971
5291
|
}
|
|
4972
5292
|
function isValidProfileName(name) {
|
|
@@ -4977,22 +5297,22 @@ var init_profiles = __esm({
|
|
|
4977
5297
|
"src/review/profiles.ts"() {
|
|
4978
5298
|
"use strict";
|
|
4979
5299
|
DEFAULT_PROFILE = "default";
|
|
4980
|
-
PromotionCalibrationSummarySchema =
|
|
4981
|
-
fixture:
|
|
4982
|
-
good_false_positive_rate:
|
|
4983
|
-
bad_any_flag_recall:
|
|
4984
|
-
strict_category_recall:
|
|
4985
|
-
unsupported_claim_recall:
|
|
4986
|
-
notes:
|
|
4987
|
-
});
|
|
4988
|
-
ReviewActiveSchema =
|
|
4989
|
-
active_profile:
|
|
4990
|
-
promoted_at:
|
|
4991
|
-
promoted_method:
|
|
4992
|
-
promoted_reviewer:
|
|
5300
|
+
PromotionCalibrationSummarySchema = z13.object({
|
|
5301
|
+
fixture: z13.string().nullable().default(null),
|
|
5302
|
+
good_false_positive_rate: z13.string().nullable().default(null),
|
|
5303
|
+
bad_any_flag_recall: z13.string().nullable().default(null),
|
|
5304
|
+
strict_category_recall: z13.string().nullable().default(null),
|
|
5305
|
+
unsupported_claim_recall: z13.string().nullable().default(null),
|
|
5306
|
+
notes: z13.string().nullable().default(null)
|
|
5307
|
+
});
|
|
5308
|
+
ReviewActiveSchema = z13.object({
|
|
5309
|
+
active_profile: z13.string().min(1),
|
|
5310
|
+
promoted_at: z13.string(),
|
|
5311
|
+
promoted_method: z13.string(),
|
|
5312
|
+
promoted_reviewer: z13.string(),
|
|
4993
5313
|
// Free-text reason the profile was promoted. Recorded once at promotion
|
|
4994
5314
|
// time; not derived from artifacts. Required.
|
|
4995
|
-
promotion_reason:
|
|
5315
|
+
promotion_reason: z13.string().min(8).default("promoted via review-promote without an explicit reason"),
|
|
4996
5316
|
// Optional calibration evidence captured at promotion time so downstream
|
|
4997
5317
|
// consumers can see WHY the reviewer was trusted.
|
|
4998
5318
|
calibration_summary: PromotionCalibrationSummarySchema.nullable().default(null)
|
|
@@ -5813,14 +6133,14 @@ var init_markdown3 = __esm({
|
|
|
5813
6133
|
|
|
5814
6134
|
// src/review/run.ts
|
|
5815
6135
|
import { createHash as createHash4 } from "crypto";
|
|
5816
|
-
import { existsSync as
|
|
5817
|
-
import { appendFile as
|
|
5818
|
-
import { join as
|
|
6136
|
+
import { existsSync as existsSync14 } from "fs";
|
|
6137
|
+
import { appendFile as appendFile6, mkdir as mkdir12, readFile as readFile14, writeFile as writeFile12 } from "fs/promises";
|
|
6138
|
+
import { join as join15, resolve as resolve10 } from "path";
|
|
5819
6139
|
import { parse as yamlParse3, stringify as yamlStringify4 } from "yaml";
|
|
5820
6140
|
async function readJsonl2(packPath, rel, parse) {
|
|
5821
|
-
const path =
|
|
5822
|
-
if (!
|
|
5823
|
-
const text = await
|
|
6141
|
+
const path = join15(packPath, rel);
|
|
6142
|
+
if (!existsSync14(path)) return [];
|
|
6143
|
+
const text = await readFile14(path, "utf8");
|
|
5824
6144
|
const out = [];
|
|
5825
6145
|
for (const line of text.split(/\r?\n/)) {
|
|
5826
6146
|
if (!line.trim()) continue;
|
|
@@ -5829,37 +6149,37 @@ async function readJsonl2(packPath, rel, parse) {
|
|
|
5829
6149
|
return out;
|
|
5830
6150
|
}
|
|
5831
6151
|
async function readSourceCards3(packPath) {
|
|
5832
|
-
const dir =
|
|
5833
|
-
if (!
|
|
6152
|
+
const dir = join15(packPath, "evidence", "source-cards");
|
|
6153
|
+
if (!existsSync14(dir)) return [];
|
|
5834
6154
|
const { readdir: readdir4 } = await import("fs/promises");
|
|
5835
6155
|
const entries = await readdir4(dir);
|
|
5836
6156
|
const cards = [];
|
|
5837
6157
|
for (const entry of entries) {
|
|
5838
6158
|
if (!entry.endsWith(".json")) continue;
|
|
5839
|
-
const text = await
|
|
6159
|
+
const text = await readFile14(join15(dir, entry), "utf8");
|
|
5840
6160
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
5841
6161
|
}
|
|
5842
6162
|
return cards;
|
|
5843
6163
|
}
|
|
5844
6164
|
async function readGateResult(packPath, sectionId) {
|
|
5845
|
-
const path =
|
|
5846
|
-
if (!
|
|
5847
|
-
const text = await
|
|
6165
|
+
const path = join15(packPath, "audits", `${sectionId}-gate.json`);
|
|
6166
|
+
if (!existsSync14(path)) return null;
|
|
6167
|
+
const text = await readFile14(path, "utf8");
|
|
5848
6168
|
return SectionGateResultSchema.parse(JSON.parse(text));
|
|
5849
6169
|
}
|
|
5850
6170
|
async function readBriefText(packPath, sectionId) {
|
|
5851
|
-
const path =
|
|
5852
|
-
if (!
|
|
5853
|
-
return
|
|
6171
|
+
const path = join15(packPath, "sections", sectionId, "brief.md");
|
|
6172
|
+
if (!existsSync14(path)) return null;
|
|
6173
|
+
return readFile14(path, "utf8");
|
|
5854
6174
|
}
|
|
5855
6175
|
async function readRawTextBySource(packPath, receipts) {
|
|
5856
6176
|
const map2 = /* @__PURE__ */ new Map();
|
|
5857
6177
|
for (const r of receipts) {
|
|
5858
6178
|
if (r.fetch_outcome !== "ok" || !r.raw_text_path) continue;
|
|
5859
|
-
const path =
|
|
5860
|
-
if (!
|
|
6179
|
+
const path = join15(packPath, r.raw_text_path);
|
|
6180
|
+
if (!existsSync14(path)) continue;
|
|
5861
6181
|
if (map2.has(r.source_id)) continue;
|
|
5862
|
-
map2.set(r.source_id, await
|
|
6182
|
+
map2.set(r.source_id, await readFile14(path, "utf8"));
|
|
5863
6183
|
}
|
|
5864
6184
|
return map2;
|
|
5865
6185
|
}
|
|
@@ -5869,8 +6189,8 @@ async function readExcerptsBySource(packPath, claims) {
|
|
|
5869
6189
|
for (const c of claims) for (const sid of c.source_ids) sourceIds.add(sid);
|
|
5870
6190
|
for (const sid of sourceIds) {
|
|
5871
6191
|
const path = ledgerPathFor(packPath, sid);
|
|
5872
|
-
if (!
|
|
5873
|
-
const text = await
|
|
6192
|
+
if (!existsSync14(path)) continue;
|
|
6193
|
+
const text = await readFile14(path, "utf8");
|
|
5874
6194
|
const index = /* @__PURE__ */ new Map();
|
|
5875
6195
|
for (const line of text.split(/\r?\n/)) {
|
|
5876
6196
|
if (!line.trim()) continue;
|
|
@@ -5923,15 +6243,15 @@ function buildFinding(args) {
|
|
|
5923
6243
|
});
|
|
5924
6244
|
}
|
|
5925
6245
|
async function loadResearchYaml3(packPath) {
|
|
5926
|
-
const yamlPath =
|
|
5927
|
-
if (!
|
|
5928
|
-
const text = await
|
|
6246
|
+
const yamlPath = join15(packPath, "research.yaml");
|
|
6247
|
+
if (!existsSync14(yamlPath)) throw new PackNotFoundError(packPath);
|
|
6248
|
+
const text = await readFile14(yamlPath, "utf8");
|
|
5929
6249
|
return ResearchYamlSchema.parse(yamlParse3(text));
|
|
5930
6250
|
}
|
|
5931
6251
|
async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
|
|
5932
6252
|
if (!allAccepted) return false;
|
|
5933
|
-
const yamlPath =
|
|
5934
|
-
const text = await
|
|
6253
|
+
const yamlPath = join15(packPath, "research.yaml");
|
|
6254
|
+
const text = await readFile14(yamlPath, "utf8");
|
|
5935
6255
|
const research = ResearchYamlSchema.parse(yamlParse3(text));
|
|
5936
6256
|
const idx = research.sections.findIndex((s) => s.id === sectionId);
|
|
5937
6257
|
if (idx < 0) return false;
|
|
@@ -5943,9 +6263,9 @@ async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
|
|
|
5943
6263
|
}
|
|
5944
6264
|
async function review(options) {
|
|
5945
6265
|
const packPath = options.packPath ? resolve10(options.packPath) : process.cwd();
|
|
5946
|
-
if (!
|
|
5947
|
-
const sectionDir =
|
|
5948
|
-
if (!
|
|
6266
|
+
if (!existsSync14(join15(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
6267
|
+
const sectionDir = join15(packPath, "sections", options.sectionId);
|
|
6268
|
+
if (!existsSync14(sectionDir)) throw new SectionNotFoundError(options.sectionId);
|
|
5949
6269
|
const research = await loadResearchYaml3(packPath);
|
|
5950
6270
|
const section = research.sections.find((s) => s.id === options.sectionId);
|
|
5951
6271
|
if (!section) throw new SectionNotFoundError(options.sectionId);
|
|
@@ -6195,39 +6515,39 @@ async function finalizeReview(args) {
|
|
|
6195
6515
|
promoted_to_reviewed: promoted
|
|
6196
6516
|
});
|
|
6197
6517
|
const profDir = profileDir(args.packPath, args.sectionId, args.profile);
|
|
6198
|
-
await
|
|
6199
|
-
await writeFile12(
|
|
6200
|
-
await writeFile12(
|
|
6201
|
-
const profileFindingsPath =
|
|
6518
|
+
await mkdir12(profDir, { recursive: true });
|
|
6519
|
+
await writeFile12(join15(profDir, "review.json"), JSON.stringify(snapshot, null, 2), "utf8");
|
|
6520
|
+
await writeFile12(join15(profDir, "review.md"), renderReviewMarkdown(snapshot), "utf8");
|
|
6521
|
+
const profileFindingsPath = join15(profDir, "findings.jsonl");
|
|
6202
6522
|
for (const f of dedupedFindings) {
|
|
6203
|
-
await
|
|
6523
|
+
await appendFile6(profileFindingsPath, JSON.stringify(f) + "\n", "utf8");
|
|
6204
6524
|
}
|
|
6205
|
-
const profileReviewsPath =
|
|
6525
|
+
const profileReviewsPath = join15(profDir, "claim-reviews.jsonl");
|
|
6206
6526
|
for (const r of claimReviews) {
|
|
6207
|
-
await
|
|
6527
|
+
await appendFile6(profileReviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
|
|
6208
6528
|
}
|
|
6209
6529
|
const activeProfile = await readActiveProfile(args.packPath, args.sectionId);
|
|
6210
6530
|
const isActive = args.profile === activeProfile || activeProfile === DEFAULT_PROFILE && args.profile === DEFAULT_PROFILE;
|
|
6211
6531
|
if (isActive) {
|
|
6212
|
-
const auditsDir =
|
|
6213
|
-
await
|
|
6532
|
+
const auditsDir = join15(args.packPath, "audits");
|
|
6533
|
+
await mkdir12(auditsDir, { recursive: true });
|
|
6214
6534
|
await writeFile12(
|
|
6215
|
-
|
|
6535
|
+
join15(auditsDir, `${args.sectionId}-review.json`),
|
|
6216
6536
|
JSON.stringify(snapshot, null, 2),
|
|
6217
6537
|
"utf8"
|
|
6218
6538
|
);
|
|
6219
6539
|
await writeFile12(
|
|
6220
|
-
|
|
6540
|
+
join15(auditsDir, `${args.sectionId}-review.md`),
|
|
6221
6541
|
renderReviewMarkdown(snapshot),
|
|
6222
6542
|
"utf8"
|
|
6223
6543
|
);
|
|
6224
|
-
const findingsPath =
|
|
6544
|
+
const findingsPath = join15(auditsDir, `${args.sectionId}-findings.jsonl`);
|
|
6225
6545
|
for (const f of dedupedFindings) {
|
|
6226
|
-
await
|
|
6546
|
+
await appendFile6(findingsPath, JSON.stringify(f) + "\n", "utf8");
|
|
6227
6547
|
}
|
|
6228
|
-
const reviewsPath =
|
|
6548
|
+
const reviewsPath = join15(args.packPath, "sections", args.sectionId, "claim-reviews.jsonl");
|
|
6229
6549
|
for (const r of claimReviews) {
|
|
6230
|
-
await
|
|
6550
|
+
await appendFile6(reviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
|
|
6231
6551
|
}
|
|
6232
6552
|
}
|
|
6233
6553
|
return {
|
|
@@ -6264,14 +6584,14 @@ var init_run4 = __esm({
|
|
|
6264
6584
|
});
|
|
6265
6585
|
|
|
6266
6586
|
// src/review/promote.ts
|
|
6267
|
-
import { existsSync as
|
|
6268
|
-
import { copyFile, mkdir as
|
|
6269
|
-
import { join as
|
|
6587
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6588
|
+
import { copyFile, mkdir as mkdir13, readFile as readFile15, writeFile as writeFile13, appendFile as appendFile7 } from "fs/promises";
|
|
6589
|
+
import { join as join16, resolve as resolve11 } from "path";
|
|
6270
6590
|
import { parse as yamlParse4, stringify as yamlStringify5 } from "yaml";
|
|
6271
6591
|
async function promote(options) {
|
|
6272
6592
|
const packPath = options.packPath ? resolve11(options.packPath) : process.cwd();
|
|
6273
|
-
if (!
|
|
6274
|
-
if (!
|
|
6593
|
+
if (!existsSync15(join16(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
6594
|
+
if (!existsSync15(join16(packPath, "sections", options.sectionId)))
|
|
6275
6595
|
throw new SectionNotFoundError(options.sectionId);
|
|
6276
6596
|
if (!isValidProfileName(options.profile)) {
|
|
6277
6597
|
throw new Error(
|
|
@@ -6279,39 +6599,39 @@ async function promote(options) {
|
|
|
6279
6599
|
);
|
|
6280
6600
|
}
|
|
6281
6601
|
const dir = profileDir(packPath, options.sectionId, options.profile);
|
|
6282
|
-
const reviewJsonPath =
|
|
6283
|
-
if (!
|
|
6602
|
+
const reviewJsonPath = join16(dir, "review.json");
|
|
6603
|
+
if (!existsSync15(reviewJsonPath)) {
|
|
6284
6604
|
throw new Error(
|
|
6285
6605
|
`Profile "${options.profile}" not found at ${dir}. Run \`research-os review --profile ${options.profile}\` first.`
|
|
6286
6606
|
);
|
|
6287
6607
|
}
|
|
6288
6608
|
const snapshot = ReviewSnapshotSchema.parse(
|
|
6289
|
-
JSON.parse(await
|
|
6609
|
+
JSON.parse(await readFile15(reviewJsonPath, "utf8"))
|
|
6290
6610
|
);
|
|
6291
|
-
const auditsDir =
|
|
6292
|
-
await
|
|
6293
|
-
const canonicalReviewJson =
|
|
6294
|
-
const canonicalReviewMd =
|
|
6611
|
+
const auditsDir = join16(packPath, "audits");
|
|
6612
|
+
await mkdir13(auditsDir, { recursive: true });
|
|
6613
|
+
const canonicalReviewJson = join16(auditsDir, `${options.sectionId}-review.json`);
|
|
6614
|
+
const canonicalReviewMd = join16(auditsDir, `${options.sectionId}-review.md`);
|
|
6295
6615
|
await copyFile(reviewJsonPath, canonicalReviewJson);
|
|
6296
6616
|
await writeFile13(canonicalReviewMd, renderReviewMarkdown(snapshot), "utf8");
|
|
6297
|
-
const canonicalFindings =
|
|
6298
|
-
const profileFindings =
|
|
6617
|
+
const canonicalFindings = join16(auditsDir, `${options.sectionId}-findings.jsonl`);
|
|
6618
|
+
const profileFindings = join16(dir, "findings.jsonl");
|
|
6299
6619
|
const writtenFindings = [];
|
|
6300
|
-
if (
|
|
6301
|
-
const text = await
|
|
6620
|
+
if (existsSync15(profileFindings)) {
|
|
6621
|
+
const text = await readFile15(profileFindings, "utf8");
|
|
6302
6622
|
for (const line of text.split(/\r?\n/)) {
|
|
6303
6623
|
if (!line.trim()) continue;
|
|
6304
|
-
await
|
|
6624
|
+
await appendFile7(canonicalFindings, line + "\n", "utf8");
|
|
6305
6625
|
writtenFindings.push(line);
|
|
6306
6626
|
}
|
|
6307
6627
|
}
|
|
6308
|
-
const canonicalReviews =
|
|
6309
|
-
const profileReviews =
|
|
6310
|
-
if (
|
|
6311
|
-
const text = await
|
|
6628
|
+
const canonicalReviews = join16(packPath, "sections", options.sectionId, "claim-reviews.jsonl");
|
|
6629
|
+
const profileReviews = join16(dir, "claim-reviews.jsonl");
|
|
6630
|
+
if (existsSync15(profileReviews)) {
|
|
6631
|
+
const text = await readFile15(profileReviews, "utf8");
|
|
6312
6632
|
for (const line of text.split(/\r?\n/)) {
|
|
6313
6633
|
if (!line.trim()) continue;
|
|
6314
|
-
await
|
|
6634
|
+
await appendFile7(canonicalReviews, line + "\n", "utf8");
|
|
6315
6635
|
}
|
|
6316
6636
|
}
|
|
6317
6637
|
const stamp = (options.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
@@ -6325,8 +6645,8 @@ async function promote(options) {
|
|
|
6325
6645
|
});
|
|
6326
6646
|
let sectionStatusBumped = false;
|
|
6327
6647
|
if (options.promoteSectionStatus) {
|
|
6328
|
-
const yamlPath =
|
|
6329
|
-
const research = ResearchYamlSchema.parse(yamlParse4(await
|
|
6648
|
+
const yamlPath = join16(packPath, "research.yaml");
|
|
6649
|
+
const research = ResearchYamlSchema.parse(yamlParse4(await readFile15(yamlPath, "utf8")));
|
|
6330
6650
|
const idx = research.sections.findIndex((s) => s.id === options.sectionId);
|
|
6331
6651
|
if (idx >= 0 && research.sections[idx].status === "gated") {
|
|
6332
6652
|
const allAccepted = snapshot.candidate_claims > 0 && snapshot.claim_reviews.every((r) => r.decision === "accepted_for_synthesis");
|
|
@@ -6531,16 +6851,16 @@ var init_schema11 = __esm({
|
|
|
6531
6851
|
});
|
|
6532
6852
|
|
|
6533
6853
|
// src/indexer/db.ts
|
|
6534
|
-
import { mkdirSync, writeFileSync, existsSync as
|
|
6535
|
-
import { dirname as
|
|
6854
|
+
import { mkdirSync, writeFileSync, existsSync as existsSync16 } from "fs";
|
|
6855
|
+
import { dirname as dirname4, join as join17 } from "path";
|
|
6536
6856
|
import Database from "better-sqlite3";
|
|
6537
6857
|
function indexDbPath(packPath) {
|
|
6538
|
-
return
|
|
6858
|
+
return join17(packPath, ".research-os", "index.sqlite");
|
|
6539
6859
|
}
|
|
6540
6860
|
function ensureGitIgnore(packPath) {
|
|
6541
|
-
const dir =
|
|
6542
|
-
const gi =
|
|
6543
|
-
if (!
|
|
6861
|
+
const dir = join17(packPath, ".research-os");
|
|
6862
|
+
const gi = join17(dir, ".gitignore");
|
|
6863
|
+
if (!existsSync16(gi)) {
|
|
6544
6864
|
mkdirSync(dir, { recursive: true });
|
|
6545
6865
|
writeFileSync(gi, "*\n", "utf8");
|
|
6546
6866
|
}
|
|
@@ -6548,7 +6868,7 @@ function ensureGitIgnore(packPath) {
|
|
|
6548
6868
|
function openIndexDb(opts) {
|
|
6549
6869
|
const dbPath = indexDbPath(opts.packPath);
|
|
6550
6870
|
if (!opts.readonly) {
|
|
6551
|
-
mkdirSync(
|
|
6871
|
+
mkdirSync(dirname4(dbPath), { recursive: true });
|
|
6552
6872
|
ensureGitIgnore(opts.packPath);
|
|
6553
6873
|
}
|
|
6554
6874
|
const db = new Database(dbPath, { readonly: opts.readonly ?? false, fileMustExist: opts.readonly ?? false });
|
|
@@ -6563,18 +6883,18 @@ var init_db = __esm({
|
|
|
6563
6883
|
});
|
|
6564
6884
|
|
|
6565
6885
|
// src/indexer/build.ts
|
|
6566
|
-
import { existsSync as
|
|
6567
|
-
import { readFile as
|
|
6568
|
-
import { join as
|
|
6886
|
+
import { existsSync as existsSync17 } from "fs";
|
|
6887
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
6888
|
+
import { join as join18, resolve as resolve12, relative as relative2 } from "path";
|
|
6569
6889
|
import { createHash as createHash5 } from "crypto";
|
|
6570
6890
|
import { parse as yamlParse5 } from "yaml";
|
|
6571
6891
|
function relPath(packPath, abs) {
|
|
6572
6892
|
return relative2(packPath, abs).split("\\").join("/");
|
|
6573
6893
|
}
|
|
6574
6894
|
async function tryReadJsonl(packPath, rel, parse) {
|
|
6575
|
-
const path =
|
|
6576
|
-
if (!
|
|
6577
|
-
const text = await
|
|
6895
|
+
const path = join18(packPath, rel);
|
|
6896
|
+
if (!existsSync17(path)) return [];
|
|
6897
|
+
const text = await readFile16(path, "utf8");
|
|
6578
6898
|
const out = [];
|
|
6579
6899
|
for (const line of text.split(/\r?\n/)) {
|
|
6580
6900
|
if (!line.trim()) continue;
|
|
@@ -6583,22 +6903,22 @@ async function tryReadJsonl(packPath, rel, parse) {
|
|
|
6583
6903
|
return out;
|
|
6584
6904
|
}
|
|
6585
6905
|
async function readSourceCards4(packPath) {
|
|
6586
|
-
const dir =
|
|
6587
|
-
if (!
|
|
6906
|
+
const dir = join18(packPath, "evidence", "source-cards");
|
|
6907
|
+
if (!existsSync17(dir)) return [];
|
|
6588
6908
|
const { readdir: readdir4 } = await import("fs/promises");
|
|
6589
6909
|
const entries = await readdir4(dir);
|
|
6590
6910
|
const cards = [];
|
|
6591
6911
|
for (const entry of entries) {
|
|
6592
6912
|
if (!entry.endsWith(".json")) continue;
|
|
6593
|
-
const text = await
|
|
6913
|
+
const text = await readFile16(join18(dir, entry), "utf8");
|
|
6594
6914
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
6595
6915
|
}
|
|
6596
6916
|
return cards;
|
|
6597
6917
|
}
|
|
6598
6918
|
async function readGateResult2(packPath, sectionId) {
|
|
6599
|
-
const path =
|
|
6600
|
-
if (!
|
|
6601
|
-
const text = await
|
|
6919
|
+
const path = join18(packPath, "audits", `${sectionId}-gate.json`);
|
|
6920
|
+
if (!existsSync17(path)) return null;
|
|
6921
|
+
const text = await readFile16(path, "utf8");
|
|
6602
6922
|
return SectionGateResultSchema.parse(JSON.parse(text));
|
|
6603
6923
|
}
|
|
6604
6924
|
function fileSha256(text) {
|
|
@@ -6608,8 +6928,8 @@ async function indexSection(args) {
|
|
|
6608
6928
|
const { db, packPath, research, sectionId, now, counts } = args;
|
|
6609
6929
|
const section = research.sections.find((s) => s.id === sectionId);
|
|
6610
6930
|
if (!section) throw new SectionNotFoundError(sectionId);
|
|
6611
|
-
const sectionDir =
|
|
6612
|
-
if (!
|
|
6931
|
+
const sectionDir = join18(packPath, "sections", sectionId);
|
|
6932
|
+
if (!existsSync17(sectionDir)) throw new SectionNotFoundError(sectionId);
|
|
6613
6933
|
db.prepare("DELETE FROM sources WHERE section_id = ?").run(sectionId);
|
|
6614
6934
|
db.prepare("DELETE FROM claims WHERE section_id = ?").run(sectionId);
|
|
6615
6935
|
db.prepare("DELETE FROM contradictions WHERE section_id = ?").run(sectionId);
|
|
@@ -6648,9 +6968,9 @@ async function indexSection(args) {
|
|
|
6648
6968
|
};
|
|
6649
6969
|
const allCards = await readSourceCards4(packPath);
|
|
6650
6970
|
const sectionSourceIds = /* @__PURE__ */ new Set();
|
|
6651
|
-
const sourcesJsonlPath =
|
|
6652
|
-
if (
|
|
6653
|
-
const text = await
|
|
6971
|
+
const sourcesJsonlPath = join18(sectionDir, "sources.jsonl");
|
|
6972
|
+
if (existsSync17(sourcesJsonlPath)) {
|
|
6973
|
+
const text = await readFile16(sourcesJsonlPath, "utf8");
|
|
6654
6974
|
recordArtifact("sources_jsonl", relPath(packPath, sourcesJsonlPath), text);
|
|
6655
6975
|
for (const line of text.split(/\r?\n/)) {
|
|
6656
6976
|
if (!line.trim()) continue;
|
|
@@ -6660,7 +6980,7 @@ async function indexSection(args) {
|
|
|
6660
6980
|
}
|
|
6661
6981
|
for (const card of allCards) {
|
|
6662
6982
|
if (!sectionSourceIds.has(card.source_id) && card.section_id !== sectionId) continue;
|
|
6663
|
-
const cardPath = relPath(packPath,
|
|
6983
|
+
const cardPath = relPath(packPath, join18(packPath, "evidence", "source-cards", `${card.source_id}.json`));
|
|
6664
6984
|
db.prepare(
|
|
6665
6985
|
`INSERT OR REPLACE INTO sources(
|
|
6666
6986
|
source_id, section_id, url, publisher, source_type, relevance,
|
|
@@ -6694,9 +7014,9 @@ ${card.key_points.join("\n")}`;
|
|
|
6694
7014
|
`sections/${sectionId}/claims.jsonl`,
|
|
6695
7015
|
(r) => ClaimSchema.parse(r)
|
|
6696
7016
|
);
|
|
6697
|
-
const claimsArtifact = relPath(packPath,
|
|
6698
|
-
if (
|
|
6699
|
-
const text = await
|
|
7017
|
+
const claimsArtifact = relPath(packPath, join18(sectionDir, "claims.jsonl"));
|
|
7018
|
+
if (existsSync17(join18(sectionDir, "claims.jsonl"))) {
|
|
7019
|
+
const text = await readFile16(join18(sectionDir, "claims.jsonl"), "utf8");
|
|
6700
7020
|
recordArtifact("claims_jsonl", claimsArtifact, text);
|
|
6701
7021
|
}
|
|
6702
7022
|
for (const claim of claims) {
|
|
@@ -6733,9 +7053,9 @@ ${claim.evidence_excerpt}`;
|
|
|
6733
7053
|
`sections/${sectionId}/contradictions.jsonl`,
|
|
6734
7054
|
(r) => ContradictionSchema.parse(r)
|
|
6735
7055
|
);
|
|
6736
|
-
const contradictionsArtifact = relPath(packPath,
|
|
6737
|
-
if (
|
|
6738
|
-
const text = await
|
|
7056
|
+
const contradictionsArtifact = relPath(packPath, join18(sectionDir, "contradictions.jsonl"));
|
|
7057
|
+
if (existsSync17(join18(sectionDir, "contradictions.jsonl"))) {
|
|
7058
|
+
const text = await readFile16(join18(sectionDir, "contradictions.jsonl"), "utf8");
|
|
6739
7059
|
recordArtifact("contradictions_jsonl", contradictionsArtifact, text);
|
|
6740
7060
|
}
|
|
6741
7061
|
for (const c of contradictions) {
|
|
@@ -6771,10 +7091,10 @@ ${c.severity}`;
|
|
|
6771
7091
|
`audits/${sectionId}-findings.jsonl`,
|
|
6772
7092
|
(r) => ReviewFindingSchema.parse(r)
|
|
6773
7093
|
);
|
|
6774
|
-
const findingsArtifact = relPath(packPath,
|
|
6775
|
-
const findingsAbs =
|
|
6776
|
-
if (
|
|
6777
|
-
const text = await
|
|
7094
|
+
const findingsArtifact = relPath(packPath, join18(packPath, "audits", `${sectionId}-findings.jsonl`));
|
|
7095
|
+
const findingsAbs = join18(packPath, "audits", `${sectionId}-findings.jsonl`);
|
|
7096
|
+
if (existsSync17(findingsAbs)) {
|
|
7097
|
+
const text = await readFile16(findingsAbs, "utf8");
|
|
6778
7098
|
recordArtifact("findings_jsonl", findingsArtifact, text);
|
|
6779
7099
|
}
|
|
6780
7100
|
const findingById = /* @__PURE__ */ new Map();
|
|
@@ -6811,10 +7131,10 @@ ${f.evidence}`;
|
|
|
6811
7131
|
`sections/${sectionId}/claim-reviews.jsonl`,
|
|
6812
7132
|
(r) => ClaimReviewSchema.parse(r)
|
|
6813
7133
|
);
|
|
6814
|
-
const reviewsArtifact = relPath(packPath,
|
|
6815
|
-
const reviewsAbs =
|
|
6816
|
-
if (
|
|
6817
|
-
const text = await
|
|
7134
|
+
const reviewsArtifact = relPath(packPath, join18(sectionDir, "claim-reviews.jsonl"));
|
|
7135
|
+
const reviewsAbs = join18(sectionDir, "claim-reviews.jsonl");
|
|
7136
|
+
if (existsSync17(reviewsAbs)) {
|
|
7137
|
+
const text = await readFile16(reviewsAbs, "utf8");
|
|
6818
7138
|
recordArtifact("claim_reviews_jsonl", reviewsArtifact, text);
|
|
6819
7139
|
}
|
|
6820
7140
|
for (const r of reviews) {
|
|
@@ -6839,8 +7159,8 @@ ${r.reason}`;
|
|
|
6839
7159
|
}
|
|
6840
7160
|
const gate2 = await readGateResult2(packPath, sectionId);
|
|
6841
7161
|
if (gate2) {
|
|
6842
|
-
const gateArtifact = relPath(packPath,
|
|
6843
|
-
const gateText = await
|
|
7162
|
+
const gateArtifact = relPath(packPath, join18(packPath, "audits", `${sectionId}-gate.json`));
|
|
7163
|
+
const gateText = await readFile16(join18(packPath, "audits", `${sectionId}-gate.json`), "utf8");
|
|
6844
7164
|
recordArtifact("gate_json", gateArtifact, gateText);
|
|
6845
7165
|
db.prepare(
|
|
6846
7166
|
`INSERT OR REPLACE INTO gate_results(
|
|
@@ -6873,9 +7193,9 @@ ${gate2.next_actions.join("\n")}`;
|
|
|
6873
7193
|
"evidence/fetch-log.jsonl",
|
|
6874
7194
|
(r) => FetchReceiptSchema.parse(r)
|
|
6875
7195
|
);
|
|
6876
|
-
const fetchLogAbs =
|
|
6877
|
-
if (
|
|
6878
|
-
const text = await
|
|
7196
|
+
const fetchLogAbs = join18(packPath, "evidence", "fetch-log.jsonl");
|
|
7197
|
+
if (existsSync17(fetchLogAbs)) {
|
|
7198
|
+
const text = await readFile16(fetchLogAbs, "utf8");
|
|
6879
7199
|
recordArtifact("fetch_log_jsonl", relPath(packPath, fetchLogAbs), text);
|
|
6880
7200
|
}
|
|
6881
7201
|
for (const receipt of allReceipts.filter((r) => r.section_id === sectionId)) {
|
|
@@ -6911,9 +7231,9 @@ async function indexPackAuditRollups(db, packPath, _now) {
|
|
|
6911
7231
|
db.prepare(`DELETE FROM facts_fts WHERE record_type = ? AND record_id = ?`).run(f.recordType, f.recordId);
|
|
6912
7232
|
}
|
|
6913
7233
|
for (const f of ROLLUP_FILES) {
|
|
6914
|
-
const abs =
|
|
6915
|
-
if (!
|
|
6916
|
-
const text = await
|
|
7234
|
+
const abs = join18(packPath, "audits", f.filename);
|
|
7235
|
+
if (!existsSync17(abs)) continue;
|
|
7236
|
+
const text = await readFile16(abs, "utf8");
|
|
6917
7237
|
insertFts.run(
|
|
6918
7238
|
f.recordType,
|
|
6919
7239
|
f.recordId,
|
|
@@ -6925,8 +7245,8 @@ async function indexPackAuditRollups(db, packPath, _now) {
|
|
|
6925
7245
|
}
|
|
6926
7246
|
async function build(options) {
|
|
6927
7247
|
const packPath = options.packPath ? resolve12(options.packPath) : process.cwd();
|
|
6928
|
-
if (!
|
|
6929
|
-
const research = ResearchYamlSchema.parse(yamlParse5(await
|
|
7248
|
+
if (!existsSync17(join18(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
7249
|
+
const research = ResearchYamlSchema.parse(yamlParse5(await readFile16(join18(packPath, "research.yaml"), "utf8")));
|
|
6930
7250
|
const targets = options.sectionId ? [options.sectionId] : options.all ? research.sections.map((s) => s.id) : research.sections.map((s) => s.id);
|
|
6931
7251
|
if (targets.length === 0) {
|
|
6932
7252
|
throw new Error("No sections to index. Add at least one section to the pack.");
|
|
@@ -6983,7 +7303,7 @@ var init_build = __esm({
|
|
|
6983
7303
|
});
|
|
6984
7304
|
|
|
6985
7305
|
// src/indexer/query.ts
|
|
6986
|
-
import { existsSync as
|
|
7306
|
+
import { existsSync as existsSync18 } from "fs";
|
|
6987
7307
|
import { resolve as resolve13 } from "path";
|
|
6988
7308
|
function escapeFtsTerm(term) {
|
|
6989
7309
|
const trimmed = term.trim();
|
|
@@ -6994,8 +7314,8 @@ function escapeFtsTerm(term) {
|
|
|
6994
7314
|
function query(options) {
|
|
6995
7315
|
const packPath = options.packPath ? resolve13(options.packPath) : process.cwd();
|
|
6996
7316
|
const dbPath = indexDbPath(packPath);
|
|
6997
|
-
if (!
|
|
6998
|
-
if (!
|
|
7317
|
+
if (!existsSync18(packPath)) throw new PackNotFoundError(packPath);
|
|
7318
|
+
if (!existsSync18(dbPath)) throw new IndexNotBuiltError(dbPath);
|
|
6999
7319
|
const db = openIndexDb({ packPath, readonly: true });
|
|
7000
7320
|
const limit = options.limit ?? 25;
|
|
7001
7321
|
const ftsTerm = escapeFtsTerm(options.term);
|
|
@@ -7069,16 +7389,16 @@ var init_query = __esm({
|
|
|
7069
7389
|
});
|
|
7070
7390
|
|
|
7071
7391
|
// src/indexer/export.ts
|
|
7072
|
-
import { existsSync as
|
|
7073
|
-
import { mkdir as
|
|
7074
|
-
import { dirname as
|
|
7392
|
+
import { existsSync as existsSync19 } from "fs";
|
|
7393
|
+
import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
|
|
7394
|
+
import { dirname as dirname5, join as join19, resolve as resolve14 } from "path";
|
|
7075
7395
|
async function exportRepoKnowledge(options) {
|
|
7076
7396
|
const packPath = options.packPath ? resolve14(options.packPath) : process.cwd();
|
|
7077
|
-
if (!
|
|
7397
|
+
if (!existsSync19(packPath)) throw new PackNotFoundError(packPath);
|
|
7078
7398
|
const dbPath = indexDbPath(packPath);
|
|
7079
|
-
if (!
|
|
7080
|
-
const outPath = options.outPath ? resolve14(options.outPath) :
|
|
7081
|
-
await
|
|
7399
|
+
if (!existsSync19(dbPath)) throw new IndexNotBuiltError(dbPath);
|
|
7400
|
+
const outPath = options.outPath ? resolve14(options.outPath) : join19(packPath, "evidence", "repo-knowledge", "research-os-facts.jsonl");
|
|
7401
|
+
await mkdir14(dirname5(outPath), { recursive: true });
|
|
7082
7402
|
const db = openIndexDb({ packPath, readonly: true });
|
|
7083
7403
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7084
7404
|
const facts = [];
|
|
@@ -7237,8 +7557,8 @@ async function syncRepoKnowledge(options) {
|
|
|
7237
7557
|
};
|
|
7238
7558
|
}
|
|
7239
7559
|
const exportResult = await exportRepoKnowledge({ packPath });
|
|
7240
|
-
const { readFile:
|
|
7241
|
-
const text = await
|
|
7560
|
+
const { readFile: readFile25 } = await import("fs/promises");
|
|
7561
|
+
const text = await readFile25(exportResult.outPath, "utf8");
|
|
7242
7562
|
const facts = text.split(/\r?\n/).filter((l) => l.trim().length > 0).map((l) => JSON.parse(l));
|
|
7243
7563
|
try {
|
|
7244
7564
|
const r = await rk.ingestFacts({ facts, namespace: "research-os" });
|
|
@@ -7278,26 +7598,26 @@ var init_indexer = __esm({
|
|
|
7278
7598
|
});
|
|
7279
7599
|
|
|
7280
7600
|
// src/dispositions/schema.ts
|
|
7281
|
-
import { z as
|
|
7601
|
+
import { z as z14 } from "zod";
|
|
7282
7602
|
var ClaimSynthesisDispositionStatusSchema, ClaimSynthesisDispositionSchema;
|
|
7283
7603
|
var init_schema12 = __esm({
|
|
7284
7604
|
"src/dispositions/schema.ts"() {
|
|
7285
7605
|
"use strict";
|
|
7286
|
-
ClaimSynthesisDispositionStatusSchema =
|
|
7606
|
+
ClaimSynthesisDispositionStatusSchema = z14.enum([
|
|
7287
7607
|
"parked_not_for_synthesis",
|
|
7288
7608
|
"preserved_for_human_note",
|
|
7289
7609
|
"needs_human_review_excluded",
|
|
7290
7610
|
"out_of_bounds_regression_fixture"
|
|
7291
7611
|
]);
|
|
7292
|
-
ClaimSynthesisDispositionSchema =
|
|
7293
|
-
claim_id:
|
|
7294
|
-
section_id:
|
|
7612
|
+
ClaimSynthesisDispositionSchema = z14.object({
|
|
7613
|
+
claim_id: z14.string().min(1),
|
|
7614
|
+
section_id: z14.string().min(1),
|
|
7295
7615
|
status: ClaimSynthesisDispositionStatusSchema,
|
|
7296
|
-
reason:
|
|
7297
|
-
decided_by:
|
|
7298
|
-
authorized_by:
|
|
7299
|
-
source:
|
|
7300
|
-
created_at:
|
|
7616
|
+
reason: z14.string().min(4),
|
|
7617
|
+
decided_by: z14.string().min(1),
|
|
7618
|
+
authorized_by: z14.string().min(1),
|
|
7619
|
+
source: z14.string().min(1),
|
|
7620
|
+
created_at: z14.string().min(1)
|
|
7301
7621
|
});
|
|
7302
7622
|
}
|
|
7303
7623
|
});
|
|
@@ -7628,91 +7948,91 @@ var init_derive = __esm({
|
|
|
7628
7948
|
});
|
|
7629
7949
|
|
|
7630
7950
|
// src/cowork/schema.ts
|
|
7631
|
-
import { z as
|
|
7951
|
+
import { z as z15 } from "zod";
|
|
7632
7952
|
var HandoffModeSchema, IndexStatusSchema, ProvenanceSummarySchema, SectionStateSchema, WaiverEntrySchema, GateVerdictEntrySchema, ReviewDecisionCountSchema, CoworkHandoffPayloadSchema;
|
|
7633
7953
|
var init_schema13 = __esm({
|
|
7634
7954
|
"src/cowork/schema.ts"() {
|
|
7635
7955
|
"use strict";
|
|
7636
|
-
HandoffModeSchema =
|
|
7956
|
+
HandoffModeSchema = z15.enum([
|
|
7637
7957
|
"repair_required",
|
|
7638
7958
|
"synthesis_ready",
|
|
7639
7959
|
"human_review_required"
|
|
7640
7960
|
]);
|
|
7641
|
-
IndexStatusSchema =
|
|
7642
|
-
ProvenanceSummarySchema =
|
|
7643
|
-
accepted_count:
|
|
7644
|
-
rejected_count:
|
|
7645
|
-
triage_parked_count:
|
|
7646
|
-
needs_review_undispositioned_count:
|
|
7647
|
-
dispositioned_count:
|
|
7648
|
-
dispositioned_breakdown:
|
|
7649
|
-
parked_not_for_synthesis:
|
|
7650
|
-
preserved_for_human_note:
|
|
7651
|
-
needs_human_review_excluded:
|
|
7652
|
-
out_of_bounds_regression_fixture:
|
|
7961
|
+
IndexStatusSchema = z15.enum(["present", "missing"]);
|
|
7962
|
+
ProvenanceSummarySchema = z15.object({
|
|
7963
|
+
accepted_count: z15.number().int().nonnegative(),
|
|
7964
|
+
rejected_count: z15.number().int().nonnegative(),
|
|
7965
|
+
triage_parked_count: z15.number().int().nonnegative(),
|
|
7966
|
+
needs_review_undispositioned_count: z15.number().int().nonnegative(),
|
|
7967
|
+
dispositioned_count: z15.number().int().nonnegative(),
|
|
7968
|
+
dispositioned_breakdown: z15.object({
|
|
7969
|
+
parked_not_for_synthesis: z15.number().int().nonnegative(),
|
|
7970
|
+
preserved_for_human_note: z15.number().int().nonnegative(),
|
|
7971
|
+
needs_human_review_excluded: z15.number().int().nonnegative(),
|
|
7972
|
+
out_of_bounds_regression_fixture: z15.number().int().nonnegative()
|
|
7653
7973
|
}),
|
|
7654
|
-
active_repair_blockers:
|
|
7655
|
-
active_unresolved_contradictions:
|
|
7656
|
-
waivers_active:
|
|
7657
|
-
overrides_applied_count:
|
|
7658
|
-
});
|
|
7659
|
-
SectionStateSchema =
|
|
7660
|
-
section_id:
|
|
7661
|
-
purpose:
|
|
7662
|
-
status:
|
|
7663
|
-
has_gate_run:
|
|
7664
|
-
has_review_run:
|
|
7665
|
-
gate_verdict:
|
|
7666
|
-
synthesis_eligible:
|
|
7667
|
-
accepted_claim_ids:
|
|
7668
|
-
repair_claim_ids:
|
|
7669
|
-
rejected_claim_ids:
|
|
7670
|
-
dispositioned_claim_ids:
|
|
7671
|
-
candidate_claims_total:
|
|
7672
|
-
unresolved_contradiction_ids:
|
|
7673
|
-
blocking_reasons:
|
|
7674
|
-
active_blockers:
|
|
7675
|
-
blocking_contradictions_unresolved:
|
|
7974
|
+
active_repair_blockers: z15.number().int().nonnegative(),
|
|
7975
|
+
active_unresolved_contradictions: z15.number().int().nonnegative(),
|
|
7976
|
+
waivers_active: z15.array(z15.string()),
|
|
7977
|
+
overrides_applied_count: z15.number().int().nonnegative()
|
|
7978
|
+
});
|
|
7979
|
+
SectionStateSchema = z15.object({
|
|
7980
|
+
section_id: z15.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
7981
|
+
purpose: z15.string(),
|
|
7982
|
+
status: z15.string(),
|
|
7983
|
+
has_gate_run: z15.boolean(),
|
|
7984
|
+
has_review_run: z15.boolean(),
|
|
7985
|
+
gate_verdict: z15.string().nullable(),
|
|
7986
|
+
synthesis_eligible: z15.boolean(),
|
|
7987
|
+
accepted_claim_ids: z15.array(z15.string()),
|
|
7988
|
+
repair_claim_ids: z15.array(z15.string()),
|
|
7989
|
+
rejected_claim_ids: z15.array(z15.string()),
|
|
7990
|
+
dispositioned_claim_ids: z15.array(z15.string()).default([]),
|
|
7991
|
+
candidate_claims_total: z15.number().int().nonnegative(),
|
|
7992
|
+
unresolved_contradiction_ids: z15.array(z15.string()),
|
|
7993
|
+
blocking_reasons: z15.array(z15.string()),
|
|
7994
|
+
active_blockers: z15.array(z15.string()).default([]),
|
|
7995
|
+
blocking_contradictions_unresolved: z15.number().int().nonnegative(),
|
|
7676
7996
|
provenance_summary: ProvenanceSummarySchema.optional()
|
|
7677
7997
|
});
|
|
7678
|
-
WaiverEntrySchema =
|
|
7679
|
-
scope:
|
|
7680
|
-
family:
|
|
7681
|
-
reason:
|
|
7682
|
-
compensating_controls:
|
|
7683
|
-
applied_to:
|
|
7684
|
-
});
|
|
7685
|
-
GateVerdictEntrySchema =
|
|
7686
|
-
section_id:
|
|
7687
|
-
verdict:
|
|
7688
|
-
synthesis_eligible:
|
|
7689
|
-
});
|
|
7690
|
-
ReviewDecisionCountSchema =
|
|
7691
|
-
section_id:
|
|
7692
|
-
decision:
|
|
7693
|
-
count:
|
|
7694
|
-
});
|
|
7695
|
-
CoworkHandoffPayloadSchema =
|
|
7696
|
-
pack_id:
|
|
7697
|
-
pack_topic:
|
|
7698
|
-
generated_at:
|
|
7998
|
+
WaiverEntrySchema = z15.object({
|
|
7999
|
+
scope: z15.enum(["pack", "gate"]),
|
|
8000
|
+
family: z15.string(),
|
|
8001
|
+
reason: z15.string(),
|
|
8002
|
+
compensating_controls: z15.array(z15.string()),
|
|
8003
|
+
applied_to: z15.string()
|
|
8004
|
+
});
|
|
8005
|
+
GateVerdictEntrySchema = z15.object({
|
|
8006
|
+
section_id: z15.string(),
|
|
8007
|
+
verdict: z15.string(),
|
|
8008
|
+
synthesis_eligible: z15.boolean()
|
|
8009
|
+
});
|
|
8010
|
+
ReviewDecisionCountSchema = z15.object({
|
|
8011
|
+
section_id: z15.string(),
|
|
8012
|
+
decision: z15.string(),
|
|
8013
|
+
count: z15.number().int().nonnegative()
|
|
8014
|
+
});
|
|
8015
|
+
CoworkHandoffPayloadSchema = z15.object({
|
|
8016
|
+
pack_id: z15.string(),
|
|
8017
|
+
pack_topic: z15.string(),
|
|
8018
|
+
generated_at: z15.string(),
|
|
7699
8019
|
mode: HandoffModeSchema,
|
|
7700
|
-
synthesis_allowed:
|
|
7701
|
-
summary:
|
|
7702
|
-
sections:
|
|
7703
|
-
accepted_claim_ids:
|
|
7704
|
-
repair_claim_ids:
|
|
7705
|
-
blocked_claim_ids:
|
|
7706
|
-
dispositioned_claim_ids:
|
|
7707
|
-
unresolved_contradiction_ids:
|
|
7708
|
-
waivers:
|
|
7709
|
-
gate_verdicts:
|
|
7710
|
-
review_decisions:
|
|
7711
|
-
recommended_next_actions:
|
|
7712
|
-
allowed_write_paths:
|
|
7713
|
-
forbidden_actions:
|
|
8020
|
+
synthesis_allowed: z15.boolean(),
|
|
8021
|
+
summary: z15.string(),
|
|
8022
|
+
sections: z15.array(SectionStateSchema),
|
|
8023
|
+
accepted_claim_ids: z15.array(z15.string()),
|
|
8024
|
+
repair_claim_ids: z15.array(z15.string()),
|
|
8025
|
+
blocked_claim_ids: z15.array(z15.string()),
|
|
8026
|
+
dispositioned_claim_ids: z15.array(z15.string()).default([]),
|
|
8027
|
+
unresolved_contradiction_ids: z15.array(z15.string()),
|
|
8028
|
+
waivers: z15.array(WaiverEntrySchema),
|
|
8029
|
+
gate_verdicts: z15.array(GateVerdictEntrySchema),
|
|
8030
|
+
review_decisions: z15.array(ReviewDecisionCountSchema),
|
|
8031
|
+
recommended_next_actions: z15.array(z15.string()),
|
|
8032
|
+
allowed_write_paths: z15.array(z15.string()),
|
|
8033
|
+
forbidden_actions: z15.array(z15.string()),
|
|
7714
8034
|
index_status: IndexStatusSchema,
|
|
7715
|
-
warnings:
|
|
8035
|
+
warnings: z15.array(z15.string())
|
|
7716
8036
|
});
|
|
7717
8037
|
}
|
|
7718
8038
|
});
|
|
@@ -7891,19 +8211,19 @@ var init_markdown4 = __esm({
|
|
|
7891
8211
|
});
|
|
7892
8212
|
|
|
7893
8213
|
// src/cowork/run.ts
|
|
7894
|
-
import { existsSync as
|
|
7895
|
-
import { mkdir as
|
|
7896
|
-
import { join as
|
|
8214
|
+
import { existsSync as existsSync20 } from "fs";
|
|
8215
|
+
import { mkdir as mkdir15, readFile as readFile17, writeFile as writeFile15 } from "fs/promises";
|
|
8216
|
+
import { join as join20, resolve as resolve16 } from "path";
|
|
7897
8217
|
import { parse as yamlParse6 } from "yaml";
|
|
7898
8218
|
async function loadResearchYaml4(packPath) {
|
|
7899
|
-
const yamlPath =
|
|
7900
|
-
if (!
|
|
7901
|
-
const text = await
|
|
8219
|
+
const yamlPath = join20(packPath, "research.yaml");
|
|
8220
|
+
if (!existsSync20(yamlPath)) throw new PackNotFoundError(packPath);
|
|
8221
|
+
const text = await readFile17(yamlPath, "utf8");
|
|
7902
8222
|
return ResearchYamlSchema.parse(yamlParse6(text));
|
|
7903
8223
|
}
|
|
7904
8224
|
async function readJsonl3(path, parse, warnings) {
|
|
7905
|
-
if (!
|
|
7906
|
-
const text = await
|
|
8225
|
+
if (!existsSync20(path)) return [];
|
|
8226
|
+
const text = await readFile17(path, "utf8");
|
|
7907
8227
|
const out = [];
|
|
7908
8228
|
for (const line of text.split(/\r?\n/)) {
|
|
7909
8229
|
if (!line.trim()) continue;
|
|
@@ -7917,10 +8237,10 @@ async function readJsonl3(path, parse, warnings) {
|
|
|
7917
8237
|
return out;
|
|
7918
8238
|
}
|
|
7919
8239
|
async function readGate(packPath, sectionId, warnings) {
|
|
7920
|
-
const path =
|
|
7921
|
-
if (!
|
|
8240
|
+
const path = join20(packPath, "audits", `${sectionId}-gate.json`);
|
|
8241
|
+
if (!existsSync20(path)) return null;
|
|
7922
8242
|
try {
|
|
7923
|
-
const text = await
|
|
8243
|
+
const text = await readFile17(path, "utf8");
|
|
7924
8244
|
return SectionGateResultSchema.parse(JSON.parse(text));
|
|
7925
8245
|
} catch (err) {
|
|
7926
8246
|
warnings.push(`malformed gate result at audits/${sectionId}-gate.json: ${err instanceof Error ? err.message : err}`);
|
|
@@ -7935,35 +8255,35 @@ async function handoff(options) {
|
|
|
7935
8255
|
for (const section of research.sections) {
|
|
7936
8256
|
const sid = section.id;
|
|
7937
8257
|
const claims = await readJsonl3(
|
|
7938
|
-
|
|
8258
|
+
join20(packPath, "sections", sid, "claims.jsonl"),
|
|
7939
8259
|
(r) => ClaimSchema.parse(r),
|
|
7940
8260
|
warnings
|
|
7941
8261
|
);
|
|
7942
8262
|
const candidateClaims = claims.filter((c) => c.review_state === "candidate");
|
|
7943
8263
|
const claimReviews = await readJsonl3(
|
|
7944
|
-
|
|
8264
|
+
join20(packPath, "sections", sid, "claim-reviews.jsonl"),
|
|
7945
8265
|
(r) => ClaimReviewSchema.parse(r),
|
|
7946
8266
|
warnings
|
|
7947
8267
|
);
|
|
7948
8268
|
const contradictions = await readJsonl3(
|
|
7949
|
-
|
|
8269
|
+
join20(packPath, "sections", sid, "contradictions.jsonl"),
|
|
7950
8270
|
(r) => ContradictionSchema.parse(r),
|
|
7951
8271
|
warnings
|
|
7952
8272
|
);
|
|
7953
8273
|
const resolutions = await readJsonl3(
|
|
7954
|
-
|
|
8274
|
+
join20(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
|
|
7955
8275
|
(r) => ContradictionResolutionSchema.parse(r),
|
|
7956
8276
|
warnings
|
|
7957
8277
|
);
|
|
7958
8278
|
const dispositions = await readJsonl3(
|
|
7959
|
-
|
|
8279
|
+
join20(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
|
|
7960
8280
|
(r) => ClaimSynthesisDispositionSchema.parse(r),
|
|
7961
8281
|
warnings
|
|
7962
8282
|
);
|
|
7963
8283
|
const gate2 = await readGate(packPath, sid, warnings);
|
|
7964
8284
|
perSection.set(sid, { gate: gate2, candidateClaims, claimReviews, contradictions, resolutions, dispositions });
|
|
7965
8285
|
}
|
|
7966
|
-
const indexExists =
|
|
8286
|
+
const indexExists = existsSync20(indexDbPath(packPath));
|
|
7967
8287
|
if (!indexExists) {
|
|
7968
8288
|
warnings.push(
|
|
7969
8289
|
"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`."
|
|
@@ -7979,10 +8299,10 @@ async function handoff(options) {
|
|
|
7979
8299
|
warnings
|
|
7980
8300
|
})
|
|
7981
8301
|
);
|
|
7982
|
-
const handoffsDir =
|
|
7983
|
-
await
|
|
7984
|
-
const jsonPath =
|
|
7985
|
-
const mdPath =
|
|
8302
|
+
const handoffsDir = join20(packPath, "handoffs");
|
|
8303
|
+
await mkdir15(handoffsDir, { recursive: true });
|
|
8304
|
+
const jsonPath = join20(handoffsDir, "cowork-handoff.json");
|
|
8305
|
+
const mdPath = join20(handoffsDir, "cowork-master.md");
|
|
7986
8306
|
await writeFile15(jsonPath, JSON.stringify(payload, null, 2), "utf8");
|
|
7987
8307
|
await writeFile15(mdPath, renderCoworkMaster(payload), "utf8");
|
|
7988
8308
|
return {
|
|
@@ -8262,86 +8582,86 @@ var init_derive2 = __esm({
|
|
|
8262
8582
|
});
|
|
8263
8583
|
|
|
8264
8584
|
// src/synth/schema.ts
|
|
8265
|
-
import { z as
|
|
8585
|
+
import { z as z16 } from "zod";
|
|
8266
8586
|
var SectionAcceptedSummarySchema, ClaimClusterSchema, SharedSourceSchema, ScopeOverlapSchema, CrossSectionContradictionRefSchema, WaiverDependencySchema, AllowedSynthesisInputSchema, ForbiddenInputSchema, CrossSectionMapSchema;
|
|
8267
8587
|
var init_schema14 = __esm({
|
|
8268
8588
|
"src/synth/schema.ts"() {
|
|
8269
8589
|
"use strict";
|
|
8270
|
-
SectionAcceptedSummarySchema =
|
|
8271
|
-
section_id:
|
|
8272
|
-
purpose:
|
|
8273
|
-
status:
|
|
8274
|
-
accepted_claim_ids:
|
|
8275
|
-
excluded_reason:
|
|
8276
|
-
});
|
|
8277
|
-
ClaimClusterSchema = z15.object({
|
|
8278
|
-
cluster_id: z15.string(),
|
|
8279
|
-
shared_source_ids: z15.array(z15.string()),
|
|
8280
|
-
member_claim_ids: z15.array(z15.string()).min(1),
|
|
8281
|
-
spans_sections: z15.array(z15.string())
|
|
8282
|
-
});
|
|
8283
|
-
SharedSourceSchema = z15.object({
|
|
8284
|
-
source_id: z15.string(),
|
|
8285
|
-
publisher: z15.string().nullable(),
|
|
8286
|
-
source_type: z15.string(),
|
|
8287
|
-
used_by_claim_ids: z15.array(z15.string()),
|
|
8288
|
-
spans_sections: z15.array(z15.string())
|
|
8289
|
-
});
|
|
8290
|
-
ScopeOverlapSchema = z15.object({
|
|
8291
|
-
claim_a: z15.string(),
|
|
8292
|
-
claim_b: z15.string(),
|
|
8293
|
-
scope_a: z15.string().nullable(),
|
|
8294
|
-
scope_b: z15.string().nullable(),
|
|
8295
|
-
jaccard: z15.number().min(0).max(1),
|
|
8296
|
-
cross_section: z15.boolean(),
|
|
8297
|
-
warning: z15.string()
|
|
8298
|
-
});
|
|
8299
|
-
CrossSectionContradictionRefSchema = z15.object({
|
|
8300
|
-
contradiction_id: z15.string(),
|
|
8301
|
-
claim_ids: z15.array(z15.string()),
|
|
8302
|
-
sections: z15.array(z15.string()),
|
|
8303
|
-
type: z15.string(),
|
|
8304
|
-
severity: z15.string(),
|
|
8305
|
-
status: z15.string()
|
|
8306
|
-
});
|
|
8307
|
-
WaiverDependencySchema = z15.object({
|
|
8308
|
-
scope: z15.enum(["pack", "gate"]),
|
|
8309
|
-
family: z15.string(),
|
|
8310
|
-
reason: z15.string(),
|
|
8311
|
-
compensating_controls: z15.array(z15.string()),
|
|
8312
|
-
applied_to: z15.string(),
|
|
8313
|
-
must_disclose_in: z15.enum(["decision-brief.md", "final-report.md", "both"])
|
|
8590
|
+
SectionAcceptedSummarySchema = z16.object({
|
|
8591
|
+
section_id: z16.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
8592
|
+
purpose: z16.string(),
|
|
8593
|
+
status: z16.string(),
|
|
8594
|
+
accepted_claim_ids: z16.array(z16.string()),
|
|
8595
|
+
excluded_reason: z16.string().nullable()
|
|
8314
8596
|
});
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
scope: z15.string().nullable(),
|
|
8321
|
-
not: z15.string().nullable(),
|
|
8322
|
-
source_ids: z15.array(z15.string())
|
|
8323
|
-
});
|
|
8324
|
-
ForbiddenInputSchema = z15.object({
|
|
8325
|
-
claim_id: z15.string(),
|
|
8326
|
-
section_id: z15.string(),
|
|
8327
|
-
decision: z15.string(),
|
|
8328
|
-
reason: z15.string()
|
|
8597
|
+
ClaimClusterSchema = z16.object({
|
|
8598
|
+
cluster_id: z16.string(),
|
|
8599
|
+
shared_source_ids: z16.array(z16.string()),
|
|
8600
|
+
member_claim_ids: z16.array(z16.string()).min(1),
|
|
8601
|
+
spans_sections: z16.array(z16.string())
|
|
8329
8602
|
});
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8603
|
+
SharedSourceSchema = z16.object({
|
|
8604
|
+
source_id: z16.string(),
|
|
8605
|
+
publisher: z16.string().nullable(),
|
|
8606
|
+
source_type: z16.string(),
|
|
8607
|
+
used_by_claim_ids: z16.array(z16.string()),
|
|
8608
|
+
spans_sections: z16.array(z16.string())
|
|
8609
|
+
});
|
|
8610
|
+
ScopeOverlapSchema = z16.object({
|
|
8611
|
+
claim_a: z16.string(),
|
|
8612
|
+
claim_b: z16.string(),
|
|
8613
|
+
scope_a: z16.string().nullable(),
|
|
8614
|
+
scope_b: z16.string().nullable(),
|
|
8615
|
+
jaccard: z16.number().min(0).max(1),
|
|
8616
|
+
cross_section: z16.boolean(),
|
|
8617
|
+
warning: z16.string()
|
|
8618
|
+
});
|
|
8619
|
+
CrossSectionContradictionRefSchema = z16.object({
|
|
8620
|
+
contradiction_id: z16.string(),
|
|
8621
|
+
claim_ids: z16.array(z16.string()),
|
|
8622
|
+
sections: z16.array(z16.string()),
|
|
8623
|
+
type: z16.string(),
|
|
8624
|
+
severity: z16.string(),
|
|
8625
|
+
status: z16.string()
|
|
8626
|
+
});
|
|
8627
|
+
WaiverDependencySchema = z16.object({
|
|
8628
|
+
scope: z16.enum(["pack", "gate"]),
|
|
8629
|
+
family: z16.string(),
|
|
8630
|
+
reason: z16.string(),
|
|
8631
|
+
compensating_controls: z16.array(z16.string()),
|
|
8632
|
+
applied_to: z16.string(),
|
|
8633
|
+
must_disclose_in: z16.enum(["decision-brief.md", "final-report.md", "both"])
|
|
8634
|
+
});
|
|
8635
|
+
AllowedSynthesisInputSchema = z16.object({
|
|
8636
|
+
claim_id: z16.string(),
|
|
8637
|
+
section_id: z16.string(),
|
|
8638
|
+
artifact_path: z16.string(),
|
|
8639
|
+
asserts: z16.string(),
|
|
8640
|
+
scope: z16.string().nullable(),
|
|
8641
|
+
not: z16.string().nullable(),
|
|
8642
|
+
source_ids: z16.array(z16.string())
|
|
8643
|
+
});
|
|
8644
|
+
ForbiddenInputSchema = z16.object({
|
|
8645
|
+
claim_id: z16.string(),
|
|
8646
|
+
section_id: z16.string(),
|
|
8647
|
+
decision: z16.string(),
|
|
8648
|
+
reason: z16.string()
|
|
8649
|
+
});
|
|
8650
|
+
CrossSectionMapSchema = z16.object({
|
|
8651
|
+
pack_id: z16.string(),
|
|
8652
|
+
pack_topic: z16.string(),
|
|
8653
|
+
pack_decision: z16.string(),
|
|
8654
|
+
generated_at: z16.string(),
|
|
8655
|
+
accepted_claim_ids: z16.array(z16.string()),
|
|
8656
|
+
sections: z16.array(SectionAcceptedSummarySchema),
|
|
8657
|
+
claim_clusters: z16.array(ClaimClusterSchema),
|
|
8658
|
+
shared_sources: z16.array(SharedSourceSchema),
|
|
8659
|
+
scope_overlaps: z16.array(ScopeOverlapSchema),
|
|
8660
|
+
cross_section_contradictions: z16.array(CrossSectionContradictionRefSchema),
|
|
8661
|
+
waiver_dependencies: z16.array(WaiverDependencySchema),
|
|
8662
|
+
open_questions: z16.array(z16.string()),
|
|
8663
|
+
allowed_synthesis_inputs: z16.array(AllowedSynthesisInputSchema),
|
|
8664
|
+
forbidden_inputs: z16.array(ForbiddenInputSchema)
|
|
8345
8665
|
});
|
|
8346
8666
|
}
|
|
8347
8667
|
});
|
|
@@ -8601,13 +8921,13 @@ var init_markdown5 = __esm({
|
|
|
8601
8921
|
});
|
|
8602
8922
|
|
|
8603
8923
|
// src/synth/run.ts
|
|
8604
|
-
import { existsSync as
|
|
8605
|
-
import { mkdir as
|
|
8606
|
-
import { join as
|
|
8924
|
+
import { existsSync as existsSync21 } from "fs";
|
|
8925
|
+
import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile16 } from "fs/promises";
|
|
8926
|
+
import { join as join21, resolve as resolve17 } from "path";
|
|
8607
8927
|
import { parse as yamlParse7 } from "yaml";
|
|
8608
8928
|
async function readJsonl4(path, parse) {
|
|
8609
|
-
if (!
|
|
8610
|
-
const text = await
|
|
8929
|
+
if (!existsSync21(path)) return [];
|
|
8930
|
+
const text = await readFile18(path, "utf8");
|
|
8611
8931
|
const out = [];
|
|
8612
8932
|
for (const line of text.split(/\r?\n/)) {
|
|
8613
8933
|
if (!line.trim()) continue;
|
|
@@ -8616,27 +8936,27 @@ async function readJsonl4(path, parse) {
|
|
|
8616
8936
|
return out;
|
|
8617
8937
|
}
|
|
8618
8938
|
async function readSourceCards5(packPath) {
|
|
8619
|
-
const dir =
|
|
8620
|
-
if (!
|
|
8939
|
+
const dir = join21(packPath, "evidence", "source-cards");
|
|
8940
|
+
if (!existsSync21(dir)) return [];
|
|
8621
8941
|
const { readdir: readdir4 } = await import("fs/promises");
|
|
8622
8942
|
const entries = await readdir4(dir);
|
|
8623
8943
|
const cards = [];
|
|
8624
8944
|
for (const entry of entries) {
|
|
8625
8945
|
if (!entry.endsWith(".json")) continue;
|
|
8626
|
-
const text = await
|
|
8946
|
+
const text = await readFile18(join21(dir, entry), "utf8");
|
|
8627
8947
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
8628
8948
|
}
|
|
8629
8949
|
return cards;
|
|
8630
8950
|
}
|
|
8631
8951
|
async function workspace(options) {
|
|
8632
8952
|
const packPath = options.packPath ? resolve17(options.packPath) : process.cwd();
|
|
8633
|
-
const yamlPath =
|
|
8634
|
-
if (!
|
|
8635
|
-
const research = ResearchYamlSchema.parse(yamlParse7(await
|
|
8636
|
-
const handoffPath =
|
|
8637
|
-
if (!
|
|
8953
|
+
const yamlPath = join21(packPath, "research.yaml");
|
|
8954
|
+
if (!existsSync21(yamlPath)) throw new PackNotFoundError(packPath);
|
|
8955
|
+
const research = ResearchYamlSchema.parse(yamlParse7(await readFile18(yamlPath, "utf8")));
|
|
8956
|
+
const handoffPath = join21(packPath, "handoffs", "cowork-handoff.json");
|
|
8957
|
+
if (!existsSync21(handoffPath)) throw new HandoffNotFoundError();
|
|
8638
8958
|
const handoff2 = CoworkHandoffPayloadSchema.parse(
|
|
8639
|
-
JSON.parse(await
|
|
8959
|
+
JSON.parse(await readFile18(handoffPath, "utf8"))
|
|
8640
8960
|
);
|
|
8641
8961
|
if (handoff2.mode !== "synthesis_ready") {
|
|
8642
8962
|
return {
|
|
@@ -8658,21 +8978,21 @@ async function workspace(options) {
|
|
|
8658
8978
|
claimsBySection.set(
|
|
8659
8979
|
section.id,
|
|
8660
8980
|
await readJsonl4(
|
|
8661
|
-
|
|
8981
|
+
join21(packPath, "sections", section.id, "claims.jsonl"),
|
|
8662
8982
|
(r) => ClaimSchema.parse(r)
|
|
8663
8983
|
)
|
|
8664
8984
|
);
|
|
8665
8985
|
reviewsBySection.set(
|
|
8666
8986
|
section.id,
|
|
8667
8987
|
await readJsonl4(
|
|
8668
|
-
|
|
8988
|
+
join21(packPath, "sections", section.id, "claim-reviews.jsonl"),
|
|
8669
8989
|
(r) => ClaimReviewSchema.parse(r)
|
|
8670
8990
|
)
|
|
8671
8991
|
);
|
|
8672
8992
|
contradictionsBySection.set(
|
|
8673
8993
|
section.id,
|
|
8674
8994
|
await readJsonl4(
|
|
8675
|
-
|
|
8995
|
+
join21(packPath, "sections", section.id, "contradictions.jsonl"),
|
|
8676
8996
|
(r) => ContradictionSchema.parse(r)
|
|
8677
8997
|
)
|
|
8678
8998
|
);
|
|
@@ -8689,17 +9009,17 @@ async function workspace(options) {
|
|
|
8689
9009
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8690
9010
|
})
|
|
8691
9011
|
);
|
|
8692
|
-
const synthDir =
|
|
8693
|
-
await
|
|
9012
|
+
const synthDir = join21(packPath, "synthesis");
|
|
9013
|
+
await mkdir16(synthDir, { recursive: true });
|
|
8694
9014
|
const filesWritten = [];
|
|
8695
9015
|
const writeIfAbsent = async (relPath2, content) => {
|
|
8696
|
-
const abs =
|
|
8697
|
-
if (
|
|
9016
|
+
const abs = join21(synthDir, relPath2);
|
|
9017
|
+
if (existsSync21(abs)) return;
|
|
8698
9018
|
await writeFile16(abs, content, "utf8");
|
|
8699
9019
|
filesWritten.push(abs);
|
|
8700
9020
|
};
|
|
8701
9021
|
const writeAlways = async (relPath2, content) => {
|
|
8702
|
-
const abs =
|
|
9022
|
+
const abs = join21(synthDir, relPath2);
|
|
8703
9023
|
await writeFile16(abs, content, "utf8");
|
|
8704
9024
|
filesWritten.push(abs);
|
|
8705
9025
|
};
|
|
@@ -9509,179 +9829,179 @@ var init_aggregate = __esm({
|
|
|
9509
9829
|
});
|
|
9510
9830
|
|
|
9511
9831
|
// src/audit/schema.ts
|
|
9512
|
-
import { z as
|
|
9832
|
+
import { z as z17 } from "zod";
|
|
9513
9833
|
var PackVerdictSchema, HandoffModeSchema2, OrphanClaimRowSchema, StaleSourceRowSchema, WeakSourceRowSchema, UnresolvedContradictionRowSchema, ScopeWideningRiskRowSchema, SourceDiversityGapRowSchema, SynthesisReadinessRowSchema, PackAuditPayloadSchema;
|
|
9514
9834
|
var init_schema15 = __esm({
|
|
9515
9835
|
"src/audit/schema.ts"() {
|
|
9516
9836
|
"use strict";
|
|
9517
|
-
PackVerdictSchema =
|
|
9837
|
+
PackVerdictSchema = z17.enum([
|
|
9518
9838
|
"ready_for_synthesis",
|
|
9519
9839
|
"repair_required",
|
|
9520
9840
|
"human_review_required",
|
|
9521
9841
|
"blocked"
|
|
9522
9842
|
]);
|
|
9523
|
-
HandoffModeSchema2 =
|
|
9843
|
+
HandoffModeSchema2 = z17.enum([
|
|
9524
9844
|
"repair_required",
|
|
9525
9845
|
"synthesis_ready",
|
|
9526
9846
|
"human_review_required",
|
|
9527
9847
|
"unknown"
|
|
9528
9848
|
]);
|
|
9529
|
-
OrphanClaimRowSchema =
|
|
9530
|
-
claim_id:
|
|
9531
|
-
section_id:
|
|
9532
|
-
reason:
|
|
9849
|
+
OrphanClaimRowSchema = z17.object({
|
|
9850
|
+
claim_id: z17.string(),
|
|
9851
|
+
section_id: z17.string(),
|
|
9852
|
+
reason: z17.enum([
|
|
9533
9853
|
"missing_source_card",
|
|
9534
9854
|
"missing_source_hash",
|
|
9535
9855
|
"missing_evidence_excerpt",
|
|
9536
9856
|
"unresolvable_source_id"
|
|
9537
9857
|
]),
|
|
9538
|
-
details:
|
|
9539
|
-
artifact_path:
|
|
9540
|
-
});
|
|
9541
|
-
StaleSourceRowSchema =
|
|
9542
|
-
source_id:
|
|
9543
|
-
section_id:
|
|
9544
|
-
publisher:
|
|
9545
|
-
reason:
|
|
9546
|
-
details:
|
|
9547
|
-
artifact_path:
|
|
9548
|
-
policy:
|
|
9549
|
-
required:
|
|
9550
|
-
max_source_age_months:
|
|
9551
|
-
stale_source_policy:
|
|
9858
|
+
details: z17.string(),
|
|
9859
|
+
artifact_path: z17.string()
|
|
9860
|
+
});
|
|
9861
|
+
StaleSourceRowSchema = z17.object({
|
|
9862
|
+
source_id: z17.string(),
|
|
9863
|
+
section_id: z17.string(),
|
|
9864
|
+
publisher: z17.string().nullable(),
|
|
9865
|
+
reason: z17.enum(["too_old", "missing_date", "unparseable_date"]),
|
|
9866
|
+
details: z17.string(),
|
|
9867
|
+
artifact_path: z17.string(),
|
|
9868
|
+
policy: z17.object({
|
|
9869
|
+
required: z17.boolean(),
|
|
9870
|
+
max_source_age_months: z17.number().int().nullable(),
|
|
9871
|
+
stale_source_policy: z17.enum(["warn", "fail"])
|
|
9552
9872
|
})
|
|
9553
9873
|
});
|
|
9554
|
-
WeakSourceRowSchema =
|
|
9555
|
-
reason:
|
|
9874
|
+
WeakSourceRowSchema = z17.object({
|
|
9875
|
+
reason: z17.enum([
|
|
9556
9876
|
"source_cluster_monopoly",
|
|
9557
9877
|
"low_independent_publishers",
|
|
9558
9878
|
"missing_primary_source",
|
|
9559
9879
|
"excessive_type_imbalance",
|
|
9560
9880
|
"failed_fetches_reducing_floor"
|
|
9561
9881
|
]),
|
|
9562
|
-
section_id:
|
|
9563
|
-
details:
|
|
9564
|
-
evidence_ids:
|
|
9565
|
-
artifact_path:
|
|
9566
|
-
});
|
|
9567
|
-
UnresolvedContradictionRowSchema =
|
|
9568
|
-
contradiction_id:
|
|
9569
|
-
section_id:
|
|
9570
|
-
type:
|
|
9571
|
-
severity:
|
|
9572
|
-
status:
|
|
9573
|
-
claim_ids:
|
|
9574
|
-
artifact_path:
|
|
9575
|
-
});
|
|
9576
|
-
ScopeWideningRiskRowSchema =
|
|
9577
|
-
reason:
|
|
9882
|
+
section_id: z17.string(),
|
|
9883
|
+
details: z17.string(),
|
|
9884
|
+
evidence_ids: z17.array(z17.string()),
|
|
9885
|
+
artifact_path: z17.string()
|
|
9886
|
+
});
|
|
9887
|
+
UnresolvedContradictionRowSchema = z17.object({
|
|
9888
|
+
contradiction_id: z17.string(),
|
|
9889
|
+
section_id: z17.string(),
|
|
9890
|
+
type: z17.string(),
|
|
9891
|
+
severity: z17.string(),
|
|
9892
|
+
status: z17.string(),
|
|
9893
|
+
claim_ids: z17.array(z17.string()),
|
|
9894
|
+
artifact_path: z17.string()
|
|
9895
|
+
});
|
|
9896
|
+
ScopeWideningRiskRowSchema = z17.object({
|
|
9897
|
+
reason: z17.enum([
|
|
9578
9898
|
"overgeneralization_finding",
|
|
9579
9899
|
"scope_null_in_use",
|
|
9580
9900
|
"missing_not_flagged",
|
|
9581
9901
|
"contextual_to_universal_risk"
|
|
9582
9902
|
]),
|
|
9583
|
-
claim_id:
|
|
9584
|
-
section_id:
|
|
9585
|
-
details:
|
|
9586
|
-
artifact_path:
|
|
9903
|
+
claim_id: z17.string(),
|
|
9904
|
+
section_id: z17.string(),
|
|
9905
|
+
details: z17.string(),
|
|
9906
|
+
artifact_path: z17.string()
|
|
9587
9907
|
});
|
|
9588
|
-
SourceDiversityGapRowSchema =
|
|
9589
|
-
reason:
|
|
9908
|
+
SourceDiversityGapRowSchema = z17.object({
|
|
9909
|
+
reason: z17.enum([
|
|
9590
9910
|
"section_publisher_monopoly",
|
|
9591
9911
|
"low_section_publisher_count",
|
|
9592
9912
|
"cross_section_publisher_overlap",
|
|
9593
9913
|
"section_has_no_sources"
|
|
9594
9914
|
]),
|
|
9595
|
-
section_id:
|
|
9596
|
-
details:
|
|
9597
|
-
evidence_ids:
|
|
9598
|
-
});
|
|
9599
|
-
SynthesisReadinessRowSchema =
|
|
9600
|
-
section_id:
|
|
9601
|
-
purpose:
|
|
9602
|
-
status:
|
|
9603
|
-
has_gate_run:
|
|
9604
|
-
has_review_run:
|
|
9605
|
-
gate_verdict:
|
|
9606
|
-
synthesis_eligible:
|
|
9607
|
-
candidate_claims:
|
|
9608
|
-
accepted_claims:
|
|
9609
|
-
repair_claims:
|
|
9610
|
-
rejected_claims:
|
|
9611
|
-
dispositioned_claims:
|
|
9612
|
-
blocking_reasons:
|
|
9915
|
+
section_id: z17.string(),
|
|
9916
|
+
details: z17.string(),
|
|
9917
|
+
evidence_ids: z17.array(z17.string())
|
|
9918
|
+
});
|
|
9919
|
+
SynthesisReadinessRowSchema = z17.object({
|
|
9920
|
+
section_id: z17.string(),
|
|
9921
|
+
purpose: z17.string(),
|
|
9922
|
+
status: z17.string(),
|
|
9923
|
+
has_gate_run: z17.boolean(),
|
|
9924
|
+
has_review_run: z17.boolean(),
|
|
9925
|
+
gate_verdict: z17.string().nullable(),
|
|
9926
|
+
synthesis_eligible: z17.boolean(),
|
|
9927
|
+
candidate_claims: z17.number().int().nonnegative(),
|
|
9928
|
+
accepted_claims: z17.number().int().nonnegative(),
|
|
9929
|
+
repair_claims: z17.number().int().nonnegative(),
|
|
9930
|
+
rejected_claims: z17.number().int().nonnegative(),
|
|
9931
|
+
dispositioned_claims: z17.number().int().nonnegative(),
|
|
9932
|
+
blocking_reasons: z17.array(z17.string()),
|
|
9613
9933
|
cowork_handoff_mode: HandoffModeSchema2,
|
|
9614
|
-
workspace_allowed:
|
|
9934
|
+
workspace_allowed: z17.boolean()
|
|
9615
9935
|
});
|
|
9616
|
-
PackAuditPayloadSchema =
|
|
9617
|
-
pack_id:
|
|
9618
|
-
pack_topic:
|
|
9619
|
-
generated_at:
|
|
9936
|
+
PackAuditPayloadSchema = z17.object({
|
|
9937
|
+
pack_id: z17.string(),
|
|
9938
|
+
pack_topic: z17.string(),
|
|
9939
|
+
generated_at: z17.string(),
|
|
9620
9940
|
verdict: PackVerdictSchema,
|
|
9621
|
-
synthesis_allowed:
|
|
9622
|
-
section_summaries:
|
|
9623
|
-
claim_summary:
|
|
9624
|
-
total:
|
|
9625
|
-
candidate:
|
|
9626
|
-
accepted_for_synthesis:
|
|
9627
|
-
rejected:
|
|
9628
|
-
needs_repair:
|
|
9629
|
-
dispositioned:
|
|
9630
|
-
no_review:
|
|
9631
|
-
with_evidence_excerpt:
|
|
9632
|
-
with_source_hashes:
|
|
9633
|
-
scope_null:
|
|
9634
|
-
not_null:
|
|
9635
|
-
orphans:
|
|
9941
|
+
synthesis_allowed: z17.boolean(),
|
|
9942
|
+
section_summaries: z17.array(SynthesisReadinessRowSchema),
|
|
9943
|
+
claim_summary: z17.object({
|
|
9944
|
+
total: z17.number().int().nonnegative(),
|
|
9945
|
+
candidate: z17.number().int().nonnegative(),
|
|
9946
|
+
accepted_for_synthesis: z17.number().int().nonnegative(),
|
|
9947
|
+
rejected: z17.number().int().nonnegative(),
|
|
9948
|
+
needs_repair: z17.number().int().nonnegative(),
|
|
9949
|
+
dispositioned: z17.number().int().nonnegative(),
|
|
9950
|
+
no_review: z17.number().int().nonnegative(),
|
|
9951
|
+
with_evidence_excerpt: z17.number().int().nonnegative(),
|
|
9952
|
+
with_source_hashes: z17.number().int().nonnegative(),
|
|
9953
|
+
scope_null: z17.number().int().nonnegative(),
|
|
9954
|
+
not_null: z17.number().int().nonnegative(),
|
|
9955
|
+
orphans: z17.number().int().nonnegative()
|
|
9636
9956
|
}),
|
|
9637
|
-
source_summary:
|
|
9638
|
-
total:
|
|
9639
|
-
primary:
|
|
9640
|
-
secondary:
|
|
9641
|
-
forum:
|
|
9642
|
-
benchmark:
|
|
9643
|
-
docs:
|
|
9644
|
-
unknown:
|
|
9645
|
-
independent_publishers:
|
|
9646
|
-
failed_fetches:
|
|
9647
|
-
sections_with_sources:
|
|
9648
|
-
sections_without_sources:
|
|
9957
|
+
source_summary: z17.object({
|
|
9958
|
+
total: z17.number().int().nonnegative(),
|
|
9959
|
+
primary: z17.number().int().nonnegative(),
|
|
9960
|
+
secondary: z17.number().int().nonnegative(),
|
|
9961
|
+
forum: z17.number().int().nonnegative(),
|
|
9962
|
+
benchmark: z17.number().int().nonnegative(),
|
|
9963
|
+
docs: z17.number().int().nonnegative(),
|
|
9964
|
+
unknown: z17.number().int().nonnegative(),
|
|
9965
|
+
independent_publishers: z17.number().int().nonnegative(),
|
|
9966
|
+
failed_fetches: z17.number().int().nonnegative(),
|
|
9967
|
+
sections_with_sources: z17.number().int().nonnegative(),
|
|
9968
|
+
sections_without_sources: z17.number().int().nonnegative()
|
|
9649
9969
|
}),
|
|
9650
|
-
contradiction_summary:
|
|
9651
|
-
total:
|
|
9652
|
-
unresolved:
|
|
9653
|
-
blocking:
|
|
9654
|
-
reconciled:
|
|
9655
|
-
preserved_deliberately:
|
|
9656
|
-
rejected:
|
|
9657
|
-
by_type:
|
|
9658
|
-
sections_with_clean_ledger:
|
|
9970
|
+
contradiction_summary: z17.object({
|
|
9971
|
+
total: z17.number().int().nonnegative(),
|
|
9972
|
+
unresolved: z17.number().int().nonnegative(),
|
|
9973
|
+
blocking: z17.number().int().nonnegative(),
|
|
9974
|
+
reconciled: z17.number().int().nonnegative(),
|
|
9975
|
+
preserved_deliberately: z17.number().int().nonnegative(),
|
|
9976
|
+
rejected: z17.number().int().nonnegative(),
|
|
9977
|
+
by_type: z17.record(z17.string(), z17.number().int().nonnegative()),
|
|
9978
|
+
sections_with_clean_ledger: z17.number().int().nonnegative()
|
|
9659
9979
|
}),
|
|
9660
|
-
review_summary:
|
|
9661
|
-
sections_with_review_run:
|
|
9662
|
-
sections_without_review_run:
|
|
9663
|
-
decision_counts:
|
|
9664
|
-
blocking_findings:
|
|
9980
|
+
review_summary: z17.object({
|
|
9981
|
+
sections_with_review_run: z17.number().int().nonnegative(),
|
|
9982
|
+
sections_without_review_run: z17.number().int().nonnegative(),
|
|
9983
|
+
decision_counts: z17.record(z17.string(), z17.number().int().nonnegative()),
|
|
9984
|
+
blocking_findings: z17.number().int().nonnegative()
|
|
9665
9985
|
}),
|
|
9666
|
-
waiver_summary:
|
|
9667
|
-
total:
|
|
9668
|
-
invalid:
|
|
9669
|
-
by_family:
|
|
9986
|
+
waiver_summary: z17.object({
|
|
9987
|
+
total: z17.number().int().nonnegative(),
|
|
9988
|
+
invalid: z17.number().int().nonnegative(),
|
|
9989
|
+
by_family: z17.record(z17.string(), z17.number().int().nonnegative())
|
|
9670
9990
|
}),
|
|
9671
|
-
readiness_summary:
|
|
9672
|
-
total_sections:
|
|
9673
|
-
ready_sections:
|
|
9674
|
-
repair_sections:
|
|
9675
|
-
blocked_sections:
|
|
9676
|
-
no_gate_sections:
|
|
9677
|
-
no_review_sections:
|
|
9991
|
+
readiness_summary: z17.object({
|
|
9992
|
+
total_sections: z17.number().int().nonnegative(),
|
|
9993
|
+
ready_sections: z17.number().int().nonnegative(),
|
|
9994
|
+
repair_sections: z17.number().int().nonnegative(),
|
|
9995
|
+
blocked_sections: z17.number().int().nonnegative(),
|
|
9996
|
+
no_gate_sections: z17.number().int().nonnegative(),
|
|
9997
|
+
no_review_sections: z17.number().int().nonnegative(),
|
|
9678
9998
|
cowork_handoff_mode: HandoffModeSchema2,
|
|
9679
|
-
workspace_allowed:
|
|
9999
|
+
workspace_allowed: z17.boolean()
|
|
9680
10000
|
}),
|
|
9681
|
-
audit_files:
|
|
9682
|
-
blocking_reasons:
|
|
9683
|
-
warnings:
|
|
9684
|
-
next_actions:
|
|
10001
|
+
audit_files: z17.array(z17.string()),
|
|
10002
|
+
blocking_reasons: z17.array(z17.string()),
|
|
10003
|
+
warnings: z17.array(z17.string()),
|
|
10004
|
+
next_actions: z17.array(z17.string())
|
|
9685
10005
|
});
|
|
9686
10006
|
}
|
|
9687
10007
|
});
|
|
@@ -9903,13 +10223,13 @@ var init_markdown6 = __esm({
|
|
|
9903
10223
|
});
|
|
9904
10224
|
|
|
9905
10225
|
// src/audit/run.ts
|
|
9906
|
-
import { existsSync as
|
|
9907
|
-
import { mkdir as
|
|
9908
|
-
import { join as
|
|
10226
|
+
import { existsSync as existsSync22 } from "fs";
|
|
10227
|
+
import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile17 } from "fs/promises";
|
|
10228
|
+
import { join as join22, resolve as resolve18 } from "path";
|
|
9909
10229
|
import { parse as yamlParse8 } from "yaml";
|
|
9910
10230
|
async function readJsonl5(path, parse, warnings) {
|
|
9911
|
-
if (!
|
|
9912
|
-
const text = await
|
|
10231
|
+
if (!existsSync22(path)) return [];
|
|
10232
|
+
const text = await readFile19(path, "utf8");
|
|
9913
10233
|
const out = [];
|
|
9914
10234
|
for (const line of text.split(/\r?\n/)) {
|
|
9915
10235
|
if (!line.trim()) continue;
|
|
@@ -9922,15 +10242,15 @@ async function readJsonl5(path, parse, warnings) {
|
|
|
9922
10242
|
return out;
|
|
9923
10243
|
}
|
|
9924
10244
|
async function readSourceCards6(packPath, warnings) {
|
|
9925
|
-
const dir =
|
|
9926
|
-
if (!
|
|
10245
|
+
const dir = join22(packPath, "evidence", "source-cards");
|
|
10246
|
+
if (!existsSync22(dir)) return [];
|
|
9927
10247
|
const { readdir: readdir4 } = await import("fs/promises");
|
|
9928
10248
|
const entries = await readdir4(dir);
|
|
9929
10249
|
const cards = [];
|
|
9930
10250
|
for (const entry of entries) {
|
|
9931
10251
|
if (!entry.endsWith(".json")) continue;
|
|
9932
10252
|
try {
|
|
9933
|
-
const text = await
|
|
10253
|
+
const text = await readFile19(join22(dir, entry), "utf8");
|
|
9934
10254
|
cards.push(SourceCardSchema.parse(JSON.parse(text)));
|
|
9935
10255
|
} catch (err) {
|
|
9936
10256
|
warnings.push(`malformed source card ${entry}: ${err instanceof Error ? err.message : "parse error"}`);
|
|
@@ -9939,29 +10259,29 @@ async function readSourceCards6(packPath, warnings) {
|
|
|
9939
10259
|
return cards;
|
|
9940
10260
|
}
|
|
9941
10261
|
async function readGate2(packPath, sectionId, warnings) {
|
|
9942
|
-
const path =
|
|
9943
|
-
if (!
|
|
10262
|
+
const path = join22(packPath, "audits", `${sectionId}-gate.json`);
|
|
10263
|
+
if (!existsSync22(path)) return null;
|
|
9944
10264
|
try {
|
|
9945
|
-
return SectionGateResultSchema.parse(JSON.parse(await
|
|
10265
|
+
return SectionGateResultSchema.parse(JSON.parse(await readFile19(path, "utf8")));
|
|
9946
10266
|
} catch (err) {
|
|
9947
10267
|
warnings.push(`malformed gate result for ${sectionId}: ${err instanceof Error ? err.message : "parse error"}`);
|
|
9948
10268
|
return null;
|
|
9949
10269
|
}
|
|
9950
10270
|
}
|
|
9951
10271
|
async function readHandoff(packPath, warnings) {
|
|
9952
|
-
const path =
|
|
9953
|
-
if (!
|
|
10272
|
+
const path = join22(packPath, "handoffs", "cowork-handoff.json");
|
|
10273
|
+
if (!existsSync22(path)) return null;
|
|
9954
10274
|
try {
|
|
9955
|
-
return CoworkHandoffPayloadSchema.parse(JSON.parse(await
|
|
10275
|
+
return CoworkHandoffPayloadSchema.parse(JSON.parse(await readFile19(path, "utf8")));
|
|
9956
10276
|
} catch (err) {
|
|
9957
10277
|
warnings.push(`malformed handoff: ${err instanceof Error ? err.message : "parse error"}`);
|
|
9958
10278
|
return null;
|
|
9959
10279
|
}
|
|
9960
10280
|
}
|
|
9961
10281
|
async function readSourceIdsForSection(packPath, sectionId) {
|
|
9962
|
-
const path =
|
|
9963
|
-
if (!
|
|
9964
|
-
const text = await
|
|
10282
|
+
const path = join22(packPath, "sections", sectionId, "sources.jsonl");
|
|
10283
|
+
if (!existsSync22(path)) return [];
|
|
10284
|
+
const text = await readFile19(path, "utf8");
|
|
9965
10285
|
const ids = [];
|
|
9966
10286
|
for (const line of text.split(/\r?\n/)) {
|
|
9967
10287
|
if (!line.trim()) continue;
|
|
@@ -9975,41 +10295,41 @@ async function readSourceIdsForSection(packPath, sectionId) {
|
|
|
9975
10295
|
}
|
|
9976
10296
|
async function audit(options) {
|
|
9977
10297
|
const packPath = options.packPath ? resolve18(options.packPath) : process.cwd();
|
|
9978
|
-
const yamlPath =
|
|
9979
|
-
if (!
|
|
9980
|
-
const research = ResearchYamlSchema.parse(yamlParse8(await
|
|
10298
|
+
const yamlPath = join22(packPath, "research.yaml");
|
|
10299
|
+
if (!existsSync22(yamlPath)) throw new PackNotFoundError(packPath);
|
|
10300
|
+
const research = ResearchYamlSchema.parse(yamlParse8(await readFile19(yamlPath, "utf8")));
|
|
9981
10301
|
const warnings = [];
|
|
9982
10302
|
const perSection = /* @__PURE__ */ new Map();
|
|
9983
10303
|
for (const section of research.sections) {
|
|
9984
10304
|
const sid = section.id;
|
|
9985
10305
|
const claims = await readJsonl5(
|
|
9986
|
-
|
|
10306
|
+
join22(packPath, "sections", sid, "claims.jsonl"),
|
|
9987
10307
|
(r) => ClaimSchema.parse(r),
|
|
9988
10308
|
warnings
|
|
9989
10309
|
);
|
|
9990
10310
|
const candidateClaims = claims.filter((c) => c.review_state === "candidate");
|
|
9991
10311
|
const claimReviews = await readJsonl5(
|
|
9992
|
-
|
|
10312
|
+
join22(packPath, "sections", sid, "claim-reviews.jsonl"),
|
|
9993
10313
|
(r) => ClaimReviewSchema.parse(r),
|
|
9994
10314
|
warnings
|
|
9995
10315
|
);
|
|
9996
10316
|
const contradictions = await readJsonl5(
|
|
9997
|
-
|
|
10317
|
+
join22(packPath, "sections", sid, "contradictions.jsonl"),
|
|
9998
10318
|
(r) => ContradictionSchema.parse(r),
|
|
9999
10319
|
warnings
|
|
10000
10320
|
);
|
|
10001
10321
|
const resolutions = await readJsonl5(
|
|
10002
|
-
|
|
10322
|
+
join22(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
|
|
10003
10323
|
(r) => ContradictionResolutionSchema.parse(r),
|
|
10004
10324
|
warnings
|
|
10005
10325
|
);
|
|
10006
10326
|
const dispositions = await readJsonl5(
|
|
10007
|
-
|
|
10327
|
+
join22(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
|
|
10008
10328
|
(r) => ClaimSynthesisDispositionSchema.parse(r),
|
|
10009
10329
|
warnings
|
|
10010
10330
|
);
|
|
10011
10331
|
const findings = await readJsonl5(
|
|
10012
|
-
|
|
10332
|
+
join22(packPath, "audits", `${sid}-findings.jsonl`),
|
|
10013
10333
|
(r) => ReviewFindingSchema.parse(r),
|
|
10014
10334
|
warnings
|
|
10015
10335
|
);
|
|
@@ -10029,7 +10349,7 @@ async function audit(options) {
|
|
|
10029
10349
|
}
|
|
10030
10350
|
const sources = await readSourceCards6(packPath, warnings);
|
|
10031
10351
|
const receipts = await readJsonl5(
|
|
10032
|
-
|
|
10352
|
+
join22(packPath, "evidence", "fetch-log.jsonl"),
|
|
10033
10353
|
(r) => FetchReceiptSchema.parse(r),
|
|
10034
10354
|
warnings
|
|
10035
10355
|
);
|
|
@@ -10045,11 +10365,11 @@ async function audit(options) {
|
|
|
10045
10365
|
warnings
|
|
10046
10366
|
});
|
|
10047
10367
|
const payload = PackAuditPayloadSchema.parse(result.payload);
|
|
10048
|
-
const auditsDir =
|
|
10049
|
-
await
|
|
10368
|
+
const auditsDir = join22(packPath, "audits");
|
|
10369
|
+
await mkdir17(auditsDir, { recursive: true });
|
|
10050
10370
|
const filesWritten = [];
|
|
10051
10371
|
const writeFileAndTrack = async (rel, content) => {
|
|
10052
|
-
const abs =
|
|
10372
|
+
const abs = join22(packPath, rel);
|
|
10053
10373
|
await writeFile17(abs, content, "utf8");
|
|
10054
10374
|
filesWritten.push(abs);
|
|
10055
10375
|
};
|
|
@@ -10133,18 +10453,18 @@ var init_audit = __esm({
|
|
|
10133
10453
|
});
|
|
10134
10454
|
|
|
10135
10455
|
// src/freeze/checks.ts
|
|
10136
|
-
import { existsSync as
|
|
10137
|
-
import { readFile as
|
|
10456
|
+
import { existsSync as existsSync23 } from "fs";
|
|
10457
|
+
import { readFile as readFile20, stat as stat2 } from "fs/promises";
|
|
10138
10458
|
import { createHash as createHash8 } from "crypto";
|
|
10139
|
-
import { join as
|
|
10459
|
+
import { join as join23 } from "path";
|
|
10140
10460
|
async function fileSha2562(absPath) {
|
|
10141
|
-
const buf = await
|
|
10461
|
+
const buf = await readFile20(absPath);
|
|
10142
10462
|
const sha = createHash8("sha256").update(buf).digest("hex");
|
|
10143
10463
|
return { path: absPath, sha256: sha, bytes: buf.byteLength };
|
|
10144
10464
|
}
|
|
10145
10465
|
async function hashArtifact(ctx, rel) {
|
|
10146
|
-
const abs =
|
|
10147
|
-
if (!
|
|
10466
|
+
const abs = join23(ctx.packPath, rel);
|
|
10467
|
+
if (!existsSync23(abs)) {
|
|
10148
10468
|
ctx.missingArtifacts.push(rel);
|
|
10149
10469
|
return null;
|
|
10150
10470
|
}
|
|
@@ -10355,88 +10675,88 @@ var init_markdown7 = __esm({
|
|
|
10355
10675
|
});
|
|
10356
10676
|
|
|
10357
10677
|
// src/freeze/schema.ts
|
|
10358
|
-
import { z as
|
|
10678
|
+
import { z as z18 } from "zod";
|
|
10359
10679
|
var ArtifactHashSchema, IntegrityCheckSchema, FreezeReceiptPayloadSchema, FreezeRefusalPayloadSchema;
|
|
10360
10680
|
var init_schema16 = __esm({
|
|
10361
10681
|
"src/freeze/schema.ts"() {
|
|
10362
10682
|
"use strict";
|
|
10363
|
-
ArtifactHashSchema =
|
|
10364
|
-
path:
|
|
10365
|
-
sha256:
|
|
10366
|
-
bytes:
|
|
10367
|
-
});
|
|
10368
|
-
IntegrityCheckSchema =
|
|
10369
|
-
name:
|
|
10370
|
-
passed:
|
|
10371
|
-
detail:
|
|
10372
|
-
});
|
|
10373
|
-
FreezeReceiptPayloadSchema =
|
|
10374
|
-
pack_id:
|
|
10375
|
-
pack_topic:
|
|
10376
|
-
frozen_at:
|
|
10377
|
-
verdict:
|
|
10378
|
-
pack_audit_hash:
|
|
10379
|
-
handoff_hash:
|
|
10380
|
-
synthesis_hashes:
|
|
10381
|
-
canonical_artifact_hashes:
|
|
10382
|
-
accepted_claim_ids:
|
|
10383
|
-
cited_claim_ids:
|
|
10384
|
-
uncited_accepted_claim_ids:
|
|
10385
|
-
unresolved_contradictions:
|
|
10386
|
-
|
|
10387
|
-
contradiction_id:
|
|
10388
|
-
section_id:
|
|
10389
|
-
type:
|
|
10390
|
-
severity:
|
|
10391
|
-
status:
|
|
10392
|
-
disclosed_in:
|
|
10683
|
+
ArtifactHashSchema = z18.object({
|
|
10684
|
+
path: z18.string(),
|
|
10685
|
+
sha256: z18.string().regex(/^[a-f0-9]{64}$/),
|
|
10686
|
+
bytes: z18.number().int().nonnegative()
|
|
10687
|
+
});
|
|
10688
|
+
IntegrityCheckSchema = z18.object({
|
|
10689
|
+
name: z18.string().min(1),
|
|
10690
|
+
passed: z18.boolean(),
|
|
10691
|
+
detail: z18.string()
|
|
10692
|
+
});
|
|
10693
|
+
FreezeReceiptPayloadSchema = z18.object({
|
|
10694
|
+
pack_id: z18.string(),
|
|
10695
|
+
pack_topic: z18.string(),
|
|
10696
|
+
frozen_at: z18.string(),
|
|
10697
|
+
verdict: z18.literal("frozen"),
|
|
10698
|
+
pack_audit_hash: z18.string().regex(/^[a-f0-9]{64}$/),
|
|
10699
|
+
handoff_hash: z18.string().regex(/^[a-f0-9]{64}$/),
|
|
10700
|
+
synthesis_hashes: z18.array(ArtifactHashSchema),
|
|
10701
|
+
canonical_artifact_hashes: z18.array(ArtifactHashSchema),
|
|
10702
|
+
accepted_claim_ids: z18.array(z18.string()),
|
|
10703
|
+
cited_claim_ids: z18.array(z18.string()),
|
|
10704
|
+
uncited_accepted_claim_ids: z18.array(z18.string()),
|
|
10705
|
+
unresolved_contradictions: z18.array(
|
|
10706
|
+
z18.object({
|
|
10707
|
+
contradiction_id: z18.string(),
|
|
10708
|
+
section_id: z18.string(),
|
|
10709
|
+
type: z18.string(),
|
|
10710
|
+
severity: z18.string(),
|
|
10711
|
+
status: z18.string(),
|
|
10712
|
+
disclosed_in: z18.array(z18.string())
|
|
10393
10713
|
})
|
|
10394
10714
|
),
|
|
10395
|
-
waivers_disclosed:
|
|
10396
|
-
|
|
10397
|
-
family:
|
|
10398
|
-
applied_to:
|
|
10399
|
-
reason:
|
|
10400
|
-
compensating_controls:
|
|
10401
|
-
disclosed_in:
|
|
10715
|
+
waivers_disclosed: z18.array(
|
|
10716
|
+
z18.object({
|
|
10717
|
+
family: z18.string(),
|
|
10718
|
+
applied_to: z18.string(),
|
|
10719
|
+
reason: z18.string(),
|
|
10720
|
+
compensating_controls: z18.array(z18.string()),
|
|
10721
|
+
disclosed_in: z18.array(z18.string())
|
|
10402
10722
|
})
|
|
10403
10723
|
),
|
|
10404
|
-
sections:
|
|
10405
|
-
|
|
10406
|
-
section_id:
|
|
10407
|
-
status:
|
|
10408
|
-
accepted_claims:
|
|
10409
|
-
sources:
|
|
10410
|
-
contradictions:
|
|
10724
|
+
sections: z18.array(
|
|
10725
|
+
z18.object({
|
|
10726
|
+
section_id: z18.string(),
|
|
10727
|
+
status: z18.string(),
|
|
10728
|
+
accepted_claims: z18.number().int().nonnegative(),
|
|
10729
|
+
sources: z18.number().int().nonnegative(),
|
|
10730
|
+
contradictions: z18.number().int().nonnegative()
|
|
10411
10731
|
})
|
|
10412
10732
|
),
|
|
10413
|
-
source_count:
|
|
10414
|
-
claim_count:
|
|
10415
|
-
contradiction_count:
|
|
10416
|
-
review_finding_count:
|
|
10417
|
-
gate_result_count:
|
|
10418
|
-
integrity_checks:
|
|
10419
|
-
});
|
|
10420
|
-
FreezeRefusalPayloadSchema =
|
|
10421
|
-
pack_id:
|
|
10422
|
-
pack_topic:
|
|
10423
|
-
checked_at:
|
|
10424
|
-
verdict:
|
|
10425
|
-
reasons:
|
|
10426
|
-
blocking_reasons:
|
|
10427
|
-
missing_artifacts:
|
|
10428
|
-
invalid_artifacts:
|
|
10429
|
-
next_actions:
|
|
10430
|
-
would_freeze:
|
|
10733
|
+
source_count: z18.number().int().nonnegative(),
|
|
10734
|
+
claim_count: z18.number().int().nonnegative(),
|
|
10735
|
+
contradiction_count: z18.number().int().nonnegative(),
|
|
10736
|
+
review_finding_count: z18.number().int().nonnegative(),
|
|
10737
|
+
gate_result_count: z18.number().int().nonnegative(),
|
|
10738
|
+
integrity_checks: z18.array(IntegrityCheckSchema)
|
|
10739
|
+
});
|
|
10740
|
+
FreezeRefusalPayloadSchema = z18.object({
|
|
10741
|
+
pack_id: z18.string(),
|
|
10742
|
+
pack_topic: z18.string(),
|
|
10743
|
+
checked_at: z18.string(),
|
|
10744
|
+
verdict: z18.literal("refused"),
|
|
10745
|
+
reasons: z18.array(z18.string()),
|
|
10746
|
+
blocking_reasons: z18.array(z18.string()),
|
|
10747
|
+
missing_artifacts: z18.array(z18.string()),
|
|
10748
|
+
invalid_artifacts: z18.array(z18.object({ path: z18.string(), error: z18.string() })),
|
|
10749
|
+
next_actions: z18.array(z18.string()),
|
|
10750
|
+
would_freeze: z18.literal(false)
|
|
10431
10751
|
});
|
|
10432
10752
|
}
|
|
10433
10753
|
});
|
|
10434
10754
|
|
|
10435
10755
|
// src/freeze/run.ts
|
|
10436
|
-
import { existsSync as
|
|
10437
|
-
import { mkdir as
|
|
10756
|
+
import { existsSync as existsSync24 } from "fs";
|
|
10757
|
+
import { mkdir as mkdir18, readFile as readFile21, readdir as readdir2, unlink, writeFile as writeFile18 } from "fs/promises";
|
|
10438
10758
|
import { createHash as createHash9 } from "crypto";
|
|
10439
|
-
import { join as
|
|
10759
|
+
import { join as join24, resolve as resolve19 } from "path";
|
|
10440
10760
|
import { parse as yamlParse9, stringify as yamlStringify6 } from "yaml";
|
|
10441
10761
|
function packId3(research) {
|
|
10442
10762
|
const fingerprint = `${research.topic}|${research.created_at}`;
|
|
@@ -10455,10 +10775,10 @@ function noteRefusal(ctx, reason, blocking = true) {
|
|
|
10455
10775
|
if (blocking) ctx.blockingReasons.push(reason);
|
|
10456
10776
|
}
|
|
10457
10777
|
async function tryReadJson(packPath, rel, parse, invalid) {
|
|
10458
|
-
const abs =
|
|
10459
|
-
if (!
|
|
10778
|
+
const abs = join24(packPath, rel);
|
|
10779
|
+
if (!existsSync24(abs)) return null;
|
|
10460
10780
|
try {
|
|
10461
|
-
return parse(JSON.parse(await
|
|
10781
|
+
return parse(JSON.parse(await readFile21(abs, "utf8")));
|
|
10462
10782
|
} catch (err) {
|
|
10463
10783
|
invalid.push({ path: rel, error: err instanceof Error ? err.message : "parse error" });
|
|
10464
10784
|
return null;
|
|
@@ -10466,8 +10786,8 @@ async function tryReadJson(packPath, rel, parse, invalid) {
|
|
|
10466
10786
|
}
|
|
10467
10787
|
async function freeze(options) {
|
|
10468
10788
|
const packPath = options.packPath ? resolve19(options.packPath) : process.cwd();
|
|
10469
|
-
const yamlPath =
|
|
10470
|
-
if (!
|
|
10789
|
+
const yamlPath = join24(packPath, "research.yaml");
|
|
10790
|
+
if (!existsSync24(yamlPath)) throw new PackNotFoundError(packPath);
|
|
10471
10791
|
const refusal = {
|
|
10472
10792
|
reasons: [],
|
|
10473
10793
|
blockingReasons: [],
|
|
@@ -10476,7 +10796,7 @@ async function freeze(options) {
|
|
|
10476
10796
|
};
|
|
10477
10797
|
let research;
|
|
10478
10798
|
try {
|
|
10479
|
-
research = ResearchYamlSchema.parse(yamlParse9(await
|
|
10799
|
+
research = ResearchYamlSchema.parse(yamlParse9(await readFile21(yamlPath, "utf8")));
|
|
10480
10800
|
} catch (err) {
|
|
10481
10801
|
refusal.invalidArtifacts.push({ path: "research.yaml", error: err instanceof Error ? err.message : "parse error" });
|
|
10482
10802
|
return writeRefusal({
|
|
@@ -10488,13 +10808,13 @@ async function freeze(options) {
|
|
|
10488
10808
|
}
|
|
10489
10809
|
const pid = packId3(research);
|
|
10490
10810
|
for (const rel of REQUIRED_PACK_ARTIFACTS) {
|
|
10491
|
-
if (!
|
|
10811
|
+
if (!existsSync24(join24(packPath, rel))) {
|
|
10492
10812
|
refusal.missingArtifacts.push(rel);
|
|
10493
10813
|
noteRefusal(refusal, `Required artifact missing: ${rel}.`);
|
|
10494
10814
|
}
|
|
10495
10815
|
}
|
|
10496
10816
|
for (const rel of SYNTHESIS_FILES) {
|
|
10497
|
-
if (!
|
|
10817
|
+
if (!existsSync24(join24(packPath, rel))) {
|
|
10498
10818
|
refusal.missingArtifacts.push(rel);
|
|
10499
10819
|
noteRefusal(refusal, `Synthesis artifact missing: ${rel}. Run \`research-os synth workspace\` after the pack reaches synthesis_ready.`);
|
|
10500
10820
|
}
|
|
@@ -10528,22 +10848,22 @@ async function freeze(options) {
|
|
|
10528
10848
|
if (handoff2 && handoff2.mode !== "synthesis_ready") {
|
|
10529
10849
|
noteRefusal(refusal, `Cowork handoff mode is "${handoff2.mode}", not "synthesis_ready".`);
|
|
10530
10850
|
}
|
|
10531
|
-
const finalReportAbs =
|
|
10532
|
-
const decisionBriefAbs =
|
|
10533
|
-
const workingReportAbs =
|
|
10851
|
+
const finalReportAbs = join24(packPath, "synthesis/final-report.md");
|
|
10852
|
+
const decisionBriefAbs = join24(packPath, "synthesis/decision-brief.md");
|
|
10853
|
+
const workingReportAbs = join24(packPath, "synthesis/working-report.md");
|
|
10534
10854
|
let finalReportText = "";
|
|
10535
10855
|
let decisionBriefText = "";
|
|
10536
10856
|
let workingReportText = "";
|
|
10537
|
-
if (
|
|
10538
|
-
if (
|
|
10539
|
-
if (
|
|
10857
|
+
if (existsSync24(finalReportAbs)) finalReportText = await readFile21(finalReportAbs, "utf8");
|
|
10858
|
+
if (existsSync24(decisionBriefAbs)) decisionBriefText = await readFile21(decisionBriefAbs, "utf8");
|
|
10859
|
+
if (existsSync24(workingReportAbs)) workingReportText = await readFile21(workingReportAbs, "utf8");
|
|
10540
10860
|
const livePackClaimIds = /* @__PURE__ */ new Set();
|
|
10541
10861
|
const liveLatestDecisionByClaim = /* @__PURE__ */ new Map();
|
|
10542
10862
|
const liveLatestCreatedAtByClaim = /* @__PURE__ */ new Map();
|
|
10543
10863
|
for (const section of research.sections) {
|
|
10544
|
-
const claimsFile =
|
|
10545
|
-
if (
|
|
10546
|
-
const text = await
|
|
10864
|
+
const claimsFile = join24(packPath, "sections", section.id, "claims.jsonl");
|
|
10865
|
+
if (existsSync24(claimsFile)) {
|
|
10866
|
+
const text = await readFile21(claimsFile, "utf8");
|
|
10547
10867
|
for (const line of text.split(/\r?\n/)) {
|
|
10548
10868
|
if (!line.trim()) continue;
|
|
10549
10869
|
try {
|
|
@@ -10557,9 +10877,9 @@ async function freeze(options) {
|
|
|
10557
10877
|
}
|
|
10558
10878
|
}
|
|
10559
10879
|
}
|
|
10560
|
-
const reviewsFile =
|
|
10561
|
-
if (
|
|
10562
|
-
const text = await
|
|
10880
|
+
const reviewsFile = join24(packPath, "sections", section.id, "claim-reviews.jsonl");
|
|
10881
|
+
if (existsSync24(reviewsFile)) {
|
|
10882
|
+
const text = await readFile21(reviewsFile, "utf8");
|
|
10563
10883
|
const reviews = [];
|
|
10564
10884
|
for (const line of text.split(/\r?\n/)) {
|
|
10565
10885
|
if (!line.trim()) continue;
|
|
@@ -10601,7 +10921,7 @@ async function freeze(options) {
|
|
|
10601
10921
|
const unknownCitations = allCited.filter((c) => isWellFormedClaimId(c) && !allClaimIds.has(c));
|
|
10602
10922
|
const repairCitations = allCited.filter((c) => repairOrRejected.has(c));
|
|
10603
10923
|
const uncitedAccepted = acceptedClaimIds.filter((c) => !allCitedSet.has(c));
|
|
10604
|
-
if (
|
|
10924
|
+
if (existsSync24(finalReportAbs) && finalReportText.trim().length > 0) {
|
|
10605
10925
|
if (citationsInFinal.length === 0 && acceptedClaimIds.length > 0) {
|
|
10606
10926
|
noteRefusal(refusal, "final-report.md contains no [claim:...] citations even though accepted claims exist.");
|
|
10607
10927
|
}
|
|
@@ -10653,7 +10973,7 @@ async function freeze(options) {
|
|
|
10653
10973
|
}
|
|
10654
10974
|
}
|
|
10655
10975
|
for (const section of research.sections) {
|
|
10656
|
-
if (!
|
|
10976
|
+
if (!existsSync24(join24(packPath, "audits", `${section.id}-gate.json`))) {
|
|
10657
10977
|
noteRefusal(refusal, `Section ${section.id} has no gate result on file.`);
|
|
10658
10978
|
refusal.missingArtifacts.push(`audits/${section.id}-gate.json`);
|
|
10659
10979
|
}
|
|
@@ -10693,16 +11013,16 @@ async function freeze(options) {
|
|
|
10693
11013
|
}
|
|
10694
11014
|
canonicalPaths.push("evidence/fetch-log.jsonl");
|
|
10695
11015
|
canonicalPaths.push("evidence/citation-ledger.jsonl");
|
|
10696
|
-
if (
|
|
10697
|
-
const entries = await readdir2(
|
|
11016
|
+
if (existsSync24(join24(packPath, "evidence", "source-cards"))) {
|
|
11017
|
+
const entries = await readdir2(join24(packPath, "evidence", "source-cards"));
|
|
10698
11018
|
for (const e of entries) {
|
|
10699
11019
|
if (e.endsWith(".json")) canonicalPaths.push(`evidence/source-cards/${e}`);
|
|
10700
11020
|
}
|
|
10701
11021
|
}
|
|
10702
11022
|
const canonicalHashes = [];
|
|
10703
11023
|
for (const rel of canonicalPaths) {
|
|
10704
|
-
const abs =
|
|
10705
|
-
if (!
|
|
11024
|
+
const abs = join24(packPath, rel);
|
|
11025
|
+
if (!existsSync24(abs)) continue;
|
|
10706
11026
|
const h = await hashArtifact(ctx, rel);
|
|
10707
11027
|
if (h) canonicalHashes.push(h);
|
|
10708
11028
|
}
|
|
@@ -10756,7 +11076,7 @@ async function freeze(options) {
|
|
|
10756
11076
|
claim_count: acceptedClaimIds.length,
|
|
10757
11077
|
contradiction_count: unresolvedContradictionRefs.length,
|
|
10758
11078
|
review_finding_count: 0,
|
|
10759
|
-
gate_result_count: research.sections.filter((s) =>
|
|
11079
|
+
gate_result_count: research.sections.filter((s) => existsSync24(join24(packPath, "audits", `${s.id}-gate.json`))).length,
|
|
10760
11080
|
integrity_checks: checks
|
|
10761
11081
|
});
|
|
10762
11082
|
const frozenResearch = {
|
|
@@ -10765,14 +11085,14 @@ async function freeze(options) {
|
|
|
10765
11085
|
sections: research.sections.map((s) => ({ ...s, status: "frozen" }))
|
|
10766
11086
|
};
|
|
10767
11087
|
await writeFile18(yamlPath, yamlStringify6(frozenResearch, { lineWidth: 0 }), "utf8");
|
|
10768
|
-
const auditsDir =
|
|
10769
|
-
await
|
|
11088
|
+
const auditsDir = join24(packPath, "audits");
|
|
11089
|
+
await mkdir18(auditsDir, { recursive: true });
|
|
10770
11090
|
for (const stale of ["freeze-refusal.json", "freeze-refusal.md"]) {
|
|
10771
|
-
const p =
|
|
10772
|
-
if (
|
|
11091
|
+
const p = join24(auditsDir, stale);
|
|
11092
|
+
if (existsSync24(p)) await unlink(p);
|
|
10773
11093
|
}
|
|
10774
|
-
const jsonPath =
|
|
10775
|
-
const mdPath =
|
|
11094
|
+
const jsonPath = join24(auditsDir, "freeze-receipt.json");
|
|
11095
|
+
const mdPath = join24(auditsDir, "freeze-receipt.md");
|
|
10776
11096
|
await writeFile18(jsonPath, JSON.stringify(receipt, null, 2), "utf8");
|
|
10777
11097
|
await writeFile18(mdPath, renderFreezeReceiptMarkdown(receipt), "utf8");
|
|
10778
11098
|
return {
|
|
@@ -10802,14 +11122,14 @@ async function writeRefusal(args) {
|
|
|
10802
11122
|
next_actions: nextActions,
|
|
10803
11123
|
would_freeze: false
|
|
10804
11124
|
});
|
|
10805
|
-
const auditsDir =
|
|
10806
|
-
await
|
|
11125
|
+
const auditsDir = join24(args.packPath, "audits");
|
|
11126
|
+
await mkdir18(auditsDir, { recursive: true });
|
|
10807
11127
|
for (const stale of ["freeze-receipt.json", "freeze-receipt.md"]) {
|
|
10808
|
-
const p =
|
|
10809
|
-
if (
|
|
11128
|
+
const p = join24(auditsDir, stale);
|
|
11129
|
+
if (existsSync24(p)) await unlink(p);
|
|
10810
11130
|
}
|
|
10811
|
-
const jsonPath =
|
|
10812
|
-
const mdPath =
|
|
11131
|
+
const jsonPath = join24(auditsDir, "freeze-refusal.json");
|
|
11132
|
+
const mdPath = join24(auditsDir, "freeze-refusal.md");
|
|
10813
11133
|
await writeFile18(jsonPath, JSON.stringify(payload, null, 2), "utf8");
|
|
10814
11134
|
await writeFile18(mdPath, renderFreezeRefusalMarkdown(payload), "utf8");
|
|
10815
11135
|
return {
|
|
@@ -10901,43 +11221,43 @@ var init_freeze = __esm({
|
|
|
10901
11221
|
});
|
|
10902
11222
|
|
|
10903
11223
|
// src/invalidate/schema.ts
|
|
10904
|
-
import { z as
|
|
11224
|
+
import { z as z19 } from "zod";
|
|
10905
11225
|
var ArchivedArtifactSchema, SectionStatusChangeSchema, InvalidationReceiptSchema;
|
|
10906
11226
|
var init_schema17 = __esm({
|
|
10907
11227
|
"src/invalidate/schema.ts"() {
|
|
10908
11228
|
"use strict";
|
|
10909
|
-
ArchivedArtifactSchema =
|
|
10910
|
-
src:
|
|
11229
|
+
ArchivedArtifactSchema = z19.object({
|
|
11230
|
+
src: z19.string(),
|
|
10911
11231
|
// path relative to packPath, before archival
|
|
10912
|
-
dst:
|
|
11232
|
+
dst: z19.string()
|
|
10913
11233
|
// path relative to packPath, after archival
|
|
10914
11234
|
});
|
|
10915
|
-
SectionStatusChangeSchema =
|
|
10916
|
-
section_id:
|
|
10917
|
-
before:
|
|
10918
|
-
after:
|
|
10919
|
-
});
|
|
10920
|
-
InvalidationReceiptSchema =
|
|
10921
|
-
receipt_id:
|
|
10922
|
-
contract_label:
|
|
10923
|
-
superseded_contract:
|
|
10924
|
-
new_contract:
|
|
10925
|
-
reason:
|
|
10926
|
-
invalidated_at:
|
|
10927
|
-
research_os_version:
|
|
10928
|
-
affected_sections:
|
|
10929
|
-
archived_artifacts:
|
|
10930
|
-
section_status_changes:
|
|
10931
|
-
frozen_at_cleared:
|
|
10932
|
-
notes:
|
|
11235
|
+
SectionStatusChangeSchema = z19.object({
|
|
11236
|
+
section_id: z19.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
11237
|
+
before: z19.string(),
|
|
11238
|
+
after: z19.string()
|
|
11239
|
+
});
|
|
11240
|
+
InvalidationReceiptSchema = z19.object({
|
|
11241
|
+
receipt_id: z19.string().regex(/^inv_[0-9]+_[a-z0-9-]+$/),
|
|
11242
|
+
contract_label: z19.string().min(1),
|
|
11243
|
+
superseded_contract: z19.string().min(1).nullable(),
|
|
11244
|
+
new_contract: z19.string().min(1),
|
|
11245
|
+
reason: z19.string().min(8),
|
|
11246
|
+
invalidated_at: z19.string(),
|
|
11247
|
+
research_os_version: z19.string(),
|
|
11248
|
+
affected_sections: z19.array(z19.string().regex(/^[0-9]{2}-[a-z0-9-]+$/)),
|
|
11249
|
+
archived_artifacts: z19.array(ArchivedArtifactSchema),
|
|
11250
|
+
section_status_changes: z19.array(SectionStatusChangeSchema),
|
|
11251
|
+
frozen_at_cleared: z19.boolean(),
|
|
11252
|
+
notes: z19.string().nullable()
|
|
10933
11253
|
});
|
|
10934
11254
|
}
|
|
10935
11255
|
});
|
|
10936
11256
|
|
|
10937
11257
|
// src/invalidate/run.ts
|
|
10938
|
-
import { existsSync as
|
|
10939
|
-
import { mkdir as
|
|
10940
|
-
import { dirname as
|
|
11258
|
+
import { existsSync as existsSync25 } from "fs";
|
|
11259
|
+
import { mkdir as mkdir19, readFile as readFile22, readdir as readdir3, rename, writeFile as writeFile19 } from "fs/promises";
|
|
11260
|
+
import { dirname as dirname6, join as join25, posix, relative as relative3, resolve as resolve20, sep } from "path";
|
|
10941
11261
|
import { parse as yamlParse10, stringify as yamlStringify7 } from "yaml";
|
|
10942
11262
|
function posixify(p) {
|
|
10943
11263
|
return p.split(sep).join("/");
|
|
@@ -10955,15 +11275,15 @@ function isLegacyClaimLine(line) {
|
|
|
10955
11275
|
}
|
|
10956
11276
|
}
|
|
10957
11277
|
async function detectLegacyClaimSections(packPath) {
|
|
10958
|
-
const sectionsDir =
|
|
10959
|
-
if (!
|
|
11278
|
+
const sectionsDir = join25(packPath, "sections");
|
|
11279
|
+
if (!existsSync25(sectionsDir)) return [];
|
|
10960
11280
|
const entries = await readdir3(sectionsDir, { withFileTypes: true });
|
|
10961
11281
|
const affected = [];
|
|
10962
11282
|
for (const entry of entries) {
|
|
10963
11283
|
if (!entry.isDirectory()) continue;
|
|
10964
|
-
const claimsPath =
|
|
10965
|
-
if (!
|
|
10966
|
-
const text = await
|
|
11284
|
+
const claimsPath = join25(sectionsDir, entry.name, "claims.jsonl");
|
|
11285
|
+
if (!existsSync25(claimsPath)) continue;
|
|
11286
|
+
const text = await readFile22(claimsPath, "utf8");
|
|
10967
11287
|
let hasLegacy = false;
|
|
10968
11288
|
for (const line of text.split(/\r?\n/)) {
|
|
10969
11289
|
if (isLegacyClaimLine(line)) {
|
|
@@ -10976,16 +11296,16 @@ async function detectLegacyClaimSections(packPath) {
|
|
|
10976
11296
|
return affected.sort();
|
|
10977
11297
|
}
|
|
10978
11298
|
async function moveIfExists(packPath, archiveRel, rel) {
|
|
10979
|
-
const src =
|
|
10980
|
-
if (!
|
|
10981
|
-
const dst =
|
|
10982
|
-
await
|
|
11299
|
+
const src = join25(packPath, rel);
|
|
11300
|
+
if (!existsSync25(src)) return null;
|
|
11301
|
+
const dst = join25(packPath, archiveRel, rel);
|
|
11302
|
+
await mkdir19(dirname6(dst), { recursive: true });
|
|
10983
11303
|
await rename(src, dst);
|
|
10984
|
-
return { src: posixify(rel), dst: posixify(
|
|
11304
|
+
return { src: posixify(rel), dst: posixify(join25(archiveRel, rel)) };
|
|
10985
11305
|
}
|
|
10986
11306
|
async function moveDirContentsIfExists(packPath, archiveRel, relDir, skipPrefix) {
|
|
10987
|
-
const fullDir =
|
|
10988
|
-
if (!
|
|
11307
|
+
const fullDir = join25(packPath, relDir);
|
|
11308
|
+
if (!existsSync25(fullDir)) return [];
|
|
10989
11309
|
const entries = await readdir3(fullDir, { withFileTypes: true });
|
|
10990
11310
|
const moved = [];
|
|
10991
11311
|
for (const entry of entries) {
|
|
@@ -11063,8 +11383,8 @@ function buildReceiptMarkdown(receipt) {
|
|
|
11063
11383
|
}
|
|
11064
11384
|
async function invalidateExtraction(options) {
|
|
11065
11385
|
const packPath = options.packPath ? resolve20(options.packPath) : process.cwd();
|
|
11066
|
-
const yamlPath =
|
|
11067
|
-
if (!
|
|
11386
|
+
const yamlPath = join25(packPath, "research.yaml");
|
|
11387
|
+
if (!existsSync25(yamlPath)) throw new PackNotFoundError(packPath);
|
|
11068
11388
|
const reason = options.reason.trim();
|
|
11069
11389
|
if (reason.length < 8) {
|
|
11070
11390
|
throw new Error("invalidation reason must be at least 8 characters");
|
|
@@ -11082,13 +11402,13 @@ async function invalidateExtraction(options) {
|
|
|
11082
11402
|
const stampPath = stampIso.replace(/[:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
11083
11403
|
const archiveRel = posix.join("audits", "legacy", label, stampPath);
|
|
11084
11404
|
const affectedSections = await detectLegacyClaimSections(packPath);
|
|
11085
|
-
const research = ResearchYamlSchema.parse(yamlParse10(await
|
|
11405
|
+
const research = ResearchYamlSchema.parse(yamlParse10(await readFile22(yamlPath, "utf8")));
|
|
11086
11406
|
const packLevelDirs = ["handoffs", "synthesis", "audits"];
|
|
11087
11407
|
let probeArtifactsExist = affectedSections.length > 0;
|
|
11088
11408
|
if (!probeArtifactsExist) {
|
|
11089
11409
|
for (const d of packLevelDirs) {
|
|
11090
|
-
const full =
|
|
11091
|
-
if (!
|
|
11410
|
+
const full = join25(packPath, d);
|
|
11411
|
+
if (!existsSync25(full)) continue;
|
|
11092
11412
|
const entries = await readdir3(full, { withFileTypes: true });
|
|
11093
11413
|
const meaningful = entries.some(
|
|
11094
11414
|
(e) => !(d === "audits" && e.isDirectory() && e.name === "legacy")
|
|
@@ -11110,7 +11430,7 @@ async function invalidateExtraction(options) {
|
|
|
11110
11430
|
message: "No legacy artifacts found. Pack is on the current extraction contract."
|
|
11111
11431
|
};
|
|
11112
11432
|
}
|
|
11113
|
-
await
|
|
11433
|
+
await mkdir19(join25(packPath, archiveRel), { recursive: true });
|
|
11114
11434
|
const archived = [];
|
|
11115
11435
|
for (const sectionId of affectedSections) {
|
|
11116
11436
|
for (const filename of [
|
|
@@ -11159,14 +11479,14 @@ async function invalidateExtraction(options) {
|
|
|
11159
11479
|
frozen_at_cleared: frozenAtCleared,
|
|
11160
11480
|
notes: options.notes ?? null
|
|
11161
11481
|
});
|
|
11162
|
-
const receiptDir =
|
|
11482
|
+
const receiptDir = join25(packPath, archiveRel);
|
|
11163
11483
|
await writeFile19(
|
|
11164
|
-
|
|
11484
|
+
join25(receiptDir, "invalidation.json"),
|
|
11165
11485
|
JSON.stringify(receipt, null, 2),
|
|
11166
11486
|
"utf8"
|
|
11167
11487
|
);
|
|
11168
11488
|
await writeFile19(
|
|
11169
|
-
|
|
11489
|
+
join25(receiptDir, "invalidation.md"),
|
|
11170
11490
|
buildReceiptMarkdown(receipt),
|
|
11171
11491
|
"utf8"
|
|
11172
11492
|
);
|
|
@@ -11192,20 +11512,20 @@ var init_run9 = __esm({
|
|
|
11192
11512
|
});
|
|
11193
11513
|
|
|
11194
11514
|
// src/invalidate/review.ts
|
|
11195
|
-
import { existsSync as
|
|
11196
|
-
import { mkdir as
|
|
11197
|
-
import { dirname as
|
|
11198
|
-
import { z as
|
|
11515
|
+
import { existsSync as existsSync26 } from "fs";
|
|
11516
|
+
import { mkdir as mkdir20, rename as rename2, writeFile as writeFile20 } from "fs/promises";
|
|
11517
|
+
import { dirname as dirname7, join as join26, posix as posix2, relative as relative4, resolve as resolve21, sep as sep2 } from "path";
|
|
11518
|
+
import { z as z20 } from "zod";
|
|
11199
11519
|
function posixify2(p) {
|
|
11200
11520
|
return p.split(sep2).join("/");
|
|
11201
11521
|
}
|
|
11202
11522
|
async function moveIfExists2(packPath, archiveRel, rel) {
|
|
11203
|
-
const src =
|
|
11204
|
-
if (!
|
|
11205
|
-
const dst =
|
|
11206
|
-
await
|
|
11523
|
+
const src = join26(packPath, rel);
|
|
11524
|
+
if (!existsSync26(src)) return null;
|
|
11525
|
+
const dst = join26(packPath, archiveRel, rel);
|
|
11526
|
+
await mkdir20(dirname7(dst), { recursive: true });
|
|
11207
11527
|
await rename2(src, dst);
|
|
11208
|
-
return { src: posixify2(rel), dst: posixify2(
|
|
11528
|
+
return { src: posixify2(rel), dst: posixify2(join26(archiveRel, rel)) };
|
|
11209
11529
|
}
|
|
11210
11530
|
function buildReceiptMarkdown2(receipt) {
|
|
11211
11531
|
const lines = [];
|
|
@@ -11247,8 +11567,8 @@ function buildReceiptMarkdown2(receipt) {
|
|
|
11247
11567
|
}
|
|
11248
11568
|
async function invalidateReview(options) {
|
|
11249
11569
|
const packPath = options.packPath ? resolve21(options.packPath) : process.cwd();
|
|
11250
|
-
if (!
|
|
11251
|
-
if (!
|
|
11570
|
+
if (!existsSync26(join26(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
11571
|
+
if (!existsSync26(join26(packPath, "sections", options.sectionId)))
|
|
11252
11572
|
throw new SectionNotFoundError(options.sectionId);
|
|
11253
11573
|
const reason = options.reason.trim();
|
|
11254
11574
|
if (reason.length < 8) {
|
|
@@ -11271,7 +11591,7 @@ async function invalidateReview(options) {
|
|
|
11271
11591
|
// Also clear review-active.json so the next review run is unambiguous.
|
|
11272
11592
|
posix2.join("sections", options.sectionId, "review-active.json")
|
|
11273
11593
|
];
|
|
11274
|
-
const present = candidatePaths.filter((rel) =>
|
|
11594
|
+
const present = candidatePaths.filter((rel) => existsSync26(join26(packPath, rel)));
|
|
11275
11595
|
if (present.length === 0) {
|
|
11276
11596
|
return {
|
|
11277
11597
|
performed: false,
|
|
@@ -11283,7 +11603,7 @@ async function invalidateReview(options) {
|
|
|
11283
11603
|
message: "No canonical review artifacts found to invalidate. Section has no active review state."
|
|
11284
11604
|
};
|
|
11285
11605
|
}
|
|
11286
|
-
await
|
|
11606
|
+
await mkdir20(join26(packPath, archiveRel), { recursive: true });
|
|
11287
11607
|
const archived = [];
|
|
11288
11608
|
for (const rel of present) {
|
|
11289
11609
|
const a = await moveIfExists2(packPath, archiveRel, rel);
|
|
@@ -11299,14 +11619,14 @@ async function invalidateReview(options) {
|
|
|
11299
11619
|
archived_artifacts: archived,
|
|
11300
11620
|
notes: options.notes ?? null
|
|
11301
11621
|
});
|
|
11302
|
-
const receiptDir =
|
|
11622
|
+
const receiptDir = join26(packPath, archiveRel);
|
|
11303
11623
|
await writeFile20(
|
|
11304
|
-
|
|
11624
|
+
join26(receiptDir, "invalidation.json"),
|
|
11305
11625
|
JSON.stringify(receipt, null, 2),
|
|
11306
11626
|
"utf8"
|
|
11307
11627
|
);
|
|
11308
11628
|
await writeFile20(
|
|
11309
|
-
|
|
11629
|
+
join26(receiptDir, "invalidation.md"),
|
|
11310
11630
|
buildReceiptMarkdown2(receipt),
|
|
11311
11631
|
"utf8"
|
|
11312
11632
|
);
|
|
@@ -11327,15 +11647,15 @@ var init_review2 = __esm({
|
|
|
11327
11647
|
init_errors();
|
|
11328
11648
|
init_src();
|
|
11329
11649
|
init_schema17();
|
|
11330
|
-
ReviewInvalidationReceiptSchema =
|
|
11331
|
-
receipt_id:
|
|
11332
|
-
section_id:
|
|
11333
|
-
contract_label:
|
|
11334
|
-
reason:
|
|
11335
|
-
invalidated_at:
|
|
11336
|
-
research_os_version:
|
|
11337
|
-
archived_artifacts:
|
|
11338
|
-
notes:
|
|
11650
|
+
ReviewInvalidationReceiptSchema = z20.object({
|
|
11651
|
+
receipt_id: z20.string().regex(/^invr_[0-9]+_[a-z0-9-]+$/),
|
|
11652
|
+
section_id: z20.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
11653
|
+
contract_label: z20.string().min(1),
|
|
11654
|
+
reason: z20.string().min(8),
|
|
11655
|
+
invalidated_at: z20.string(),
|
|
11656
|
+
research_os_version: z20.string(),
|
|
11657
|
+
archived_artifacts: z20.array(ArchivedArtifactSchema),
|
|
11658
|
+
notes: z20.string().nullable()
|
|
11339
11659
|
});
|
|
11340
11660
|
}
|
|
11341
11661
|
});
|
|
@@ -11351,103 +11671,103 @@ var init_invalidate = __esm({
|
|
|
11351
11671
|
});
|
|
11352
11672
|
|
|
11353
11673
|
// src/section_report/schema.ts
|
|
11354
|
-
import { z as
|
|
11674
|
+
import { z as z21 } from "zod";
|
|
11355
11675
|
var SectionReportSourcesSchema, SectionReportExtractionSchema, SectionReportContradictionsSchema, SectionReportReviewSchema, SectionReportAcceptanceSchema, SectionReportSchema;
|
|
11356
11676
|
var init_schema18 = __esm({
|
|
11357
11677
|
"src/section_report/schema.ts"() {
|
|
11358
11678
|
"use strict";
|
|
11359
|
-
SectionReportSourcesSchema =
|
|
11360
|
-
fetched_ok:
|
|
11361
|
-
source_cards:
|
|
11362
|
-
publishers:
|
|
11363
|
-
primary_source_waiver:
|
|
11364
|
-
status:
|
|
11365
|
-
reason:
|
|
11366
|
-
compensating_controls:
|
|
11679
|
+
SectionReportSourcesSchema = z21.object({
|
|
11680
|
+
fetched_ok: z21.number().int().nonnegative(),
|
|
11681
|
+
source_cards: z21.number().int().nonnegative(),
|
|
11682
|
+
publishers: z21.array(z21.string()),
|
|
11683
|
+
primary_source_waiver: z21.object({
|
|
11684
|
+
status: z21.enum(["none", "requested", "granted"]),
|
|
11685
|
+
reason: z21.string().nullable(),
|
|
11686
|
+
compensating_controls: z21.array(z21.string())
|
|
11367
11687
|
})
|
|
11368
11688
|
});
|
|
11369
|
-
SectionReportExtractionSchema =
|
|
11370
|
-
candidate_claims:
|
|
11371
|
-
claims_per_source:
|
|
11372
|
-
|
|
11373
|
-
source_id:
|
|
11374
|
-
claims:
|
|
11689
|
+
SectionReportExtractionSchema = z21.object({
|
|
11690
|
+
candidate_claims: z21.number().int().nonnegative(),
|
|
11691
|
+
claims_per_source: z21.array(
|
|
11692
|
+
z21.object({
|
|
11693
|
+
source_id: z21.string(),
|
|
11694
|
+
claims: z21.number().int().nonnegative()
|
|
11375
11695
|
})
|
|
11376
11696
|
),
|
|
11377
|
-
claims_per_1k_words:
|
|
11378
|
-
excerpt_pages_processed:
|
|
11379
|
-
excerpt_id_failures:
|
|
11380
|
-
malformed_extractor_outputs:
|
|
11381
|
-
near_duplicate_clusters:
|
|
11382
|
-
weak_scope_count:
|
|
11383
|
-
generic_scope_count:
|
|
11384
|
-
density_flags:
|
|
11385
|
-
});
|
|
11386
|
-
SectionReportContradictionsSchema =
|
|
11387
|
-
pairs_compared:
|
|
11388
|
-
contradiction_candidates:
|
|
11389
|
-
overgeneralization_risks:
|
|
11390
|
-
});
|
|
11391
|
-
SectionReportReviewSchema =
|
|
11392
|
-
reviewed:
|
|
11393
|
-
accepted_for_synthesis:
|
|
11394
|
-
needs_scope_repair:
|
|
11395
|
-
needs_source_repair:
|
|
11396
|
-
needs_contradiction_mapping:
|
|
11397
|
-
rejected:
|
|
11398
|
-
needs_human_review:
|
|
11399
|
-
rejection_or_repair_by_category:
|
|
11400
|
-
|
|
11401
|
-
category:
|
|
11402
|
-
count:
|
|
11697
|
+
claims_per_1k_words: z21.number(),
|
|
11698
|
+
excerpt_pages_processed: z21.number().int().nonnegative().nullable(),
|
|
11699
|
+
excerpt_id_failures: z21.number().int().nonnegative().nullable(),
|
|
11700
|
+
malformed_extractor_outputs: z21.number().int().nonnegative().nullable(),
|
|
11701
|
+
near_duplicate_clusters: z21.number().int().nonnegative(),
|
|
11702
|
+
weak_scope_count: z21.number().int().nonnegative(),
|
|
11703
|
+
generic_scope_count: z21.number().int().nonnegative(),
|
|
11704
|
+
density_flags: z21.number().int().nonnegative()
|
|
11705
|
+
});
|
|
11706
|
+
SectionReportContradictionsSchema = z21.object({
|
|
11707
|
+
pairs_compared: z21.number().int().nonnegative().nullable(),
|
|
11708
|
+
contradiction_candidates: z21.number().int().nonnegative(),
|
|
11709
|
+
overgeneralization_risks: z21.number().int().nonnegative()
|
|
11710
|
+
});
|
|
11711
|
+
SectionReportReviewSchema = z21.object({
|
|
11712
|
+
reviewed: z21.boolean(),
|
|
11713
|
+
accepted_for_synthesis: z21.number().int().nonnegative(),
|
|
11714
|
+
needs_scope_repair: z21.number().int().nonnegative(),
|
|
11715
|
+
needs_source_repair: z21.number().int().nonnegative(),
|
|
11716
|
+
needs_contradiction_mapping: z21.number().int().nonnegative(),
|
|
11717
|
+
rejected: z21.number().int().nonnegative(),
|
|
11718
|
+
needs_human_review: z21.number().int().nonnegative(),
|
|
11719
|
+
rejection_or_repair_by_category: z21.array(
|
|
11720
|
+
z21.object({
|
|
11721
|
+
category: z21.string(),
|
|
11722
|
+
count: z21.number().int().nonnegative()
|
|
11403
11723
|
})
|
|
11404
11724
|
)
|
|
11405
11725
|
});
|
|
11406
|
-
SectionReportAcceptanceSchema =
|
|
11407
|
-
candidate_claims:
|
|
11408
|
-
accepted_for_synthesis:
|
|
11409
|
-
acceptance_ratio:
|
|
11726
|
+
SectionReportAcceptanceSchema = z21.object({
|
|
11727
|
+
candidate_claims: z21.number().int().nonnegative(),
|
|
11728
|
+
accepted_for_synthesis: z21.number().int().nonnegative(),
|
|
11729
|
+
acceptance_ratio: z21.number(),
|
|
11410
11730
|
// 0..1
|
|
11411
|
-
accepted_per_source:
|
|
11731
|
+
accepted_per_source: z21.number(),
|
|
11412
11732
|
// accepted / source_count, 0 if no sources
|
|
11413
|
-
accepted_per_1k_words:
|
|
11414
|
-
top_rejection_category:
|
|
11415
|
-
claim_overproduction_fired:
|
|
11416
|
-
synthesis_ready:
|
|
11733
|
+
accepted_per_1k_words: z21.number(),
|
|
11734
|
+
top_rejection_category: z21.string().nullable(),
|
|
11735
|
+
claim_overproduction_fired: z21.boolean(),
|
|
11736
|
+
synthesis_ready: z21.boolean()
|
|
11417
11737
|
});
|
|
11418
|
-
SectionReportSchema =
|
|
11419
|
-
report_id:
|
|
11420
|
-
section_id:
|
|
11421
|
-
reported_at:
|
|
11422
|
-
research_os_version:
|
|
11423
|
-
status:
|
|
11738
|
+
SectionReportSchema = z21.object({
|
|
11739
|
+
report_id: z21.string().regex(/^secrep_[0-9]+_[a-z0-9-]+$/),
|
|
11740
|
+
section_id: z21.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
11741
|
+
reported_at: z21.string(),
|
|
11742
|
+
research_os_version: z21.string(),
|
|
11743
|
+
status: z21.string(),
|
|
11424
11744
|
sources: SectionReportSourcesSchema,
|
|
11425
11745
|
extraction: SectionReportExtractionSchema,
|
|
11426
11746
|
contradictions: SectionReportContradictionsSchema,
|
|
11427
11747
|
review: SectionReportReviewSchema,
|
|
11428
11748
|
acceptance: SectionReportAcceptanceSchema,
|
|
11429
|
-
gate_verdict:
|
|
11430
|
-
gate_synthesis_eligible:
|
|
11749
|
+
gate_verdict: z21.string().nullable(),
|
|
11750
|
+
gate_synthesis_eligible: z21.boolean().nullable()
|
|
11431
11751
|
});
|
|
11432
11752
|
}
|
|
11433
11753
|
});
|
|
11434
11754
|
|
|
11435
11755
|
// src/section_report/run.ts
|
|
11436
|
-
import { existsSync as
|
|
11437
|
-
import { mkdir as
|
|
11438
|
-
import { join as
|
|
11756
|
+
import { existsSync as existsSync27 } from "fs";
|
|
11757
|
+
import { mkdir as mkdir21, readFile as readFile23, writeFile as writeFile21 } from "fs/promises";
|
|
11758
|
+
import { join as join27, resolve as resolve22 } from "path";
|
|
11439
11759
|
import { parse as yamlParse11 } from "yaml";
|
|
11440
11760
|
async function readJson(path, parser) {
|
|
11441
|
-
if (!
|
|
11761
|
+
if (!existsSync27(path)) return null;
|
|
11442
11762
|
try {
|
|
11443
|
-
return parser(JSON.parse(await
|
|
11763
|
+
return parser(JSON.parse(await readFile23(path, "utf8")));
|
|
11444
11764
|
} catch {
|
|
11445
11765
|
return null;
|
|
11446
11766
|
}
|
|
11447
11767
|
}
|
|
11448
11768
|
async function readJsonl6(path, parse) {
|
|
11449
|
-
if (!
|
|
11450
|
-
const text = await
|
|
11769
|
+
if (!existsSync27(path)) return [];
|
|
11770
|
+
const text = await readFile23(path, "utf8");
|
|
11451
11771
|
const out = [];
|
|
11452
11772
|
for (const line of text.split(/\r?\n/)) {
|
|
11453
11773
|
if (!line.trim()) continue;
|
|
@@ -11464,15 +11784,15 @@ function countWords2(text) {
|
|
|
11464
11784
|
}
|
|
11465
11785
|
async function reportSection(options) {
|
|
11466
11786
|
const packPath = options.packPath ? resolve22(options.packPath) : process.cwd();
|
|
11467
|
-
if (!
|
|
11468
|
-
if (!
|
|
11787
|
+
if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
11788
|
+
if (!existsSync27(join27(packPath, "sections", options.sectionId)))
|
|
11469
11789
|
throw new SectionNotFoundError(options.sectionId);
|
|
11470
11790
|
const research = ResearchYamlSchema.parse(
|
|
11471
|
-
yamlParse11(await
|
|
11791
|
+
yamlParse11(await readFile23(join27(packPath, "research.yaml"), "utf8"))
|
|
11472
11792
|
);
|
|
11473
11793
|
const section = research.sections.find((s) => s.id === options.sectionId);
|
|
11474
11794
|
const allReceipts = await readJsonl6(
|
|
11475
|
-
|
|
11795
|
+
join27(packPath, "evidence", "fetch-log.jsonl"),
|
|
11476
11796
|
(raw) => FetchReceiptSchema.parse(raw)
|
|
11477
11797
|
);
|
|
11478
11798
|
const okReceipts = /* @__PURE__ */ new Map();
|
|
@@ -11483,9 +11803,9 @@ async function reportSection(options) {
|
|
|
11483
11803
|
if (!prev || prev.fetched_at < r.fetched_at) okReceipts.set(r.source_id, r);
|
|
11484
11804
|
}
|
|
11485
11805
|
const sectionSourceIds = [];
|
|
11486
|
-
const sourcesPath =
|
|
11487
|
-
if (
|
|
11488
|
-
const text = await
|
|
11806
|
+
const sourcesPath = join27(packPath, "sections", options.sectionId, "sources.jsonl");
|
|
11807
|
+
if (existsSync27(sourcesPath)) {
|
|
11808
|
+
const text = await readFile23(sourcesPath, "utf8");
|
|
11489
11809
|
for (const line of text.split(/\r?\n/)) {
|
|
11490
11810
|
if (!line.trim()) continue;
|
|
11491
11811
|
try {
|
|
@@ -11497,10 +11817,10 @@ async function reportSection(options) {
|
|
|
11497
11817
|
}
|
|
11498
11818
|
const cards = /* @__PURE__ */ new Map();
|
|
11499
11819
|
for (const sid of sectionSourceIds) {
|
|
11500
|
-
const cardPath =
|
|
11501
|
-
if (!
|
|
11820
|
+
const cardPath = join27(packPath, "evidence", "source-cards", `${sid}.json`);
|
|
11821
|
+
if (!existsSync27(cardPath)) continue;
|
|
11502
11822
|
try {
|
|
11503
|
-
cards.set(sid, SourceCardSchema.parse(JSON.parse(await
|
|
11823
|
+
cards.set(sid, SourceCardSchema.parse(JSON.parse(await readFile23(cardPath, "utf8"))));
|
|
11504
11824
|
} catch {
|
|
11505
11825
|
}
|
|
11506
11826
|
}
|
|
@@ -11511,12 +11831,12 @@ async function reportSection(options) {
|
|
|
11511
11831
|
for (const sid of sectionSourceIds) {
|
|
11512
11832
|
const r = okReceipts.get(sid);
|
|
11513
11833
|
if (!r?.raw_text_path) continue;
|
|
11514
|
-
const p =
|
|
11515
|
-
if (!
|
|
11516
|
-
totalWords += countWords2(await
|
|
11834
|
+
const p = join27(packPath, r.raw_text_path);
|
|
11835
|
+
if (!existsSync27(p)) continue;
|
|
11836
|
+
totalWords += countWords2(await readFile23(p, "utf8"));
|
|
11517
11837
|
}
|
|
11518
11838
|
const claims = await readJsonl6(
|
|
11519
|
-
|
|
11839
|
+
join27(packPath, "sections", options.sectionId, "claims.jsonl"),
|
|
11520
11840
|
(raw) => ClaimSchema.parse(raw)
|
|
11521
11841
|
);
|
|
11522
11842
|
const claimsBySrc = /* @__PURE__ */ new Map();
|
|
@@ -11528,11 +11848,11 @@ async function reportSection(options) {
|
|
|
11528
11848
|
}
|
|
11529
11849
|
}
|
|
11530
11850
|
const density = await readJson(
|
|
11531
|
-
|
|
11851
|
+
join27(packPath, "audits", `${options.sectionId}-claim-density.json`),
|
|
11532
11852
|
(raw) => raw
|
|
11533
11853
|
);
|
|
11534
11854
|
const extractReceipt = await readJson(
|
|
11535
|
-
|
|
11855
|
+
join27(packPath, "audits", `${options.sectionId}-claim-extract.json`),
|
|
11536
11856
|
(raw) => raw
|
|
11537
11857
|
);
|
|
11538
11858
|
let excerptPagesProcessed = null;
|
|
@@ -11550,7 +11870,7 @@ async function reportSection(options) {
|
|
|
11550
11870
|
).length;
|
|
11551
11871
|
}
|
|
11552
11872
|
const contradictions = await readJsonl6(
|
|
11553
|
-
|
|
11873
|
+
join27(packPath, "sections", options.sectionId, "contradictions.jsonl"),
|
|
11554
11874
|
(raw) => ContradictionSchema.parse(raw)
|
|
11555
11875
|
);
|
|
11556
11876
|
let pairsCompared = null;
|
|
@@ -11559,7 +11879,7 @@ async function reportSection(options) {
|
|
|
11559
11879
|
(c) => c.type === "overgeneralization_risk"
|
|
11560
11880
|
).length;
|
|
11561
11881
|
const reviews = await readJsonl6(
|
|
11562
|
-
|
|
11882
|
+
join27(packPath, "sections", options.sectionId, "claim-reviews.jsonl"),
|
|
11563
11883
|
(raw) => ClaimReviewSchema.parse(raw)
|
|
11564
11884
|
);
|
|
11565
11885
|
const latestReviewByClaim = /* @__PURE__ */ new Map();
|
|
@@ -11568,7 +11888,7 @@ async function reportSection(options) {
|
|
|
11568
11888
|
if (!prev || prev.created_at < r.created_at) latestReviewByClaim.set(r.claim_id, r);
|
|
11569
11889
|
}
|
|
11570
11890
|
const findings = await readJsonl6(
|
|
11571
|
-
|
|
11891
|
+
join27(packPath, "audits", `${options.sectionId}-findings.jsonl`),
|
|
11572
11892
|
(raw) => ReviewFindingSchema.parse(raw)
|
|
11573
11893
|
);
|
|
11574
11894
|
const decisionCounts = {
|
|
@@ -11593,7 +11913,7 @@ async function reportSection(options) {
|
|
|
11593
11913
|
const topRejectionCategory = rejectionByCategory[0]?.category ?? null;
|
|
11594
11914
|
const claimOverproductionFired = (categoryCounts.get("claim_overproduction") ?? 0) > 0;
|
|
11595
11915
|
const gate2 = await readJson(
|
|
11596
|
-
|
|
11916
|
+
join27(packPath, "audits", `${options.sectionId}-gate.json`),
|
|
11597
11917
|
(raw) => raw
|
|
11598
11918
|
);
|
|
11599
11919
|
const candidate = claims.length;
|
|
@@ -11729,10 +12049,10 @@ async function reportSection(options) {
|
|
|
11729
12049
|
md.push("---");
|
|
11730
12050
|
md.push("");
|
|
11731
12051
|
md.push("_This report is read-only \u2014 it does not mutate any canonical artifact. Re-run after any pipeline step to refresh._");
|
|
11732
|
-
const auditsDir =
|
|
11733
|
-
await
|
|
11734
|
-
const jsonPath =
|
|
11735
|
-
const markdownPath =
|
|
12052
|
+
const auditsDir = join27(packPath, "audits");
|
|
12053
|
+
await mkdir21(auditsDir, { recursive: true });
|
|
12054
|
+
const jsonPath = join27(auditsDir, `${options.sectionId}-section-report.json`);
|
|
12055
|
+
const markdownPath = join27(auditsDir, `${options.sectionId}-section-report.md`);
|
|
11736
12056
|
await writeFile21(jsonPath, JSON.stringify(report, null, 2), "utf8");
|
|
11737
12057
|
await writeFile21(markdownPath, md.join("\n"), "utf8");
|
|
11738
12058
|
return { report, jsonPath, markdownPath };
|
|
@@ -11770,17 +12090,17 @@ var init_triage = __esm({
|
|
|
11770
12090
|
});
|
|
11771
12091
|
|
|
11772
12092
|
// src/discover/schema.ts
|
|
11773
|
-
import { z as
|
|
12093
|
+
import { z as z22 } from "zod";
|
|
11774
12094
|
var DiscoveryCandidateStatusSchema, SourceTypeGuessSchema, DiscoveryCandidateSchema, DiscoverySummarySchema;
|
|
11775
12095
|
var init_schema19 = __esm({
|
|
11776
12096
|
"src/discover/schema.ts"() {
|
|
11777
12097
|
"use strict";
|
|
11778
|
-
DiscoveryCandidateStatusSchema =
|
|
12098
|
+
DiscoveryCandidateStatusSchema = z22.enum([
|
|
11779
12099
|
"candidate",
|
|
11780
12100
|
"approved",
|
|
11781
12101
|
"rejected"
|
|
11782
12102
|
]);
|
|
11783
|
-
SourceTypeGuessSchema =
|
|
12103
|
+
SourceTypeGuessSchema = z22.enum([
|
|
11784
12104
|
"primary",
|
|
11785
12105
|
"docs",
|
|
11786
12106
|
"paper",
|
|
@@ -11790,36 +12110,36 @@ var init_schema19 = __esm({
|
|
|
11790
12110
|
"benchmark",
|
|
11791
12111
|
"unknown"
|
|
11792
12112
|
]);
|
|
11793
|
-
DiscoveryCandidateSchema =
|
|
11794
|
-
candidate_id:
|
|
11795
|
-
section_id:
|
|
11796
|
-
url:
|
|
11797
|
-
title:
|
|
11798
|
-
publisher:
|
|
12113
|
+
DiscoveryCandidateSchema = z22.object({
|
|
12114
|
+
candidate_id: z22.string().regex(/^disc_[a-f0-9]{12}$/),
|
|
12115
|
+
section_id: z22.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
12116
|
+
url: z22.string().url(),
|
|
12117
|
+
title: z22.string().min(1),
|
|
12118
|
+
publisher: z22.string().nullable(),
|
|
11799
12119
|
source_type_guess: SourceTypeGuessSchema,
|
|
11800
|
-
why_relevant:
|
|
12120
|
+
why_relevant: z22.string().min(1),
|
|
11801
12121
|
// The free-text query that produced this candidate (for traceability).
|
|
11802
|
-
query:
|
|
12122
|
+
query: z22.string().min(1),
|
|
11803
12123
|
// Lower rank = more central. Stable per-(query, provider) ordering.
|
|
11804
|
-
rank:
|
|
11805
|
-
discovered_at:
|
|
12124
|
+
rank: z22.number().int().positive(),
|
|
12125
|
+
discovered_at: z22.string(),
|
|
11806
12126
|
// 'candidate' | 'approved' | 'rejected'. Append-only: new entries with
|
|
11807
12127
|
// same candidate_id supersede older ones; the latest entry's status wins.
|
|
11808
12128
|
status: DiscoveryCandidateStatusSchema,
|
|
11809
|
-
discovered_by:
|
|
11810
|
-
reason:
|
|
11811
|
-
});
|
|
11812
|
-
DiscoverySummarySchema =
|
|
11813
|
-
summary_id:
|
|
11814
|
-
section_id:
|
|
11815
|
-
ran_at:
|
|
11816
|
-
research_os_version:
|
|
11817
|
-
query:
|
|
11818
|
-
provider:
|
|
11819
|
-
candidates_proposed:
|
|
11820
|
-
candidates_validated:
|
|
11821
|
-
candidates_rejected_invalid_url:
|
|
11822
|
-
warnings:
|
|
12129
|
+
discovered_by: z22.string().min(1),
|
|
12130
|
+
reason: z22.string().nullable().default(null)
|
|
12131
|
+
});
|
|
12132
|
+
DiscoverySummarySchema = z22.object({
|
|
12133
|
+
summary_id: z22.string().regex(/^disum_[0-9]+_[a-z0-9-]+$/),
|
|
12134
|
+
section_id: z22.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
|
|
12135
|
+
ran_at: z22.string(),
|
|
12136
|
+
research_os_version: z22.string(),
|
|
12137
|
+
query: z22.string().min(1),
|
|
12138
|
+
provider: z22.string().min(1),
|
|
12139
|
+
candidates_proposed: z22.number().int().nonnegative(),
|
|
12140
|
+
candidates_validated: z22.number().int().nonnegative(),
|
|
12141
|
+
candidates_rejected_invalid_url: z22.number().int().nonnegative(),
|
|
12142
|
+
warnings: z22.array(z22.string())
|
|
11823
12143
|
});
|
|
11824
12144
|
}
|
|
11825
12145
|
});
|
|
@@ -11971,10 +12291,10 @@ Propose source-URL candidates for this section. Quality over quantity.`;
|
|
|
11971
12291
|
});
|
|
11972
12292
|
|
|
11973
12293
|
// src/discover/run.ts
|
|
11974
|
-
import { existsSync as
|
|
12294
|
+
import { existsSync as existsSync28 } from "fs";
|
|
11975
12295
|
import { createHash as createHash10 } from "crypto";
|
|
11976
|
-
import { mkdir as
|
|
11977
|
-
import { join as
|
|
12296
|
+
import { mkdir as mkdir22, readFile as readFile24, writeFile as writeFile22, appendFile as appendFile8 } from "fs/promises";
|
|
12297
|
+
import { join as join28, resolve as resolve23 } from "path";
|
|
11978
12298
|
import { parse as yamlParse12 } from "yaml";
|
|
11979
12299
|
function makeCandidateId(sectionId, url) {
|
|
11980
12300
|
const hex = createHash10("sha256").update(`${sectionId}|${url}`).digest("hex").slice(0, 12);
|
|
@@ -11989,21 +12309,21 @@ function isHttpsUrl(s) {
|
|
|
11989
12309
|
}
|
|
11990
12310
|
}
|
|
11991
12311
|
function candidatesPath(packPath, sectionId) {
|
|
11992
|
-
return
|
|
12312
|
+
return join28(packPath, "sections", sectionId, "discovery-candidates.jsonl");
|
|
11993
12313
|
}
|
|
11994
12314
|
function reportPath(packPath, sectionId) {
|
|
11995
|
-
return
|
|
12315
|
+
return join28(packPath, "sections", sectionId, "discovery-report.md");
|
|
11996
12316
|
}
|
|
11997
12317
|
function summaryPath(packPath, sectionId) {
|
|
11998
|
-
return
|
|
12318
|
+
return join28(packPath, "audits", `${sectionId}-discovery.json`);
|
|
11999
12319
|
}
|
|
12000
12320
|
function approvedUrlsPath(packPath, sectionId) {
|
|
12001
|
-
return
|
|
12321
|
+
return join28(packPath, "sections", sectionId, "urls.approved.txt");
|
|
12002
12322
|
}
|
|
12003
12323
|
async function readCandidates(packPath, sectionId) {
|
|
12004
12324
|
const path = candidatesPath(packPath, sectionId);
|
|
12005
|
-
if (!
|
|
12006
|
-
const text = await
|
|
12325
|
+
if (!existsSync28(path)) return [];
|
|
12326
|
+
const text = await readFile24(path, "utf8");
|
|
12007
12327
|
const out = [];
|
|
12008
12328
|
for (const line of text.split(/\r?\n/)) {
|
|
12009
12329
|
if (!line.trim()) continue;
|
|
@@ -12033,7 +12353,7 @@ async function pickProvider(providers) {
|
|
|
12033
12353
|
}
|
|
12034
12354
|
async function loadSectionPurpose(packPath, sectionId) {
|
|
12035
12355
|
const research = ResearchYamlSchema.parse(
|
|
12036
|
-
yamlParse12(await
|
|
12356
|
+
yamlParse12(await readFile24(join28(packPath, "research.yaml"), "utf8"))
|
|
12037
12357
|
);
|
|
12038
12358
|
const section = research.sections.find((s) => s.id === sectionId);
|
|
12039
12359
|
return section?.purpose ?? "";
|
|
@@ -12071,8 +12391,8 @@ function buildReportMarkdown(args) {
|
|
|
12071
12391
|
}
|
|
12072
12392
|
async function discover(options) {
|
|
12073
12393
|
const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
|
|
12074
|
-
if (!
|
|
12075
|
-
if (!
|
|
12394
|
+
if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12395
|
+
if (!existsSync28(join28(packPath, "sections", options.sectionId)))
|
|
12076
12396
|
throw new SectionNotFoundError(options.sectionId);
|
|
12077
12397
|
const query2 = options.query.trim();
|
|
12078
12398
|
if (query2.length < 4) throw new Error("discover query must be at least 4 characters");
|
|
@@ -12132,9 +12452,9 @@ async function discover(options) {
|
|
|
12132
12452
|
newCandidates.push(candidate);
|
|
12133
12453
|
}
|
|
12134
12454
|
const ledgerPath = candidatesPath(packPath, options.sectionId);
|
|
12135
|
-
await
|
|
12455
|
+
await mkdir22(join28(packPath, "sections", options.sectionId), { recursive: true });
|
|
12136
12456
|
for (const c of newCandidates) {
|
|
12137
|
-
await
|
|
12457
|
+
await appendFile8(ledgerPath, JSON.stringify(c) + "\n", "utf8");
|
|
12138
12458
|
}
|
|
12139
12459
|
const all = await readCandidates(packPath, options.sectionId);
|
|
12140
12460
|
const latest = Array.from(latestPerCandidate(all).values());
|
|
@@ -12158,7 +12478,7 @@ async function discover(options) {
|
|
|
12158
12478
|
candidates_rejected_invalid_url: invalidCount,
|
|
12159
12479
|
warnings
|
|
12160
12480
|
});
|
|
12161
|
-
await
|
|
12481
|
+
await mkdir22(join28(packPath, "audits"), { recursive: true });
|
|
12162
12482
|
await writeFile22(summaryPath(packPath, options.sectionId), JSON.stringify(summary, null, 2), "utf8");
|
|
12163
12483
|
return {
|
|
12164
12484
|
candidatesAdded: newCandidates.length,
|
|
@@ -12178,13 +12498,13 @@ async function appendStatusUpdate(packPath, sectionId, candidate, newStatus, rea
|
|
|
12178
12498
|
reason,
|
|
12179
12499
|
discovered_at: stampIso
|
|
12180
12500
|
});
|
|
12181
|
-
await
|
|
12501
|
+
await appendFile8(candidatesPath(packPath, sectionId), JSON.stringify(updated) + "\n", "utf8");
|
|
12182
12502
|
return updated;
|
|
12183
12503
|
}
|
|
12184
12504
|
async function approve(options) {
|
|
12185
12505
|
const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
|
|
12186
|
-
if (!
|
|
12187
|
-
if (!
|
|
12506
|
+
if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12507
|
+
if (!existsSync28(join28(packPath, "sections", options.sectionId)))
|
|
12188
12508
|
throw new SectionNotFoundError(options.sectionId);
|
|
12189
12509
|
const all = await readCandidates(packPath, options.sectionId);
|
|
12190
12510
|
const latest = latestPerCandidate(all);
|
|
@@ -12217,8 +12537,8 @@ async function approve(options) {
|
|
|
12217
12537
|
}
|
|
12218
12538
|
async function reject(options) {
|
|
12219
12539
|
const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
|
|
12220
|
-
if (!
|
|
12221
|
-
if (!
|
|
12540
|
+
if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12541
|
+
if (!existsSync28(join28(packPath, "sections", options.sectionId)))
|
|
12222
12542
|
throw new SectionNotFoundError(options.sectionId);
|
|
12223
12543
|
if (options.reason.trim().length < 4) {
|
|
12224
12544
|
throw new Error("reject requires --reason of at least 4 characters");
|
|
@@ -12246,15 +12566,15 @@ async function reject(options) {
|
|
|
12246
12566
|
}
|
|
12247
12567
|
async function exportUrls(options) {
|
|
12248
12568
|
const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
|
|
12249
|
-
if (!
|
|
12250
|
-
if (!
|
|
12569
|
+
if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
12570
|
+
if (!existsSync28(join28(packPath, "sections", options.sectionId)))
|
|
12251
12571
|
throw new SectionNotFoundError(options.sectionId);
|
|
12252
12572
|
const all = await readCandidates(packPath, options.sectionId);
|
|
12253
12573
|
const latest = latestPerCandidate(all);
|
|
12254
12574
|
const approved = Array.from(latest.values()).filter((c) => c.status === "approved").sort((a, b) => a.rank - b.rank);
|
|
12255
12575
|
const lines = approved.map((c) => c.url).join("\n");
|
|
12256
12576
|
const path = approvedUrlsPath(packPath, options.sectionId);
|
|
12257
|
-
await
|
|
12577
|
+
await mkdir22(join28(packPath, "sections", options.sectionId), { recursive: true });
|
|
12258
12578
|
await writeFile22(path, lines + (lines.length > 0 ? "\n" : ""), "utf8");
|
|
12259
12579
|
return { exportPath: path, approvedCount: approved.length };
|
|
12260
12580
|
}
|
|
@@ -12302,7 +12622,7 @@ var init_src = __esm({
|
|
|
12302
12622
|
init_triage();
|
|
12303
12623
|
init_discover();
|
|
12304
12624
|
init_errors();
|
|
12305
|
-
RESEARCH_OS_VERSION = "0.
|
|
12625
|
+
RESEARCH_OS_VERSION = "0.4.0";
|
|
12306
12626
|
}
|
|
12307
12627
|
});
|
|
12308
12628
|
init_src();
|
|
@@ -12409,6 +12729,7 @@ export {
|
|
|
12409
12729
|
SectionStatusChangeSchema,
|
|
12410
12730
|
SeveritySchema,
|
|
12411
12731
|
SharedSourceSchema,
|
|
12732
|
+
SourceCardOverrideSchema,
|
|
12412
12733
|
SourceCardSchema,
|
|
12413
12734
|
SourceDiversityGapRowSchema,
|
|
12414
12735
|
SourceTypeGuessSchema,
|
|
@@ -12428,6 +12749,7 @@ export {
|
|
|
12428
12749
|
add,
|
|
12429
12750
|
aggregate,
|
|
12430
12751
|
appendFetchLog,
|
|
12752
|
+
appendOverride,
|
|
12431
12753
|
appendSectionSourceId,
|
|
12432
12754
|
applySchema,
|
|
12433
12755
|
applyWaivers,
|
|
@@ -12468,6 +12790,8 @@ export {
|
|
|
12468
12790
|
freeze,
|
|
12469
12791
|
gate,
|
|
12470
12792
|
gather,
|
|
12793
|
+
getEffectivePublisher,
|
|
12794
|
+
getEffectiveSourceType,
|
|
12471
12795
|
handoff,
|
|
12472
12796
|
hasNegation,
|
|
12473
12797
|
indexDbPath,
|
|
@@ -12492,6 +12816,7 @@ export {
|
|
|
12492
12816
|
promote,
|
|
12493
12817
|
query,
|
|
12494
12818
|
readActiveProfile,
|
|
12819
|
+
readOverrides,
|
|
12495
12820
|
readTriagedClaimIds,
|
|
12496
12821
|
readUrlsFile,
|
|
12497
12822
|
reject,
|
|
@@ -12522,6 +12847,7 @@ export {
|
|
|
12522
12847
|
syncRepoKnowledge,
|
|
12523
12848
|
tokenize,
|
|
12524
12849
|
triage,
|
|
12850
|
+
validateSourceCardOverride,
|
|
12525
12851
|
workspace,
|
|
12526
12852
|
writeActiveProfile,
|
|
12527
12853
|
writeSourceCard
|