@mcptoolshop/research-os 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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: extraction.publisher,
1247
+ publisher: resolvedPublisher,
1021
1248
  published_at: extraction.published_at,
1022
1249
  title: extraction.title,
1023
- source_type: extraction.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/gather.ts
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, resolve as resolve3 } from "path";
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 (!existsSync4(join5(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
1077
- if (!existsSync4(join5(packPath, "sections", options.sectionId)))
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 z4 } from "zod";
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 = z4.enum(["raw_text", "key_point"]);
1158
- ExcerptSchema = z4.object({
1159
- excerpt_id: z4.string().regex(EXCERPT_ID_PATTERN),
1160
- source_id: z4.string().regex(/^src_[a-f0-9]{12}$/),
1161
- source_hash: z4.string().regex(/^[a-f0-9]{64}$/).nullable(),
1162
- text: z4.string().min(1),
1163
- location_hint: z4.string().nullable(),
1164
- char_start: z4.number().int().nonnegative(),
1165
- char_end: z4.number().int().nonnegative(),
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: z4.string()
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 existsSync5 } from "fs";
1297
- import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
1298
- import { dirname as dirname2, join as join6 } from "path";
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 join6(packPath, "evidence", "excerpts", `${sourceId}.jsonl`);
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 readFile5(path, "utf8");
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 mkdir5(dirname2(path), { recursive: true });
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 (existsSync5(path)) {
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 z5 } from "zod";
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 = z5.enum(["low", "medium", "high"]);
1714
- ClaimExtractorSchema = z5.enum(["heuristic", "ollama-intern"]);
1715
- ReviewStateSchema = z5.enum([
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 = z5.object({
1723
- claim_id: z5.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
1724
- section_id: z5.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
1725
- source_ids: z5.array(z5.string().regex(/^src_[a-f0-9]{12}$/)).min(1, "every claim must reference at least one source_id"),
1726
- source_hashes: z5.array(z5.string().regex(/^[a-f0-9]{64}$/)),
1727
- asserts: z5.string().min(1),
1728
- scope: z5.string().nullable(),
1729
- not: z5.string().nullable(),
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: z5.array(z5.string().regex(EXCERPT_ID_PATTERN)).default([]),
1736
- evidence_excerpt: z5.string().min(1),
1737
- evidence_location: z5.string().nullable(),
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: z5.string().min(1),
1741
- created_at: z5.string(),
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 existsSync6 } from "fs";
1749
- import { appendFile as appendFile2, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
1750
- import { join as join7, resolve as resolve4 } from "path";
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 = join7(packPath, "sections", sectionId, "sources.jsonl");
1762
- if (!existsSync6(path)) return [];
1763
- const text = await readFile6(path, "utf8");
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 = join7(packPath, "evidence", "source-cards", `${sourceId}.json`);
1777
- if (!existsSync6(path)) return null;
1778
- const text = await readFile6(path, "utf8");
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 = join7(packPath, "evidence", "fetch-log.jsonl");
1783
- if (!existsSync6(path)) return null;
1784
- const text = await readFile6(path, "utf8");
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 = join7(packPath, "sections", sectionId, "claims.jsonl");
1800
- if (!existsSync6(path)) return /* @__PURE__ */ new Set();
1801
- const text = await readFile6(path, "utf8");
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 (!existsSync6(join7(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
1877
- if (!existsSync6(join7(packPath, "sections", options.sectionId)))
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 = join7(packPath, "sections", options.sectionId, "claims.jsonl");
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 = join7(packPath, receipt2.raw_text_path);
1913
- if (existsSync6(raw)) {
1914
- rawText = await readFile6(raw, "utf8");
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 appendFile2(claimsPath, JSON.stringify(claim) + "\n", "utf8");
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 = join7(packPath, "audits");
2000
- await mkdir6(auditsDir, { recursive: true });
2317
+ const auditsDir = join8(packPath, "audits");
2318
+ await mkdir7(auditsDir, { recursive: true });
2001
2319
  await writeFile6(
2002
- join7(auditsDir, `${options.sectionId}-claim-extract.json`),
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 z6 } from "zod";
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 = z6.object({
2035
- source_id: z6.string().regex(/^src_[a-f0-9]{12}$/),
2036
- publisher: z6.string().nullable(),
2037
- source_word_count: z6.number().int().nonnegative(),
2038
- claim_count: z6.number().int().nonnegative(),
2039
- claims_per_1k_words: z6.number(),
2040
- share_of_section: z6.number(),
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: z6.number().int().nonnegative(),
2043
- generic_scope_count: z6.number().int().nonnegative()
2360
+ weak_scope_count: z7.number().int().nonnegative(),
2361
+ generic_scope_count: z7.number().int().nonnegative()
2044
2362
  });
2045
- NearDuplicateClusterSchema = z6.object({
2046
- representative_assert: z6.string(),
2047
- member_count: z6.number().int().nonnegative(),
2048
- claim_ids: z6.array(z6.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/))
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 = z6.object({
2051
- type: z6.enum([
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: z6.enum(["info", "warn", "block"]),
2058
- message: z6.string(),
2059
- affects_source_ids: z6.array(z6.string().regex(/^src_[a-f0-9]{12}$/)).default([]),
2060
- affects_claim_ids: z6.array(z6.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)).default([])
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 = z6.object({
2063
- audit_id: z6.string().regex(/^cda_[0-9]+_[a-z0-9-]+$/),
2064
- section_id: z6.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
2065
- audited_at: z6.string(),
2066
- research_os_version: z6.string(),
2067
- candidate_claim_count: z6.number().int().nonnegative(),
2068
- source_count: z6.number().int().nonnegative(),
2069
- total_source_word_count: z6.number().int().nonnegative(),
2070
- claims_per_1k_words: z6.number(),
2071
- weak_scope_count: z6.number().int().nonnegative(),
2072
- generic_scope_count: z6.number().int().nonnegative(),
2073
- per_source: z6.array(PerSourceDensitySchema),
2074
- near_duplicate_clusters: z6.array(NearDuplicateClusterSchema),
2075
- flags: z6.array(DensityFlagSchema)
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 existsSync7 } from "fs";
2082
- import { mkdir as mkdir7, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
2083
- import { join as join8, resolve as resolve5 } from "path";
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 = join8(packPath, "sections", sectionId, "claims.jsonl");
2093
- if (!existsSync7(path)) return [];
2094
- const text = await readFile7(path, "utf8");
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 = join8(packPath, "evidence", "source-cards", `${sid}.json`);
2109
- if (!existsSync7(p)) continue;
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 readFile7(p, "utf8"))));
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 = join8(packPath, "evidence", "fetch-log.jsonl");
2120
- if (!existsSync7(path)) return out;
2121
- const text = await readFile7(path, "utf8");
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 = join8(packPath, receipt.raw_text_path);
2137
- if (!existsSync7(p)) return null;
2138
- return await readFile7(p, "utf8");
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 (!existsSync7(join8(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
2272
- if (!existsSync7(join8(packPath, "sections", options.sectionId)))
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 = join8(packPath, "audits");
2346
- await mkdir7(auditsDir, { recursive: true });
2347
- const jsonPath = join8(auditsDir, `${options.sectionId}-claim-density.json`);
2348
- const markdownPath = join8(auditsDir, `${options.sectionId}-claim-density.md`);
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 z7 } from "zod";
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 = z7.enum([
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 = z7.enum(["low", "medium", "high", "blocking"]);
2406
- OverlapAssessmentSchema = z7.enum([
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 = z7.enum(["heuristic", "ollama-intern"]);
2413
- ContradictionStatusSchema = z7.enum([
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 = z7.enum(["low", "medium", "high"]);
2420
- ContradictionSchema = z7.object({
2421
- contradiction_id: z7.string().regex(/^cnt_[a-f0-9]{12}_(heuristic|ollama_intern)$/),
2422
- section_id: z7.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
2423
- claim_ids: z7.array(z7.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)).length(2, "contradictions are pair-wise in v0.1"),
2424
- source_ids: z7.array(z7.string().regex(/^src_[a-f0-9]{12}$/)).min(1),
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: z7.string().min(1),
2427
- scope_analysis: z7.string(),
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: z7.string().min(1),
2433
- evidence: z7.string(),
2750
+ detection_method: z8.string().min(1),
2751
+ evidence: z8.string(),
2434
2752
  status: ContradictionStatusSchema,
2435
- created_at: z7.string()
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 z8 } from "zod";
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 = z8.enum([
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 = z8.object({
2898
- triage_id: z8.string().regex(/^tri_[a-f0-9]{12}$/),
2899
- claim_id: z8.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
2900
- section_id: z8.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
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: z8.string().min(1),
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: z8.number().int().positive().nullable(),
3223
+ rank: z9.number().int().positive().nullable(),
2906
3224
  // Quality score [0..1] used to sort. Stable. Higher = better.
2907
- quality_score: z8.number().min(0).max(1),
2908
- triage_method: z8.string().min(1),
2909
- created_at: z8.string()
2910
- });
2911
- TriageSummarySchema = z8.object({
2912
- summary_id: z8.string().regex(/^tris_[0-9]+_[a-z0-9-]+$/),
2913
- section_id: z8.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
2914
- triaged_at: z8.string(),
2915
- research_os_version: z8.string(),
2916
- triage_method: z8.string(),
2917
- candidate_claims: z8.number().int().nonnegative(),
2918
- decisions: z8.record(TriageDecisionSchema, z8.number().int().nonnegative()),
2919
- per_source_cap: z8.number().int().positive(),
2920
- duplicate_clusters_collapsed: z8.number().int().nonnegative(),
2921
- selected_count: z8.number().int().nonnegative(),
2922
- selected_per_source: z8.array(
2923
- z8.object({
2924
- source_id: z8.string().regex(/^src_[a-f0-9]{12}$/),
2925
- selected: z8.number().int().nonnegative(),
2926
- total: z8.number().int().nonnegative()
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 existsSync8 } from "fs";
3257
+ import { existsSync as existsSync9 } from "fs";
2940
3258
  import { createHash as createHash2 } from "crypto";
2941
- import { mkdir as mkdir8, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
2942
- import { join as join9, resolve as resolve6 } from "path";
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 = join9(packPath, "sections", sectionId, "claims.jsonl");
2963
- if (!existsSync8(path)) return [];
2964
- const text = await readFile8(path, "utf8");
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 (!existsSync8(join9(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
2978
- if (!existsSync8(join9(packPath, "sections", options.sectionId)))
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 = join9(packPath, "sections", options.sectionId);
3113
- const auditsDir = join9(packPath, "audits");
3114
- await mkdir8(sectionDir, { recursive: true });
3115
- await mkdir8(auditsDir, { recursive: true });
3116
- const triageJsonlPath = join9(sectionDir, "claim-triage.jsonl");
3117
- const triageMarkdownPath = join9(sectionDir, "claim-triage.md");
3118
- const summaryJsonPath = join9(auditsDir, `${options.sectionId}-claim-triage.json`);
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 = join9(packPath, "sections", sectionId, "claim-triage.jsonl");
3456
+ const path = join10(packPath, "sections", sectionId, "claim-triage.jsonl");
3139
3457
  const out = /* @__PURE__ */ new Set();
3140
- if (!existsSync8(path)) return out;
3141
- const text = await readFile8(path, "utf8");
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 existsSync9 } from "fs";
3216
- import { appendFile as appendFile3, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
3217
- import { join as join10, resolve as resolve7 } from "path";
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 = join10(packPath, "sections", sectionId, "claims.jsonl");
3220
- if (!existsSync9(path)) return [];
3221
- const text = await readFile9(path, "utf8");
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 = join10(packPath, "sections", sectionId, "contradictions.jsonl");
3233
- if (!existsSync9(path)) return [];
3234
- const text = await readFile9(path, "utf8");
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 (!existsSync9(join10(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
3320
- const sectionDir = join10(packPath, "sections", options.sectionId);
3321
- if (!existsSync9(sectionDir)) throw new SectionNotFoundError(options.sectionId);
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 = join10(sectionDir, "contradictions.jsonl");
3342
- const mdPath = join10(sectionDir, "contradictions.md");
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 appendFile3(ledgerPath, JSON.stringify(c) + "\n", "utf8");
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 z9 } from "zod";
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 = z9.enum([
3746
+ ResolutionStatusSchema = z10.enum([
3429
3747
  "unresolved",
3430
3748
  "resolved",
3431
3749
  "preserved",
3432
3750
  "rejected"
3433
3751
  ]);
3434
- ContradictionResolutionSchema = z9.object({
3435
- contradiction_id: z9.string().min(1),
3752
+ ContradictionResolutionSchema = z10.object({
3753
+ contradiction_id: z10.string().min(1),
3436
3754
  status: ResolutionStatusSchema,
3437
- reason: z9.string().min(4),
3438
- resolved_at: z9.string().min(1),
3439
- resolved_by: z9.string().min(1)
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 appendFile4, readFile as readFile10 } from "fs/promises";
3446
- import { existsSync as existsSync10 } from "fs";
3447
- import { join as join11 } from "path";
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 = join11(packPath, "sections", sectionId);
3475
- if (!existsSync10(sectionDir)) {
3792
+ const sectionDir = join12(packPath, "sections", sectionId);
3793
+ if (!existsSync11(sectionDir)) {
3476
3794
  throw new SectionNotFoundError(sectionId);
3477
3795
  }
3478
- const candidatesPath2 = join11(sectionDir, "contradictions.jsonl");
3479
- const ledgerPath = join11(sectionDir, "contradiction-resolutions.jsonl");
3796
+ const candidatesPath2 = join12(sectionDir, "contradictions.jsonl");
3797
+ const ledgerPath = join12(sectionDir, "contradiction-resolutions.jsonl");
3480
3798
  const candidates = [];
3481
- if (existsSync10(candidatesPath2)) {
3482
- const text = await readFile10(candidatesPath2, "utf8");
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 = existsSync10(ledgerPath) ? latestEffectiveStatuses(ledgerPath, await readFile10(ledgerPath, "utf8")) : /* @__PURE__ */ new Map();
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 appendFile4(ledgerPath, lines.join("\n") + "\n", "utf8");
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 z10 } from "zod";
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 = z10.enum([
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 = z10.enum(["info", "warn", "block"]);
3589
- ReviewerNameSchema = z10.enum(["heuristic", "ollama-intern"]);
3590
- ReviewDecisionSchema = z10.enum([
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 = z10.enum(["low", "medium", "high"]);
3599
- ReviewFindingSchema = z10.object({
3600
- finding_id: z10.string().regex(/^fnd_[a-f0-9]{12}$/),
3601
- section_id: z10.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
3602
- claim_ids: z10.array(z10.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/)),
3603
- source_ids: z10.array(z10.string().regex(/^src_[a-f0-9]{12}$/)),
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: z10.string().min(1),
3607
- evidence: z10.string(),
3608
- required_action: z10.string(),
3924
+ summary: z11.string().min(1),
3925
+ evidence: z11.string(),
3926
+ required_action: z11.string(),
3609
3927
  reviewer: ReviewerNameSchema,
3610
- review_method: z10.string().min(1),
3928
+ review_method: z11.string().min(1),
3611
3929
  confidence: ReviewConfidenceSchema,
3612
- created_at: z10.string()
3930
+ created_at: z11.string()
3613
3931
  });
3614
- ClaimReviewSchema = z10.object({
3615
- claim_id: z10.string().regex(/^clm_[a-f0-9]{12}_(heuristic|ollama_intern)_\d+$/),
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: z10.string().min(1),
3618
- finding_ids: z10.array(z10.string()),
3935
+ reason: z11.string().min(1),
3936
+ finding_ids: z11.array(z11.string()),
3619
3937
  reviewer: ReviewerNameSchema,
3620
- review_method: z10.string().min(1),
3621
- created_at: z10.string()
3938
+ review_method: z11.string().min(1),
3939
+ created_at: z11.string()
3622
3940
  });
3623
- ReviewSnapshotSchema = z10.object({
3624
- section_id: z10.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
3941
+ ReviewSnapshotSchema = z11.object({
3942
+ section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
3625
3943
  reviewer: ReviewerNameSchema,
3626
- review_method: z10.string(),
3627
- reviewed_at: z10.string(),
3628
- candidate_claims: z10.number().int().nonnegative(),
3629
- findings: z10.array(ReviewFindingSchema),
3630
- claim_reviews: z10.array(ClaimReviewSchema),
3631
- decision_counts: z10.record(ReviewDecisionSchema, z10.number().int().nonnegative()),
3632
- severity_counts: z10.record(FindingSeveritySchema, z10.number().int().nonnegative()),
3633
- llm_findings_rejected_ungrounded: z10.number().int().nonnegative(),
3634
- promoted_to_reviewed: z10.boolean()
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
  });
@@ -4519,12 +4837,12 @@ var init_markdown2 = __esm({
4519
4837
  });
4520
4838
 
4521
4839
  // src/gates/schema.ts
4522
- import { z as z11 } from "zod";
4840
+ import { z as z12 } from "zod";
4523
4841
  var GateFamilySchema, GateCheckStatusSchema, VerdictSchema, GateCheckResultSchema, WaiverApplicationSchema, SectionGateResultSchema;
4524
4842
  var init_schema10 = __esm({
4525
4843
  "src/gates/schema.ts"() {
4526
4844
  "use strict";
4527
- GateFamilySchema = z11.enum([
4845
+ GateFamilySchema = z12.enum([
4528
4846
  "source_floor",
4529
4847
  "claim_integrity",
4530
4848
  "scope_integrity",
@@ -4534,98 +4852,98 @@ var init_schema10 = __esm({
4534
4852
  "waivers",
4535
4853
  "accepted_claim_floor"
4536
4854
  ]);
4537
- GateCheckStatusSchema = z11.enum([
4855
+ GateCheckStatusSchema = z12.enum([
4538
4856
  "pass",
4539
4857
  "warn",
4540
4858
  "fail",
4541
4859
  "pass_with_waiver",
4542
4860
  "warn_with_waiver"
4543
4861
  ]);
4544
- VerdictSchema = z11.enum(["pass", "warn", "fail", "blocked"]);
4545
- GateCheckResultSchema = z11.object({
4862
+ VerdictSchema = z12.enum(["pass", "warn", "fail", "blocked"]);
4863
+ GateCheckResultSchema = z12.object({
4546
4864
  family: GateFamilySchema,
4547
- check: z11.string().min(1),
4865
+ check: z12.string().min(1),
4548
4866
  status: GateCheckStatusSchema,
4549
- detail: z11.string(),
4550
- evidence: z11.array(z11.string()),
4551
- blocks_synthesis: z11.boolean()
4867
+ detail: z12.string(),
4868
+ evidence: z12.array(z12.string()),
4869
+ blocks_synthesis: z12.boolean()
4552
4870
  });
4553
- WaiverApplicationSchema = z11.object({
4871
+ WaiverApplicationSchema = z12.object({
4554
4872
  family: GateFamilySchema,
4555
- check: z11.string().min(1),
4556
- reason: z11.string().min(1),
4557
- compensating_controls: z11.array(z11.string()),
4873
+ check: z12.string().min(1),
4874
+ reason: z12.string().min(1),
4875
+ compensating_controls: z12.array(z12.string()),
4558
4876
  original_status: GateCheckStatusSchema,
4559
4877
  new_status: GateCheckStatusSchema
4560
4878
  });
4561
- SectionGateResultSchema = z11.object({
4562
- section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
4879
+ SectionGateResultSchema = z12.object({
4880
+ section_id: z12.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
4563
4881
  verdict: VerdictSchema,
4564
- summary: z11.string(),
4565
- checked_at: z11.string(),
4566
- synthesis_eligible: z11.boolean(),
4567
- gate_results: z11.array(GateCheckResultSchema),
4568
- failures: z11.array(GateCheckResultSchema),
4569
- warnings: z11.array(GateCheckResultSchema),
4570
- waivers_applied: z11.array(WaiverApplicationSchema),
4571
- blocking_reasons: z11.array(z11.string()),
4572
- claim_counts: z11.object({
4573
- total: z11.number().int().nonnegative(),
4574
- candidate: z11.number().int().nonnegative(),
4575
- with_evidence_excerpt: z11.number().int().nonnegative(),
4576
- with_source_hashes: z11.number().int().nonnegative(),
4577
- with_scope: z11.number().int().nonnegative(),
4578
- with_not: z11.number().int().nonnegative(),
4579
- universal_scope_null: z11.number().int().nonnegative(),
4580
- orphans: z11.number().int().nonnegative()
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()
4581
4899
  }),
4582
- source_counts: z11.object({
4583
- total: z11.number().int().nonnegative(),
4584
- primary: z11.number().int().nonnegative(),
4585
- secondary: z11.number().int().nonnegative(),
4586
- forum: z11.number().int().nonnegative(),
4587
- benchmark: z11.number().int().nonnegative(),
4588
- docs: z11.number().int().nonnegative(),
4589
- unknown: z11.number().int().nonnegative(),
4590
- independent_publishers: z11.number().int().nonnegative(),
4591
- failed_fetches: z11.number().int().nonnegative(),
4592
- section_primary: z11.number().int().nonnegative(),
4593
- section_independent_publishers: z11.number().int().nonnegative()
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()
4594
4912
  }),
4595
- contradiction_counts: z11.object({
4596
- total: z11.number().int().nonnegative(),
4597
- unresolved: z11.number().int().nonnegative(),
4598
- blocking: z11.number().int().nonnegative(),
4599
- by_type: z11.record(z11.string(), z11.number().int().nonnegative())
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())
4600
4918
  }),
4601
- freshness_summary: z11.object({
4602
- policy_required: z11.boolean(),
4603
- max_source_age_months: z11.number().int().nullable(),
4604
- stale_source_policy: z11.enum(["warn", "fail"]),
4605
- stale_count: z11.number().int().nonnegative(),
4606
- unknown_date_count: z11.number().int().nonnegative()
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()
4607
4925
  }),
4608
- scope_integrity_summary: z11.object({
4609
- universal_claims: z11.number().int().nonnegative(),
4610
- scoped_claims: z11.number().int().nonnegative(),
4611
- with_not_constraint: z11.number().int().nonnegative(),
4612
- overgen_risks_total: z11.number().int().nonnegative(),
4613
- overgen_risks_blocking: z11.number().int().nonnegative()
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()
4614
4932
  }),
4615
- next_actions: z11.array(z11.string())
4933
+ next_actions: z12.array(z12.string())
4616
4934
  });
4617
4935
  }
4618
4936
  });
4619
4937
 
4620
4938
  // src/gates/run.ts
4621
- import { existsSync as existsSync11 } from "fs";
4622
- import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile10 } from "fs/promises";
4623
- import { join as join12, resolve as resolve9 } from "path";
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";
4624
4942
  import { parse as yamlParse2, stringify as yamlStringify3 } from "yaml";
4625
4943
  async function readJsonl(packPath, rel, parse) {
4626
- const path = join12(packPath, rel);
4627
- if (!existsSync11(path)) return [];
4628
- const text = await readFile11(path, "utf8");
4944
+ const path = join13(packPath, rel);
4945
+ if (!existsSync12(path)) return [];
4946
+ const text = await readFile12(path, "utf8");
4629
4947
  const out = [];
4630
4948
  for (const line of text.split(/\r?\n/)) {
4631
4949
  if (!line.trim()) continue;
@@ -4634,14 +4952,14 @@ async function readJsonl(packPath, rel, parse) {
4634
4952
  return out;
4635
4953
  }
4636
4954
  async function readSourceCards2(packPath) {
4637
- const dir = join12(packPath, "evidence", "source-cards");
4638
- if (!existsSync11(dir)) return [];
4955
+ const dir = join13(packPath, "evidence", "source-cards");
4956
+ if (!existsSync12(dir)) return [];
4639
4957
  const { readdir: readdir4 } = await import("fs/promises");
4640
4958
  const entries = await readdir4(dir);
4641
4959
  const cards = [];
4642
4960
  for (const entry of entries) {
4643
4961
  if (!entry.endsWith(".json")) continue;
4644
- const text = await readFile11(join12(dir, entry), "utf8");
4962
+ const text = await readFile12(join13(dir, entry), "utf8");
4645
4963
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
4646
4964
  }
4647
4965
  return cards;
@@ -4816,14 +5134,14 @@ function buildNextActions(results) {
4816
5134
  return Array.from(new Set(actions));
4817
5135
  }
4818
5136
  async function loadResearchYaml2(packPath) {
4819
- const yamlPath = join12(packPath, "research.yaml");
4820
- if (!existsSync11(yamlPath)) throw new PackNotFoundError(packPath);
4821
- const text = await readFile11(yamlPath, "utf8");
5137
+ const yamlPath = join13(packPath, "research.yaml");
5138
+ if (!existsSync12(yamlPath)) throw new PackNotFoundError(packPath);
5139
+ const text = await readFile12(yamlPath, "utf8");
4822
5140
  return ResearchYamlSchema.parse(yamlParse2(text));
4823
5141
  }
4824
5142
  async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
4825
- const yamlPath = join12(packPath, "research.yaml");
4826
- const text = await readFile11(yamlPath, "utf8");
5143
+ const yamlPath = join13(packPath, "research.yaml");
5144
+ const text = await readFile12(yamlPath, "utf8");
4827
5145
  const research = ResearchYamlSchema.parse(yamlParse2(text));
4828
5146
  const idx = research.sections.findIndex((s) => s.id === sectionId);
4829
5147
  if (idx < 0) return;
@@ -4840,9 +5158,9 @@ async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
4840
5158
  }
4841
5159
  async function gate(options) {
4842
5160
  const packPath = options.packPath ? resolve9(options.packPath) : process.cwd();
4843
- if (!existsSync11(join12(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
4844
- const sectionDir = join12(packPath, "sections", options.sectionId);
4845
- if (!existsSync11(sectionDir)) throw new SectionNotFoundError(options.sectionId);
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);
4846
5164
  const research = await loadResearchYaml2(packPath);
4847
5165
  const section = research.sections.find((s) => s.id === options.sectionId);
4848
5166
  if (!section) throw new SectionNotFoundError(options.sectionId);
@@ -4896,15 +5214,15 @@ async function gate(options) {
4896
5214
  scope_integrity_summary: summarizeScopeIntegrity(input),
4897
5215
  next_actions: buildNextActions(finalResults)
4898
5216
  });
4899
- const auditsDir = join12(packPath, "audits");
4900
- await mkdir9(auditsDir, { recursive: true });
5217
+ const auditsDir = join13(packPath, "audits");
5218
+ await mkdir10(auditsDir, { recursive: true });
4901
5219
  await writeFile10(
4902
- join12(auditsDir, `${options.sectionId}-gate.json`),
5220
+ join13(auditsDir, `${options.sectionId}-gate.json`),
4903
5221
  JSON.stringify(result, null, 2),
4904
5222
  "utf8"
4905
5223
  );
4906
5224
  await writeFile10(
4907
- join12(auditsDir, `${options.sectionId}-gate.md`),
5225
+ join13(auditsDir, `${options.sectionId}-gate.md`),
4908
5226
  renderGateMarkdown(result),
4909
5227
  "utf8"
4910
5228
  );
@@ -4946,21 +5264,21 @@ var init_gates = __esm({
4946
5264
  });
4947
5265
 
4948
5266
  // src/review/profiles.ts
4949
- import { existsSync as existsSync12 } from "fs";
4950
- import { mkdir as mkdir10, readFile as readFile12, writeFile as writeFile11 } from "fs/promises";
4951
- import { join as join13 } from "path";
4952
- import { z as z12 } from "zod";
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";
4953
5271
  function reviewActivePath(packPath, sectionId) {
4954
- return join13(packPath, "sections", sectionId, "review-active.json");
5272
+ return join14(packPath, "sections", sectionId, "review-active.json");
4955
5273
  }
4956
5274
  function profileDir(packPath, sectionId, profile) {
4957
- return join13(packPath, "sections", sectionId, "reviews", profile);
5275
+ return join14(packPath, "sections", sectionId, "reviews", profile);
4958
5276
  }
4959
5277
  async function readActiveProfile(packPath, sectionId) {
4960
5278
  const path = reviewActivePath(packPath, sectionId);
4961
- if (!existsSync12(path)) return DEFAULT_PROFILE;
5279
+ if (!existsSync13(path)) return DEFAULT_PROFILE;
4962
5280
  try {
4963
- const parsed = ReviewActiveSchema.parse(JSON.parse(await readFile12(path, "utf8")));
5281
+ const parsed = ReviewActiveSchema.parse(JSON.parse(await readFile13(path, "utf8")));
4964
5282
  return parsed.active_profile;
4965
5283
  } catch {
4966
5284
  return DEFAULT_PROFILE;
@@ -4968,7 +5286,7 @@ async function readActiveProfile(packPath, sectionId) {
4968
5286
  }
4969
5287
  async function writeActiveProfile(packPath, sectionId, active) {
4970
5288
  const path = reviewActivePath(packPath, sectionId);
4971
- await mkdir10(join13(packPath, "sections", sectionId), { recursive: true });
5289
+ await mkdir11(join14(packPath, "sections", sectionId), { recursive: true });
4972
5290
  await writeFile11(path, JSON.stringify(ReviewActiveSchema.parse(active), null, 2), "utf8");
4973
5291
  }
4974
5292
  function isValidProfileName(name) {
@@ -4979,22 +5297,22 @@ var init_profiles = __esm({
4979
5297
  "src/review/profiles.ts"() {
4980
5298
  "use strict";
4981
5299
  DEFAULT_PROFILE = "default";
4982
- PromotionCalibrationSummarySchema = z12.object({
4983
- fixture: z12.string().nullable().default(null),
4984
- good_false_positive_rate: z12.string().nullable().default(null),
4985
- bad_any_flag_recall: z12.string().nullable().default(null),
4986
- strict_category_recall: z12.string().nullable().default(null),
4987
- unsupported_claim_recall: z12.string().nullable().default(null),
4988
- notes: z12.string().nullable().default(null)
4989
- });
4990
- ReviewActiveSchema = z12.object({
4991
- active_profile: z12.string().min(1),
4992
- promoted_at: z12.string(),
4993
- promoted_method: z12.string(),
4994
- promoted_reviewer: z12.string(),
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(),
4995
5313
  // Free-text reason the profile was promoted. Recorded once at promotion
4996
5314
  // time; not derived from artifacts. Required.
4997
- promotion_reason: z12.string().min(8).default("promoted via review-promote without an explicit reason"),
5315
+ promotion_reason: z13.string().min(8).default("promoted via review-promote without an explicit reason"),
4998
5316
  // Optional calibration evidence captured at promotion time so downstream
4999
5317
  // consumers can see WHY the reviewer was trusted.
5000
5318
  calibration_summary: PromotionCalibrationSummarySchema.nullable().default(null)
@@ -5815,14 +6133,14 @@ var init_markdown3 = __esm({
5815
6133
 
5816
6134
  // src/review/run.ts
5817
6135
  import { createHash as createHash4 } from "crypto";
5818
- import { existsSync as existsSync13 } from "fs";
5819
- import { appendFile as appendFile5, mkdir as mkdir11, readFile as readFile13, writeFile as writeFile12 } from "fs/promises";
5820
- import { join as join14, resolve as resolve10 } from "path";
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";
5821
6139
  import { parse as yamlParse3, stringify as yamlStringify4 } from "yaml";
5822
6140
  async function readJsonl2(packPath, rel, parse) {
5823
- const path = join14(packPath, rel);
5824
- if (!existsSync13(path)) return [];
5825
- const text = await readFile13(path, "utf8");
6141
+ const path = join15(packPath, rel);
6142
+ if (!existsSync14(path)) return [];
6143
+ const text = await readFile14(path, "utf8");
5826
6144
  const out = [];
5827
6145
  for (const line of text.split(/\r?\n/)) {
5828
6146
  if (!line.trim()) continue;
@@ -5831,37 +6149,37 @@ async function readJsonl2(packPath, rel, parse) {
5831
6149
  return out;
5832
6150
  }
5833
6151
  async function readSourceCards3(packPath) {
5834
- const dir = join14(packPath, "evidence", "source-cards");
5835
- if (!existsSync13(dir)) return [];
6152
+ const dir = join15(packPath, "evidence", "source-cards");
6153
+ if (!existsSync14(dir)) return [];
5836
6154
  const { readdir: readdir4 } = await import("fs/promises");
5837
6155
  const entries = await readdir4(dir);
5838
6156
  const cards = [];
5839
6157
  for (const entry of entries) {
5840
6158
  if (!entry.endsWith(".json")) continue;
5841
- const text = await readFile13(join14(dir, entry), "utf8");
6159
+ const text = await readFile14(join15(dir, entry), "utf8");
5842
6160
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
5843
6161
  }
5844
6162
  return cards;
5845
6163
  }
5846
6164
  async function readGateResult(packPath, sectionId) {
5847
- const path = join14(packPath, "audits", `${sectionId}-gate.json`);
5848
- if (!existsSync13(path)) return null;
5849
- const text = await readFile13(path, "utf8");
6165
+ const path = join15(packPath, "audits", `${sectionId}-gate.json`);
6166
+ if (!existsSync14(path)) return null;
6167
+ const text = await readFile14(path, "utf8");
5850
6168
  return SectionGateResultSchema.parse(JSON.parse(text));
5851
6169
  }
5852
6170
  async function readBriefText(packPath, sectionId) {
5853
- const path = join14(packPath, "sections", sectionId, "brief.md");
5854
- if (!existsSync13(path)) return null;
5855
- return readFile13(path, "utf8");
6171
+ const path = join15(packPath, "sections", sectionId, "brief.md");
6172
+ if (!existsSync14(path)) return null;
6173
+ return readFile14(path, "utf8");
5856
6174
  }
5857
6175
  async function readRawTextBySource(packPath, receipts) {
5858
6176
  const map2 = /* @__PURE__ */ new Map();
5859
6177
  for (const r of receipts) {
5860
6178
  if (r.fetch_outcome !== "ok" || !r.raw_text_path) continue;
5861
- const path = join14(packPath, r.raw_text_path);
5862
- if (!existsSync13(path)) continue;
6179
+ const path = join15(packPath, r.raw_text_path);
6180
+ if (!existsSync14(path)) continue;
5863
6181
  if (map2.has(r.source_id)) continue;
5864
- map2.set(r.source_id, await readFile13(path, "utf8"));
6182
+ map2.set(r.source_id, await readFile14(path, "utf8"));
5865
6183
  }
5866
6184
  return map2;
5867
6185
  }
@@ -5871,8 +6189,8 @@ async function readExcerptsBySource(packPath, claims) {
5871
6189
  for (const c of claims) for (const sid of c.source_ids) sourceIds.add(sid);
5872
6190
  for (const sid of sourceIds) {
5873
6191
  const path = ledgerPathFor(packPath, sid);
5874
- if (!existsSync13(path)) continue;
5875
- const text = await readFile13(path, "utf8");
6192
+ if (!existsSync14(path)) continue;
6193
+ const text = await readFile14(path, "utf8");
5876
6194
  const index = /* @__PURE__ */ new Map();
5877
6195
  for (const line of text.split(/\r?\n/)) {
5878
6196
  if (!line.trim()) continue;
@@ -5925,15 +6243,15 @@ function buildFinding(args) {
5925
6243
  });
5926
6244
  }
5927
6245
  async function loadResearchYaml3(packPath) {
5928
- const yamlPath = join14(packPath, "research.yaml");
5929
- if (!existsSync13(yamlPath)) throw new PackNotFoundError(packPath);
5930
- const text = await readFile13(yamlPath, "utf8");
6246
+ const yamlPath = join15(packPath, "research.yaml");
6247
+ if (!existsSync14(yamlPath)) throw new PackNotFoundError(packPath);
6248
+ const text = await readFile14(yamlPath, "utf8");
5931
6249
  return ResearchYamlSchema.parse(yamlParse3(text));
5932
6250
  }
5933
6251
  async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
5934
6252
  if (!allAccepted) return false;
5935
- const yamlPath = join14(packPath, "research.yaml");
5936
- const text = await readFile13(yamlPath, "utf8");
6253
+ const yamlPath = join15(packPath, "research.yaml");
6254
+ const text = await readFile14(yamlPath, "utf8");
5937
6255
  const research = ResearchYamlSchema.parse(yamlParse3(text));
5938
6256
  const idx = research.sections.findIndex((s) => s.id === sectionId);
5939
6257
  if (idx < 0) return false;
@@ -5945,9 +6263,9 @@ async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
5945
6263
  }
5946
6264
  async function review(options) {
5947
6265
  const packPath = options.packPath ? resolve10(options.packPath) : process.cwd();
5948
- if (!existsSync13(join14(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
5949
- const sectionDir = join14(packPath, "sections", options.sectionId);
5950
- if (!existsSync13(sectionDir)) throw new SectionNotFoundError(options.sectionId);
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);
5951
6269
  const research = await loadResearchYaml3(packPath);
5952
6270
  const section = research.sections.find((s) => s.id === options.sectionId);
5953
6271
  if (!section) throw new SectionNotFoundError(options.sectionId);
@@ -6197,39 +6515,39 @@ async function finalizeReview(args) {
6197
6515
  promoted_to_reviewed: promoted
6198
6516
  });
6199
6517
  const profDir = profileDir(args.packPath, args.sectionId, args.profile);
6200
- await mkdir11(profDir, { recursive: true });
6201
- await writeFile12(join14(profDir, "review.json"), JSON.stringify(snapshot, null, 2), "utf8");
6202
- await writeFile12(join14(profDir, "review.md"), renderReviewMarkdown(snapshot), "utf8");
6203
- const profileFindingsPath = join14(profDir, "findings.jsonl");
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");
6204
6522
  for (const f of dedupedFindings) {
6205
- await appendFile5(profileFindingsPath, JSON.stringify(f) + "\n", "utf8");
6523
+ await appendFile6(profileFindingsPath, JSON.stringify(f) + "\n", "utf8");
6206
6524
  }
6207
- const profileReviewsPath = join14(profDir, "claim-reviews.jsonl");
6525
+ const profileReviewsPath = join15(profDir, "claim-reviews.jsonl");
6208
6526
  for (const r of claimReviews) {
6209
- await appendFile5(profileReviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6527
+ await appendFile6(profileReviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6210
6528
  }
6211
6529
  const activeProfile = await readActiveProfile(args.packPath, args.sectionId);
6212
6530
  const isActive = args.profile === activeProfile || activeProfile === DEFAULT_PROFILE && args.profile === DEFAULT_PROFILE;
6213
6531
  if (isActive) {
6214
- const auditsDir = join14(args.packPath, "audits");
6215
- await mkdir11(auditsDir, { recursive: true });
6532
+ const auditsDir = join15(args.packPath, "audits");
6533
+ await mkdir12(auditsDir, { recursive: true });
6216
6534
  await writeFile12(
6217
- join14(auditsDir, `${args.sectionId}-review.json`),
6535
+ join15(auditsDir, `${args.sectionId}-review.json`),
6218
6536
  JSON.stringify(snapshot, null, 2),
6219
6537
  "utf8"
6220
6538
  );
6221
6539
  await writeFile12(
6222
- join14(auditsDir, `${args.sectionId}-review.md`),
6540
+ join15(auditsDir, `${args.sectionId}-review.md`),
6223
6541
  renderReviewMarkdown(snapshot),
6224
6542
  "utf8"
6225
6543
  );
6226
- const findingsPath = join14(auditsDir, `${args.sectionId}-findings.jsonl`);
6544
+ const findingsPath = join15(auditsDir, `${args.sectionId}-findings.jsonl`);
6227
6545
  for (const f of dedupedFindings) {
6228
- await appendFile5(findingsPath, JSON.stringify(f) + "\n", "utf8");
6546
+ await appendFile6(findingsPath, JSON.stringify(f) + "\n", "utf8");
6229
6547
  }
6230
- const reviewsPath = join14(args.packPath, "sections", args.sectionId, "claim-reviews.jsonl");
6548
+ const reviewsPath = join15(args.packPath, "sections", args.sectionId, "claim-reviews.jsonl");
6231
6549
  for (const r of claimReviews) {
6232
- await appendFile5(reviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6550
+ await appendFile6(reviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6233
6551
  }
6234
6552
  }
6235
6553
  return {
@@ -6266,14 +6584,14 @@ var init_run4 = __esm({
6266
6584
  });
6267
6585
 
6268
6586
  // src/review/promote.ts
6269
- import { existsSync as existsSync14 } from "fs";
6270
- import { copyFile, mkdir as mkdir12, readFile as readFile14, writeFile as writeFile13, appendFile as appendFile6 } from "fs/promises";
6271
- import { join as join15, resolve as resolve11 } from "path";
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";
6272
6590
  import { parse as yamlParse4, stringify as yamlStringify5 } from "yaml";
6273
6591
  async function promote(options) {
6274
6592
  const packPath = options.packPath ? resolve11(options.packPath) : process.cwd();
6275
- if (!existsSync14(join15(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
6276
- if (!existsSync14(join15(packPath, "sections", options.sectionId)))
6593
+ if (!existsSync15(join16(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
6594
+ if (!existsSync15(join16(packPath, "sections", options.sectionId)))
6277
6595
  throw new SectionNotFoundError(options.sectionId);
6278
6596
  if (!isValidProfileName(options.profile)) {
6279
6597
  throw new Error(
@@ -6281,39 +6599,39 @@ async function promote(options) {
6281
6599
  );
6282
6600
  }
6283
6601
  const dir = profileDir(packPath, options.sectionId, options.profile);
6284
- const reviewJsonPath = join15(dir, "review.json");
6285
- if (!existsSync14(reviewJsonPath)) {
6602
+ const reviewJsonPath = join16(dir, "review.json");
6603
+ if (!existsSync15(reviewJsonPath)) {
6286
6604
  throw new Error(
6287
6605
  `Profile "${options.profile}" not found at ${dir}. Run \`research-os review --profile ${options.profile}\` first.`
6288
6606
  );
6289
6607
  }
6290
6608
  const snapshot = ReviewSnapshotSchema.parse(
6291
- JSON.parse(await readFile14(reviewJsonPath, "utf8"))
6609
+ JSON.parse(await readFile15(reviewJsonPath, "utf8"))
6292
6610
  );
6293
- const auditsDir = join15(packPath, "audits");
6294
- await mkdir12(auditsDir, { recursive: true });
6295
- const canonicalReviewJson = join15(auditsDir, `${options.sectionId}-review.json`);
6296
- const canonicalReviewMd = join15(auditsDir, `${options.sectionId}-review.md`);
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`);
6297
6615
  await copyFile(reviewJsonPath, canonicalReviewJson);
6298
6616
  await writeFile13(canonicalReviewMd, renderReviewMarkdown(snapshot), "utf8");
6299
- const canonicalFindings = join15(auditsDir, `${options.sectionId}-findings.jsonl`);
6300
- const profileFindings = join15(dir, "findings.jsonl");
6617
+ const canonicalFindings = join16(auditsDir, `${options.sectionId}-findings.jsonl`);
6618
+ const profileFindings = join16(dir, "findings.jsonl");
6301
6619
  const writtenFindings = [];
6302
- if (existsSync14(profileFindings)) {
6303
- const text = await readFile14(profileFindings, "utf8");
6620
+ if (existsSync15(profileFindings)) {
6621
+ const text = await readFile15(profileFindings, "utf8");
6304
6622
  for (const line of text.split(/\r?\n/)) {
6305
6623
  if (!line.trim()) continue;
6306
- await appendFile6(canonicalFindings, line + "\n", "utf8");
6624
+ await appendFile7(canonicalFindings, line + "\n", "utf8");
6307
6625
  writtenFindings.push(line);
6308
6626
  }
6309
6627
  }
6310
- const canonicalReviews = join15(packPath, "sections", options.sectionId, "claim-reviews.jsonl");
6311
- const profileReviews = join15(dir, "claim-reviews.jsonl");
6312
- if (existsSync14(profileReviews)) {
6313
- const text = await readFile14(profileReviews, "utf8");
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");
6314
6632
  for (const line of text.split(/\r?\n/)) {
6315
6633
  if (!line.trim()) continue;
6316
- await appendFile6(canonicalReviews, line + "\n", "utf8");
6634
+ await appendFile7(canonicalReviews, line + "\n", "utf8");
6317
6635
  }
6318
6636
  }
6319
6637
  const stamp = (options.now ?? (() => /* @__PURE__ */ new Date()))();
@@ -6327,8 +6645,8 @@ async function promote(options) {
6327
6645
  });
6328
6646
  let sectionStatusBumped = false;
6329
6647
  if (options.promoteSectionStatus) {
6330
- const yamlPath = join15(packPath, "research.yaml");
6331
- const research = ResearchYamlSchema.parse(yamlParse4(await readFile14(yamlPath, "utf8")));
6648
+ const yamlPath = join16(packPath, "research.yaml");
6649
+ const research = ResearchYamlSchema.parse(yamlParse4(await readFile15(yamlPath, "utf8")));
6332
6650
  const idx = research.sections.findIndex((s) => s.id === options.sectionId);
6333
6651
  if (idx >= 0 && research.sections[idx].status === "gated") {
6334
6652
  const allAccepted = snapshot.candidate_claims > 0 && snapshot.claim_reviews.every((r) => r.decision === "accepted_for_synthesis");
@@ -6533,16 +6851,16 @@ var init_schema11 = __esm({
6533
6851
  });
6534
6852
 
6535
6853
  // src/indexer/db.ts
6536
- import { mkdirSync, writeFileSync, existsSync as existsSync15 } from "fs";
6537
- import { dirname as dirname3, join as join16 } from "path";
6854
+ import { mkdirSync, writeFileSync, existsSync as existsSync16 } from "fs";
6855
+ import { dirname as dirname4, join as join17 } from "path";
6538
6856
  import Database from "better-sqlite3";
6539
6857
  function indexDbPath(packPath) {
6540
- return join16(packPath, ".research-os", "index.sqlite");
6858
+ return join17(packPath, ".research-os", "index.sqlite");
6541
6859
  }
6542
6860
  function ensureGitIgnore(packPath) {
6543
- const dir = join16(packPath, ".research-os");
6544
- const gi = join16(dir, ".gitignore");
6545
- if (!existsSync15(gi)) {
6861
+ const dir = join17(packPath, ".research-os");
6862
+ const gi = join17(dir, ".gitignore");
6863
+ if (!existsSync16(gi)) {
6546
6864
  mkdirSync(dir, { recursive: true });
6547
6865
  writeFileSync(gi, "*\n", "utf8");
6548
6866
  }
@@ -6550,7 +6868,7 @@ function ensureGitIgnore(packPath) {
6550
6868
  function openIndexDb(opts) {
6551
6869
  const dbPath = indexDbPath(opts.packPath);
6552
6870
  if (!opts.readonly) {
6553
- mkdirSync(dirname3(dbPath), { recursive: true });
6871
+ mkdirSync(dirname4(dbPath), { recursive: true });
6554
6872
  ensureGitIgnore(opts.packPath);
6555
6873
  }
6556
6874
  const db = new Database(dbPath, { readonly: opts.readonly ?? false, fileMustExist: opts.readonly ?? false });
@@ -6565,18 +6883,18 @@ var init_db = __esm({
6565
6883
  });
6566
6884
 
6567
6885
  // src/indexer/build.ts
6568
- import { existsSync as existsSync16 } from "fs";
6569
- import { readFile as readFile15 } from "fs/promises";
6570
- import { join as join17, resolve as resolve12, relative as relative2 } from "path";
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";
6571
6889
  import { createHash as createHash5 } from "crypto";
6572
6890
  import { parse as yamlParse5 } from "yaml";
6573
6891
  function relPath(packPath, abs) {
6574
6892
  return relative2(packPath, abs).split("\\").join("/");
6575
6893
  }
6576
6894
  async function tryReadJsonl(packPath, rel, parse) {
6577
- const path = join17(packPath, rel);
6578
- if (!existsSync16(path)) return [];
6579
- const text = await readFile15(path, "utf8");
6895
+ const path = join18(packPath, rel);
6896
+ if (!existsSync17(path)) return [];
6897
+ const text = await readFile16(path, "utf8");
6580
6898
  const out = [];
6581
6899
  for (const line of text.split(/\r?\n/)) {
6582
6900
  if (!line.trim()) continue;
@@ -6585,22 +6903,22 @@ async function tryReadJsonl(packPath, rel, parse) {
6585
6903
  return out;
6586
6904
  }
6587
6905
  async function readSourceCards4(packPath) {
6588
- const dir = join17(packPath, "evidence", "source-cards");
6589
- if (!existsSync16(dir)) return [];
6906
+ const dir = join18(packPath, "evidence", "source-cards");
6907
+ if (!existsSync17(dir)) return [];
6590
6908
  const { readdir: readdir4 } = await import("fs/promises");
6591
6909
  const entries = await readdir4(dir);
6592
6910
  const cards = [];
6593
6911
  for (const entry of entries) {
6594
6912
  if (!entry.endsWith(".json")) continue;
6595
- const text = await readFile15(join17(dir, entry), "utf8");
6913
+ const text = await readFile16(join18(dir, entry), "utf8");
6596
6914
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
6597
6915
  }
6598
6916
  return cards;
6599
6917
  }
6600
6918
  async function readGateResult2(packPath, sectionId) {
6601
- const path = join17(packPath, "audits", `${sectionId}-gate.json`);
6602
- if (!existsSync16(path)) return null;
6603
- const text = await readFile15(path, "utf8");
6919
+ const path = join18(packPath, "audits", `${sectionId}-gate.json`);
6920
+ if (!existsSync17(path)) return null;
6921
+ const text = await readFile16(path, "utf8");
6604
6922
  return SectionGateResultSchema.parse(JSON.parse(text));
6605
6923
  }
6606
6924
  function fileSha256(text) {
@@ -6610,8 +6928,8 @@ async function indexSection(args) {
6610
6928
  const { db, packPath, research, sectionId, now, counts } = args;
6611
6929
  const section = research.sections.find((s) => s.id === sectionId);
6612
6930
  if (!section) throw new SectionNotFoundError(sectionId);
6613
- const sectionDir = join17(packPath, "sections", sectionId);
6614
- if (!existsSync16(sectionDir)) throw new SectionNotFoundError(sectionId);
6931
+ const sectionDir = join18(packPath, "sections", sectionId);
6932
+ if (!existsSync17(sectionDir)) throw new SectionNotFoundError(sectionId);
6615
6933
  db.prepare("DELETE FROM sources WHERE section_id = ?").run(sectionId);
6616
6934
  db.prepare("DELETE FROM claims WHERE section_id = ?").run(sectionId);
6617
6935
  db.prepare("DELETE FROM contradictions WHERE section_id = ?").run(sectionId);
@@ -6650,9 +6968,9 @@ async function indexSection(args) {
6650
6968
  };
6651
6969
  const allCards = await readSourceCards4(packPath);
6652
6970
  const sectionSourceIds = /* @__PURE__ */ new Set();
6653
- const sourcesJsonlPath = join17(sectionDir, "sources.jsonl");
6654
- if (existsSync16(sourcesJsonlPath)) {
6655
- const text = await readFile15(sourcesJsonlPath, "utf8");
6971
+ const sourcesJsonlPath = join18(sectionDir, "sources.jsonl");
6972
+ if (existsSync17(sourcesJsonlPath)) {
6973
+ const text = await readFile16(sourcesJsonlPath, "utf8");
6656
6974
  recordArtifact("sources_jsonl", relPath(packPath, sourcesJsonlPath), text);
6657
6975
  for (const line of text.split(/\r?\n/)) {
6658
6976
  if (!line.trim()) continue;
@@ -6662,7 +6980,7 @@ async function indexSection(args) {
6662
6980
  }
6663
6981
  for (const card of allCards) {
6664
6982
  if (!sectionSourceIds.has(card.source_id) && card.section_id !== sectionId) continue;
6665
- const cardPath = relPath(packPath, join17(packPath, "evidence", "source-cards", `${card.source_id}.json`));
6983
+ const cardPath = relPath(packPath, join18(packPath, "evidence", "source-cards", `${card.source_id}.json`));
6666
6984
  db.prepare(
6667
6985
  `INSERT OR REPLACE INTO sources(
6668
6986
  source_id, section_id, url, publisher, source_type, relevance,
@@ -6696,9 +7014,9 @@ ${card.key_points.join("\n")}`;
6696
7014
  `sections/${sectionId}/claims.jsonl`,
6697
7015
  (r) => ClaimSchema.parse(r)
6698
7016
  );
6699
- const claimsArtifact = relPath(packPath, join17(sectionDir, "claims.jsonl"));
6700
- if (existsSync16(join17(sectionDir, "claims.jsonl"))) {
6701
- const text = await readFile15(join17(sectionDir, "claims.jsonl"), "utf8");
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");
6702
7020
  recordArtifact("claims_jsonl", claimsArtifact, text);
6703
7021
  }
6704
7022
  for (const claim of claims) {
@@ -6735,9 +7053,9 @@ ${claim.evidence_excerpt}`;
6735
7053
  `sections/${sectionId}/contradictions.jsonl`,
6736
7054
  (r) => ContradictionSchema.parse(r)
6737
7055
  );
6738
- const contradictionsArtifact = relPath(packPath, join17(sectionDir, "contradictions.jsonl"));
6739
- if (existsSync16(join17(sectionDir, "contradictions.jsonl"))) {
6740
- const text = await readFile15(join17(sectionDir, "contradictions.jsonl"), "utf8");
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");
6741
7059
  recordArtifact("contradictions_jsonl", contradictionsArtifact, text);
6742
7060
  }
6743
7061
  for (const c of contradictions) {
@@ -6773,10 +7091,10 @@ ${c.severity}`;
6773
7091
  `audits/${sectionId}-findings.jsonl`,
6774
7092
  (r) => ReviewFindingSchema.parse(r)
6775
7093
  );
6776
- const findingsArtifact = relPath(packPath, join17(packPath, "audits", `${sectionId}-findings.jsonl`));
6777
- const findingsAbs = join17(packPath, "audits", `${sectionId}-findings.jsonl`);
6778
- if (existsSync16(findingsAbs)) {
6779
- const text = await readFile15(findingsAbs, "utf8");
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");
6780
7098
  recordArtifact("findings_jsonl", findingsArtifact, text);
6781
7099
  }
6782
7100
  const findingById = /* @__PURE__ */ new Map();
@@ -6813,10 +7131,10 @@ ${f.evidence}`;
6813
7131
  `sections/${sectionId}/claim-reviews.jsonl`,
6814
7132
  (r) => ClaimReviewSchema.parse(r)
6815
7133
  );
6816
- const reviewsArtifact = relPath(packPath, join17(sectionDir, "claim-reviews.jsonl"));
6817
- const reviewsAbs = join17(sectionDir, "claim-reviews.jsonl");
6818
- if (existsSync16(reviewsAbs)) {
6819
- const text = await readFile15(reviewsAbs, "utf8");
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");
6820
7138
  recordArtifact("claim_reviews_jsonl", reviewsArtifact, text);
6821
7139
  }
6822
7140
  for (const r of reviews) {
@@ -6841,8 +7159,8 @@ ${r.reason}`;
6841
7159
  }
6842
7160
  const gate2 = await readGateResult2(packPath, sectionId);
6843
7161
  if (gate2) {
6844
- const gateArtifact = relPath(packPath, join17(packPath, "audits", `${sectionId}-gate.json`));
6845
- const gateText = await readFile15(join17(packPath, "audits", `${sectionId}-gate.json`), "utf8");
7162
+ const gateArtifact = relPath(packPath, join18(packPath, "audits", `${sectionId}-gate.json`));
7163
+ const gateText = await readFile16(join18(packPath, "audits", `${sectionId}-gate.json`), "utf8");
6846
7164
  recordArtifact("gate_json", gateArtifact, gateText);
6847
7165
  db.prepare(
6848
7166
  `INSERT OR REPLACE INTO gate_results(
@@ -6875,9 +7193,9 @@ ${gate2.next_actions.join("\n")}`;
6875
7193
  "evidence/fetch-log.jsonl",
6876
7194
  (r) => FetchReceiptSchema.parse(r)
6877
7195
  );
6878
- const fetchLogAbs = join17(packPath, "evidence", "fetch-log.jsonl");
6879
- if (existsSync16(fetchLogAbs)) {
6880
- const text = await readFile15(fetchLogAbs, "utf8");
7196
+ const fetchLogAbs = join18(packPath, "evidence", "fetch-log.jsonl");
7197
+ if (existsSync17(fetchLogAbs)) {
7198
+ const text = await readFile16(fetchLogAbs, "utf8");
6881
7199
  recordArtifact("fetch_log_jsonl", relPath(packPath, fetchLogAbs), text);
6882
7200
  }
6883
7201
  for (const receipt of allReceipts.filter((r) => r.section_id === sectionId)) {
@@ -6913,9 +7231,9 @@ async function indexPackAuditRollups(db, packPath, _now) {
6913
7231
  db.prepare(`DELETE FROM facts_fts WHERE record_type = ? AND record_id = ?`).run(f.recordType, f.recordId);
6914
7232
  }
6915
7233
  for (const f of ROLLUP_FILES) {
6916
- const abs = join17(packPath, "audits", f.filename);
6917
- if (!existsSync16(abs)) continue;
6918
- const text = await readFile15(abs, "utf8");
7234
+ const abs = join18(packPath, "audits", f.filename);
7235
+ if (!existsSync17(abs)) continue;
7236
+ const text = await readFile16(abs, "utf8");
6919
7237
  insertFts.run(
6920
7238
  f.recordType,
6921
7239
  f.recordId,
@@ -6927,8 +7245,8 @@ async function indexPackAuditRollups(db, packPath, _now) {
6927
7245
  }
6928
7246
  async function build(options) {
6929
7247
  const packPath = options.packPath ? resolve12(options.packPath) : process.cwd();
6930
- if (!existsSync16(join17(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
6931
- const research = ResearchYamlSchema.parse(yamlParse5(await readFile15(join17(packPath, "research.yaml"), "utf8")));
7248
+ if (!existsSync17(join18(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
7249
+ const research = ResearchYamlSchema.parse(yamlParse5(await readFile16(join18(packPath, "research.yaml"), "utf8")));
6932
7250
  const targets = options.sectionId ? [options.sectionId] : options.all ? research.sections.map((s) => s.id) : research.sections.map((s) => s.id);
6933
7251
  if (targets.length === 0) {
6934
7252
  throw new Error("No sections to index. Add at least one section to the pack.");
@@ -6985,7 +7303,7 @@ var init_build = __esm({
6985
7303
  });
6986
7304
 
6987
7305
  // src/indexer/query.ts
6988
- import { existsSync as existsSync17 } from "fs";
7306
+ import { existsSync as existsSync18 } from "fs";
6989
7307
  import { resolve as resolve13 } from "path";
6990
7308
  function escapeFtsTerm(term) {
6991
7309
  const trimmed = term.trim();
@@ -6996,8 +7314,8 @@ function escapeFtsTerm(term) {
6996
7314
  function query(options) {
6997
7315
  const packPath = options.packPath ? resolve13(options.packPath) : process.cwd();
6998
7316
  const dbPath = indexDbPath(packPath);
6999
- if (!existsSync17(packPath)) throw new PackNotFoundError(packPath);
7000
- if (!existsSync17(dbPath)) throw new IndexNotBuiltError(dbPath);
7317
+ if (!existsSync18(packPath)) throw new PackNotFoundError(packPath);
7318
+ if (!existsSync18(dbPath)) throw new IndexNotBuiltError(dbPath);
7001
7319
  const db = openIndexDb({ packPath, readonly: true });
7002
7320
  const limit = options.limit ?? 25;
7003
7321
  const ftsTerm = escapeFtsTerm(options.term);
@@ -7071,16 +7389,16 @@ var init_query = __esm({
7071
7389
  });
7072
7390
 
7073
7391
  // src/indexer/export.ts
7074
- import { existsSync as existsSync18 } from "fs";
7075
- import { mkdir as mkdir13, writeFile as writeFile14 } from "fs/promises";
7076
- import { dirname as dirname4, join as join18, resolve as resolve14 } from "path";
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";
7077
7395
  async function exportRepoKnowledge(options) {
7078
7396
  const packPath = options.packPath ? resolve14(options.packPath) : process.cwd();
7079
- if (!existsSync18(packPath)) throw new PackNotFoundError(packPath);
7397
+ if (!existsSync19(packPath)) throw new PackNotFoundError(packPath);
7080
7398
  const dbPath = indexDbPath(packPath);
7081
- if (!existsSync18(dbPath)) throw new IndexNotBuiltError(dbPath);
7082
- const outPath = options.outPath ? resolve14(options.outPath) : join18(packPath, "evidence", "repo-knowledge", "research-os-facts.jsonl");
7083
- await mkdir13(dirname4(outPath), { recursive: true });
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 });
7084
7402
  const db = openIndexDb({ packPath, readonly: true });
7085
7403
  const now = (/* @__PURE__ */ new Date()).toISOString();
7086
7404
  const facts = [];
@@ -7239,8 +7557,8 @@ async function syncRepoKnowledge(options) {
7239
7557
  };
7240
7558
  }
7241
7559
  const exportResult = await exportRepoKnowledge({ packPath });
7242
- const { readFile: readFile24 } = await import("fs/promises");
7243
- const text = await readFile24(exportResult.outPath, "utf8");
7560
+ const { readFile: readFile25 } = await import("fs/promises");
7561
+ const text = await readFile25(exportResult.outPath, "utf8");
7244
7562
  const facts = text.split(/\r?\n/).filter((l) => l.trim().length > 0).map((l) => JSON.parse(l));
7245
7563
  try {
7246
7564
  const r = await rk.ingestFacts({ facts, namespace: "research-os" });
@@ -7280,26 +7598,26 @@ var init_indexer = __esm({
7280
7598
  });
7281
7599
 
7282
7600
  // src/dispositions/schema.ts
7283
- import { z as z13 } from "zod";
7601
+ import { z as z14 } from "zod";
7284
7602
  var ClaimSynthesisDispositionStatusSchema, ClaimSynthesisDispositionSchema;
7285
7603
  var init_schema12 = __esm({
7286
7604
  "src/dispositions/schema.ts"() {
7287
7605
  "use strict";
7288
- ClaimSynthesisDispositionStatusSchema = z13.enum([
7606
+ ClaimSynthesisDispositionStatusSchema = z14.enum([
7289
7607
  "parked_not_for_synthesis",
7290
7608
  "preserved_for_human_note",
7291
7609
  "needs_human_review_excluded",
7292
7610
  "out_of_bounds_regression_fixture"
7293
7611
  ]);
7294
- ClaimSynthesisDispositionSchema = z13.object({
7295
- claim_id: z13.string().min(1),
7296
- section_id: z13.string().min(1),
7612
+ ClaimSynthesisDispositionSchema = z14.object({
7613
+ claim_id: z14.string().min(1),
7614
+ section_id: z14.string().min(1),
7297
7615
  status: ClaimSynthesisDispositionStatusSchema,
7298
- reason: z13.string().min(4),
7299
- decided_by: z13.string().min(1),
7300
- authorized_by: z13.string().min(1),
7301
- source: z13.string().min(1),
7302
- created_at: z13.string().min(1)
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)
7303
7621
  });
7304
7622
  }
7305
7623
  });
@@ -7630,91 +7948,91 @@ var init_derive = __esm({
7630
7948
  });
7631
7949
 
7632
7950
  // src/cowork/schema.ts
7633
- import { z as z14 } from "zod";
7951
+ import { z as z15 } from "zod";
7634
7952
  var HandoffModeSchema, IndexStatusSchema, ProvenanceSummarySchema, SectionStateSchema, WaiverEntrySchema, GateVerdictEntrySchema, ReviewDecisionCountSchema, CoworkHandoffPayloadSchema;
7635
7953
  var init_schema13 = __esm({
7636
7954
  "src/cowork/schema.ts"() {
7637
7955
  "use strict";
7638
- HandoffModeSchema = z14.enum([
7956
+ HandoffModeSchema = z15.enum([
7639
7957
  "repair_required",
7640
7958
  "synthesis_ready",
7641
7959
  "human_review_required"
7642
7960
  ]);
7643
- IndexStatusSchema = z14.enum(["present", "missing"]);
7644
- ProvenanceSummarySchema = z14.object({
7645
- accepted_count: z14.number().int().nonnegative(),
7646
- rejected_count: z14.number().int().nonnegative(),
7647
- triage_parked_count: z14.number().int().nonnegative(),
7648
- needs_review_undispositioned_count: z14.number().int().nonnegative(),
7649
- dispositioned_count: z14.number().int().nonnegative(),
7650
- dispositioned_breakdown: z14.object({
7651
- parked_not_for_synthesis: z14.number().int().nonnegative(),
7652
- preserved_for_human_note: z14.number().int().nonnegative(),
7653
- needs_human_review_excluded: z14.number().int().nonnegative(),
7654
- out_of_bounds_regression_fixture: z14.number().int().nonnegative()
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()
7655
7973
  }),
7656
- active_repair_blockers: z14.number().int().nonnegative(),
7657
- active_unresolved_contradictions: z14.number().int().nonnegative(),
7658
- waivers_active: z14.array(z14.string()),
7659
- overrides_applied_count: z14.number().int().nonnegative()
7660
- });
7661
- SectionStateSchema = z14.object({
7662
- section_id: z14.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
7663
- purpose: z14.string(),
7664
- status: z14.string(),
7665
- has_gate_run: z14.boolean(),
7666
- has_review_run: z14.boolean(),
7667
- gate_verdict: z14.string().nullable(),
7668
- synthesis_eligible: z14.boolean(),
7669
- accepted_claim_ids: z14.array(z14.string()),
7670
- repair_claim_ids: z14.array(z14.string()),
7671
- rejected_claim_ids: z14.array(z14.string()),
7672
- dispositioned_claim_ids: z14.array(z14.string()).default([]),
7673
- candidate_claims_total: z14.number().int().nonnegative(),
7674
- unresolved_contradiction_ids: z14.array(z14.string()),
7675
- blocking_reasons: z14.array(z14.string()),
7676
- active_blockers: z14.array(z14.string()).default([]),
7677
- blocking_contradictions_unresolved: z14.number().int().nonnegative(),
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(),
7678
7996
  provenance_summary: ProvenanceSummarySchema.optional()
7679
7997
  });
7680
- WaiverEntrySchema = z14.object({
7681
- scope: z14.enum(["pack", "gate"]),
7682
- family: z14.string(),
7683
- reason: z14.string(),
7684
- compensating_controls: z14.array(z14.string()),
7685
- applied_to: z14.string()
7686
- });
7687
- GateVerdictEntrySchema = z14.object({
7688
- section_id: z14.string(),
7689
- verdict: z14.string(),
7690
- synthesis_eligible: z14.boolean()
7691
- });
7692
- ReviewDecisionCountSchema = z14.object({
7693
- section_id: z14.string(),
7694
- decision: z14.string(),
7695
- count: z14.number().int().nonnegative()
7696
- });
7697
- CoworkHandoffPayloadSchema = z14.object({
7698
- pack_id: z14.string(),
7699
- pack_topic: z14.string(),
7700
- generated_at: z14.string(),
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(),
7701
8019
  mode: HandoffModeSchema,
7702
- synthesis_allowed: z14.boolean(),
7703
- summary: z14.string(),
7704
- sections: z14.array(SectionStateSchema),
7705
- accepted_claim_ids: z14.array(z14.string()),
7706
- repair_claim_ids: z14.array(z14.string()),
7707
- blocked_claim_ids: z14.array(z14.string()),
7708
- dispositioned_claim_ids: z14.array(z14.string()).default([]),
7709
- unresolved_contradiction_ids: z14.array(z14.string()),
7710
- waivers: z14.array(WaiverEntrySchema),
7711
- gate_verdicts: z14.array(GateVerdictEntrySchema),
7712
- review_decisions: z14.array(ReviewDecisionCountSchema),
7713
- recommended_next_actions: z14.array(z14.string()),
7714
- allowed_write_paths: z14.array(z14.string()),
7715
- forbidden_actions: z14.array(z14.string()),
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()),
7716
8034
  index_status: IndexStatusSchema,
7717
- warnings: z14.array(z14.string())
8035
+ warnings: z15.array(z15.string())
7718
8036
  });
7719
8037
  }
7720
8038
  });
@@ -7893,19 +8211,19 @@ var init_markdown4 = __esm({
7893
8211
  });
7894
8212
 
7895
8213
  // src/cowork/run.ts
7896
- import { existsSync as existsSync19 } from "fs";
7897
- import { mkdir as mkdir14, readFile as readFile16, writeFile as writeFile15 } from "fs/promises";
7898
- import { join as join19, resolve as resolve16 } from "path";
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";
7899
8217
  import { parse as yamlParse6 } from "yaml";
7900
8218
  async function loadResearchYaml4(packPath) {
7901
- const yamlPath = join19(packPath, "research.yaml");
7902
- if (!existsSync19(yamlPath)) throw new PackNotFoundError(packPath);
7903
- const text = await readFile16(yamlPath, "utf8");
8219
+ const yamlPath = join20(packPath, "research.yaml");
8220
+ if (!existsSync20(yamlPath)) throw new PackNotFoundError(packPath);
8221
+ const text = await readFile17(yamlPath, "utf8");
7904
8222
  return ResearchYamlSchema.parse(yamlParse6(text));
7905
8223
  }
7906
8224
  async function readJsonl3(path, parse, warnings) {
7907
- if (!existsSync19(path)) return [];
7908
- const text = await readFile16(path, "utf8");
8225
+ if (!existsSync20(path)) return [];
8226
+ const text = await readFile17(path, "utf8");
7909
8227
  const out = [];
7910
8228
  for (const line of text.split(/\r?\n/)) {
7911
8229
  if (!line.trim()) continue;
@@ -7919,10 +8237,10 @@ async function readJsonl3(path, parse, warnings) {
7919
8237
  return out;
7920
8238
  }
7921
8239
  async function readGate(packPath, sectionId, warnings) {
7922
- const path = join19(packPath, "audits", `${sectionId}-gate.json`);
7923
- if (!existsSync19(path)) return null;
8240
+ const path = join20(packPath, "audits", `${sectionId}-gate.json`);
8241
+ if (!existsSync20(path)) return null;
7924
8242
  try {
7925
- const text = await readFile16(path, "utf8");
8243
+ const text = await readFile17(path, "utf8");
7926
8244
  return SectionGateResultSchema.parse(JSON.parse(text));
7927
8245
  } catch (err) {
7928
8246
  warnings.push(`malformed gate result at audits/${sectionId}-gate.json: ${err instanceof Error ? err.message : err}`);
@@ -7937,35 +8255,35 @@ async function handoff(options) {
7937
8255
  for (const section of research.sections) {
7938
8256
  const sid = section.id;
7939
8257
  const claims = await readJsonl3(
7940
- join19(packPath, "sections", sid, "claims.jsonl"),
8258
+ join20(packPath, "sections", sid, "claims.jsonl"),
7941
8259
  (r) => ClaimSchema.parse(r),
7942
8260
  warnings
7943
8261
  );
7944
8262
  const candidateClaims = claims.filter((c) => c.review_state === "candidate");
7945
8263
  const claimReviews = await readJsonl3(
7946
- join19(packPath, "sections", sid, "claim-reviews.jsonl"),
8264
+ join20(packPath, "sections", sid, "claim-reviews.jsonl"),
7947
8265
  (r) => ClaimReviewSchema.parse(r),
7948
8266
  warnings
7949
8267
  );
7950
8268
  const contradictions = await readJsonl3(
7951
- join19(packPath, "sections", sid, "contradictions.jsonl"),
8269
+ join20(packPath, "sections", sid, "contradictions.jsonl"),
7952
8270
  (r) => ContradictionSchema.parse(r),
7953
8271
  warnings
7954
8272
  );
7955
8273
  const resolutions = await readJsonl3(
7956
- join19(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
8274
+ join20(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
7957
8275
  (r) => ContradictionResolutionSchema.parse(r),
7958
8276
  warnings
7959
8277
  );
7960
8278
  const dispositions = await readJsonl3(
7961
- join19(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
8279
+ join20(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
7962
8280
  (r) => ClaimSynthesisDispositionSchema.parse(r),
7963
8281
  warnings
7964
8282
  );
7965
8283
  const gate2 = await readGate(packPath, sid, warnings);
7966
8284
  perSection.set(sid, { gate: gate2, candidateClaims, claimReviews, contradictions, resolutions, dispositions });
7967
8285
  }
7968
- const indexExists = existsSync19(indexDbPath(packPath));
8286
+ const indexExists = existsSync20(indexDbPath(packPath));
7969
8287
  if (!indexExists) {
7970
8288
  warnings.push(
7971
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`."
@@ -7981,10 +8299,10 @@ async function handoff(options) {
7981
8299
  warnings
7982
8300
  })
7983
8301
  );
7984
- const handoffsDir = join19(packPath, "handoffs");
7985
- await mkdir14(handoffsDir, { recursive: true });
7986
- const jsonPath = join19(handoffsDir, "cowork-handoff.json");
7987
- const mdPath = join19(handoffsDir, "cowork-master.md");
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");
7988
8306
  await writeFile15(jsonPath, JSON.stringify(payload, null, 2), "utf8");
7989
8307
  await writeFile15(mdPath, renderCoworkMaster(payload), "utf8");
7990
8308
  return {
@@ -8264,86 +8582,86 @@ var init_derive2 = __esm({
8264
8582
  });
8265
8583
 
8266
8584
  // src/synth/schema.ts
8267
- import { z as z15 } from "zod";
8585
+ import { z as z16 } from "zod";
8268
8586
  var SectionAcceptedSummarySchema, ClaimClusterSchema, SharedSourceSchema, ScopeOverlapSchema, CrossSectionContradictionRefSchema, WaiverDependencySchema, AllowedSynthesisInputSchema, ForbiddenInputSchema, CrossSectionMapSchema;
8269
8587
  var init_schema14 = __esm({
8270
8588
  "src/synth/schema.ts"() {
8271
8589
  "use strict";
8272
- SectionAcceptedSummarySchema = z15.object({
8273
- section_id: z15.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
8274
- purpose: z15.string(),
8275
- status: z15.string(),
8276
- accepted_claim_ids: z15.array(z15.string()),
8277
- excluded_reason: z15.string().nullable()
8278
- });
8279
- ClaimClusterSchema = z15.object({
8280
- cluster_id: z15.string(),
8281
- shared_source_ids: z15.array(z15.string()),
8282
- member_claim_ids: z15.array(z15.string()).min(1),
8283
- spans_sections: z15.array(z15.string())
8284
- });
8285
- SharedSourceSchema = z15.object({
8286
- source_id: z15.string(),
8287
- publisher: z15.string().nullable(),
8288
- source_type: z15.string(),
8289
- used_by_claim_ids: z15.array(z15.string()),
8290
- spans_sections: z15.array(z15.string())
8291
- });
8292
- ScopeOverlapSchema = z15.object({
8293
- claim_a: z15.string(),
8294
- claim_b: z15.string(),
8295
- scope_a: z15.string().nullable(),
8296
- scope_b: z15.string().nullable(),
8297
- jaccard: z15.number().min(0).max(1),
8298
- cross_section: z15.boolean(),
8299
- warning: z15.string()
8300
- });
8301
- CrossSectionContradictionRefSchema = z15.object({
8302
- contradiction_id: z15.string(),
8303
- claim_ids: z15.array(z15.string()),
8304
- sections: z15.array(z15.string()),
8305
- type: z15.string(),
8306
- severity: z15.string(),
8307
- status: z15.string()
8308
- });
8309
- WaiverDependencySchema = z15.object({
8310
- scope: z15.enum(["pack", "gate"]),
8311
- family: z15.string(),
8312
- reason: z15.string(),
8313
- compensating_controls: z15.array(z15.string()),
8314
- applied_to: z15.string(),
8315
- 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()
8316
8596
  });
8317
- AllowedSynthesisInputSchema = z15.object({
8318
- claim_id: z15.string(),
8319
- section_id: z15.string(),
8320
- artifact_path: z15.string(),
8321
- asserts: z15.string(),
8322
- scope: z15.string().nullable(),
8323
- not: z15.string().nullable(),
8324
- source_ids: z15.array(z15.string())
8325
- });
8326
- ForbiddenInputSchema = z15.object({
8327
- claim_id: z15.string(),
8328
- section_id: z15.string(),
8329
- decision: z15.string(),
8330
- 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())
8331
8602
  });
8332
- CrossSectionMapSchema = z15.object({
8333
- pack_id: z15.string(),
8334
- pack_topic: z15.string(),
8335
- pack_decision: z15.string(),
8336
- generated_at: z15.string(),
8337
- accepted_claim_ids: z15.array(z15.string()),
8338
- sections: z15.array(SectionAcceptedSummarySchema),
8339
- claim_clusters: z15.array(ClaimClusterSchema),
8340
- shared_sources: z15.array(SharedSourceSchema),
8341
- scope_overlaps: z15.array(ScopeOverlapSchema),
8342
- cross_section_contradictions: z15.array(CrossSectionContradictionRefSchema),
8343
- waiver_dependencies: z15.array(WaiverDependencySchema),
8344
- open_questions: z15.array(z15.string()),
8345
- allowed_synthesis_inputs: z15.array(AllowedSynthesisInputSchema),
8346
- forbidden_inputs: z15.array(ForbiddenInputSchema)
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)
8347
8665
  });
8348
8666
  }
8349
8667
  });
@@ -8603,13 +8921,13 @@ var init_markdown5 = __esm({
8603
8921
  });
8604
8922
 
8605
8923
  // src/synth/run.ts
8606
- import { existsSync as existsSync20 } from "fs";
8607
- import { mkdir as mkdir15, readFile as readFile17, writeFile as writeFile16 } from "fs/promises";
8608
- import { join as join20, resolve as resolve17 } from "path";
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";
8609
8927
  import { parse as yamlParse7 } from "yaml";
8610
8928
  async function readJsonl4(path, parse) {
8611
- if (!existsSync20(path)) return [];
8612
- const text = await readFile17(path, "utf8");
8929
+ if (!existsSync21(path)) return [];
8930
+ const text = await readFile18(path, "utf8");
8613
8931
  const out = [];
8614
8932
  for (const line of text.split(/\r?\n/)) {
8615
8933
  if (!line.trim()) continue;
@@ -8618,27 +8936,27 @@ async function readJsonl4(path, parse) {
8618
8936
  return out;
8619
8937
  }
8620
8938
  async function readSourceCards5(packPath) {
8621
- const dir = join20(packPath, "evidence", "source-cards");
8622
- if (!existsSync20(dir)) return [];
8939
+ const dir = join21(packPath, "evidence", "source-cards");
8940
+ if (!existsSync21(dir)) return [];
8623
8941
  const { readdir: readdir4 } = await import("fs/promises");
8624
8942
  const entries = await readdir4(dir);
8625
8943
  const cards = [];
8626
8944
  for (const entry of entries) {
8627
8945
  if (!entry.endsWith(".json")) continue;
8628
- const text = await readFile17(join20(dir, entry), "utf8");
8946
+ const text = await readFile18(join21(dir, entry), "utf8");
8629
8947
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
8630
8948
  }
8631
8949
  return cards;
8632
8950
  }
8633
8951
  async function workspace(options) {
8634
8952
  const packPath = options.packPath ? resolve17(options.packPath) : process.cwd();
8635
- const yamlPath = join20(packPath, "research.yaml");
8636
- if (!existsSync20(yamlPath)) throw new PackNotFoundError(packPath);
8637
- const research = ResearchYamlSchema.parse(yamlParse7(await readFile17(yamlPath, "utf8")));
8638
- const handoffPath = join20(packPath, "handoffs", "cowork-handoff.json");
8639
- if (!existsSync20(handoffPath)) throw new HandoffNotFoundError();
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();
8640
8958
  const handoff2 = CoworkHandoffPayloadSchema.parse(
8641
- JSON.parse(await readFile17(handoffPath, "utf8"))
8959
+ JSON.parse(await readFile18(handoffPath, "utf8"))
8642
8960
  );
8643
8961
  if (handoff2.mode !== "synthesis_ready") {
8644
8962
  return {
@@ -8660,21 +8978,21 @@ async function workspace(options) {
8660
8978
  claimsBySection.set(
8661
8979
  section.id,
8662
8980
  await readJsonl4(
8663
- join20(packPath, "sections", section.id, "claims.jsonl"),
8981
+ join21(packPath, "sections", section.id, "claims.jsonl"),
8664
8982
  (r) => ClaimSchema.parse(r)
8665
8983
  )
8666
8984
  );
8667
8985
  reviewsBySection.set(
8668
8986
  section.id,
8669
8987
  await readJsonl4(
8670
- join20(packPath, "sections", section.id, "claim-reviews.jsonl"),
8988
+ join21(packPath, "sections", section.id, "claim-reviews.jsonl"),
8671
8989
  (r) => ClaimReviewSchema.parse(r)
8672
8990
  )
8673
8991
  );
8674
8992
  contradictionsBySection.set(
8675
8993
  section.id,
8676
8994
  await readJsonl4(
8677
- join20(packPath, "sections", section.id, "contradictions.jsonl"),
8995
+ join21(packPath, "sections", section.id, "contradictions.jsonl"),
8678
8996
  (r) => ContradictionSchema.parse(r)
8679
8997
  )
8680
8998
  );
@@ -8691,17 +9009,17 @@ async function workspace(options) {
8691
9009
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
8692
9010
  })
8693
9011
  );
8694
- const synthDir = join20(packPath, "synthesis");
8695
- await mkdir15(synthDir, { recursive: true });
9012
+ const synthDir = join21(packPath, "synthesis");
9013
+ await mkdir16(synthDir, { recursive: true });
8696
9014
  const filesWritten = [];
8697
9015
  const writeIfAbsent = async (relPath2, content) => {
8698
- const abs = join20(synthDir, relPath2);
8699
- if (existsSync20(abs)) return;
9016
+ const abs = join21(synthDir, relPath2);
9017
+ if (existsSync21(abs)) return;
8700
9018
  await writeFile16(abs, content, "utf8");
8701
9019
  filesWritten.push(abs);
8702
9020
  };
8703
9021
  const writeAlways = async (relPath2, content) => {
8704
- const abs = join20(synthDir, relPath2);
9022
+ const abs = join21(synthDir, relPath2);
8705
9023
  await writeFile16(abs, content, "utf8");
8706
9024
  filesWritten.push(abs);
8707
9025
  };
@@ -9511,179 +9829,179 @@ var init_aggregate = __esm({
9511
9829
  });
9512
9830
 
9513
9831
  // src/audit/schema.ts
9514
- import { z as z16 } from "zod";
9832
+ import { z as z17 } from "zod";
9515
9833
  var PackVerdictSchema, HandoffModeSchema2, OrphanClaimRowSchema, StaleSourceRowSchema, WeakSourceRowSchema, UnresolvedContradictionRowSchema, ScopeWideningRiskRowSchema, SourceDiversityGapRowSchema, SynthesisReadinessRowSchema, PackAuditPayloadSchema;
9516
9834
  var init_schema15 = __esm({
9517
9835
  "src/audit/schema.ts"() {
9518
9836
  "use strict";
9519
- PackVerdictSchema = z16.enum([
9837
+ PackVerdictSchema = z17.enum([
9520
9838
  "ready_for_synthesis",
9521
9839
  "repair_required",
9522
9840
  "human_review_required",
9523
9841
  "blocked"
9524
9842
  ]);
9525
- HandoffModeSchema2 = z16.enum([
9843
+ HandoffModeSchema2 = z17.enum([
9526
9844
  "repair_required",
9527
9845
  "synthesis_ready",
9528
9846
  "human_review_required",
9529
9847
  "unknown"
9530
9848
  ]);
9531
- OrphanClaimRowSchema = z16.object({
9532
- claim_id: z16.string(),
9533
- section_id: z16.string(),
9534
- reason: z16.enum([
9849
+ OrphanClaimRowSchema = z17.object({
9850
+ claim_id: z17.string(),
9851
+ section_id: z17.string(),
9852
+ reason: z17.enum([
9535
9853
  "missing_source_card",
9536
9854
  "missing_source_hash",
9537
9855
  "missing_evidence_excerpt",
9538
9856
  "unresolvable_source_id"
9539
9857
  ]),
9540
- details: z16.string(),
9541
- artifact_path: z16.string()
9542
- });
9543
- StaleSourceRowSchema = z16.object({
9544
- source_id: z16.string(),
9545
- section_id: z16.string(),
9546
- publisher: z16.string().nullable(),
9547
- reason: z16.enum(["too_old", "missing_date", "unparseable_date"]),
9548
- details: z16.string(),
9549
- artifact_path: z16.string(),
9550
- policy: z16.object({
9551
- required: z16.boolean(),
9552
- max_source_age_months: z16.number().int().nullable(),
9553
- stale_source_policy: z16.enum(["warn", "fail"])
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"])
9554
9872
  })
9555
9873
  });
9556
- WeakSourceRowSchema = z16.object({
9557
- reason: z16.enum([
9874
+ WeakSourceRowSchema = z17.object({
9875
+ reason: z17.enum([
9558
9876
  "source_cluster_monopoly",
9559
9877
  "low_independent_publishers",
9560
9878
  "missing_primary_source",
9561
9879
  "excessive_type_imbalance",
9562
9880
  "failed_fetches_reducing_floor"
9563
9881
  ]),
9564
- section_id: z16.string(),
9565
- details: z16.string(),
9566
- evidence_ids: z16.array(z16.string()),
9567
- artifact_path: z16.string()
9568
- });
9569
- UnresolvedContradictionRowSchema = z16.object({
9570
- contradiction_id: z16.string(),
9571
- section_id: z16.string(),
9572
- type: z16.string(),
9573
- severity: z16.string(),
9574
- status: z16.string(),
9575
- claim_ids: z16.array(z16.string()),
9576
- artifact_path: z16.string()
9577
- });
9578
- ScopeWideningRiskRowSchema = z16.object({
9579
- reason: z16.enum([
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([
9580
9898
  "overgeneralization_finding",
9581
9899
  "scope_null_in_use",
9582
9900
  "missing_not_flagged",
9583
9901
  "contextual_to_universal_risk"
9584
9902
  ]),
9585
- claim_id: z16.string(),
9586
- section_id: z16.string(),
9587
- details: z16.string(),
9588
- artifact_path: z16.string()
9903
+ claim_id: z17.string(),
9904
+ section_id: z17.string(),
9905
+ details: z17.string(),
9906
+ artifact_path: z17.string()
9589
9907
  });
9590
- SourceDiversityGapRowSchema = z16.object({
9591
- reason: z16.enum([
9908
+ SourceDiversityGapRowSchema = z17.object({
9909
+ reason: z17.enum([
9592
9910
  "section_publisher_monopoly",
9593
9911
  "low_section_publisher_count",
9594
9912
  "cross_section_publisher_overlap",
9595
9913
  "section_has_no_sources"
9596
9914
  ]),
9597
- section_id: z16.string(),
9598
- details: z16.string(),
9599
- evidence_ids: z16.array(z16.string())
9600
- });
9601
- SynthesisReadinessRowSchema = z16.object({
9602
- section_id: z16.string(),
9603
- purpose: z16.string(),
9604
- status: z16.string(),
9605
- has_gate_run: z16.boolean(),
9606
- has_review_run: z16.boolean(),
9607
- gate_verdict: z16.string().nullable(),
9608
- synthesis_eligible: z16.boolean(),
9609
- candidate_claims: z16.number().int().nonnegative(),
9610
- accepted_claims: z16.number().int().nonnegative(),
9611
- repair_claims: z16.number().int().nonnegative(),
9612
- rejected_claims: z16.number().int().nonnegative(),
9613
- dispositioned_claims: z16.number().int().nonnegative(),
9614
- blocking_reasons: z16.array(z16.string()),
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()),
9615
9933
  cowork_handoff_mode: HandoffModeSchema2,
9616
- workspace_allowed: z16.boolean()
9934
+ workspace_allowed: z17.boolean()
9617
9935
  });
9618
- PackAuditPayloadSchema = z16.object({
9619
- pack_id: z16.string(),
9620
- pack_topic: z16.string(),
9621
- generated_at: z16.string(),
9936
+ PackAuditPayloadSchema = z17.object({
9937
+ pack_id: z17.string(),
9938
+ pack_topic: z17.string(),
9939
+ generated_at: z17.string(),
9622
9940
  verdict: PackVerdictSchema,
9623
- synthesis_allowed: z16.boolean(),
9624
- section_summaries: z16.array(SynthesisReadinessRowSchema),
9625
- claim_summary: z16.object({
9626
- total: z16.number().int().nonnegative(),
9627
- candidate: z16.number().int().nonnegative(),
9628
- accepted_for_synthesis: z16.number().int().nonnegative(),
9629
- rejected: z16.number().int().nonnegative(),
9630
- needs_repair: z16.number().int().nonnegative(),
9631
- dispositioned: z16.number().int().nonnegative(),
9632
- no_review: z16.number().int().nonnegative(),
9633
- with_evidence_excerpt: z16.number().int().nonnegative(),
9634
- with_source_hashes: z16.number().int().nonnegative(),
9635
- scope_null: z16.number().int().nonnegative(),
9636
- not_null: z16.number().int().nonnegative(),
9637
- orphans: z16.number().int().nonnegative()
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()
9638
9956
  }),
9639
- source_summary: z16.object({
9640
- total: z16.number().int().nonnegative(),
9641
- primary: z16.number().int().nonnegative(),
9642
- secondary: z16.number().int().nonnegative(),
9643
- forum: z16.number().int().nonnegative(),
9644
- benchmark: z16.number().int().nonnegative(),
9645
- docs: z16.number().int().nonnegative(),
9646
- unknown: z16.number().int().nonnegative(),
9647
- independent_publishers: z16.number().int().nonnegative(),
9648
- failed_fetches: z16.number().int().nonnegative(),
9649
- sections_with_sources: z16.number().int().nonnegative(),
9650
- sections_without_sources: z16.number().int().nonnegative()
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()
9651
9969
  }),
9652
- contradiction_summary: z16.object({
9653
- total: z16.number().int().nonnegative(),
9654
- unresolved: z16.number().int().nonnegative(),
9655
- blocking: z16.number().int().nonnegative(),
9656
- reconciled: z16.number().int().nonnegative(),
9657
- preserved_deliberately: z16.number().int().nonnegative(),
9658
- rejected: z16.number().int().nonnegative(),
9659
- by_type: z16.record(z16.string(), z16.number().int().nonnegative()),
9660
- sections_with_clean_ledger: z16.number().int().nonnegative()
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()
9661
9979
  }),
9662
- review_summary: z16.object({
9663
- sections_with_review_run: z16.number().int().nonnegative(),
9664
- sections_without_review_run: z16.number().int().nonnegative(),
9665
- decision_counts: z16.record(z16.string(), z16.number().int().nonnegative()),
9666
- blocking_findings: z16.number().int().nonnegative()
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()
9667
9985
  }),
9668
- waiver_summary: z16.object({
9669
- total: z16.number().int().nonnegative(),
9670
- invalid: z16.number().int().nonnegative(),
9671
- by_family: z16.record(z16.string(), z16.number().int().nonnegative())
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())
9672
9990
  }),
9673
- readiness_summary: z16.object({
9674
- total_sections: z16.number().int().nonnegative(),
9675
- ready_sections: z16.number().int().nonnegative(),
9676
- repair_sections: z16.number().int().nonnegative(),
9677
- blocked_sections: z16.number().int().nonnegative(),
9678
- no_gate_sections: z16.number().int().nonnegative(),
9679
- no_review_sections: z16.number().int().nonnegative(),
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(),
9680
9998
  cowork_handoff_mode: HandoffModeSchema2,
9681
- workspace_allowed: z16.boolean()
9999
+ workspace_allowed: z17.boolean()
9682
10000
  }),
9683
- audit_files: z16.array(z16.string()),
9684
- blocking_reasons: z16.array(z16.string()),
9685
- warnings: z16.array(z16.string()),
9686
- next_actions: z16.array(z16.string())
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())
9687
10005
  });
9688
10006
  }
9689
10007
  });
@@ -9905,13 +10223,13 @@ var init_markdown6 = __esm({
9905
10223
  });
9906
10224
 
9907
10225
  // src/audit/run.ts
9908
- import { existsSync as existsSync21 } from "fs";
9909
- import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile17 } from "fs/promises";
9910
- import { join as join21, resolve as resolve18 } from "path";
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";
9911
10229
  import { parse as yamlParse8 } from "yaml";
9912
10230
  async function readJsonl5(path, parse, warnings) {
9913
- if (!existsSync21(path)) return [];
9914
- const text = await readFile18(path, "utf8");
10231
+ if (!existsSync22(path)) return [];
10232
+ const text = await readFile19(path, "utf8");
9915
10233
  const out = [];
9916
10234
  for (const line of text.split(/\r?\n/)) {
9917
10235
  if (!line.trim()) continue;
@@ -9924,15 +10242,15 @@ async function readJsonl5(path, parse, warnings) {
9924
10242
  return out;
9925
10243
  }
9926
10244
  async function readSourceCards6(packPath, warnings) {
9927
- const dir = join21(packPath, "evidence", "source-cards");
9928
- if (!existsSync21(dir)) return [];
10245
+ const dir = join22(packPath, "evidence", "source-cards");
10246
+ if (!existsSync22(dir)) return [];
9929
10247
  const { readdir: readdir4 } = await import("fs/promises");
9930
10248
  const entries = await readdir4(dir);
9931
10249
  const cards = [];
9932
10250
  for (const entry of entries) {
9933
10251
  if (!entry.endsWith(".json")) continue;
9934
10252
  try {
9935
- const text = await readFile18(join21(dir, entry), "utf8");
10253
+ const text = await readFile19(join22(dir, entry), "utf8");
9936
10254
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
9937
10255
  } catch (err) {
9938
10256
  warnings.push(`malformed source card ${entry}: ${err instanceof Error ? err.message : "parse error"}`);
@@ -9941,29 +10259,29 @@ async function readSourceCards6(packPath, warnings) {
9941
10259
  return cards;
9942
10260
  }
9943
10261
  async function readGate2(packPath, sectionId, warnings) {
9944
- const path = join21(packPath, "audits", `${sectionId}-gate.json`);
9945
- if (!existsSync21(path)) return null;
10262
+ const path = join22(packPath, "audits", `${sectionId}-gate.json`);
10263
+ if (!existsSync22(path)) return null;
9946
10264
  try {
9947
- return SectionGateResultSchema.parse(JSON.parse(await readFile18(path, "utf8")));
10265
+ return SectionGateResultSchema.parse(JSON.parse(await readFile19(path, "utf8")));
9948
10266
  } catch (err) {
9949
10267
  warnings.push(`malformed gate result for ${sectionId}: ${err instanceof Error ? err.message : "parse error"}`);
9950
10268
  return null;
9951
10269
  }
9952
10270
  }
9953
10271
  async function readHandoff(packPath, warnings) {
9954
- const path = join21(packPath, "handoffs", "cowork-handoff.json");
9955
- if (!existsSync21(path)) return null;
10272
+ const path = join22(packPath, "handoffs", "cowork-handoff.json");
10273
+ if (!existsSync22(path)) return null;
9956
10274
  try {
9957
- return CoworkHandoffPayloadSchema.parse(JSON.parse(await readFile18(path, "utf8")));
10275
+ return CoworkHandoffPayloadSchema.parse(JSON.parse(await readFile19(path, "utf8")));
9958
10276
  } catch (err) {
9959
10277
  warnings.push(`malformed handoff: ${err instanceof Error ? err.message : "parse error"}`);
9960
10278
  return null;
9961
10279
  }
9962
10280
  }
9963
10281
  async function readSourceIdsForSection(packPath, sectionId) {
9964
- const path = join21(packPath, "sections", sectionId, "sources.jsonl");
9965
- if (!existsSync21(path)) return [];
9966
- const text = await readFile18(path, "utf8");
10282
+ const path = join22(packPath, "sections", sectionId, "sources.jsonl");
10283
+ if (!existsSync22(path)) return [];
10284
+ const text = await readFile19(path, "utf8");
9967
10285
  const ids = [];
9968
10286
  for (const line of text.split(/\r?\n/)) {
9969
10287
  if (!line.trim()) continue;
@@ -9977,41 +10295,41 @@ async function readSourceIdsForSection(packPath, sectionId) {
9977
10295
  }
9978
10296
  async function audit(options) {
9979
10297
  const packPath = options.packPath ? resolve18(options.packPath) : process.cwd();
9980
- const yamlPath = join21(packPath, "research.yaml");
9981
- if (!existsSync21(yamlPath)) throw new PackNotFoundError(packPath);
9982
- const research = ResearchYamlSchema.parse(yamlParse8(await readFile18(yamlPath, "utf8")));
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")));
9983
10301
  const warnings = [];
9984
10302
  const perSection = /* @__PURE__ */ new Map();
9985
10303
  for (const section of research.sections) {
9986
10304
  const sid = section.id;
9987
10305
  const claims = await readJsonl5(
9988
- join21(packPath, "sections", sid, "claims.jsonl"),
10306
+ join22(packPath, "sections", sid, "claims.jsonl"),
9989
10307
  (r) => ClaimSchema.parse(r),
9990
10308
  warnings
9991
10309
  );
9992
10310
  const candidateClaims = claims.filter((c) => c.review_state === "candidate");
9993
10311
  const claimReviews = await readJsonl5(
9994
- join21(packPath, "sections", sid, "claim-reviews.jsonl"),
10312
+ join22(packPath, "sections", sid, "claim-reviews.jsonl"),
9995
10313
  (r) => ClaimReviewSchema.parse(r),
9996
10314
  warnings
9997
10315
  );
9998
10316
  const contradictions = await readJsonl5(
9999
- join21(packPath, "sections", sid, "contradictions.jsonl"),
10317
+ join22(packPath, "sections", sid, "contradictions.jsonl"),
10000
10318
  (r) => ContradictionSchema.parse(r),
10001
10319
  warnings
10002
10320
  );
10003
10321
  const resolutions = await readJsonl5(
10004
- join21(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
10322
+ join22(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
10005
10323
  (r) => ContradictionResolutionSchema.parse(r),
10006
10324
  warnings
10007
10325
  );
10008
10326
  const dispositions = await readJsonl5(
10009
- join21(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
10327
+ join22(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
10010
10328
  (r) => ClaimSynthesisDispositionSchema.parse(r),
10011
10329
  warnings
10012
10330
  );
10013
10331
  const findings = await readJsonl5(
10014
- join21(packPath, "audits", `${sid}-findings.jsonl`),
10332
+ join22(packPath, "audits", `${sid}-findings.jsonl`),
10015
10333
  (r) => ReviewFindingSchema.parse(r),
10016
10334
  warnings
10017
10335
  );
@@ -10031,7 +10349,7 @@ async function audit(options) {
10031
10349
  }
10032
10350
  const sources = await readSourceCards6(packPath, warnings);
10033
10351
  const receipts = await readJsonl5(
10034
- join21(packPath, "evidence", "fetch-log.jsonl"),
10352
+ join22(packPath, "evidence", "fetch-log.jsonl"),
10035
10353
  (r) => FetchReceiptSchema.parse(r),
10036
10354
  warnings
10037
10355
  );
@@ -10047,11 +10365,11 @@ async function audit(options) {
10047
10365
  warnings
10048
10366
  });
10049
10367
  const payload = PackAuditPayloadSchema.parse(result.payload);
10050
- const auditsDir = join21(packPath, "audits");
10051
- await mkdir16(auditsDir, { recursive: true });
10368
+ const auditsDir = join22(packPath, "audits");
10369
+ await mkdir17(auditsDir, { recursive: true });
10052
10370
  const filesWritten = [];
10053
10371
  const writeFileAndTrack = async (rel, content) => {
10054
- const abs = join21(packPath, rel);
10372
+ const abs = join22(packPath, rel);
10055
10373
  await writeFile17(abs, content, "utf8");
10056
10374
  filesWritten.push(abs);
10057
10375
  };
@@ -10135,18 +10453,18 @@ var init_audit = __esm({
10135
10453
  });
10136
10454
 
10137
10455
  // src/freeze/checks.ts
10138
- import { existsSync as existsSync22 } from "fs";
10139
- import { readFile as readFile19, stat as stat2 } from "fs/promises";
10456
+ import { existsSync as existsSync23 } from "fs";
10457
+ import { readFile as readFile20, stat as stat2 } from "fs/promises";
10140
10458
  import { createHash as createHash8 } from "crypto";
10141
- import { join as join22 } from "path";
10459
+ import { join as join23 } from "path";
10142
10460
  async function fileSha2562(absPath) {
10143
- const buf = await readFile19(absPath);
10461
+ const buf = await readFile20(absPath);
10144
10462
  const sha = createHash8("sha256").update(buf).digest("hex");
10145
10463
  return { path: absPath, sha256: sha, bytes: buf.byteLength };
10146
10464
  }
10147
10465
  async function hashArtifact(ctx, rel) {
10148
- const abs = join22(ctx.packPath, rel);
10149
- if (!existsSync22(abs)) {
10466
+ const abs = join23(ctx.packPath, rel);
10467
+ if (!existsSync23(abs)) {
10150
10468
  ctx.missingArtifacts.push(rel);
10151
10469
  return null;
10152
10470
  }
@@ -10357,88 +10675,88 @@ var init_markdown7 = __esm({
10357
10675
  });
10358
10676
 
10359
10677
  // src/freeze/schema.ts
10360
- import { z as z17 } from "zod";
10678
+ import { z as z18 } from "zod";
10361
10679
  var ArtifactHashSchema, IntegrityCheckSchema, FreezeReceiptPayloadSchema, FreezeRefusalPayloadSchema;
10362
10680
  var init_schema16 = __esm({
10363
10681
  "src/freeze/schema.ts"() {
10364
10682
  "use strict";
10365
- ArtifactHashSchema = z17.object({
10366
- path: z17.string(),
10367
- sha256: z17.string().regex(/^[a-f0-9]{64}$/),
10368
- bytes: z17.number().int().nonnegative()
10369
- });
10370
- IntegrityCheckSchema = z17.object({
10371
- name: z17.string().min(1),
10372
- passed: z17.boolean(),
10373
- detail: z17.string()
10374
- });
10375
- FreezeReceiptPayloadSchema = z17.object({
10376
- pack_id: z17.string(),
10377
- pack_topic: z17.string(),
10378
- frozen_at: z17.string(),
10379
- verdict: z17.literal("frozen"),
10380
- pack_audit_hash: z17.string().regex(/^[a-f0-9]{64}$/),
10381
- handoff_hash: z17.string().regex(/^[a-f0-9]{64}$/),
10382
- synthesis_hashes: z17.array(ArtifactHashSchema),
10383
- canonical_artifact_hashes: z17.array(ArtifactHashSchema),
10384
- accepted_claim_ids: z17.array(z17.string()),
10385
- cited_claim_ids: z17.array(z17.string()),
10386
- uncited_accepted_claim_ids: z17.array(z17.string()),
10387
- unresolved_contradictions: z17.array(
10388
- z17.object({
10389
- contradiction_id: z17.string(),
10390
- section_id: z17.string(),
10391
- type: z17.string(),
10392
- severity: z17.string(),
10393
- status: z17.string(),
10394
- disclosed_in: z17.array(z17.string())
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())
10395
10713
  })
10396
10714
  ),
10397
- waivers_disclosed: z17.array(
10398
- z17.object({
10399
- family: z17.string(),
10400
- applied_to: z17.string(),
10401
- reason: z17.string(),
10402
- compensating_controls: z17.array(z17.string()),
10403
- disclosed_in: z17.array(z17.string())
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())
10404
10722
  })
10405
10723
  ),
10406
- sections: z17.array(
10407
- z17.object({
10408
- section_id: z17.string(),
10409
- status: z17.string(),
10410
- accepted_claims: z17.number().int().nonnegative(),
10411
- sources: z17.number().int().nonnegative(),
10412
- contradictions: z17.number().int().nonnegative()
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()
10413
10731
  })
10414
10732
  ),
10415
- source_count: z17.number().int().nonnegative(),
10416
- claim_count: z17.number().int().nonnegative(),
10417
- contradiction_count: z17.number().int().nonnegative(),
10418
- review_finding_count: z17.number().int().nonnegative(),
10419
- gate_result_count: z17.number().int().nonnegative(),
10420
- integrity_checks: z17.array(IntegrityCheckSchema)
10421
- });
10422
- FreezeRefusalPayloadSchema = z17.object({
10423
- pack_id: z17.string(),
10424
- pack_topic: z17.string(),
10425
- checked_at: z17.string(),
10426
- verdict: z17.literal("refused"),
10427
- reasons: z17.array(z17.string()),
10428
- blocking_reasons: z17.array(z17.string()),
10429
- missing_artifacts: z17.array(z17.string()),
10430
- invalid_artifacts: z17.array(z17.object({ path: z17.string(), error: z17.string() })),
10431
- next_actions: z17.array(z17.string()),
10432
- would_freeze: z17.literal(false)
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)
10433
10751
  });
10434
10752
  }
10435
10753
  });
10436
10754
 
10437
10755
  // src/freeze/run.ts
10438
- import { existsSync as existsSync23 } from "fs";
10439
- import { mkdir as mkdir17, readFile as readFile20, readdir as readdir2, unlink, writeFile as writeFile18 } from "fs/promises";
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";
10440
10758
  import { createHash as createHash9 } from "crypto";
10441
- import { join as join23, resolve as resolve19 } from "path";
10759
+ import { join as join24, resolve as resolve19 } from "path";
10442
10760
  import { parse as yamlParse9, stringify as yamlStringify6 } from "yaml";
10443
10761
  function packId3(research) {
10444
10762
  const fingerprint = `${research.topic}|${research.created_at}`;
@@ -10457,10 +10775,10 @@ function noteRefusal(ctx, reason, blocking = true) {
10457
10775
  if (blocking) ctx.blockingReasons.push(reason);
10458
10776
  }
10459
10777
  async function tryReadJson(packPath, rel, parse, invalid) {
10460
- const abs = join23(packPath, rel);
10461
- if (!existsSync23(abs)) return null;
10778
+ const abs = join24(packPath, rel);
10779
+ if (!existsSync24(abs)) return null;
10462
10780
  try {
10463
- return parse(JSON.parse(await readFile20(abs, "utf8")));
10781
+ return parse(JSON.parse(await readFile21(abs, "utf8")));
10464
10782
  } catch (err) {
10465
10783
  invalid.push({ path: rel, error: err instanceof Error ? err.message : "parse error" });
10466
10784
  return null;
@@ -10468,8 +10786,8 @@ async function tryReadJson(packPath, rel, parse, invalid) {
10468
10786
  }
10469
10787
  async function freeze(options) {
10470
10788
  const packPath = options.packPath ? resolve19(options.packPath) : process.cwd();
10471
- const yamlPath = join23(packPath, "research.yaml");
10472
- if (!existsSync23(yamlPath)) throw new PackNotFoundError(packPath);
10789
+ const yamlPath = join24(packPath, "research.yaml");
10790
+ if (!existsSync24(yamlPath)) throw new PackNotFoundError(packPath);
10473
10791
  const refusal = {
10474
10792
  reasons: [],
10475
10793
  blockingReasons: [],
@@ -10478,7 +10796,7 @@ async function freeze(options) {
10478
10796
  };
10479
10797
  let research;
10480
10798
  try {
10481
- research = ResearchYamlSchema.parse(yamlParse9(await readFile20(yamlPath, "utf8")));
10799
+ research = ResearchYamlSchema.parse(yamlParse9(await readFile21(yamlPath, "utf8")));
10482
10800
  } catch (err) {
10483
10801
  refusal.invalidArtifacts.push({ path: "research.yaml", error: err instanceof Error ? err.message : "parse error" });
10484
10802
  return writeRefusal({
@@ -10490,13 +10808,13 @@ async function freeze(options) {
10490
10808
  }
10491
10809
  const pid = packId3(research);
10492
10810
  for (const rel of REQUIRED_PACK_ARTIFACTS) {
10493
- if (!existsSync23(join23(packPath, rel))) {
10811
+ if (!existsSync24(join24(packPath, rel))) {
10494
10812
  refusal.missingArtifacts.push(rel);
10495
10813
  noteRefusal(refusal, `Required artifact missing: ${rel}.`);
10496
10814
  }
10497
10815
  }
10498
10816
  for (const rel of SYNTHESIS_FILES) {
10499
- if (!existsSync23(join23(packPath, rel))) {
10817
+ if (!existsSync24(join24(packPath, rel))) {
10500
10818
  refusal.missingArtifacts.push(rel);
10501
10819
  noteRefusal(refusal, `Synthesis artifact missing: ${rel}. Run \`research-os synth workspace\` after the pack reaches synthesis_ready.`);
10502
10820
  }
@@ -10530,22 +10848,22 @@ async function freeze(options) {
10530
10848
  if (handoff2 && handoff2.mode !== "synthesis_ready") {
10531
10849
  noteRefusal(refusal, `Cowork handoff mode is "${handoff2.mode}", not "synthesis_ready".`);
10532
10850
  }
10533
- const finalReportAbs = join23(packPath, "synthesis/final-report.md");
10534
- const decisionBriefAbs = join23(packPath, "synthesis/decision-brief.md");
10535
- const workingReportAbs = join23(packPath, "synthesis/working-report.md");
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");
10536
10854
  let finalReportText = "";
10537
10855
  let decisionBriefText = "";
10538
10856
  let workingReportText = "";
10539
- if (existsSync23(finalReportAbs)) finalReportText = await readFile20(finalReportAbs, "utf8");
10540
- if (existsSync23(decisionBriefAbs)) decisionBriefText = await readFile20(decisionBriefAbs, "utf8");
10541
- if (existsSync23(workingReportAbs)) workingReportText = await readFile20(workingReportAbs, "utf8");
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");
10542
10860
  const livePackClaimIds = /* @__PURE__ */ new Set();
10543
10861
  const liveLatestDecisionByClaim = /* @__PURE__ */ new Map();
10544
10862
  const liveLatestCreatedAtByClaim = /* @__PURE__ */ new Map();
10545
10863
  for (const section of research.sections) {
10546
- const claimsFile = join23(packPath, "sections", section.id, "claims.jsonl");
10547
- if (existsSync23(claimsFile)) {
10548
- const text = await readFile20(claimsFile, "utf8");
10864
+ const claimsFile = join24(packPath, "sections", section.id, "claims.jsonl");
10865
+ if (existsSync24(claimsFile)) {
10866
+ const text = await readFile21(claimsFile, "utf8");
10549
10867
  for (const line of text.split(/\r?\n/)) {
10550
10868
  if (!line.trim()) continue;
10551
10869
  try {
@@ -10559,9 +10877,9 @@ async function freeze(options) {
10559
10877
  }
10560
10878
  }
10561
10879
  }
10562
- const reviewsFile = join23(packPath, "sections", section.id, "claim-reviews.jsonl");
10563
- if (existsSync23(reviewsFile)) {
10564
- const text = await readFile20(reviewsFile, "utf8");
10880
+ const reviewsFile = join24(packPath, "sections", section.id, "claim-reviews.jsonl");
10881
+ if (existsSync24(reviewsFile)) {
10882
+ const text = await readFile21(reviewsFile, "utf8");
10565
10883
  const reviews = [];
10566
10884
  for (const line of text.split(/\r?\n/)) {
10567
10885
  if (!line.trim()) continue;
@@ -10603,7 +10921,7 @@ async function freeze(options) {
10603
10921
  const unknownCitations = allCited.filter((c) => isWellFormedClaimId(c) && !allClaimIds.has(c));
10604
10922
  const repairCitations = allCited.filter((c) => repairOrRejected.has(c));
10605
10923
  const uncitedAccepted = acceptedClaimIds.filter((c) => !allCitedSet.has(c));
10606
- if (existsSync23(finalReportAbs) && finalReportText.trim().length > 0) {
10924
+ if (existsSync24(finalReportAbs) && finalReportText.trim().length > 0) {
10607
10925
  if (citationsInFinal.length === 0 && acceptedClaimIds.length > 0) {
10608
10926
  noteRefusal(refusal, "final-report.md contains no [claim:...] citations even though accepted claims exist.");
10609
10927
  }
@@ -10655,7 +10973,7 @@ async function freeze(options) {
10655
10973
  }
10656
10974
  }
10657
10975
  for (const section of research.sections) {
10658
- if (!existsSync23(join23(packPath, "audits", `${section.id}-gate.json`))) {
10976
+ if (!existsSync24(join24(packPath, "audits", `${section.id}-gate.json`))) {
10659
10977
  noteRefusal(refusal, `Section ${section.id} has no gate result on file.`);
10660
10978
  refusal.missingArtifacts.push(`audits/${section.id}-gate.json`);
10661
10979
  }
@@ -10695,16 +11013,16 @@ async function freeze(options) {
10695
11013
  }
10696
11014
  canonicalPaths.push("evidence/fetch-log.jsonl");
10697
11015
  canonicalPaths.push("evidence/citation-ledger.jsonl");
10698
- if (existsSync23(join23(packPath, "evidence", "source-cards"))) {
10699
- const entries = await readdir2(join23(packPath, "evidence", "source-cards"));
11016
+ if (existsSync24(join24(packPath, "evidence", "source-cards"))) {
11017
+ const entries = await readdir2(join24(packPath, "evidence", "source-cards"));
10700
11018
  for (const e of entries) {
10701
11019
  if (e.endsWith(".json")) canonicalPaths.push(`evidence/source-cards/${e}`);
10702
11020
  }
10703
11021
  }
10704
11022
  const canonicalHashes = [];
10705
11023
  for (const rel of canonicalPaths) {
10706
- const abs = join23(packPath, rel);
10707
- if (!existsSync23(abs)) continue;
11024
+ const abs = join24(packPath, rel);
11025
+ if (!existsSync24(abs)) continue;
10708
11026
  const h = await hashArtifact(ctx, rel);
10709
11027
  if (h) canonicalHashes.push(h);
10710
11028
  }
@@ -10758,7 +11076,7 @@ async function freeze(options) {
10758
11076
  claim_count: acceptedClaimIds.length,
10759
11077
  contradiction_count: unresolvedContradictionRefs.length,
10760
11078
  review_finding_count: 0,
10761
- gate_result_count: research.sections.filter((s) => existsSync23(join23(packPath, "audits", `${s.id}-gate.json`))).length,
11079
+ gate_result_count: research.sections.filter((s) => existsSync24(join24(packPath, "audits", `${s.id}-gate.json`))).length,
10762
11080
  integrity_checks: checks
10763
11081
  });
10764
11082
  const frozenResearch = {
@@ -10767,14 +11085,14 @@ async function freeze(options) {
10767
11085
  sections: research.sections.map((s) => ({ ...s, status: "frozen" }))
10768
11086
  };
10769
11087
  await writeFile18(yamlPath, yamlStringify6(frozenResearch, { lineWidth: 0 }), "utf8");
10770
- const auditsDir = join23(packPath, "audits");
10771
- await mkdir17(auditsDir, { recursive: true });
11088
+ const auditsDir = join24(packPath, "audits");
11089
+ await mkdir18(auditsDir, { recursive: true });
10772
11090
  for (const stale of ["freeze-refusal.json", "freeze-refusal.md"]) {
10773
- const p = join23(auditsDir, stale);
10774
- if (existsSync23(p)) await unlink(p);
11091
+ const p = join24(auditsDir, stale);
11092
+ if (existsSync24(p)) await unlink(p);
10775
11093
  }
10776
- const jsonPath = join23(auditsDir, "freeze-receipt.json");
10777
- const mdPath = join23(auditsDir, "freeze-receipt.md");
11094
+ const jsonPath = join24(auditsDir, "freeze-receipt.json");
11095
+ const mdPath = join24(auditsDir, "freeze-receipt.md");
10778
11096
  await writeFile18(jsonPath, JSON.stringify(receipt, null, 2), "utf8");
10779
11097
  await writeFile18(mdPath, renderFreezeReceiptMarkdown(receipt), "utf8");
10780
11098
  return {
@@ -10804,14 +11122,14 @@ async function writeRefusal(args) {
10804
11122
  next_actions: nextActions,
10805
11123
  would_freeze: false
10806
11124
  });
10807
- const auditsDir = join23(args.packPath, "audits");
10808
- await mkdir17(auditsDir, { recursive: true });
11125
+ const auditsDir = join24(args.packPath, "audits");
11126
+ await mkdir18(auditsDir, { recursive: true });
10809
11127
  for (const stale of ["freeze-receipt.json", "freeze-receipt.md"]) {
10810
- const p = join23(auditsDir, stale);
10811
- if (existsSync23(p)) await unlink(p);
11128
+ const p = join24(auditsDir, stale);
11129
+ if (existsSync24(p)) await unlink(p);
10812
11130
  }
10813
- const jsonPath = join23(auditsDir, "freeze-refusal.json");
10814
- const mdPath = join23(auditsDir, "freeze-refusal.md");
11131
+ const jsonPath = join24(auditsDir, "freeze-refusal.json");
11132
+ const mdPath = join24(auditsDir, "freeze-refusal.md");
10815
11133
  await writeFile18(jsonPath, JSON.stringify(payload, null, 2), "utf8");
10816
11134
  await writeFile18(mdPath, renderFreezeRefusalMarkdown(payload), "utf8");
10817
11135
  return {
@@ -10903,43 +11221,43 @@ var init_freeze = __esm({
10903
11221
  });
10904
11222
 
10905
11223
  // src/invalidate/schema.ts
10906
- import { z as z18 } from "zod";
11224
+ import { z as z19 } from "zod";
10907
11225
  var ArchivedArtifactSchema, SectionStatusChangeSchema, InvalidationReceiptSchema;
10908
11226
  var init_schema17 = __esm({
10909
11227
  "src/invalidate/schema.ts"() {
10910
11228
  "use strict";
10911
- ArchivedArtifactSchema = z18.object({
10912
- src: z18.string(),
11229
+ ArchivedArtifactSchema = z19.object({
11230
+ src: z19.string(),
10913
11231
  // path relative to packPath, before archival
10914
- dst: z18.string()
11232
+ dst: z19.string()
10915
11233
  // path relative to packPath, after archival
10916
11234
  });
10917
- SectionStatusChangeSchema = z18.object({
10918
- section_id: z18.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
10919
- before: z18.string(),
10920
- after: z18.string()
10921
- });
10922
- InvalidationReceiptSchema = z18.object({
10923
- receipt_id: z18.string().regex(/^inv_[0-9]+_[a-z0-9-]+$/),
10924
- contract_label: z18.string().min(1),
10925
- superseded_contract: z18.string().min(1).nullable(),
10926
- new_contract: z18.string().min(1),
10927
- reason: z18.string().min(8),
10928
- invalidated_at: z18.string(),
10929
- research_os_version: z18.string(),
10930
- affected_sections: z18.array(z18.string().regex(/^[0-9]{2}-[a-z0-9-]+$/)),
10931
- archived_artifacts: z18.array(ArchivedArtifactSchema),
10932
- section_status_changes: z18.array(SectionStatusChangeSchema),
10933
- frozen_at_cleared: z18.boolean(),
10934
- notes: z18.string().nullable()
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()
10935
11253
  });
10936
11254
  }
10937
11255
  });
10938
11256
 
10939
11257
  // src/invalidate/run.ts
10940
- import { existsSync as existsSync24 } from "fs";
10941
- import { mkdir as mkdir18, readFile as readFile21, readdir as readdir3, rename, writeFile as writeFile19 } from "fs/promises";
10942
- import { dirname as dirname5, join as join24, posix, relative as relative3, resolve as resolve20, sep } from "path";
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";
10943
11261
  import { parse as yamlParse10, stringify as yamlStringify7 } from "yaml";
10944
11262
  function posixify(p) {
10945
11263
  return p.split(sep).join("/");
@@ -10957,15 +11275,15 @@ function isLegacyClaimLine(line) {
10957
11275
  }
10958
11276
  }
10959
11277
  async function detectLegacyClaimSections(packPath) {
10960
- const sectionsDir = join24(packPath, "sections");
10961
- if (!existsSync24(sectionsDir)) return [];
11278
+ const sectionsDir = join25(packPath, "sections");
11279
+ if (!existsSync25(sectionsDir)) return [];
10962
11280
  const entries = await readdir3(sectionsDir, { withFileTypes: true });
10963
11281
  const affected = [];
10964
11282
  for (const entry of entries) {
10965
11283
  if (!entry.isDirectory()) continue;
10966
- const claimsPath = join24(sectionsDir, entry.name, "claims.jsonl");
10967
- if (!existsSync24(claimsPath)) continue;
10968
- const text = await readFile21(claimsPath, "utf8");
11284
+ const claimsPath = join25(sectionsDir, entry.name, "claims.jsonl");
11285
+ if (!existsSync25(claimsPath)) continue;
11286
+ const text = await readFile22(claimsPath, "utf8");
10969
11287
  let hasLegacy = false;
10970
11288
  for (const line of text.split(/\r?\n/)) {
10971
11289
  if (isLegacyClaimLine(line)) {
@@ -10978,16 +11296,16 @@ async function detectLegacyClaimSections(packPath) {
10978
11296
  return affected.sort();
10979
11297
  }
10980
11298
  async function moveIfExists(packPath, archiveRel, rel) {
10981
- const src = join24(packPath, rel);
10982
- if (!existsSync24(src)) return null;
10983
- const dst = join24(packPath, archiveRel, rel);
10984
- await mkdir18(dirname5(dst), { recursive: true });
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 });
10985
11303
  await rename(src, dst);
10986
- return { src: posixify(rel), dst: posixify(join24(archiveRel, rel)) };
11304
+ return { src: posixify(rel), dst: posixify(join25(archiveRel, rel)) };
10987
11305
  }
10988
11306
  async function moveDirContentsIfExists(packPath, archiveRel, relDir, skipPrefix) {
10989
- const fullDir = join24(packPath, relDir);
10990
- if (!existsSync24(fullDir)) return [];
11307
+ const fullDir = join25(packPath, relDir);
11308
+ if (!existsSync25(fullDir)) return [];
10991
11309
  const entries = await readdir3(fullDir, { withFileTypes: true });
10992
11310
  const moved = [];
10993
11311
  for (const entry of entries) {
@@ -11065,8 +11383,8 @@ function buildReceiptMarkdown(receipt) {
11065
11383
  }
11066
11384
  async function invalidateExtraction(options) {
11067
11385
  const packPath = options.packPath ? resolve20(options.packPath) : process.cwd();
11068
- const yamlPath = join24(packPath, "research.yaml");
11069
- if (!existsSync24(yamlPath)) throw new PackNotFoundError(packPath);
11386
+ const yamlPath = join25(packPath, "research.yaml");
11387
+ if (!existsSync25(yamlPath)) throw new PackNotFoundError(packPath);
11070
11388
  const reason = options.reason.trim();
11071
11389
  if (reason.length < 8) {
11072
11390
  throw new Error("invalidation reason must be at least 8 characters");
@@ -11084,13 +11402,13 @@ async function invalidateExtraction(options) {
11084
11402
  const stampPath = stampIso.replace(/[:]/g, "").replace(/\.\d+Z$/, "Z");
11085
11403
  const archiveRel = posix.join("audits", "legacy", label, stampPath);
11086
11404
  const affectedSections = await detectLegacyClaimSections(packPath);
11087
- const research = ResearchYamlSchema.parse(yamlParse10(await readFile21(yamlPath, "utf8")));
11405
+ const research = ResearchYamlSchema.parse(yamlParse10(await readFile22(yamlPath, "utf8")));
11088
11406
  const packLevelDirs = ["handoffs", "synthesis", "audits"];
11089
11407
  let probeArtifactsExist = affectedSections.length > 0;
11090
11408
  if (!probeArtifactsExist) {
11091
11409
  for (const d of packLevelDirs) {
11092
- const full = join24(packPath, d);
11093
- if (!existsSync24(full)) continue;
11410
+ const full = join25(packPath, d);
11411
+ if (!existsSync25(full)) continue;
11094
11412
  const entries = await readdir3(full, { withFileTypes: true });
11095
11413
  const meaningful = entries.some(
11096
11414
  (e) => !(d === "audits" && e.isDirectory() && e.name === "legacy")
@@ -11112,7 +11430,7 @@ async function invalidateExtraction(options) {
11112
11430
  message: "No legacy artifacts found. Pack is on the current extraction contract."
11113
11431
  };
11114
11432
  }
11115
- await mkdir18(join24(packPath, archiveRel), { recursive: true });
11433
+ await mkdir19(join25(packPath, archiveRel), { recursive: true });
11116
11434
  const archived = [];
11117
11435
  for (const sectionId of affectedSections) {
11118
11436
  for (const filename of [
@@ -11161,14 +11479,14 @@ async function invalidateExtraction(options) {
11161
11479
  frozen_at_cleared: frozenAtCleared,
11162
11480
  notes: options.notes ?? null
11163
11481
  });
11164
- const receiptDir = join24(packPath, archiveRel);
11482
+ const receiptDir = join25(packPath, archiveRel);
11165
11483
  await writeFile19(
11166
- join24(receiptDir, "invalidation.json"),
11484
+ join25(receiptDir, "invalidation.json"),
11167
11485
  JSON.stringify(receipt, null, 2),
11168
11486
  "utf8"
11169
11487
  );
11170
11488
  await writeFile19(
11171
- join24(receiptDir, "invalidation.md"),
11489
+ join25(receiptDir, "invalidation.md"),
11172
11490
  buildReceiptMarkdown(receipt),
11173
11491
  "utf8"
11174
11492
  );
@@ -11194,20 +11512,20 @@ var init_run9 = __esm({
11194
11512
  });
11195
11513
 
11196
11514
  // src/invalidate/review.ts
11197
- import { existsSync as existsSync25 } from "fs";
11198
- import { mkdir as mkdir19, rename as rename2, writeFile as writeFile20 } from "fs/promises";
11199
- import { dirname as dirname6, join as join25, posix as posix2, relative as relative4, resolve as resolve21, sep as sep2 } from "path";
11200
- import { z as z19 } from "zod";
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";
11201
11519
  function posixify2(p) {
11202
11520
  return p.split(sep2).join("/");
11203
11521
  }
11204
11522
  async function moveIfExists2(packPath, archiveRel, rel) {
11205
- const src = join25(packPath, rel);
11206
- if (!existsSync25(src)) return null;
11207
- const dst = join25(packPath, archiveRel, rel);
11208
- await mkdir19(dirname6(dst), { recursive: true });
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 });
11209
11527
  await rename2(src, dst);
11210
- return { src: posixify2(rel), dst: posixify2(join25(archiveRel, rel)) };
11528
+ return { src: posixify2(rel), dst: posixify2(join26(archiveRel, rel)) };
11211
11529
  }
11212
11530
  function buildReceiptMarkdown2(receipt) {
11213
11531
  const lines = [];
@@ -11249,8 +11567,8 @@ function buildReceiptMarkdown2(receipt) {
11249
11567
  }
11250
11568
  async function invalidateReview(options) {
11251
11569
  const packPath = options.packPath ? resolve21(options.packPath) : process.cwd();
11252
- if (!existsSync25(join25(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
11253
- if (!existsSync25(join25(packPath, "sections", options.sectionId)))
11570
+ if (!existsSync26(join26(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
11571
+ if (!existsSync26(join26(packPath, "sections", options.sectionId)))
11254
11572
  throw new SectionNotFoundError(options.sectionId);
11255
11573
  const reason = options.reason.trim();
11256
11574
  if (reason.length < 8) {
@@ -11273,7 +11591,7 @@ async function invalidateReview(options) {
11273
11591
  // Also clear review-active.json so the next review run is unambiguous.
11274
11592
  posix2.join("sections", options.sectionId, "review-active.json")
11275
11593
  ];
11276
- const present = candidatePaths.filter((rel) => existsSync25(join25(packPath, rel)));
11594
+ const present = candidatePaths.filter((rel) => existsSync26(join26(packPath, rel)));
11277
11595
  if (present.length === 0) {
11278
11596
  return {
11279
11597
  performed: false,
@@ -11285,7 +11603,7 @@ async function invalidateReview(options) {
11285
11603
  message: "No canonical review artifacts found to invalidate. Section has no active review state."
11286
11604
  };
11287
11605
  }
11288
- await mkdir19(join25(packPath, archiveRel), { recursive: true });
11606
+ await mkdir20(join26(packPath, archiveRel), { recursive: true });
11289
11607
  const archived = [];
11290
11608
  for (const rel of present) {
11291
11609
  const a = await moveIfExists2(packPath, archiveRel, rel);
@@ -11301,14 +11619,14 @@ async function invalidateReview(options) {
11301
11619
  archived_artifacts: archived,
11302
11620
  notes: options.notes ?? null
11303
11621
  });
11304
- const receiptDir = join25(packPath, archiveRel);
11622
+ const receiptDir = join26(packPath, archiveRel);
11305
11623
  await writeFile20(
11306
- join25(receiptDir, "invalidation.json"),
11624
+ join26(receiptDir, "invalidation.json"),
11307
11625
  JSON.stringify(receipt, null, 2),
11308
11626
  "utf8"
11309
11627
  );
11310
11628
  await writeFile20(
11311
- join25(receiptDir, "invalidation.md"),
11629
+ join26(receiptDir, "invalidation.md"),
11312
11630
  buildReceiptMarkdown2(receipt),
11313
11631
  "utf8"
11314
11632
  );
@@ -11329,15 +11647,15 @@ var init_review2 = __esm({
11329
11647
  init_errors();
11330
11648
  init_src();
11331
11649
  init_schema17();
11332
- ReviewInvalidationReceiptSchema = z19.object({
11333
- receipt_id: z19.string().regex(/^invr_[0-9]+_[a-z0-9-]+$/),
11334
- section_id: z19.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
11335
- contract_label: z19.string().min(1),
11336
- reason: z19.string().min(8),
11337
- invalidated_at: z19.string(),
11338
- research_os_version: z19.string(),
11339
- archived_artifacts: z19.array(ArchivedArtifactSchema),
11340
- notes: z19.string().nullable()
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()
11341
11659
  });
11342
11660
  }
11343
11661
  });
@@ -11353,103 +11671,103 @@ var init_invalidate = __esm({
11353
11671
  });
11354
11672
 
11355
11673
  // src/section_report/schema.ts
11356
- import { z as z20 } from "zod";
11674
+ import { z as z21 } from "zod";
11357
11675
  var SectionReportSourcesSchema, SectionReportExtractionSchema, SectionReportContradictionsSchema, SectionReportReviewSchema, SectionReportAcceptanceSchema, SectionReportSchema;
11358
11676
  var init_schema18 = __esm({
11359
11677
  "src/section_report/schema.ts"() {
11360
11678
  "use strict";
11361
- SectionReportSourcesSchema = z20.object({
11362
- fetched_ok: z20.number().int().nonnegative(),
11363
- source_cards: z20.number().int().nonnegative(),
11364
- publishers: z20.array(z20.string()),
11365
- primary_source_waiver: z20.object({
11366
- status: z20.enum(["none", "requested", "granted"]),
11367
- reason: z20.string().nullable(),
11368
- compensating_controls: z20.array(z20.string())
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())
11369
11687
  })
11370
11688
  });
11371
- SectionReportExtractionSchema = z20.object({
11372
- candidate_claims: z20.number().int().nonnegative(),
11373
- claims_per_source: z20.array(
11374
- z20.object({
11375
- source_id: z20.string(),
11376
- claims: z20.number().int().nonnegative()
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()
11377
11695
  })
11378
11696
  ),
11379
- claims_per_1k_words: z20.number(),
11380
- excerpt_pages_processed: z20.number().int().nonnegative().nullable(),
11381
- excerpt_id_failures: z20.number().int().nonnegative().nullable(),
11382
- malformed_extractor_outputs: z20.number().int().nonnegative().nullable(),
11383
- near_duplicate_clusters: z20.number().int().nonnegative(),
11384
- weak_scope_count: z20.number().int().nonnegative(),
11385
- generic_scope_count: z20.number().int().nonnegative(),
11386
- density_flags: z20.number().int().nonnegative()
11387
- });
11388
- SectionReportContradictionsSchema = z20.object({
11389
- pairs_compared: z20.number().int().nonnegative().nullable(),
11390
- contradiction_candidates: z20.number().int().nonnegative(),
11391
- overgeneralization_risks: z20.number().int().nonnegative()
11392
- });
11393
- SectionReportReviewSchema = z20.object({
11394
- reviewed: z20.boolean(),
11395
- accepted_for_synthesis: z20.number().int().nonnegative(),
11396
- needs_scope_repair: z20.number().int().nonnegative(),
11397
- needs_source_repair: z20.number().int().nonnegative(),
11398
- needs_contradiction_mapping: z20.number().int().nonnegative(),
11399
- rejected: z20.number().int().nonnegative(),
11400
- needs_human_review: z20.number().int().nonnegative(),
11401
- rejection_or_repair_by_category: z20.array(
11402
- z20.object({
11403
- category: z20.string(),
11404
- count: z20.number().int().nonnegative()
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()
11405
11723
  })
11406
11724
  )
11407
11725
  });
11408
- SectionReportAcceptanceSchema = z20.object({
11409
- candidate_claims: z20.number().int().nonnegative(),
11410
- accepted_for_synthesis: z20.number().int().nonnegative(),
11411
- acceptance_ratio: z20.number(),
11726
+ SectionReportAcceptanceSchema = z21.object({
11727
+ candidate_claims: z21.number().int().nonnegative(),
11728
+ accepted_for_synthesis: z21.number().int().nonnegative(),
11729
+ acceptance_ratio: z21.number(),
11412
11730
  // 0..1
11413
- accepted_per_source: z20.number(),
11731
+ accepted_per_source: z21.number(),
11414
11732
  // accepted / source_count, 0 if no sources
11415
- accepted_per_1k_words: z20.number(),
11416
- top_rejection_category: z20.string().nullable(),
11417
- claim_overproduction_fired: z20.boolean(),
11418
- synthesis_ready: z20.boolean()
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()
11419
11737
  });
11420
- SectionReportSchema = z20.object({
11421
- report_id: z20.string().regex(/^secrep_[0-9]+_[a-z0-9-]+$/),
11422
- section_id: z20.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
11423
- reported_at: z20.string(),
11424
- research_os_version: z20.string(),
11425
- status: z20.string(),
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(),
11426
11744
  sources: SectionReportSourcesSchema,
11427
11745
  extraction: SectionReportExtractionSchema,
11428
11746
  contradictions: SectionReportContradictionsSchema,
11429
11747
  review: SectionReportReviewSchema,
11430
11748
  acceptance: SectionReportAcceptanceSchema,
11431
- gate_verdict: z20.string().nullable(),
11432
- gate_synthesis_eligible: z20.boolean().nullable()
11749
+ gate_verdict: z21.string().nullable(),
11750
+ gate_synthesis_eligible: z21.boolean().nullable()
11433
11751
  });
11434
11752
  }
11435
11753
  });
11436
11754
 
11437
11755
  // src/section_report/run.ts
11438
- import { existsSync as existsSync26 } from "fs";
11439
- import { mkdir as mkdir20, readFile as readFile22, writeFile as writeFile21 } from "fs/promises";
11440
- import { join as join26, resolve as resolve22 } from "path";
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";
11441
11759
  import { parse as yamlParse11 } from "yaml";
11442
11760
  async function readJson(path, parser) {
11443
- if (!existsSync26(path)) return null;
11761
+ if (!existsSync27(path)) return null;
11444
11762
  try {
11445
- return parser(JSON.parse(await readFile22(path, "utf8")));
11763
+ return parser(JSON.parse(await readFile23(path, "utf8")));
11446
11764
  } catch {
11447
11765
  return null;
11448
11766
  }
11449
11767
  }
11450
11768
  async function readJsonl6(path, parse) {
11451
- if (!existsSync26(path)) return [];
11452
- const text = await readFile22(path, "utf8");
11769
+ if (!existsSync27(path)) return [];
11770
+ const text = await readFile23(path, "utf8");
11453
11771
  const out = [];
11454
11772
  for (const line of text.split(/\r?\n/)) {
11455
11773
  if (!line.trim()) continue;
@@ -11466,15 +11784,15 @@ function countWords2(text) {
11466
11784
  }
11467
11785
  async function reportSection(options) {
11468
11786
  const packPath = options.packPath ? resolve22(options.packPath) : process.cwd();
11469
- if (!existsSync26(join26(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
11470
- if (!existsSync26(join26(packPath, "sections", options.sectionId)))
11787
+ if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
11788
+ if (!existsSync27(join27(packPath, "sections", options.sectionId)))
11471
11789
  throw new SectionNotFoundError(options.sectionId);
11472
11790
  const research = ResearchYamlSchema.parse(
11473
- yamlParse11(await readFile22(join26(packPath, "research.yaml"), "utf8"))
11791
+ yamlParse11(await readFile23(join27(packPath, "research.yaml"), "utf8"))
11474
11792
  );
11475
11793
  const section = research.sections.find((s) => s.id === options.sectionId);
11476
11794
  const allReceipts = await readJsonl6(
11477
- join26(packPath, "evidence", "fetch-log.jsonl"),
11795
+ join27(packPath, "evidence", "fetch-log.jsonl"),
11478
11796
  (raw) => FetchReceiptSchema.parse(raw)
11479
11797
  );
11480
11798
  const okReceipts = /* @__PURE__ */ new Map();
@@ -11485,9 +11803,9 @@ async function reportSection(options) {
11485
11803
  if (!prev || prev.fetched_at < r.fetched_at) okReceipts.set(r.source_id, r);
11486
11804
  }
11487
11805
  const sectionSourceIds = [];
11488
- const sourcesPath = join26(packPath, "sections", options.sectionId, "sources.jsonl");
11489
- if (existsSync26(sourcesPath)) {
11490
- const text = await readFile22(sourcesPath, "utf8");
11806
+ const sourcesPath = join27(packPath, "sections", options.sectionId, "sources.jsonl");
11807
+ if (existsSync27(sourcesPath)) {
11808
+ const text = await readFile23(sourcesPath, "utf8");
11491
11809
  for (const line of text.split(/\r?\n/)) {
11492
11810
  if (!line.trim()) continue;
11493
11811
  try {
@@ -11499,10 +11817,10 @@ async function reportSection(options) {
11499
11817
  }
11500
11818
  const cards = /* @__PURE__ */ new Map();
11501
11819
  for (const sid of sectionSourceIds) {
11502
- const cardPath = join26(packPath, "evidence", "source-cards", `${sid}.json`);
11503
- if (!existsSync26(cardPath)) continue;
11820
+ const cardPath = join27(packPath, "evidence", "source-cards", `${sid}.json`);
11821
+ if (!existsSync27(cardPath)) continue;
11504
11822
  try {
11505
- cards.set(sid, SourceCardSchema.parse(JSON.parse(await readFile22(cardPath, "utf8"))));
11823
+ cards.set(sid, SourceCardSchema.parse(JSON.parse(await readFile23(cardPath, "utf8"))));
11506
11824
  } catch {
11507
11825
  }
11508
11826
  }
@@ -11513,12 +11831,12 @@ async function reportSection(options) {
11513
11831
  for (const sid of sectionSourceIds) {
11514
11832
  const r = okReceipts.get(sid);
11515
11833
  if (!r?.raw_text_path) continue;
11516
- const p = join26(packPath, r.raw_text_path);
11517
- if (!existsSync26(p)) continue;
11518
- totalWords += countWords2(await readFile22(p, "utf8"));
11834
+ const p = join27(packPath, r.raw_text_path);
11835
+ if (!existsSync27(p)) continue;
11836
+ totalWords += countWords2(await readFile23(p, "utf8"));
11519
11837
  }
11520
11838
  const claims = await readJsonl6(
11521
- join26(packPath, "sections", options.sectionId, "claims.jsonl"),
11839
+ join27(packPath, "sections", options.sectionId, "claims.jsonl"),
11522
11840
  (raw) => ClaimSchema.parse(raw)
11523
11841
  );
11524
11842
  const claimsBySrc = /* @__PURE__ */ new Map();
@@ -11530,11 +11848,11 @@ async function reportSection(options) {
11530
11848
  }
11531
11849
  }
11532
11850
  const density = await readJson(
11533
- join26(packPath, "audits", `${options.sectionId}-claim-density.json`),
11851
+ join27(packPath, "audits", `${options.sectionId}-claim-density.json`),
11534
11852
  (raw) => raw
11535
11853
  );
11536
11854
  const extractReceipt = await readJson(
11537
- join26(packPath, "audits", `${options.sectionId}-claim-extract.json`),
11855
+ join27(packPath, "audits", `${options.sectionId}-claim-extract.json`),
11538
11856
  (raw) => raw
11539
11857
  );
11540
11858
  let excerptPagesProcessed = null;
@@ -11552,7 +11870,7 @@ async function reportSection(options) {
11552
11870
  ).length;
11553
11871
  }
11554
11872
  const contradictions = await readJsonl6(
11555
- join26(packPath, "sections", options.sectionId, "contradictions.jsonl"),
11873
+ join27(packPath, "sections", options.sectionId, "contradictions.jsonl"),
11556
11874
  (raw) => ContradictionSchema.parse(raw)
11557
11875
  );
11558
11876
  let pairsCompared = null;
@@ -11561,7 +11879,7 @@ async function reportSection(options) {
11561
11879
  (c) => c.type === "overgeneralization_risk"
11562
11880
  ).length;
11563
11881
  const reviews = await readJsonl6(
11564
- join26(packPath, "sections", options.sectionId, "claim-reviews.jsonl"),
11882
+ join27(packPath, "sections", options.sectionId, "claim-reviews.jsonl"),
11565
11883
  (raw) => ClaimReviewSchema.parse(raw)
11566
11884
  );
11567
11885
  const latestReviewByClaim = /* @__PURE__ */ new Map();
@@ -11570,7 +11888,7 @@ async function reportSection(options) {
11570
11888
  if (!prev || prev.created_at < r.created_at) latestReviewByClaim.set(r.claim_id, r);
11571
11889
  }
11572
11890
  const findings = await readJsonl6(
11573
- join26(packPath, "audits", `${options.sectionId}-findings.jsonl`),
11891
+ join27(packPath, "audits", `${options.sectionId}-findings.jsonl`),
11574
11892
  (raw) => ReviewFindingSchema.parse(raw)
11575
11893
  );
11576
11894
  const decisionCounts = {
@@ -11595,7 +11913,7 @@ async function reportSection(options) {
11595
11913
  const topRejectionCategory = rejectionByCategory[0]?.category ?? null;
11596
11914
  const claimOverproductionFired = (categoryCounts.get("claim_overproduction") ?? 0) > 0;
11597
11915
  const gate2 = await readJson(
11598
- join26(packPath, "audits", `${options.sectionId}-gate.json`),
11916
+ join27(packPath, "audits", `${options.sectionId}-gate.json`),
11599
11917
  (raw) => raw
11600
11918
  );
11601
11919
  const candidate = claims.length;
@@ -11731,10 +12049,10 @@ async function reportSection(options) {
11731
12049
  md.push("---");
11732
12050
  md.push("");
11733
12051
  md.push("_This report is read-only \u2014 it does not mutate any canonical artifact. Re-run after any pipeline step to refresh._");
11734
- const auditsDir = join26(packPath, "audits");
11735
- await mkdir20(auditsDir, { recursive: true });
11736
- const jsonPath = join26(auditsDir, `${options.sectionId}-section-report.json`);
11737
- const markdownPath = join26(auditsDir, `${options.sectionId}-section-report.md`);
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`);
11738
12056
  await writeFile21(jsonPath, JSON.stringify(report, null, 2), "utf8");
11739
12057
  await writeFile21(markdownPath, md.join("\n"), "utf8");
11740
12058
  return { report, jsonPath, markdownPath };
@@ -11772,17 +12090,17 @@ var init_triage = __esm({
11772
12090
  });
11773
12091
 
11774
12092
  // src/discover/schema.ts
11775
- import { z as z21 } from "zod";
12093
+ import { z as z22 } from "zod";
11776
12094
  var DiscoveryCandidateStatusSchema, SourceTypeGuessSchema, DiscoveryCandidateSchema, DiscoverySummarySchema;
11777
12095
  var init_schema19 = __esm({
11778
12096
  "src/discover/schema.ts"() {
11779
12097
  "use strict";
11780
- DiscoveryCandidateStatusSchema = z21.enum([
12098
+ DiscoveryCandidateStatusSchema = z22.enum([
11781
12099
  "candidate",
11782
12100
  "approved",
11783
12101
  "rejected"
11784
12102
  ]);
11785
- SourceTypeGuessSchema = z21.enum([
12103
+ SourceTypeGuessSchema = z22.enum([
11786
12104
  "primary",
11787
12105
  "docs",
11788
12106
  "paper",
@@ -11792,36 +12110,36 @@ var init_schema19 = __esm({
11792
12110
  "benchmark",
11793
12111
  "unknown"
11794
12112
  ]);
11795
- DiscoveryCandidateSchema = z21.object({
11796
- candidate_id: z21.string().regex(/^disc_[a-f0-9]{12}$/),
11797
- section_id: z21.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
11798
- url: z21.string().url(),
11799
- title: z21.string().min(1),
11800
- publisher: z21.string().nullable(),
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(),
11801
12119
  source_type_guess: SourceTypeGuessSchema,
11802
- why_relevant: z21.string().min(1),
12120
+ why_relevant: z22.string().min(1),
11803
12121
  // The free-text query that produced this candidate (for traceability).
11804
- query: z21.string().min(1),
12122
+ query: z22.string().min(1),
11805
12123
  // Lower rank = more central. Stable per-(query, provider) ordering.
11806
- rank: z21.number().int().positive(),
11807
- discovered_at: z21.string(),
12124
+ rank: z22.number().int().positive(),
12125
+ discovered_at: z22.string(),
11808
12126
  // 'candidate' | 'approved' | 'rejected'. Append-only: new entries with
11809
12127
  // same candidate_id supersede older ones; the latest entry's status wins.
11810
12128
  status: DiscoveryCandidateStatusSchema,
11811
- discovered_by: z21.string().min(1),
11812
- reason: z21.string().nullable().default(null)
11813
- });
11814
- DiscoverySummarySchema = z21.object({
11815
- summary_id: z21.string().regex(/^disum_[0-9]+_[a-z0-9-]+$/),
11816
- section_id: z21.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
11817
- ran_at: z21.string(),
11818
- research_os_version: z21.string(),
11819
- query: z21.string().min(1),
11820
- provider: z21.string().min(1),
11821
- candidates_proposed: z21.number().int().nonnegative(),
11822
- candidates_validated: z21.number().int().nonnegative(),
11823
- candidates_rejected_invalid_url: z21.number().int().nonnegative(),
11824
- warnings: z21.array(z21.string())
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())
11825
12143
  });
11826
12144
  }
11827
12145
  });
@@ -11973,10 +12291,10 @@ Propose source-URL candidates for this section. Quality over quantity.`;
11973
12291
  });
11974
12292
 
11975
12293
  // src/discover/run.ts
11976
- import { existsSync as existsSync27 } from "fs";
12294
+ import { existsSync as existsSync28 } from "fs";
11977
12295
  import { createHash as createHash10 } from "crypto";
11978
- import { mkdir as mkdir21, readFile as readFile23, writeFile as writeFile22, appendFile as appendFile7 } from "fs/promises";
11979
- import { join as join27, resolve as resolve23 } from "path";
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";
11980
12298
  import { parse as yamlParse12 } from "yaml";
11981
12299
  function makeCandidateId(sectionId, url) {
11982
12300
  const hex = createHash10("sha256").update(`${sectionId}|${url}`).digest("hex").slice(0, 12);
@@ -11991,21 +12309,21 @@ function isHttpsUrl(s) {
11991
12309
  }
11992
12310
  }
11993
12311
  function candidatesPath(packPath, sectionId) {
11994
- return join27(packPath, "sections", sectionId, "discovery-candidates.jsonl");
12312
+ return join28(packPath, "sections", sectionId, "discovery-candidates.jsonl");
11995
12313
  }
11996
12314
  function reportPath(packPath, sectionId) {
11997
- return join27(packPath, "sections", sectionId, "discovery-report.md");
12315
+ return join28(packPath, "sections", sectionId, "discovery-report.md");
11998
12316
  }
11999
12317
  function summaryPath(packPath, sectionId) {
12000
- return join27(packPath, "audits", `${sectionId}-discovery.json`);
12318
+ return join28(packPath, "audits", `${sectionId}-discovery.json`);
12001
12319
  }
12002
12320
  function approvedUrlsPath(packPath, sectionId) {
12003
- return join27(packPath, "sections", sectionId, "urls.approved.txt");
12321
+ return join28(packPath, "sections", sectionId, "urls.approved.txt");
12004
12322
  }
12005
12323
  async function readCandidates(packPath, sectionId) {
12006
12324
  const path = candidatesPath(packPath, sectionId);
12007
- if (!existsSync27(path)) return [];
12008
- const text = await readFile23(path, "utf8");
12325
+ if (!existsSync28(path)) return [];
12326
+ const text = await readFile24(path, "utf8");
12009
12327
  const out = [];
12010
12328
  for (const line of text.split(/\r?\n/)) {
12011
12329
  if (!line.trim()) continue;
@@ -12035,7 +12353,7 @@ async function pickProvider(providers) {
12035
12353
  }
12036
12354
  async function loadSectionPurpose(packPath, sectionId) {
12037
12355
  const research = ResearchYamlSchema.parse(
12038
- yamlParse12(await readFile23(join27(packPath, "research.yaml"), "utf8"))
12356
+ yamlParse12(await readFile24(join28(packPath, "research.yaml"), "utf8"))
12039
12357
  );
12040
12358
  const section = research.sections.find((s) => s.id === sectionId);
12041
12359
  return section?.purpose ?? "";
@@ -12073,8 +12391,8 @@ function buildReportMarkdown(args) {
12073
12391
  }
12074
12392
  async function discover(options) {
12075
12393
  const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
12076
- if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12077
- if (!existsSync27(join27(packPath, "sections", options.sectionId)))
12394
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12395
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12078
12396
  throw new SectionNotFoundError(options.sectionId);
12079
12397
  const query2 = options.query.trim();
12080
12398
  if (query2.length < 4) throw new Error("discover query must be at least 4 characters");
@@ -12134,9 +12452,9 @@ async function discover(options) {
12134
12452
  newCandidates.push(candidate);
12135
12453
  }
12136
12454
  const ledgerPath = candidatesPath(packPath, options.sectionId);
12137
- await mkdir21(join27(packPath, "sections", options.sectionId), { recursive: true });
12455
+ await mkdir22(join28(packPath, "sections", options.sectionId), { recursive: true });
12138
12456
  for (const c of newCandidates) {
12139
- await appendFile7(ledgerPath, JSON.stringify(c) + "\n", "utf8");
12457
+ await appendFile8(ledgerPath, JSON.stringify(c) + "\n", "utf8");
12140
12458
  }
12141
12459
  const all = await readCandidates(packPath, options.sectionId);
12142
12460
  const latest = Array.from(latestPerCandidate(all).values());
@@ -12160,7 +12478,7 @@ async function discover(options) {
12160
12478
  candidates_rejected_invalid_url: invalidCount,
12161
12479
  warnings
12162
12480
  });
12163
- await mkdir21(join27(packPath, "audits"), { recursive: true });
12481
+ await mkdir22(join28(packPath, "audits"), { recursive: true });
12164
12482
  await writeFile22(summaryPath(packPath, options.sectionId), JSON.stringify(summary, null, 2), "utf8");
12165
12483
  return {
12166
12484
  candidatesAdded: newCandidates.length,
@@ -12180,13 +12498,13 @@ async function appendStatusUpdate(packPath, sectionId, candidate, newStatus, rea
12180
12498
  reason,
12181
12499
  discovered_at: stampIso
12182
12500
  });
12183
- await appendFile7(candidatesPath(packPath, sectionId), JSON.stringify(updated) + "\n", "utf8");
12501
+ await appendFile8(candidatesPath(packPath, sectionId), JSON.stringify(updated) + "\n", "utf8");
12184
12502
  return updated;
12185
12503
  }
12186
12504
  async function approve(options) {
12187
12505
  const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
12188
- if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12189
- if (!existsSync27(join27(packPath, "sections", options.sectionId)))
12506
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12507
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12190
12508
  throw new SectionNotFoundError(options.sectionId);
12191
12509
  const all = await readCandidates(packPath, options.sectionId);
12192
12510
  const latest = latestPerCandidate(all);
@@ -12219,8 +12537,8 @@ async function approve(options) {
12219
12537
  }
12220
12538
  async function reject(options) {
12221
12539
  const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
12222
- if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12223
- if (!existsSync27(join27(packPath, "sections", options.sectionId)))
12540
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12541
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12224
12542
  throw new SectionNotFoundError(options.sectionId);
12225
12543
  if (options.reason.trim().length < 4) {
12226
12544
  throw new Error("reject requires --reason of at least 4 characters");
@@ -12248,15 +12566,15 @@ async function reject(options) {
12248
12566
  }
12249
12567
  async function exportUrls(options) {
12250
12568
  const packPath = options.packPath ? resolve23(options.packPath) : process.cwd();
12251
- if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12252
- if (!existsSync27(join27(packPath, "sections", options.sectionId)))
12569
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12570
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12253
12571
  throw new SectionNotFoundError(options.sectionId);
12254
12572
  const all = await readCandidates(packPath, options.sectionId);
12255
12573
  const latest = latestPerCandidate(all);
12256
12574
  const approved = Array.from(latest.values()).filter((c) => c.status === "approved").sort((a, b) => a.rank - b.rank);
12257
12575
  const lines = approved.map((c) => c.url).join("\n");
12258
12576
  const path = approvedUrlsPath(packPath, options.sectionId);
12259
- await mkdir21(join27(packPath, "sections", options.sectionId), { recursive: true });
12577
+ await mkdir22(join28(packPath, "sections", options.sectionId), { recursive: true });
12260
12578
  await writeFile22(path, lines + (lines.length > 0 ? "\n" : ""), "utf8");
12261
12579
  return { exportPath: path, approvedCount: approved.length };
12262
12580
  }
@@ -12304,7 +12622,7 @@ var init_src = __esm({
12304
12622
  init_triage();
12305
12623
  init_discover();
12306
12624
  init_errors();
12307
- RESEARCH_OS_VERSION = "0.3.3";
12625
+ RESEARCH_OS_VERSION = "0.4.0";
12308
12626
  }
12309
12627
  });
12310
12628
  init_src();
@@ -12411,6 +12729,7 @@ export {
12411
12729
  SectionStatusChangeSchema,
12412
12730
  SeveritySchema,
12413
12731
  SharedSourceSchema,
12732
+ SourceCardOverrideSchema,
12414
12733
  SourceCardSchema,
12415
12734
  SourceDiversityGapRowSchema,
12416
12735
  SourceTypeGuessSchema,
@@ -12430,6 +12749,7 @@ export {
12430
12749
  add,
12431
12750
  aggregate,
12432
12751
  appendFetchLog,
12752
+ appendOverride,
12433
12753
  appendSectionSourceId,
12434
12754
  applySchema,
12435
12755
  applyWaivers,
@@ -12470,6 +12790,8 @@ export {
12470
12790
  freeze,
12471
12791
  gate,
12472
12792
  gather,
12793
+ getEffectivePublisher,
12794
+ getEffectiveSourceType,
12473
12795
  handoff,
12474
12796
  hasNegation,
12475
12797
  indexDbPath,
@@ -12494,6 +12816,7 @@ export {
12494
12816
  promote,
12495
12817
  query,
12496
12818
  readActiveProfile,
12819
+ readOverrides,
12497
12820
  readTriagedClaimIds,
12498
12821
  readUrlsFile,
12499
12822
  reject,
@@ -12524,6 +12847,7 @@ export {
12524
12847
  syncRepoKnowledge,
12525
12848
  tokenize,
12526
12849
  triage,
12850
+ validateSourceCardOverride,
12527
12851
  workspace,
12528
12852
  writeActiveProfile,
12529
12853
  writeSourceCard