@mcptoolshop/research-os 0.3.3 → 0.5.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,47 @@ 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()
3622
- });
3623
- ReviewSnapshotSchema = z10.object({
3624
- section_id: z10.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
3938
+ review_method: z11.string().min(1),
3939
+ created_at: z11.string(),
3940
+ // v0.5: optional profile lineage. Additive-optional — pre-v0.5 records
3941
+ // without this field parse cleanly. Frozen packs unaffected (Zod .optional()
3942
+ // with no .default() leaves absent keys absent on round-trip).
3943
+ profile: z11.string().optional()
3944
+ });
3945
+ ReviewSnapshotSchema = z11.object({
3946
+ section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
3625
3947
  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()
3948
+ review_method: z11.string(),
3949
+ reviewed_at: z11.string(),
3950
+ candidate_claims: z11.number().int().nonnegative(),
3951
+ findings: z11.array(ReviewFindingSchema),
3952
+ claim_reviews: z11.array(ClaimReviewSchema),
3953
+ decision_counts: z11.record(ReviewDecisionSchema, z11.number().int().nonnegative()),
3954
+ severity_counts: z11.record(FindingSeveritySchema, z11.number().int().nonnegative()),
3955
+ llm_findings_rejected_ungrounded: z11.number().int().nonnegative(),
3956
+ promoted_to_reviewed: z11.boolean()
3635
3957
  });
3636
3958
  }
3637
3959
  });
@@ -4519,12 +4841,12 @@ var init_markdown2 = __esm({
4519
4841
  });
4520
4842
 
4521
4843
  // src/gates/schema.ts
4522
- import { z as z11 } from "zod";
4844
+ import { z as z12 } from "zod";
4523
4845
  var GateFamilySchema, GateCheckStatusSchema, VerdictSchema, GateCheckResultSchema, WaiverApplicationSchema, SectionGateResultSchema;
4524
4846
  var init_schema10 = __esm({
4525
4847
  "src/gates/schema.ts"() {
4526
4848
  "use strict";
4527
- GateFamilySchema = z11.enum([
4849
+ GateFamilySchema = z12.enum([
4528
4850
  "source_floor",
4529
4851
  "claim_integrity",
4530
4852
  "scope_integrity",
@@ -4534,98 +4856,98 @@ var init_schema10 = __esm({
4534
4856
  "waivers",
4535
4857
  "accepted_claim_floor"
4536
4858
  ]);
4537
- GateCheckStatusSchema = z11.enum([
4859
+ GateCheckStatusSchema = z12.enum([
4538
4860
  "pass",
4539
4861
  "warn",
4540
4862
  "fail",
4541
4863
  "pass_with_waiver",
4542
4864
  "warn_with_waiver"
4543
4865
  ]);
4544
- VerdictSchema = z11.enum(["pass", "warn", "fail", "blocked"]);
4545
- GateCheckResultSchema = z11.object({
4866
+ VerdictSchema = z12.enum(["pass", "warn", "fail", "blocked"]);
4867
+ GateCheckResultSchema = z12.object({
4546
4868
  family: GateFamilySchema,
4547
- check: z11.string().min(1),
4869
+ check: z12.string().min(1),
4548
4870
  status: GateCheckStatusSchema,
4549
- detail: z11.string(),
4550
- evidence: z11.array(z11.string()),
4551
- blocks_synthesis: z11.boolean()
4871
+ detail: z12.string(),
4872
+ evidence: z12.array(z12.string()),
4873
+ blocks_synthesis: z12.boolean()
4552
4874
  });
4553
- WaiverApplicationSchema = z11.object({
4875
+ WaiverApplicationSchema = z12.object({
4554
4876
  family: GateFamilySchema,
4555
- check: z11.string().min(1),
4556
- reason: z11.string().min(1),
4557
- compensating_controls: z11.array(z11.string()),
4877
+ check: z12.string().min(1),
4878
+ reason: z12.string().min(1),
4879
+ compensating_controls: z12.array(z12.string()),
4558
4880
  original_status: GateCheckStatusSchema,
4559
4881
  new_status: GateCheckStatusSchema
4560
4882
  });
4561
- SectionGateResultSchema = z11.object({
4562
- section_id: z11.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
4883
+ SectionGateResultSchema = z12.object({
4884
+ section_id: z12.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
4563
4885
  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()
4886
+ summary: z12.string(),
4887
+ checked_at: z12.string(),
4888
+ synthesis_eligible: z12.boolean(),
4889
+ gate_results: z12.array(GateCheckResultSchema),
4890
+ failures: z12.array(GateCheckResultSchema),
4891
+ warnings: z12.array(GateCheckResultSchema),
4892
+ waivers_applied: z12.array(WaiverApplicationSchema),
4893
+ blocking_reasons: z12.array(z12.string()),
4894
+ claim_counts: z12.object({
4895
+ total: z12.number().int().nonnegative(),
4896
+ candidate: z12.number().int().nonnegative(),
4897
+ with_evidence_excerpt: z12.number().int().nonnegative(),
4898
+ with_source_hashes: z12.number().int().nonnegative(),
4899
+ with_scope: z12.number().int().nonnegative(),
4900
+ with_not: z12.number().int().nonnegative(),
4901
+ universal_scope_null: z12.number().int().nonnegative(),
4902
+ orphans: z12.number().int().nonnegative()
4581
4903
  }),
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()
4904
+ source_counts: z12.object({
4905
+ total: z12.number().int().nonnegative(),
4906
+ primary: z12.number().int().nonnegative(),
4907
+ secondary: z12.number().int().nonnegative(),
4908
+ forum: z12.number().int().nonnegative(),
4909
+ benchmark: z12.number().int().nonnegative(),
4910
+ docs: z12.number().int().nonnegative(),
4911
+ unknown: z12.number().int().nonnegative(),
4912
+ independent_publishers: z12.number().int().nonnegative(),
4913
+ failed_fetches: z12.number().int().nonnegative(),
4914
+ section_primary: z12.number().int().nonnegative(),
4915
+ section_independent_publishers: z12.number().int().nonnegative()
4594
4916
  }),
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())
4917
+ contradiction_counts: z12.object({
4918
+ total: z12.number().int().nonnegative(),
4919
+ unresolved: z12.number().int().nonnegative(),
4920
+ blocking: z12.number().int().nonnegative(),
4921
+ by_type: z12.record(z12.string(), z12.number().int().nonnegative())
4600
4922
  }),
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()
4923
+ freshness_summary: z12.object({
4924
+ policy_required: z12.boolean(),
4925
+ max_source_age_months: z12.number().int().nullable(),
4926
+ stale_source_policy: z12.enum(["warn", "fail"]),
4927
+ stale_count: z12.number().int().nonnegative(),
4928
+ unknown_date_count: z12.number().int().nonnegative()
4607
4929
  }),
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()
4930
+ scope_integrity_summary: z12.object({
4931
+ universal_claims: z12.number().int().nonnegative(),
4932
+ scoped_claims: z12.number().int().nonnegative(),
4933
+ with_not_constraint: z12.number().int().nonnegative(),
4934
+ overgen_risks_total: z12.number().int().nonnegative(),
4935
+ overgen_risks_blocking: z12.number().int().nonnegative()
4614
4936
  }),
4615
- next_actions: z11.array(z11.string())
4937
+ next_actions: z12.array(z12.string())
4616
4938
  });
4617
4939
  }
4618
4940
  });
4619
4941
 
4620
4942
  // 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";
4943
+ import { existsSync as existsSync12 } from "fs";
4944
+ import { mkdir as mkdir10, readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
4945
+ import { join as join13, resolve as resolve9 } from "path";
4624
4946
  import { parse as yamlParse2, stringify as yamlStringify3 } from "yaml";
4625
4947
  async function readJsonl(packPath, rel, parse) {
4626
- const path = join12(packPath, rel);
4627
- if (!existsSync11(path)) return [];
4628
- const text = await readFile11(path, "utf8");
4948
+ const path = join13(packPath, rel);
4949
+ if (!existsSync12(path)) return [];
4950
+ const text = await readFile12(path, "utf8");
4629
4951
  const out = [];
4630
4952
  for (const line of text.split(/\r?\n/)) {
4631
4953
  if (!line.trim()) continue;
@@ -4634,14 +4956,14 @@ async function readJsonl(packPath, rel, parse) {
4634
4956
  return out;
4635
4957
  }
4636
4958
  async function readSourceCards2(packPath) {
4637
- const dir = join12(packPath, "evidence", "source-cards");
4638
- if (!existsSync11(dir)) return [];
4959
+ const dir = join13(packPath, "evidence", "source-cards");
4960
+ if (!existsSync12(dir)) return [];
4639
4961
  const { readdir: readdir4 } = await import("fs/promises");
4640
4962
  const entries = await readdir4(dir);
4641
4963
  const cards = [];
4642
4964
  for (const entry of entries) {
4643
4965
  if (!entry.endsWith(".json")) continue;
4644
- const text = await readFile11(join12(dir, entry), "utf8");
4966
+ const text = await readFile12(join13(dir, entry), "utf8");
4645
4967
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
4646
4968
  }
4647
4969
  return cards;
@@ -4816,14 +5138,14 @@ function buildNextActions(results) {
4816
5138
  return Array.from(new Set(actions));
4817
5139
  }
4818
5140
  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");
5141
+ const yamlPath = join13(packPath, "research.yaml");
5142
+ if (!existsSync12(yamlPath)) throw new PackNotFoundError(packPath);
5143
+ const text = await readFile12(yamlPath, "utf8");
4822
5144
  return ResearchYamlSchema.parse(yamlParse2(text));
4823
5145
  }
4824
5146
  async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
4825
- const yamlPath = join12(packPath, "research.yaml");
4826
- const text = await readFile11(yamlPath, "utf8");
5147
+ const yamlPath = join13(packPath, "research.yaml");
5148
+ const text = await readFile12(yamlPath, "utf8");
4827
5149
  const research = ResearchYamlSchema.parse(yamlParse2(text));
4828
5150
  const idx = research.sections.findIndex((s) => s.id === sectionId);
4829
5151
  if (idx < 0) return;
@@ -4840,9 +5162,9 @@ async function updateSectionStatus(packPath, sectionId, synthesisEligible) {
4840
5162
  }
4841
5163
  async function gate(options) {
4842
5164
  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);
5165
+ if (!existsSync12(join13(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
5166
+ const sectionDir = join13(packPath, "sections", options.sectionId);
5167
+ if (!existsSync12(sectionDir)) throw new SectionNotFoundError(options.sectionId);
4846
5168
  const research = await loadResearchYaml2(packPath);
4847
5169
  const section = research.sections.find((s) => s.id === options.sectionId);
4848
5170
  if (!section) throw new SectionNotFoundError(options.sectionId);
@@ -4896,15 +5218,15 @@ async function gate(options) {
4896
5218
  scope_integrity_summary: summarizeScopeIntegrity(input),
4897
5219
  next_actions: buildNextActions(finalResults)
4898
5220
  });
4899
- const auditsDir = join12(packPath, "audits");
4900
- await mkdir9(auditsDir, { recursive: true });
5221
+ const auditsDir = join13(packPath, "audits");
5222
+ await mkdir10(auditsDir, { recursive: true });
4901
5223
  await writeFile10(
4902
- join12(auditsDir, `${options.sectionId}-gate.json`),
5224
+ join13(auditsDir, `${options.sectionId}-gate.json`),
4903
5225
  JSON.stringify(result, null, 2),
4904
5226
  "utf8"
4905
5227
  );
4906
5228
  await writeFile10(
4907
- join12(auditsDir, `${options.sectionId}-gate.md`),
5229
+ join13(auditsDir, `${options.sectionId}-gate.md`),
4908
5230
  renderGateMarkdown(result),
4909
5231
  "utf8"
4910
5232
  );
@@ -4946,21 +5268,21 @@ var init_gates = __esm({
4946
5268
  });
4947
5269
 
4948
5270
  // 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";
5271
+ import { existsSync as existsSync13 } from "fs";
5272
+ import { mkdir as mkdir11, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
5273
+ import { join as join14 } from "path";
5274
+ import { z as z13 } from "zod";
4953
5275
  function reviewActivePath(packPath, sectionId) {
4954
- return join13(packPath, "sections", sectionId, "review-active.json");
5276
+ return join14(packPath, "sections", sectionId, "review-active.json");
4955
5277
  }
4956
5278
  function profileDir(packPath, sectionId, profile) {
4957
- return join13(packPath, "sections", sectionId, "reviews", profile);
5279
+ return join14(packPath, "sections", sectionId, "reviews", profile);
4958
5280
  }
4959
5281
  async function readActiveProfile(packPath, sectionId) {
4960
5282
  const path = reviewActivePath(packPath, sectionId);
4961
- if (!existsSync12(path)) return DEFAULT_PROFILE;
5283
+ if (!existsSync13(path)) return DEFAULT_PROFILE;
4962
5284
  try {
4963
- const parsed = ReviewActiveSchema.parse(JSON.parse(await readFile12(path, "utf8")));
5285
+ const parsed = ReviewActiveSchema.parse(JSON.parse(await readFile13(path, "utf8")));
4964
5286
  return parsed.active_profile;
4965
5287
  } catch {
4966
5288
  return DEFAULT_PROFILE;
@@ -4968,7 +5290,7 @@ async function readActiveProfile(packPath, sectionId) {
4968
5290
  }
4969
5291
  async function writeActiveProfile(packPath, sectionId, active) {
4970
5292
  const path = reviewActivePath(packPath, sectionId);
4971
- await mkdir10(join13(packPath, "sections", sectionId), { recursive: true });
5293
+ await mkdir11(join14(packPath, "sections", sectionId), { recursive: true });
4972
5294
  await writeFile11(path, JSON.stringify(ReviewActiveSchema.parse(active), null, 2), "utf8");
4973
5295
  }
4974
5296
  function isValidProfileName(name) {
@@ -4979,22 +5301,22 @@ var init_profiles = __esm({
4979
5301
  "src/review/profiles.ts"() {
4980
5302
  "use strict";
4981
5303
  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(),
5304
+ PromotionCalibrationSummarySchema = z13.object({
5305
+ fixture: z13.string().nullable().default(null),
5306
+ good_false_positive_rate: z13.string().nullable().default(null),
5307
+ bad_any_flag_recall: z13.string().nullable().default(null),
5308
+ strict_category_recall: z13.string().nullable().default(null),
5309
+ unsupported_claim_recall: z13.string().nullable().default(null),
5310
+ notes: z13.string().nullable().default(null)
5311
+ });
5312
+ ReviewActiveSchema = z13.object({
5313
+ active_profile: z13.string().min(1),
5314
+ promoted_at: z13.string(),
5315
+ promoted_method: z13.string(),
5316
+ promoted_reviewer: z13.string(),
4995
5317
  // Free-text reason the profile was promoted. Recorded once at promotion
4996
5318
  // time; not derived from artifacts. Required.
4997
- promotion_reason: z12.string().min(8).default("promoted via review-promote without an explicit reason"),
5319
+ promotion_reason: z13.string().min(8).default("promoted via review-promote without an explicit reason"),
4998
5320
  // Optional calibration evidence captured at promotion time so downstream
4999
5321
  // consumers can see WHY the reviewer was trusted.
5000
5322
  calibration_summary: PromotionCalibrationSummarySchema.nullable().default(null)
@@ -5621,7 +5943,7 @@ function pickHighestPriority(decisions) {
5621
5943
  return "accepted_for_synthesis";
5622
5944
  }
5623
5945
  function deriveClaimReviews(args) {
5624
- const { claims, findings, reviewer, reviewMethod, activeSectionWaivers } = args;
5946
+ const { claims, findings, reviewer, reviewMethod, activeSectionWaivers, profile } = args;
5625
5947
  const reviews = [];
5626
5948
  const now = (/* @__PURE__ */ new Date()).toISOString();
5627
5949
  const monopolyWaived = Array.isArray(activeSectionWaivers) && activeSectionWaivers.some((w) => w.scope === "min_independent_publishers");
@@ -5636,7 +5958,8 @@ function deriveClaimReviews(args) {
5636
5958
  finding_ids: [],
5637
5959
  reviewer,
5638
5960
  review_method: reviewMethod,
5639
- created_at: now
5961
+ created_at: now,
5962
+ ...profile !== void 0 ? { profile } : {}
5640
5963
  });
5641
5964
  continue;
5642
5965
  }
@@ -5670,7 +5993,8 @@ function deriveClaimReviews(args) {
5670
5993
  finding_ids: claimFindings.map((f) => f.finding_id),
5671
5994
  reviewer,
5672
5995
  review_method: reviewMethod,
5673
- created_at: now
5996
+ created_at: now,
5997
+ ...profile !== void 0 ? { profile } : {}
5674
5998
  });
5675
5999
  }
5676
6000
  return reviews;
@@ -5815,14 +6139,14 @@ var init_markdown3 = __esm({
5815
6139
 
5816
6140
  // src/review/run.ts
5817
6141
  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";
6142
+ import { existsSync as existsSync14 } from "fs";
6143
+ import { appendFile as appendFile6, mkdir as mkdir12, readFile as readFile14, writeFile as writeFile12 } from "fs/promises";
6144
+ import { join as join15, resolve as resolve10 } from "path";
5821
6145
  import { parse as yamlParse3, stringify as yamlStringify4 } from "yaml";
5822
6146
  async function readJsonl2(packPath, rel, parse) {
5823
- const path = join14(packPath, rel);
5824
- if (!existsSync13(path)) return [];
5825
- const text = await readFile13(path, "utf8");
6147
+ const path = join15(packPath, rel);
6148
+ if (!existsSync14(path)) return [];
6149
+ const text = await readFile14(path, "utf8");
5826
6150
  const out = [];
5827
6151
  for (const line of text.split(/\r?\n/)) {
5828
6152
  if (!line.trim()) continue;
@@ -5831,37 +6155,37 @@ async function readJsonl2(packPath, rel, parse) {
5831
6155
  return out;
5832
6156
  }
5833
6157
  async function readSourceCards3(packPath) {
5834
- const dir = join14(packPath, "evidence", "source-cards");
5835
- if (!existsSync13(dir)) return [];
6158
+ const dir = join15(packPath, "evidence", "source-cards");
6159
+ if (!existsSync14(dir)) return [];
5836
6160
  const { readdir: readdir4 } = await import("fs/promises");
5837
6161
  const entries = await readdir4(dir);
5838
6162
  const cards = [];
5839
6163
  for (const entry of entries) {
5840
6164
  if (!entry.endsWith(".json")) continue;
5841
- const text = await readFile13(join14(dir, entry), "utf8");
6165
+ const text = await readFile14(join15(dir, entry), "utf8");
5842
6166
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
5843
6167
  }
5844
6168
  return cards;
5845
6169
  }
5846
6170
  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");
6171
+ const path = join15(packPath, "audits", `${sectionId}-gate.json`);
6172
+ if (!existsSync14(path)) return null;
6173
+ const text = await readFile14(path, "utf8");
5850
6174
  return SectionGateResultSchema.parse(JSON.parse(text));
5851
6175
  }
5852
6176
  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");
6177
+ const path = join15(packPath, "sections", sectionId, "brief.md");
6178
+ if (!existsSync14(path)) return null;
6179
+ return readFile14(path, "utf8");
5856
6180
  }
5857
6181
  async function readRawTextBySource(packPath, receipts) {
5858
6182
  const map2 = /* @__PURE__ */ new Map();
5859
6183
  for (const r of receipts) {
5860
6184
  if (r.fetch_outcome !== "ok" || !r.raw_text_path) continue;
5861
- const path = join14(packPath, r.raw_text_path);
5862
- if (!existsSync13(path)) continue;
6185
+ const path = join15(packPath, r.raw_text_path);
6186
+ if (!existsSync14(path)) continue;
5863
6187
  if (map2.has(r.source_id)) continue;
5864
- map2.set(r.source_id, await readFile13(path, "utf8"));
6188
+ map2.set(r.source_id, await readFile14(path, "utf8"));
5865
6189
  }
5866
6190
  return map2;
5867
6191
  }
@@ -5871,8 +6195,8 @@ async function readExcerptsBySource(packPath, claims) {
5871
6195
  for (const c of claims) for (const sid of c.source_ids) sourceIds.add(sid);
5872
6196
  for (const sid of sourceIds) {
5873
6197
  const path = ledgerPathFor(packPath, sid);
5874
- if (!existsSync13(path)) continue;
5875
- const text = await readFile13(path, "utf8");
6198
+ if (!existsSync14(path)) continue;
6199
+ const text = await readFile14(path, "utf8");
5876
6200
  const index = /* @__PURE__ */ new Map();
5877
6201
  for (const line of text.split(/\r?\n/)) {
5878
6202
  if (!line.trim()) continue;
@@ -5925,15 +6249,15 @@ function buildFinding(args) {
5925
6249
  });
5926
6250
  }
5927
6251
  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");
6252
+ const yamlPath = join15(packPath, "research.yaml");
6253
+ if (!existsSync14(yamlPath)) throw new PackNotFoundError(packPath);
6254
+ const text = await readFile14(yamlPath, "utf8");
5931
6255
  return ResearchYamlSchema.parse(yamlParse3(text));
5932
6256
  }
5933
6257
  async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
5934
6258
  if (!allAccepted) return false;
5935
- const yamlPath = join14(packPath, "research.yaml");
5936
- const text = await readFile13(yamlPath, "utf8");
6259
+ const yamlPath = join15(packPath, "research.yaml");
6260
+ const text = await readFile14(yamlPath, "utf8");
5937
6261
  const research = ResearchYamlSchema.parse(yamlParse3(text));
5938
6262
  const idx = research.sections.findIndex((s) => s.id === sectionId);
5939
6263
  if (idx < 0) return false;
@@ -5945,9 +6269,9 @@ async function maybePromoteToReviewed(packPath, sectionId, allAccepted) {
5945
6269
  }
5946
6270
  async function review(options) {
5947
6271
  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);
6272
+ if (!existsSync14(join15(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
6273
+ const sectionDir = join15(packPath, "sections", options.sectionId);
6274
+ if (!existsSync14(sectionDir)) throw new SectionNotFoundError(options.sectionId);
5951
6275
  const research = await loadResearchYaml3(packPath);
5952
6276
  const section = research.sections.find((s) => s.id === options.sectionId);
5953
6277
  if (!section) throw new SectionNotFoundError(options.sectionId);
@@ -6166,7 +6490,8 @@ async function finalizeReview(args) {
6166
6490
  findings: dedupedFindings,
6167
6491
  reviewer: args.reviewer,
6168
6492
  reviewMethod: args.reviewMethod,
6169
- activeSectionWaivers
6493
+ activeSectionWaivers,
6494
+ profile: args.profile !== DEFAULT_PROFILE ? args.profile : void 0
6170
6495
  });
6171
6496
  const decisionCounts = {
6172
6497
  accepted_for_synthesis: 0,
@@ -6197,39 +6522,39 @@ async function finalizeReview(args) {
6197
6522
  promoted_to_reviewed: promoted
6198
6523
  });
6199
6524
  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");
6525
+ await mkdir12(profDir, { recursive: true });
6526
+ await writeFile12(join15(profDir, "review.json"), JSON.stringify(snapshot, null, 2), "utf8");
6527
+ await writeFile12(join15(profDir, "review.md"), renderReviewMarkdown(snapshot), "utf8");
6528
+ const profileFindingsPath = join15(profDir, "findings.jsonl");
6204
6529
  for (const f of dedupedFindings) {
6205
- await appendFile5(profileFindingsPath, JSON.stringify(f) + "\n", "utf8");
6530
+ await appendFile6(profileFindingsPath, JSON.stringify(f) + "\n", "utf8");
6206
6531
  }
6207
- const profileReviewsPath = join14(profDir, "claim-reviews.jsonl");
6532
+ const profileReviewsPath = join15(profDir, "claim-reviews.jsonl");
6208
6533
  for (const r of claimReviews) {
6209
- await appendFile5(profileReviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6534
+ await appendFile6(profileReviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6210
6535
  }
6211
6536
  const activeProfile = await readActiveProfile(args.packPath, args.sectionId);
6212
6537
  const isActive = args.profile === activeProfile || activeProfile === DEFAULT_PROFILE && args.profile === DEFAULT_PROFILE;
6213
6538
  if (isActive) {
6214
- const auditsDir = join14(args.packPath, "audits");
6215
- await mkdir11(auditsDir, { recursive: true });
6539
+ const auditsDir = join15(args.packPath, "audits");
6540
+ await mkdir12(auditsDir, { recursive: true });
6216
6541
  await writeFile12(
6217
- join14(auditsDir, `${args.sectionId}-review.json`),
6542
+ join15(auditsDir, `${args.sectionId}-review.json`),
6218
6543
  JSON.stringify(snapshot, null, 2),
6219
6544
  "utf8"
6220
6545
  );
6221
6546
  await writeFile12(
6222
- join14(auditsDir, `${args.sectionId}-review.md`),
6547
+ join15(auditsDir, `${args.sectionId}-review.md`),
6223
6548
  renderReviewMarkdown(snapshot),
6224
6549
  "utf8"
6225
6550
  );
6226
- const findingsPath = join14(auditsDir, `${args.sectionId}-findings.jsonl`);
6551
+ const findingsPath = join15(auditsDir, `${args.sectionId}-findings.jsonl`);
6227
6552
  for (const f of dedupedFindings) {
6228
- await appendFile5(findingsPath, JSON.stringify(f) + "\n", "utf8");
6553
+ await appendFile6(findingsPath, JSON.stringify(f) + "\n", "utf8");
6229
6554
  }
6230
- const reviewsPath = join14(args.packPath, "sections", args.sectionId, "claim-reviews.jsonl");
6555
+ const reviewsPath = join15(args.packPath, "sections", args.sectionId, "claim-reviews.jsonl");
6231
6556
  for (const r of claimReviews) {
6232
- await appendFile5(reviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6557
+ await appendFile6(reviewsPath, JSON.stringify(ClaimReviewSchema.parse(r)) + "\n", "utf8");
6233
6558
  }
6234
6559
  }
6235
6560
  return {
@@ -6266,14 +6591,14 @@ var init_run4 = __esm({
6266
6591
  });
6267
6592
 
6268
6593
  // 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";
6594
+ import { existsSync as existsSync15 } from "fs";
6595
+ import { copyFile, mkdir as mkdir13, readFile as readFile15, writeFile as writeFile13, appendFile as appendFile7 } from "fs/promises";
6596
+ import { join as join16, resolve as resolve11 } from "path";
6272
6597
  import { parse as yamlParse4, stringify as yamlStringify5 } from "yaml";
6273
6598
  async function promote(options) {
6274
6599
  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)))
6600
+ if (!existsSync15(join16(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
6601
+ if (!existsSync15(join16(packPath, "sections", options.sectionId)))
6277
6602
  throw new SectionNotFoundError(options.sectionId);
6278
6603
  if (!isValidProfileName(options.profile)) {
6279
6604
  throw new Error(
@@ -6281,39 +6606,39 @@ async function promote(options) {
6281
6606
  );
6282
6607
  }
6283
6608
  const dir = profileDir(packPath, options.sectionId, options.profile);
6284
- const reviewJsonPath = join15(dir, "review.json");
6285
- if (!existsSync14(reviewJsonPath)) {
6609
+ const reviewJsonPath = join16(dir, "review.json");
6610
+ if (!existsSync15(reviewJsonPath)) {
6286
6611
  throw new Error(
6287
6612
  `Profile "${options.profile}" not found at ${dir}. Run \`research-os review --profile ${options.profile}\` first.`
6288
6613
  );
6289
6614
  }
6290
6615
  const snapshot = ReviewSnapshotSchema.parse(
6291
- JSON.parse(await readFile14(reviewJsonPath, "utf8"))
6616
+ JSON.parse(await readFile15(reviewJsonPath, "utf8"))
6292
6617
  );
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`);
6618
+ const auditsDir = join16(packPath, "audits");
6619
+ await mkdir13(auditsDir, { recursive: true });
6620
+ const canonicalReviewJson = join16(auditsDir, `${options.sectionId}-review.json`);
6621
+ const canonicalReviewMd = join16(auditsDir, `${options.sectionId}-review.md`);
6297
6622
  await copyFile(reviewJsonPath, canonicalReviewJson);
6298
6623
  await writeFile13(canonicalReviewMd, renderReviewMarkdown(snapshot), "utf8");
6299
- const canonicalFindings = join15(auditsDir, `${options.sectionId}-findings.jsonl`);
6300
- const profileFindings = join15(dir, "findings.jsonl");
6624
+ const canonicalFindings = join16(auditsDir, `${options.sectionId}-findings.jsonl`);
6625
+ const profileFindings = join16(dir, "findings.jsonl");
6301
6626
  const writtenFindings = [];
6302
- if (existsSync14(profileFindings)) {
6303
- const text = await readFile14(profileFindings, "utf8");
6627
+ if (existsSync15(profileFindings)) {
6628
+ const text = await readFile15(profileFindings, "utf8");
6304
6629
  for (const line of text.split(/\r?\n/)) {
6305
6630
  if (!line.trim()) continue;
6306
- await appendFile6(canonicalFindings, line + "\n", "utf8");
6631
+ await appendFile7(canonicalFindings, line + "\n", "utf8");
6307
6632
  writtenFindings.push(line);
6308
6633
  }
6309
6634
  }
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");
6635
+ const canonicalReviews = join16(packPath, "sections", options.sectionId, "claim-reviews.jsonl");
6636
+ const profileReviews = join16(dir, "claim-reviews.jsonl");
6637
+ if (existsSync15(profileReviews)) {
6638
+ const text = await readFile15(profileReviews, "utf8");
6314
6639
  for (const line of text.split(/\r?\n/)) {
6315
6640
  if (!line.trim()) continue;
6316
- await appendFile6(canonicalReviews, line + "\n", "utf8");
6641
+ await appendFile7(canonicalReviews, line + "\n", "utf8");
6317
6642
  }
6318
6643
  }
6319
6644
  const stamp = (options.now ?? (() => /* @__PURE__ */ new Date()))();
@@ -6327,8 +6652,8 @@ async function promote(options) {
6327
6652
  });
6328
6653
  let sectionStatusBumped = false;
6329
6654
  if (options.promoteSectionStatus) {
6330
- const yamlPath = join15(packPath, "research.yaml");
6331
- const research = ResearchYamlSchema.parse(yamlParse4(await readFile14(yamlPath, "utf8")));
6655
+ const yamlPath = join16(packPath, "research.yaml");
6656
+ const research = ResearchYamlSchema.parse(yamlParse4(await readFile15(yamlPath, "utf8")));
6332
6657
  const idx = research.sections.findIndex((s) => s.id === options.sectionId);
6333
6658
  if (idx >= 0 && research.sections[idx].status === "gated") {
6334
6659
  const allAccepted = snapshot.candidate_claims > 0 && snapshot.claim_reviews.every((r) => r.decision === "accepted_for_synthesis");
@@ -6533,16 +6858,16 @@ var init_schema11 = __esm({
6533
6858
  });
6534
6859
 
6535
6860
  // src/indexer/db.ts
6536
- import { mkdirSync, writeFileSync, existsSync as existsSync15 } from "fs";
6537
- import { dirname as dirname3, join as join16 } from "path";
6861
+ import { mkdirSync, writeFileSync, existsSync as existsSync16 } from "fs";
6862
+ import { dirname as dirname4, join as join17 } from "path";
6538
6863
  import Database from "better-sqlite3";
6539
6864
  function indexDbPath(packPath) {
6540
- return join16(packPath, ".research-os", "index.sqlite");
6865
+ return join17(packPath, ".research-os", "index.sqlite");
6541
6866
  }
6542
6867
  function ensureGitIgnore(packPath) {
6543
- const dir = join16(packPath, ".research-os");
6544
- const gi = join16(dir, ".gitignore");
6545
- if (!existsSync15(gi)) {
6868
+ const dir = join17(packPath, ".research-os");
6869
+ const gi = join17(dir, ".gitignore");
6870
+ if (!existsSync16(gi)) {
6546
6871
  mkdirSync(dir, { recursive: true });
6547
6872
  writeFileSync(gi, "*\n", "utf8");
6548
6873
  }
@@ -6550,7 +6875,7 @@ function ensureGitIgnore(packPath) {
6550
6875
  function openIndexDb(opts) {
6551
6876
  const dbPath = indexDbPath(opts.packPath);
6552
6877
  if (!opts.readonly) {
6553
- mkdirSync(dirname3(dbPath), { recursive: true });
6878
+ mkdirSync(dirname4(dbPath), { recursive: true });
6554
6879
  ensureGitIgnore(opts.packPath);
6555
6880
  }
6556
6881
  const db = new Database(dbPath, { readonly: opts.readonly ?? false, fileMustExist: opts.readonly ?? false });
@@ -6565,18 +6890,18 @@ var init_db = __esm({
6565
6890
  });
6566
6891
 
6567
6892
  // 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";
6893
+ import { existsSync as existsSync17 } from "fs";
6894
+ import { readFile as readFile16 } from "fs/promises";
6895
+ import { join as join18, resolve as resolve12, relative as relative2 } from "path";
6571
6896
  import { createHash as createHash5 } from "crypto";
6572
6897
  import { parse as yamlParse5 } from "yaml";
6573
6898
  function relPath(packPath, abs) {
6574
6899
  return relative2(packPath, abs).split("\\").join("/");
6575
6900
  }
6576
6901
  async function tryReadJsonl(packPath, rel, parse) {
6577
- const path = join17(packPath, rel);
6578
- if (!existsSync16(path)) return [];
6579
- const text = await readFile15(path, "utf8");
6902
+ const path = join18(packPath, rel);
6903
+ if (!existsSync17(path)) return [];
6904
+ const text = await readFile16(path, "utf8");
6580
6905
  const out = [];
6581
6906
  for (const line of text.split(/\r?\n/)) {
6582
6907
  if (!line.trim()) continue;
@@ -6585,22 +6910,22 @@ async function tryReadJsonl(packPath, rel, parse) {
6585
6910
  return out;
6586
6911
  }
6587
6912
  async function readSourceCards4(packPath) {
6588
- const dir = join17(packPath, "evidence", "source-cards");
6589
- if (!existsSync16(dir)) return [];
6913
+ const dir = join18(packPath, "evidence", "source-cards");
6914
+ if (!existsSync17(dir)) return [];
6590
6915
  const { readdir: readdir4 } = await import("fs/promises");
6591
6916
  const entries = await readdir4(dir);
6592
6917
  const cards = [];
6593
6918
  for (const entry of entries) {
6594
6919
  if (!entry.endsWith(".json")) continue;
6595
- const text = await readFile15(join17(dir, entry), "utf8");
6920
+ const text = await readFile16(join18(dir, entry), "utf8");
6596
6921
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
6597
6922
  }
6598
6923
  return cards;
6599
6924
  }
6600
6925
  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");
6926
+ const path = join18(packPath, "audits", `${sectionId}-gate.json`);
6927
+ if (!existsSync17(path)) return null;
6928
+ const text = await readFile16(path, "utf8");
6604
6929
  return SectionGateResultSchema.parse(JSON.parse(text));
6605
6930
  }
6606
6931
  function fileSha256(text) {
@@ -6610,8 +6935,8 @@ async function indexSection(args) {
6610
6935
  const { db, packPath, research, sectionId, now, counts } = args;
6611
6936
  const section = research.sections.find((s) => s.id === sectionId);
6612
6937
  if (!section) throw new SectionNotFoundError(sectionId);
6613
- const sectionDir = join17(packPath, "sections", sectionId);
6614
- if (!existsSync16(sectionDir)) throw new SectionNotFoundError(sectionId);
6938
+ const sectionDir = join18(packPath, "sections", sectionId);
6939
+ if (!existsSync17(sectionDir)) throw new SectionNotFoundError(sectionId);
6615
6940
  db.prepare("DELETE FROM sources WHERE section_id = ?").run(sectionId);
6616
6941
  db.prepare("DELETE FROM claims WHERE section_id = ?").run(sectionId);
6617
6942
  db.prepare("DELETE FROM contradictions WHERE section_id = ?").run(sectionId);
@@ -6650,9 +6975,9 @@ async function indexSection(args) {
6650
6975
  };
6651
6976
  const allCards = await readSourceCards4(packPath);
6652
6977
  const sectionSourceIds = /* @__PURE__ */ new Set();
6653
- const sourcesJsonlPath = join17(sectionDir, "sources.jsonl");
6654
- if (existsSync16(sourcesJsonlPath)) {
6655
- const text = await readFile15(sourcesJsonlPath, "utf8");
6978
+ const sourcesJsonlPath = join18(sectionDir, "sources.jsonl");
6979
+ if (existsSync17(sourcesJsonlPath)) {
6980
+ const text = await readFile16(sourcesJsonlPath, "utf8");
6656
6981
  recordArtifact("sources_jsonl", relPath(packPath, sourcesJsonlPath), text);
6657
6982
  for (const line of text.split(/\r?\n/)) {
6658
6983
  if (!line.trim()) continue;
@@ -6662,7 +6987,7 @@ async function indexSection(args) {
6662
6987
  }
6663
6988
  for (const card of allCards) {
6664
6989
  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`));
6990
+ const cardPath = relPath(packPath, join18(packPath, "evidence", "source-cards", `${card.source_id}.json`));
6666
6991
  db.prepare(
6667
6992
  `INSERT OR REPLACE INTO sources(
6668
6993
  source_id, section_id, url, publisher, source_type, relevance,
@@ -6696,9 +7021,9 @@ ${card.key_points.join("\n")}`;
6696
7021
  `sections/${sectionId}/claims.jsonl`,
6697
7022
  (r) => ClaimSchema.parse(r)
6698
7023
  );
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");
7024
+ const claimsArtifact = relPath(packPath, join18(sectionDir, "claims.jsonl"));
7025
+ if (existsSync17(join18(sectionDir, "claims.jsonl"))) {
7026
+ const text = await readFile16(join18(sectionDir, "claims.jsonl"), "utf8");
6702
7027
  recordArtifact("claims_jsonl", claimsArtifact, text);
6703
7028
  }
6704
7029
  for (const claim of claims) {
@@ -6735,9 +7060,9 @@ ${claim.evidence_excerpt}`;
6735
7060
  `sections/${sectionId}/contradictions.jsonl`,
6736
7061
  (r) => ContradictionSchema.parse(r)
6737
7062
  );
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");
7063
+ const contradictionsArtifact = relPath(packPath, join18(sectionDir, "contradictions.jsonl"));
7064
+ if (existsSync17(join18(sectionDir, "contradictions.jsonl"))) {
7065
+ const text = await readFile16(join18(sectionDir, "contradictions.jsonl"), "utf8");
6741
7066
  recordArtifact("contradictions_jsonl", contradictionsArtifact, text);
6742
7067
  }
6743
7068
  for (const c of contradictions) {
@@ -6773,10 +7098,10 @@ ${c.severity}`;
6773
7098
  `audits/${sectionId}-findings.jsonl`,
6774
7099
  (r) => ReviewFindingSchema.parse(r)
6775
7100
  );
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");
7101
+ const findingsArtifact = relPath(packPath, join18(packPath, "audits", `${sectionId}-findings.jsonl`));
7102
+ const findingsAbs = join18(packPath, "audits", `${sectionId}-findings.jsonl`);
7103
+ if (existsSync17(findingsAbs)) {
7104
+ const text = await readFile16(findingsAbs, "utf8");
6780
7105
  recordArtifact("findings_jsonl", findingsArtifact, text);
6781
7106
  }
6782
7107
  const findingById = /* @__PURE__ */ new Map();
@@ -6813,10 +7138,10 @@ ${f.evidence}`;
6813
7138
  `sections/${sectionId}/claim-reviews.jsonl`,
6814
7139
  (r) => ClaimReviewSchema.parse(r)
6815
7140
  );
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");
7141
+ const reviewsArtifact = relPath(packPath, join18(sectionDir, "claim-reviews.jsonl"));
7142
+ const reviewsAbs = join18(sectionDir, "claim-reviews.jsonl");
7143
+ if (existsSync17(reviewsAbs)) {
7144
+ const text = await readFile16(reviewsAbs, "utf8");
6820
7145
  recordArtifact("claim_reviews_jsonl", reviewsArtifact, text);
6821
7146
  }
6822
7147
  for (const r of reviews) {
@@ -6841,8 +7166,8 @@ ${r.reason}`;
6841
7166
  }
6842
7167
  const gate2 = await readGateResult2(packPath, sectionId);
6843
7168
  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");
7169
+ const gateArtifact = relPath(packPath, join18(packPath, "audits", `${sectionId}-gate.json`));
7170
+ const gateText = await readFile16(join18(packPath, "audits", `${sectionId}-gate.json`), "utf8");
6846
7171
  recordArtifact("gate_json", gateArtifact, gateText);
6847
7172
  db.prepare(
6848
7173
  `INSERT OR REPLACE INTO gate_results(
@@ -6875,9 +7200,9 @@ ${gate2.next_actions.join("\n")}`;
6875
7200
  "evidence/fetch-log.jsonl",
6876
7201
  (r) => FetchReceiptSchema.parse(r)
6877
7202
  );
6878
- const fetchLogAbs = join17(packPath, "evidence", "fetch-log.jsonl");
6879
- if (existsSync16(fetchLogAbs)) {
6880
- const text = await readFile15(fetchLogAbs, "utf8");
7203
+ const fetchLogAbs = join18(packPath, "evidence", "fetch-log.jsonl");
7204
+ if (existsSync17(fetchLogAbs)) {
7205
+ const text = await readFile16(fetchLogAbs, "utf8");
6881
7206
  recordArtifact("fetch_log_jsonl", relPath(packPath, fetchLogAbs), text);
6882
7207
  }
6883
7208
  for (const receipt of allReceipts.filter((r) => r.section_id === sectionId)) {
@@ -6913,9 +7238,9 @@ async function indexPackAuditRollups(db, packPath, _now) {
6913
7238
  db.prepare(`DELETE FROM facts_fts WHERE record_type = ? AND record_id = ?`).run(f.recordType, f.recordId);
6914
7239
  }
6915
7240
  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");
7241
+ const abs = join18(packPath, "audits", f.filename);
7242
+ if (!existsSync17(abs)) continue;
7243
+ const text = await readFile16(abs, "utf8");
6919
7244
  insertFts.run(
6920
7245
  f.recordType,
6921
7246
  f.recordId,
@@ -6927,8 +7252,8 @@ async function indexPackAuditRollups(db, packPath, _now) {
6927
7252
  }
6928
7253
  async function build(options) {
6929
7254
  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")));
7255
+ if (!existsSync17(join18(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
7256
+ const research = ResearchYamlSchema.parse(yamlParse5(await readFile16(join18(packPath, "research.yaml"), "utf8")));
6932
7257
  const targets = options.sectionId ? [options.sectionId] : options.all ? research.sections.map((s) => s.id) : research.sections.map((s) => s.id);
6933
7258
  if (targets.length === 0) {
6934
7259
  throw new Error("No sections to index. Add at least one section to the pack.");
@@ -6985,7 +7310,7 @@ var init_build = __esm({
6985
7310
  });
6986
7311
 
6987
7312
  // src/indexer/query.ts
6988
- import { existsSync as existsSync17 } from "fs";
7313
+ import { existsSync as existsSync18 } from "fs";
6989
7314
  import { resolve as resolve13 } from "path";
6990
7315
  function escapeFtsTerm(term) {
6991
7316
  const trimmed = term.trim();
@@ -6996,8 +7321,8 @@ function escapeFtsTerm(term) {
6996
7321
  function query(options) {
6997
7322
  const packPath = options.packPath ? resolve13(options.packPath) : process.cwd();
6998
7323
  const dbPath = indexDbPath(packPath);
6999
- if (!existsSync17(packPath)) throw new PackNotFoundError(packPath);
7000
- if (!existsSync17(dbPath)) throw new IndexNotBuiltError(dbPath);
7324
+ if (!existsSync18(packPath)) throw new PackNotFoundError(packPath);
7325
+ if (!existsSync18(dbPath)) throw new IndexNotBuiltError(dbPath);
7001
7326
  const db = openIndexDb({ packPath, readonly: true });
7002
7327
  const limit = options.limit ?? 25;
7003
7328
  const ftsTerm = escapeFtsTerm(options.term);
@@ -7071,16 +7396,16 @@ var init_query = __esm({
7071
7396
  });
7072
7397
 
7073
7398
  // 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";
7399
+ import { existsSync as existsSync19 } from "fs";
7400
+ import { mkdir as mkdir14, writeFile as writeFile14 } from "fs/promises";
7401
+ import { dirname as dirname5, join as join19, resolve as resolve14 } from "path";
7077
7402
  async function exportRepoKnowledge(options) {
7078
7403
  const packPath = options.packPath ? resolve14(options.packPath) : process.cwd();
7079
- if (!existsSync18(packPath)) throw new PackNotFoundError(packPath);
7404
+ if (!existsSync19(packPath)) throw new PackNotFoundError(packPath);
7080
7405
  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 });
7406
+ if (!existsSync19(dbPath)) throw new IndexNotBuiltError(dbPath);
7407
+ const outPath = options.outPath ? resolve14(options.outPath) : join19(packPath, "evidence", "repo-knowledge", "research-os-facts.jsonl");
7408
+ await mkdir14(dirname5(outPath), { recursive: true });
7084
7409
  const db = openIndexDb({ packPath, readonly: true });
7085
7410
  const now = (/* @__PURE__ */ new Date()).toISOString();
7086
7411
  const facts = [];
@@ -7239,8 +7564,8 @@ async function syncRepoKnowledge(options) {
7239
7564
  };
7240
7565
  }
7241
7566
  const exportResult = await exportRepoKnowledge({ packPath });
7242
- const { readFile: readFile24 } = await import("fs/promises");
7243
- const text = await readFile24(exportResult.outPath, "utf8");
7567
+ const { readFile: readFile25 } = await import("fs/promises");
7568
+ const text = await readFile25(exportResult.outPath, "utf8");
7244
7569
  const facts = text.split(/\r?\n/).filter((l) => l.trim().length > 0).map((l) => JSON.parse(l));
7245
7570
  try {
7246
7571
  const r = await rk.ingestFacts({ facts, namespace: "research-os" });
@@ -7280,26 +7605,26 @@ var init_indexer = __esm({
7280
7605
  });
7281
7606
 
7282
7607
  // src/dispositions/schema.ts
7283
- import { z as z13 } from "zod";
7608
+ import { z as z14 } from "zod";
7284
7609
  var ClaimSynthesisDispositionStatusSchema, ClaimSynthesisDispositionSchema;
7285
7610
  var init_schema12 = __esm({
7286
7611
  "src/dispositions/schema.ts"() {
7287
7612
  "use strict";
7288
- ClaimSynthesisDispositionStatusSchema = z13.enum([
7613
+ ClaimSynthesisDispositionStatusSchema = z14.enum([
7289
7614
  "parked_not_for_synthesis",
7290
7615
  "preserved_for_human_note",
7291
7616
  "needs_human_review_excluded",
7292
7617
  "out_of_bounds_regression_fixture"
7293
7618
  ]);
7294
- ClaimSynthesisDispositionSchema = z13.object({
7295
- claim_id: z13.string().min(1),
7296
- section_id: z13.string().min(1),
7619
+ ClaimSynthesisDispositionSchema = z14.object({
7620
+ claim_id: z14.string().min(1),
7621
+ section_id: z14.string().min(1),
7297
7622
  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)
7623
+ reason: z14.string().min(4),
7624
+ decided_by: z14.string().min(1),
7625
+ authorized_by: z14.string().min(1),
7626
+ source: z14.string().min(1),
7627
+ created_at: z14.string().min(1)
7303
7628
  });
7304
7629
  }
7305
7630
  });
@@ -7630,91 +7955,91 @@ var init_derive = __esm({
7630
7955
  });
7631
7956
 
7632
7957
  // src/cowork/schema.ts
7633
- import { z as z14 } from "zod";
7958
+ import { z as z15 } from "zod";
7634
7959
  var HandoffModeSchema, IndexStatusSchema, ProvenanceSummarySchema, SectionStateSchema, WaiverEntrySchema, GateVerdictEntrySchema, ReviewDecisionCountSchema, CoworkHandoffPayloadSchema;
7635
7960
  var init_schema13 = __esm({
7636
7961
  "src/cowork/schema.ts"() {
7637
7962
  "use strict";
7638
- HandoffModeSchema = z14.enum([
7963
+ HandoffModeSchema = z15.enum([
7639
7964
  "repair_required",
7640
7965
  "synthesis_ready",
7641
7966
  "human_review_required"
7642
7967
  ]);
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()
7968
+ IndexStatusSchema = z15.enum(["present", "missing"]);
7969
+ ProvenanceSummarySchema = z15.object({
7970
+ accepted_count: z15.number().int().nonnegative(),
7971
+ rejected_count: z15.number().int().nonnegative(),
7972
+ triage_parked_count: z15.number().int().nonnegative(),
7973
+ needs_review_undispositioned_count: z15.number().int().nonnegative(),
7974
+ dispositioned_count: z15.number().int().nonnegative(),
7975
+ dispositioned_breakdown: z15.object({
7976
+ parked_not_for_synthesis: z15.number().int().nonnegative(),
7977
+ preserved_for_human_note: z15.number().int().nonnegative(),
7978
+ needs_human_review_excluded: z15.number().int().nonnegative(),
7979
+ out_of_bounds_regression_fixture: z15.number().int().nonnegative()
7655
7980
  }),
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(),
7981
+ active_repair_blockers: z15.number().int().nonnegative(),
7982
+ active_unresolved_contradictions: z15.number().int().nonnegative(),
7983
+ waivers_active: z15.array(z15.string()),
7984
+ overrides_applied_count: z15.number().int().nonnegative()
7985
+ });
7986
+ SectionStateSchema = z15.object({
7987
+ section_id: z15.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
7988
+ purpose: z15.string(),
7989
+ status: z15.string(),
7990
+ has_gate_run: z15.boolean(),
7991
+ has_review_run: z15.boolean(),
7992
+ gate_verdict: z15.string().nullable(),
7993
+ synthesis_eligible: z15.boolean(),
7994
+ accepted_claim_ids: z15.array(z15.string()),
7995
+ repair_claim_ids: z15.array(z15.string()),
7996
+ rejected_claim_ids: z15.array(z15.string()),
7997
+ dispositioned_claim_ids: z15.array(z15.string()).default([]),
7998
+ candidate_claims_total: z15.number().int().nonnegative(),
7999
+ unresolved_contradiction_ids: z15.array(z15.string()),
8000
+ blocking_reasons: z15.array(z15.string()),
8001
+ active_blockers: z15.array(z15.string()).default([]),
8002
+ blocking_contradictions_unresolved: z15.number().int().nonnegative(),
7678
8003
  provenance_summary: ProvenanceSummarySchema.optional()
7679
8004
  });
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(),
8005
+ WaiverEntrySchema = z15.object({
8006
+ scope: z15.enum(["pack", "gate"]),
8007
+ family: z15.string(),
8008
+ reason: z15.string(),
8009
+ compensating_controls: z15.array(z15.string()),
8010
+ applied_to: z15.string()
8011
+ });
8012
+ GateVerdictEntrySchema = z15.object({
8013
+ section_id: z15.string(),
8014
+ verdict: z15.string(),
8015
+ synthesis_eligible: z15.boolean()
8016
+ });
8017
+ ReviewDecisionCountSchema = z15.object({
8018
+ section_id: z15.string(),
8019
+ decision: z15.string(),
8020
+ count: z15.number().int().nonnegative()
8021
+ });
8022
+ CoworkHandoffPayloadSchema = z15.object({
8023
+ pack_id: z15.string(),
8024
+ pack_topic: z15.string(),
8025
+ generated_at: z15.string(),
7701
8026
  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()),
8027
+ synthesis_allowed: z15.boolean(),
8028
+ summary: z15.string(),
8029
+ sections: z15.array(SectionStateSchema),
8030
+ accepted_claim_ids: z15.array(z15.string()),
8031
+ repair_claim_ids: z15.array(z15.string()),
8032
+ blocked_claim_ids: z15.array(z15.string()),
8033
+ dispositioned_claim_ids: z15.array(z15.string()).default([]),
8034
+ unresolved_contradiction_ids: z15.array(z15.string()),
8035
+ waivers: z15.array(WaiverEntrySchema),
8036
+ gate_verdicts: z15.array(GateVerdictEntrySchema),
8037
+ review_decisions: z15.array(ReviewDecisionCountSchema),
8038
+ recommended_next_actions: z15.array(z15.string()),
8039
+ allowed_write_paths: z15.array(z15.string()),
8040
+ forbidden_actions: z15.array(z15.string()),
7716
8041
  index_status: IndexStatusSchema,
7717
- warnings: z14.array(z14.string())
8042
+ warnings: z15.array(z15.string())
7718
8043
  });
7719
8044
  }
7720
8045
  });
@@ -7893,19 +8218,19 @@ var init_markdown4 = __esm({
7893
8218
  });
7894
8219
 
7895
8220
  // 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";
8221
+ import { existsSync as existsSync20 } from "fs";
8222
+ import { mkdir as mkdir15, readFile as readFile17, writeFile as writeFile15 } from "fs/promises";
8223
+ import { join as join20, resolve as resolve16 } from "path";
7899
8224
  import { parse as yamlParse6 } from "yaml";
7900
8225
  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");
8226
+ const yamlPath = join20(packPath, "research.yaml");
8227
+ if (!existsSync20(yamlPath)) throw new PackNotFoundError(packPath);
8228
+ const text = await readFile17(yamlPath, "utf8");
7904
8229
  return ResearchYamlSchema.parse(yamlParse6(text));
7905
8230
  }
7906
8231
  async function readJsonl3(path, parse, warnings) {
7907
- if (!existsSync19(path)) return [];
7908
- const text = await readFile16(path, "utf8");
8232
+ if (!existsSync20(path)) return [];
8233
+ const text = await readFile17(path, "utf8");
7909
8234
  const out = [];
7910
8235
  for (const line of text.split(/\r?\n/)) {
7911
8236
  if (!line.trim()) continue;
@@ -7919,10 +8244,10 @@ async function readJsonl3(path, parse, warnings) {
7919
8244
  return out;
7920
8245
  }
7921
8246
  async function readGate(packPath, sectionId, warnings) {
7922
- const path = join19(packPath, "audits", `${sectionId}-gate.json`);
7923
- if (!existsSync19(path)) return null;
8247
+ const path = join20(packPath, "audits", `${sectionId}-gate.json`);
8248
+ if (!existsSync20(path)) return null;
7924
8249
  try {
7925
- const text = await readFile16(path, "utf8");
8250
+ const text = await readFile17(path, "utf8");
7926
8251
  return SectionGateResultSchema.parse(JSON.parse(text));
7927
8252
  } catch (err) {
7928
8253
  warnings.push(`malformed gate result at audits/${sectionId}-gate.json: ${err instanceof Error ? err.message : err}`);
@@ -7937,35 +8262,35 @@ async function handoff(options) {
7937
8262
  for (const section of research.sections) {
7938
8263
  const sid = section.id;
7939
8264
  const claims = await readJsonl3(
7940
- join19(packPath, "sections", sid, "claims.jsonl"),
8265
+ join20(packPath, "sections", sid, "claims.jsonl"),
7941
8266
  (r) => ClaimSchema.parse(r),
7942
8267
  warnings
7943
8268
  );
7944
8269
  const candidateClaims = claims.filter((c) => c.review_state === "candidate");
7945
8270
  const claimReviews = await readJsonl3(
7946
- join19(packPath, "sections", sid, "claim-reviews.jsonl"),
8271
+ join20(packPath, "sections", sid, "claim-reviews.jsonl"),
7947
8272
  (r) => ClaimReviewSchema.parse(r),
7948
8273
  warnings
7949
8274
  );
7950
8275
  const contradictions = await readJsonl3(
7951
- join19(packPath, "sections", sid, "contradictions.jsonl"),
8276
+ join20(packPath, "sections", sid, "contradictions.jsonl"),
7952
8277
  (r) => ContradictionSchema.parse(r),
7953
8278
  warnings
7954
8279
  );
7955
8280
  const resolutions = await readJsonl3(
7956
- join19(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
8281
+ join20(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
7957
8282
  (r) => ContradictionResolutionSchema.parse(r),
7958
8283
  warnings
7959
8284
  );
7960
8285
  const dispositions = await readJsonl3(
7961
- join19(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
8286
+ join20(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
7962
8287
  (r) => ClaimSynthesisDispositionSchema.parse(r),
7963
8288
  warnings
7964
8289
  );
7965
8290
  const gate2 = await readGate(packPath, sid, warnings);
7966
8291
  perSection.set(sid, { gate: gate2, candidateClaims, claimReviews, contradictions, resolutions, dispositions });
7967
8292
  }
7968
- const indexExists = existsSync19(indexDbPath(packPath));
8293
+ const indexExists = existsSync20(indexDbPath(packPath));
7969
8294
  if (!indexExists) {
7970
8295
  warnings.push(
7971
8296
  "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 +8306,10 @@ async function handoff(options) {
7981
8306
  warnings
7982
8307
  })
7983
8308
  );
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");
8309
+ const handoffsDir = join20(packPath, "handoffs");
8310
+ await mkdir15(handoffsDir, { recursive: true });
8311
+ const jsonPath = join20(handoffsDir, "cowork-handoff.json");
8312
+ const mdPath = join20(handoffsDir, "cowork-master.md");
7988
8313
  await writeFile15(jsonPath, JSON.stringify(payload, null, 2), "utf8");
7989
8314
  await writeFile15(mdPath, renderCoworkMaster(payload), "utf8");
7990
8315
  return {
@@ -8264,86 +8589,86 @@ var init_derive2 = __esm({
8264
8589
  });
8265
8590
 
8266
8591
  // src/synth/schema.ts
8267
- import { z as z15 } from "zod";
8592
+ import { z as z16 } from "zod";
8268
8593
  var SectionAcceptedSummarySchema, ClaimClusterSchema, SharedSourceSchema, ScopeOverlapSchema, CrossSectionContradictionRefSchema, WaiverDependencySchema, AllowedSynthesisInputSchema, ForbiddenInputSchema, CrossSectionMapSchema;
8269
8594
  var init_schema14 = __esm({
8270
8595
  "src/synth/schema.ts"() {
8271
8596
  "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"])
8597
+ SectionAcceptedSummarySchema = z16.object({
8598
+ section_id: z16.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
8599
+ purpose: z16.string(),
8600
+ status: z16.string(),
8601
+ accepted_claim_ids: z16.array(z16.string()),
8602
+ excluded_reason: z16.string().nullable()
8316
8603
  });
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()
8604
+ ClaimClusterSchema = z16.object({
8605
+ cluster_id: z16.string(),
8606
+ shared_source_ids: z16.array(z16.string()),
8607
+ member_claim_ids: z16.array(z16.string()).min(1),
8608
+ spans_sections: z16.array(z16.string())
8331
8609
  });
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)
8610
+ SharedSourceSchema = z16.object({
8611
+ source_id: z16.string(),
8612
+ publisher: z16.string().nullable(),
8613
+ source_type: z16.string(),
8614
+ used_by_claim_ids: z16.array(z16.string()),
8615
+ spans_sections: z16.array(z16.string())
8616
+ });
8617
+ ScopeOverlapSchema = z16.object({
8618
+ claim_a: z16.string(),
8619
+ claim_b: z16.string(),
8620
+ scope_a: z16.string().nullable(),
8621
+ scope_b: z16.string().nullable(),
8622
+ jaccard: z16.number().min(0).max(1),
8623
+ cross_section: z16.boolean(),
8624
+ warning: z16.string()
8625
+ });
8626
+ CrossSectionContradictionRefSchema = z16.object({
8627
+ contradiction_id: z16.string(),
8628
+ claim_ids: z16.array(z16.string()),
8629
+ sections: z16.array(z16.string()),
8630
+ type: z16.string(),
8631
+ severity: z16.string(),
8632
+ status: z16.string()
8633
+ });
8634
+ WaiverDependencySchema = z16.object({
8635
+ scope: z16.enum(["pack", "gate"]),
8636
+ family: z16.string(),
8637
+ reason: z16.string(),
8638
+ compensating_controls: z16.array(z16.string()),
8639
+ applied_to: z16.string(),
8640
+ must_disclose_in: z16.enum(["decision-brief.md", "final-report.md", "both"])
8641
+ });
8642
+ AllowedSynthesisInputSchema = z16.object({
8643
+ claim_id: z16.string(),
8644
+ section_id: z16.string(),
8645
+ artifact_path: z16.string(),
8646
+ asserts: z16.string(),
8647
+ scope: z16.string().nullable(),
8648
+ not: z16.string().nullable(),
8649
+ source_ids: z16.array(z16.string())
8650
+ });
8651
+ ForbiddenInputSchema = z16.object({
8652
+ claim_id: z16.string(),
8653
+ section_id: z16.string(),
8654
+ decision: z16.string(),
8655
+ reason: z16.string()
8656
+ });
8657
+ CrossSectionMapSchema = z16.object({
8658
+ pack_id: z16.string(),
8659
+ pack_topic: z16.string(),
8660
+ pack_decision: z16.string(),
8661
+ generated_at: z16.string(),
8662
+ accepted_claim_ids: z16.array(z16.string()),
8663
+ sections: z16.array(SectionAcceptedSummarySchema),
8664
+ claim_clusters: z16.array(ClaimClusterSchema),
8665
+ shared_sources: z16.array(SharedSourceSchema),
8666
+ scope_overlaps: z16.array(ScopeOverlapSchema),
8667
+ cross_section_contradictions: z16.array(CrossSectionContradictionRefSchema),
8668
+ waiver_dependencies: z16.array(WaiverDependencySchema),
8669
+ open_questions: z16.array(z16.string()),
8670
+ allowed_synthesis_inputs: z16.array(AllowedSynthesisInputSchema),
8671
+ forbidden_inputs: z16.array(ForbiddenInputSchema)
8347
8672
  });
8348
8673
  }
8349
8674
  });
@@ -8603,13 +8928,13 @@ var init_markdown5 = __esm({
8603
8928
  });
8604
8929
 
8605
8930
  // 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";
8931
+ import { existsSync as existsSync21 } from "fs";
8932
+ import { mkdir as mkdir16, readFile as readFile18, writeFile as writeFile16 } from "fs/promises";
8933
+ import { join as join21, resolve as resolve17 } from "path";
8609
8934
  import { parse as yamlParse7 } from "yaml";
8610
8935
  async function readJsonl4(path, parse) {
8611
- if (!existsSync20(path)) return [];
8612
- const text = await readFile17(path, "utf8");
8936
+ if (!existsSync21(path)) return [];
8937
+ const text = await readFile18(path, "utf8");
8613
8938
  const out = [];
8614
8939
  for (const line of text.split(/\r?\n/)) {
8615
8940
  if (!line.trim()) continue;
@@ -8618,27 +8943,27 @@ async function readJsonl4(path, parse) {
8618
8943
  return out;
8619
8944
  }
8620
8945
  async function readSourceCards5(packPath) {
8621
- const dir = join20(packPath, "evidence", "source-cards");
8622
- if (!existsSync20(dir)) return [];
8946
+ const dir = join21(packPath, "evidence", "source-cards");
8947
+ if (!existsSync21(dir)) return [];
8623
8948
  const { readdir: readdir4 } = await import("fs/promises");
8624
8949
  const entries = await readdir4(dir);
8625
8950
  const cards = [];
8626
8951
  for (const entry of entries) {
8627
8952
  if (!entry.endsWith(".json")) continue;
8628
- const text = await readFile17(join20(dir, entry), "utf8");
8953
+ const text = await readFile18(join21(dir, entry), "utf8");
8629
8954
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
8630
8955
  }
8631
8956
  return cards;
8632
8957
  }
8633
8958
  async function workspace(options) {
8634
8959
  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();
8960
+ const yamlPath = join21(packPath, "research.yaml");
8961
+ if (!existsSync21(yamlPath)) throw new PackNotFoundError(packPath);
8962
+ const research = ResearchYamlSchema.parse(yamlParse7(await readFile18(yamlPath, "utf8")));
8963
+ const handoffPath = join21(packPath, "handoffs", "cowork-handoff.json");
8964
+ if (!existsSync21(handoffPath)) throw new HandoffNotFoundError();
8640
8965
  const handoff2 = CoworkHandoffPayloadSchema.parse(
8641
- JSON.parse(await readFile17(handoffPath, "utf8"))
8966
+ JSON.parse(await readFile18(handoffPath, "utf8"))
8642
8967
  );
8643
8968
  if (handoff2.mode !== "synthesis_ready") {
8644
8969
  return {
@@ -8660,21 +8985,21 @@ async function workspace(options) {
8660
8985
  claimsBySection.set(
8661
8986
  section.id,
8662
8987
  await readJsonl4(
8663
- join20(packPath, "sections", section.id, "claims.jsonl"),
8988
+ join21(packPath, "sections", section.id, "claims.jsonl"),
8664
8989
  (r) => ClaimSchema.parse(r)
8665
8990
  )
8666
8991
  );
8667
8992
  reviewsBySection.set(
8668
8993
  section.id,
8669
8994
  await readJsonl4(
8670
- join20(packPath, "sections", section.id, "claim-reviews.jsonl"),
8995
+ join21(packPath, "sections", section.id, "claim-reviews.jsonl"),
8671
8996
  (r) => ClaimReviewSchema.parse(r)
8672
8997
  )
8673
8998
  );
8674
8999
  contradictionsBySection.set(
8675
9000
  section.id,
8676
9001
  await readJsonl4(
8677
- join20(packPath, "sections", section.id, "contradictions.jsonl"),
9002
+ join21(packPath, "sections", section.id, "contradictions.jsonl"),
8678
9003
  (r) => ContradictionSchema.parse(r)
8679
9004
  )
8680
9005
  );
@@ -8691,17 +9016,17 @@ async function workspace(options) {
8691
9016
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
8692
9017
  })
8693
9018
  );
8694
- const synthDir = join20(packPath, "synthesis");
8695
- await mkdir15(synthDir, { recursive: true });
9019
+ const synthDir = join21(packPath, "synthesis");
9020
+ await mkdir16(synthDir, { recursive: true });
8696
9021
  const filesWritten = [];
8697
9022
  const writeIfAbsent = async (relPath2, content) => {
8698
- const abs = join20(synthDir, relPath2);
8699
- if (existsSync20(abs)) return;
9023
+ const abs = join21(synthDir, relPath2);
9024
+ if (existsSync21(abs)) return;
8700
9025
  await writeFile16(abs, content, "utf8");
8701
9026
  filesWritten.push(abs);
8702
9027
  };
8703
9028
  const writeAlways = async (relPath2, content) => {
8704
- const abs = join20(synthDir, relPath2);
9029
+ const abs = join21(synthDir, relPath2);
8705
9030
  await writeFile16(abs, content, "utf8");
8706
9031
  filesWritten.push(abs);
8707
9032
  };
@@ -9511,179 +9836,179 @@ var init_aggregate = __esm({
9511
9836
  });
9512
9837
 
9513
9838
  // src/audit/schema.ts
9514
- import { z as z16 } from "zod";
9839
+ import { z as z17 } from "zod";
9515
9840
  var PackVerdictSchema, HandoffModeSchema2, OrphanClaimRowSchema, StaleSourceRowSchema, WeakSourceRowSchema, UnresolvedContradictionRowSchema, ScopeWideningRiskRowSchema, SourceDiversityGapRowSchema, SynthesisReadinessRowSchema, PackAuditPayloadSchema;
9516
9841
  var init_schema15 = __esm({
9517
9842
  "src/audit/schema.ts"() {
9518
9843
  "use strict";
9519
- PackVerdictSchema = z16.enum([
9844
+ PackVerdictSchema = z17.enum([
9520
9845
  "ready_for_synthesis",
9521
9846
  "repair_required",
9522
9847
  "human_review_required",
9523
9848
  "blocked"
9524
9849
  ]);
9525
- HandoffModeSchema2 = z16.enum([
9850
+ HandoffModeSchema2 = z17.enum([
9526
9851
  "repair_required",
9527
9852
  "synthesis_ready",
9528
9853
  "human_review_required",
9529
9854
  "unknown"
9530
9855
  ]);
9531
- OrphanClaimRowSchema = z16.object({
9532
- claim_id: z16.string(),
9533
- section_id: z16.string(),
9534
- reason: z16.enum([
9856
+ OrphanClaimRowSchema = z17.object({
9857
+ claim_id: z17.string(),
9858
+ section_id: z17.string(),
9859
+ reason: z17.enum([
9535
9860
  "missing_source_card",
9536
9861
  "missing_source_hash",
9537
9862
  "missing_evidence_excerpt",
9538
9863
  "unresolvable_source_id"
9539
9864
  ]),
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"])
9865
+ details: z17.string(),
9866
+ artifact_path: z17.string()
9867
+ });
9868
+ StaleSourceRowSchema = z17.object({
9869
+ source_id: z17.string(),
9870
+ section_id: z17.string(),
9871
+ publisher: z17.string().nullable(),
9872
+ reason: z17.enum(["too_old", "missing_date", "unparseable_date"]),
9873
+ details: z17.string(),
9874
+ artifact_path: z17.string(),
9875
+ policy: z17.object({
9876
+ required: z17.boolean(),
9877
+ max_source_age_months: z17.number().int().nullable(),
9878
+ stale_source_policy: z17.enum(["warn", "fail"])
9554
9879
  })
9555
9880
  });
9556
- WeakSourceRowSchema = z16.object({
9557
- reason: z16.enum([
9881
+ WeakSourceRowSchema = z17.object({
9882
+ reason: z17.enum([
9558
9883
  "source_cluster_monopoly",
9559
9884
  "low_independent_publishers",
9560
9885
  "missing_primary_source",
9561
9886
  "excessive_type_imbalance",
9562
9887
  "failed_fetches_reducing_floor"
9563
9888
  ]),
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([
9889
+ section_id: z17.string(),
9890
+ details: z17.string(),
9891
+ evidence_ids: z17.array(z17.string()),
9892
+ artifact_path: z17.string()
9893
+ });
9894
+ UnresolvedContradictionRowSchema = z17.object({
9895
+ contradiction_id: z17.string(),
9896
+ section_id: z17.string(),
9897
+ type: z17.string(),
9898
+ severity: z17.string(),
9899
+ status: z17.string(),
9900
+ claim_ids: z17.array(z17.string()),
9901
+ artifact_path: z17.string()
9902
+ });
9903
+ ScopeWideningRiskRowSchema = z17.object({
9904
+ reason: z17.enum([
9580
9905
  "overgeneralization_finding",
9581
9906
  "scope_null_in_use",
9582
9907
  "missing_not_flagged",
9583
9908
  "contextual_to_universal_risk"
9584
9909
  ]),
9585
- claim_id: z16.string(),
9586
- section_id: z16.string(),
9587
- details: z16.string(),
9588
- artifact_path: z16.string()
9910
+ claim_id: z17.string(),
9911
+ section_id: z17.string(),
9912
+ details: z17.string(),
9913
+ artifact_path: z17.string()
9589
9914
  });
9590
- SourceDiversityGapRowSchema = z16.object({
9591
- reason: z16.enum([
9915
+ SourceDiversityGapRowSchema = z17.object({
9916
+ reason: z17.enum([
9592
9917
  "section_publisher_monopoly",
9593
9918
  "low_section_publisher_count",
9594
9919
  "cross_section_publisher_overlap",
9595
9920
  "section_has_no_sources"
9596
9921
  ]),
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()),
9922
+ section_id: z17.string(),
9923
+ details: z17.string(),
9924
+ evidence_ids: z17.array(z17.string())
9925
+ });
9926
+ SynthesisReadinessRowSchema = z17.object({
9927
+ section_id: z17.string(),
9928
+ purpose: z17.string(),
9929
+ status: z17.string(),
9930
+ has_gate_run: z17.boolean(),
9931
+ has_review_run: z17.boolean(),
9932
+ gate_verdict: z17.string().nullable(),
9933
+ synthesis_eligible: z17.boolean(),
9934
+ candidate_claims: z17.number().int().nonnegative(),
9935
+ accepted_claims: z17.number().int().nonnegative(),
9936
+ repair_claims: z17.number().int().nonnegative(),
9937
+ rejected_claims: z17.number().int().nonnegative(),
9938
+ dispositioned_claims: z17.number().int().nonnegative(),
9939
+ blocking_reasons: z17.array(z17.string()),
9615
9940
  cowork_handoff_mode: HandoffModeSchema2,
9616
- workspace_allowed: z16.boolean()
9941
+ workspace_allowed: z17.boolean()
9617
9942
  });
9618
- PackAuditPayloadSchema = z16.object({
9619
- pack_id: z16.string(),
9620
- pack_topic: z16.string(),
9621
- generated_at: z16.string(),
9943
+ PackAuditPayloadSchema = z17.object({
9944
+ pack_id: z17.string(),
9945
+ pack_topic: z17.string(),
9946
+ generated_at: z17.string(),
9622
9947
  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()
9948
+ synthesis_allowed: z17.boolean(),
9949
+ section_summaries: z17.array(SynthesisReadinessRowSchema),
9950
+ claim_summary: z17.object({
9951
+ total: z17.number().int().nonnegative(),
9952
+ candidate: z17.number().int().nonnegative(),
9953
+ accepted_for_synthesis: z17.number().int().nonnegative(),
9954
+ rejected: z17.number().int().nonnegative(),
9955
+ needs_repair: z17.number().int().nonnegative(),
9956
+ dispositioned: z17.number().int().nonnegative(),
9957
+ no_review: z17.number().int().nonnegative(),
9958
+ with_evidence_excerpt: z17.number().int().nonnegative(),
9959
+ with_source_hashes: z17.number().int().nonnegative(),
9960
+ scope_null: z17.number().int().nonnegative(),
9961
+ not_null: z17.number().int().nonnegative(),
9962
+ orphans: z17.number().int().nonnegative()
9638
9963
  }),
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()
9964
+ source_summary: z17.object({
9965
+ total: z17.number().int().nonnegative(),
9966
+ primary: z17.number().int().nonnegative(),
9967
+ secondary: z17.number().int().nonnegative(),
9968
+ forum: z17.number().int().nonnegative(),
9969
+ benchmark: z17.number().int().nonnegative(),
9970
+ docs: z17.number().int().nonnegative(),
9971
+ unknown: z17.number().int().nonnegative(),
9972
+ independent_publishers: z17.number().int().nonnegative(),
9973
+ failed_fetches: z17.number().int().nonnegative(),
9974
+ sections_with_sources: z17.number().int().nonnegative(),
9975
+ sections_without_sources: z17.number().int().nonnegative()
9651
9976
  }),
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()
9977
+ contradiction_summary: z17.object({
9978
+ total: z17.number().int().nonnegative(),
9979
+ unresolved: z17.number().int().nonnegative(),
9980
+ blocking: z17.number().int().nonnegative(),
9981
+ reconciled: z17.number().int().nonnegative(),
9982
+ preserved_deliberately: z17.number().int().nonnegative(),
9983
+ rejected: z17.number().int().nonnegative(),
9984
+ by_type: z17.record(z17.string(), z17.number().int().nonnegative()),
9985
+ sections_with_clean_ledger: z17.number().int().nonnegative()
9661
9986
  }),
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()
9987
+ review_summary: z17.object({
9988
+ sections_with_review_run: z17.number().int().nonnegative(),
9989
+ sections_without_review_run: z17.number().int().nonnegative(),
9990
+ decision_counts: z17.record(z17.string(), z17.number().int().nonnegative()),
9991
+ blocking_findings: z17.number().int().nonnegative()
9667
9992
  }),
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())
9993
+ waiver_summary: z17.object({
9994
+ total: z17.number().int().nonnegative(),
9995
+ invalid: z17.number().int().nonnegative(),
9996
+ by_family: z17.record(z17.string(), z17.number().int().nonnegative())
9672
9997
  }),
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(),
9998
+ readiness_summary: z17.object({
9999
+ total_sections: z17.number().int().nonnegative(),
10000
+ ready_sections: z17.number().int().nonnegative(),
10001
+ repair_sections: z17.number().int().nonnegative(),
10002
+ blocked_sections: z17.number().int().nonnegative(),
10003
+ no_gate_sections: z17.number().int().nonnegative(),
10004
+ no_review_sections: z17.number().int().nonnegative(),
9680
10005
  cowork_handoff_mode: HandoffModeSchema2,
9681
- workspace_allowed: z16.boolean()
10006
+ workspace_allowed: z17.boolean()
9682
10007
  }),
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())
10008
+ audit_files: z17.array(z17.string()),
10009
+ blocking_reasons: z17.array(z17.string()),
10010
+ warnings: z17.array(z17.string()),
10011
+ next_actions: z17.array(z17.string())
9687
10012
  });
9688
10013
  }
9689
10014
  });
@@ -9905,13 +10230,13 @@ var init_markdown6 = __esm({
9905
10230
  });
9906
10231
 
9907
10232
  // 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";
10233
+ import { existsSync as existsSync22 } from "fs";
10234
+ import { mkdir as mkdir17, readFile as readFile19, writeFile as writeFile17 } from "fs/promises";
10235
+ import { join as join22, resolve as resolve18 } from "path";
9911
10236
  import { parse as yamlParse8 } from "yaml";
9912
10237
  async function readJsonl5(path, parse, warnings) {
9913
- if (!existsSync21(path)) return [];
9914
- const text = await readFile18(path, "utf8");
10238
+ if (!existsSync22(path)) return [];
10239
+ const text = await readFile19(path, "utf8");
9915
10240
  const out = [];
9916
10241
  for (const line of text.split(/\r?\n/)) {
9917
10242
  if (!line.trim()) continue;
@@ -9924,15 +10249,15 @@ async function readJsonl5(path, parse, warnings) {
9924
10249
  return out;
9925
10250
  }
9926
10251
  async function readSourceCards6(packPath, warnings) {
9927
- const dir = join21(packPath, "evidence", "source-cards");
9928
- if (!existsSync21(dir)) return [];
10252
+ const dir = join22(packPath, "evidence", "source-cards");
10253
+ if (!existsSync22(dir)) return [];
9929
10254
  const { readdir: readdir4 } = await import("fs/promises");
9930
10255
  const entries = await readdir4(dir);
9931
10256
  const cards = [];
9932
10257
  for (const entry of entries) {
9933
10258
  if (!entry.endsWith(".json")) continue;
9934
10259
  try {
9935
- const text = await readFile18(join21(dir, entry), "utf8");
10260
+ const text = await readFile19(join22(dir, entry), "utf8");
9936
10261
  cards.push(SourceCardSchema.parse(JSON.parse(text)));
9937
10262
  } catch (err) {
9938
10263
  warnings.push(`malformed source card ${entry}: ${err instanceof Error ? err.message : "parse error"}`);
@@ -9941,29 +10266,29 @@ async function readSourceCards6(packPath, warnings) {
9941
10266
  return cards;
9942
10267
  }
9943
10268
  async function readGate2(packPath, sectionId, warnings) {
9944
- const path = join21(packPath, "audits", `${sectionId}-gate.json`);
9945
- if (!existsSync21(path)) return null;
10269
+ const path = join22(packPath, "audits", `${sectionId}-gate.json`);
10270
+ if (!existsSync22(path)) return null;
9946
10271
  try {
9947
- return SectionGateResultSchema.parse(JSON.parse(await readFile18(path, "utf8")));
10272
+ return SectionGateResultSchema.parse(JSON.parse(await readFile19(path, "utf8")));
9948
10273
  } catch (err) {
9949
10274
  warnings.push(`malformed gate result for ${sectionId}: ${err instanceof Error ? err.message : "parse error"}`);
9950
10275
  return null;
9951
10276
  }
9952
10277
  }
9953
10278
  async function readHandoff(packPath, warnings) {
9954
- const path = join21(packPath, "handoffs", "cowork-handoff.json");
9955
- if (!existsSync21(path)) return null;
10279
+ const path = join22(packPath, "handoffs", "cowork-handoff.json");
10280
+ if (!existsSync22(path)) return null;
9956
10281
  try {
9957
- return CoworkHandoffPayloadSchema.parse(JSON.parse(await readFile18(path, "utf8")));
10282
+ return CoworkHandoffPayloadSchema.parse(JSON.parse(await readFile19(path, "utf8")));
9958
10283
  } catch (err) {
9959
10284
  warnings.push(`malformed handoff: ${err instanceof Error ? err.message : "parse error"}`);
9960
10285
  return null;
9961
10286
  }
9962
10287
  }
9963
10288
  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");
10289
+ const path = join22(packPath, "sections", sectionId, "sources.jsonl");
10290
+ if (!existsSync22(path)) return [];
10291
+ const text = await readFile19(path, "utf8");
9967
10292
  const ids = [];
9968
10293
  for (const line of text.split(/\r?\n/)) {
9969
10294
  if (!line.trim()) continue;
@@ -9977,41 +10302,41 @@ async function readSourceIdsForSection(packPath, sectionId) {
9977
10302
  }
9978
10303
  async function audit(options) {
9979
10304
  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")));
10305
+ const yamlPath = join22(packPath, "research.yaml");
10306
+ if (!existsSync22(yamlPath)) throw new PackNotFoundError(packPath);
10307
+ const research = ResearchYamlSchema.parse(yamlParse8(await readFile19(yamlPath, "utf8")));
9983
10308
  const warnings = [];
9984
10309
  const perSection = /* @__PURE__ */ new Map();
9985
10310
  for (const section of research.sections) {
9986
10311
  const sid = section.id;
9987
10312
  const claims = await readJsonl5(
9988
- join21(packPath, "sections", sid, "claims.jsonl"),
10313
+ join22(packPath, "sections", sid, "claims.jsonl"),
9989
10314
  (r) => ClaimSchema.parse(r),
9990
10315
  warnings
9991
10316
  );
9992
10317
  const candidateClaims = claims.filter((c) => c.review_state === "candidate");
9993
10318
  const claimReviews = await readJsonl5(
9994
- join21(packPath, "sections", sid, "claim-reviews.jsonl"),
10319
+ join22(packPath, "sections", sid, "claim-reviews.jsonl"),
9995
10320
  (r) => ClaimReviewSchema.parse(r),
9996
10321
  warnings
9997
10322
  );
9998
10323
  const contradictions = await readJsonl5(
9999
- join21(packPath, "sections", sid, "contradictions.jsonl"),
10324
+ join22(packPath, "sections", sid, "contradictions.jsonl"),
10000
10325
  (r) => ContradictionSchema.parse(r),
10001
10326
  warnings
10002
10327
  );
10003
10328
  const resolutions = await readJsonl5(
10004
- join21(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
10329
+ join22(packPath, "sections", sid, "contradiction-resolutions.jsonl"),
10005
10330
  (r) => ContradictionResolutionSchema.parse(r),
10006
10331
  warnings
10007
10332
  );
10008
10333
  const dispositions = await readJsonl5(
10009
- join21(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
10334
+ join22(packPath, "sections", sid, "claim-synthesis-dispositions.jsonl"),
10010
10335
  (r) => ClaimSynthesisDispositionSchema.parse(r),
10011
10336
  warnings
10012
10337
  );
10013
10338
  const findings = await readJsonl5(
10014
- join21(packPath, "audits", `${sid}-findings.jsonl`),
10339
+ join22(packPath, "audits", `${sid}-findings.jsonl`),
10015
10340
  (r) => ReviewFindingSchema.parse(r),
10016
10341
  warnings
10017
10342
  );
@@ -10031,7 +10356,7 @@ async function audit(options) {
10031
10356
  }
10032
10357
  const sources = await readSourceCards6(packPath, warnings);
10033
10358
  const receipts = await readJsonl5(
10034
- join21(packPath, "evidence", "fetch-log.jsonl"),
10359
+ join22(packPath, "evidence", "fetch-log.jsonl"),
10035
10360
  (r) => FetchReceiptSchema.parse(r),
10036
10361
  warnings
10037
10362
  );
@@ -10047,11 +10372,11 @@ async function audit(options) {
10047
10372
  warnings
10048
10373
  });
10049
10374
  const payload = PackAuditPayloadSchema.parse(result.payload);
10050
- const auditsDir = join21(packPath, "audits");
10051
- await mkdir16(auditsDir, { recursive: true });
10375
+ const auditsDir = join22(packPath, "audits");
10376
+ await mkdir17(auditsDir, { recursive: true });
10052
10377
  const filesWritten = [];
10053
10378
  const writeFileAndTrack = async (rel, content) => {
10054
- const abs = join21(packPath, rel);
10379
+ const abs = join22(packPath, rel);
10055
10380
  await writeFile17(abs, content, "utf8");
10056
10381
  filesWritten.push(abs);
10057
10382
  };
@@ -10135,18 +10460,18 @@ var init_audit = __esm({
10135
10460
  });
10136
10461
 
10137
10462
  // src/freeze/checks.ts
10138
- import { existsSync as existsSync22 } from "fs";
10139
- import { readFile as readFile19, stat as stat2 } from "fs/promises";
10463
+ import { existsSync as existsSync23 } from "fs";
10464
+ import { readFile as readFile20, stat as stat2 } from "fs/promises";
10140
10465
  import { createHash as createHash8 } from "crypto";
10141
- import { join as join22 } from "path";
10466
+ import { join as join23 } from "path";
10142
10467
  async function fileSha2562(absPath) {
10143
- const buf = await readFile19(absPath);
10468
+ const buf = await readFile20(absPath);
10144
10469
  const sha = createHash8("sha256").update(buf).digest("hex");
10145
10470
  return { path: absPath, sha256: sha, bytes: buf.byteLength };
10146
10471
  }
10147
10472
  async function hashArtifact(ctx, rel) {
10148
- const abs = join22(ctx.packPath, rel);
10149
- if (!existsSync22(abs)) {
10473
+ const abs = join23(ctx.packPath, rel);
10474
+ if (!existsSync23(abs)) {
10150
10475
  ctx.missingArtifacts.push(rel);
10151
10476
  return null;
10152
10477
  }
@@ -10357,88 +10682,88 @@ var init_markdown7 = __esm({
10357
10682
  });
10358
10683
 
10359
10684
  // src/freeze/schema.ts
10360
- import { z as z17 } from "zod";
10685
+ import { z as z18 } from "zod";
10361
10686
  var ArtifactHashSchema, IntegrityCheckSchema, FreezeReceiptPayloadSchema, FreezeRefusalPayloadSchema;
10362
10687
  var init_schema16 = __esm({
10363
10688
  "src/freeze/schema.ts"() {
10364
10689
  "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())
10690
+ ArtifactHashSchema = z18.object({
10691
+ path: z18.string(),
10692
+ sha256: z18.string().regex(/^[a-f0-9]{64}$/),
10693
+ bytes: z18.number().int().nonnegative()
10694
+ });
10695
+ IntegrityCheckSchema = z18.object({
10696
+ name: z18.string().min(1),
10697
+ passed: z18.boolean(),
10698
+ detail: z18.string()
10699
+ });
10700
+ FreezeReceiptPayloadSchema = z18.object({
10701
+ pack_id: z18.string(),
10702
+ pack_topic: z18.string(),
10703
+ frozen_at: z18.string(),
10704
+ verdict: z18.literal("frozen"),
10705
+ pack_audit_hash: z18.string().regex(/^[a-f0-9]{64}$/),
10706
+ handoff_hash: z18.string().regex(/^[a-f0-9]{64}$/),
10707
+ synthesis_hashes: z18.array(ArtifactHashSchema),
10708
+ canonical_artifact_hashes: z18.array(ArtifactHashSchema),
10709
+ accepted_claim_ids: z18.array(z18.string()),
10710
+ cited_claim_ids: z18.array(z18.string()),
10711
+ uncited_accepted_claim_ids: z18.array(z18.string()),
10712
+ unresolved_contradictions: z18.array(
10713
+ z18.object({
10714
+ contradiction_id: z18.string(),
10715
+ section_id: z18.string(),
10716
+ type: z18.string(),
10717
+ severity: z18.string(),
10718
+ status: z18.string(),
10719
+ disclosed_in: z18.array(z18.string())
10395
10720
  })
10396
10721
  ),
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())
10722
+ waivers_disclosed: z18.array(
10723
+ z18.object({
10724
+ family: z18.string(),
10725
+ applied_to: z18.string(),
10726
+ reason: z18.string(),
10727
+ compensating_controls: z18.array(z18.string()),
10728
+ disclosed_in: z18.array(z18.string())
10404
10729
  })
10405
10730
  ),
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()
10731
+ sections: z18.array(
10732
+ z18.object({
10733
+ section_id: z18.string(),
10734
+ status: z18.string(),
10735
+ accepted_claims: z18.number().int().nonnegative(),
10736
+ sources: z18.number().int().nonnegative(),
10737
+ contradictions: z18.number().int().nonnegative()
10413
10738
  })
10414
10739
  ),
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)
10740
+ source_count: z18.number().int().nonnegative(),
10741
+ claim_count: z18.number().int().nonnegative(),
10742
+ contradiction_count: z18.number().int().nonnegative(),
10743
+ review_finding_count: z18.number().int().nonnegative(),
10744
+ gate_result_count: z18.number().int().nonnegative(),
10745
+ integrity_checks: z18.array(IntegrityCheckSchema)
10746
+ });
10747
+ FreezeRefusalPayloadSchema = z18.object({
10748
+ pack_id: z18.string(),
10749
+ pack_topic: z18.string(),
10750
+ checked_at: z18.string(),
10751
+ verdict: z18.literal("refused"),
10752
+ reasons: z18.array(z18.string()),
10753
+ blocking_reasons: z18.array(z18.string()),
10754
+ missing_artifacts: z18.array(z18.string()),
10755
+ invalid_artifacts: z18.array(z18.object({ path: z18.string(), error: z18.string() })),
10756
+ next_actions: z18.array(z18.string()),
10757
+ would_freeze: z18.literal(false)
10433
10758
  });
10434
10759
  }
10435
10760
  });
10436
10761
 
10437
10762
  // 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";
10763
+ import { existsSync as existsSync24 } from "fs";
10764
+ import { mkdir as mkdir18, readFile as readFile21, readdir as readdir2, unlink, writeFile as writeFile18 } from "fs/promises";
10440
10765
  import { createHash as createHash9 } from "crypto";
10441
- import { join as join23, resolve as resolve19 } from "path";
10766
+ import { join as join24, resolve as resolve19 } from "path";
10442
10767
  import { parse as yamlParse9, stringify as yamlStringify6 } from "yaml";
10443
10768
  function packId3(research) {
10444
10769
  const fingerprint = `${research.topic}|${research.created_at}`;
@@ -10457,10 +10782,10 @@ function noteRefusal(ctx, reason, blocking = true) {
10457
10782
  if (blocking) ctx.blockingReasons.push(reason);
10458
10783
  }
10459
10784
  async function tryReadJson(packPath, rel, parse, invalid) {
10460
- const abs = join23(packPath, rel);
10461
- if (!existsSync23(abs)) return null;
10785
+ const abs = join24(packPath, rel);
10786
+ if (!existsSync24(abs)) return null;
10462
10787
  try {
10463
- return parse(JSON.parse(await readFile20(abs, "utf8")));
10788
+ return parse(JSON.parse(await readFile21(abs, "utf8")));
10464
10789
  } catch (err) {
10465
10790
  invalid.push({ path: rel, error: err instanceof Error ? err.message : "parse error" });
10466
10791
  return null;
@@ -10468,8 +10793,8 @@ async function tryReadJson(packPath, rel, parse, invalid) {
10468
10793
  }
10469
10794
  async function freeze(options) {
10470
10795
  const packPath = options.packPath ? resolve19(options.packPath) : process.cwd();
10471
- const yamlPath = join23(packPath, "research.yaml");
10472
- if (!existsSync23(yamlPath)) throw new PackNotFoundError(packPath);
10796
+ const yamlPath = join24(packPath, "research.yaml");
10797
+ if (!existsSync24(yamlPath)) throw new PackNotFoundError(packPath);
10473
10798
  const refusal = {
10474
10799
  reasons: [],
10475
10800
  blockingReasons: [],
@@ -10478,7 +10803,7 @@ async function freeze(options) {
10478
10803
  };
10479
10804
  let research;
10480
10805
  try {
10481
- research = ResearchYamlSchema.parse(yamlParse9(await readFile20(yamlPath, "utf8")));
10806
+ research = ResearchYamlSchema.parse(yamlParse9(await readFile21(yamlPath, "utf8")));
10482
10807
  } catch (err) {
10483
10808
  refusal.invalidArtifacts.push({ path: "research.yaml", error: err instanceof Error ? err.message : "parse error" });
10484
10809
  return writeRefusal({
@@ -10490,13 +10815,13 @@ async function freeze(options) {
10490
10815
  }
10491
10816
  const pid = packId3(research);
10492
10817
  for (const rel of REQUIRED_PACK_ARTIFACTS) {
10493
- if (!existsSync23(join23(packPath, rel))) {
10818
+ if (!existsSync24(join24(packPath, rel))) {
10494
10819
  refusal.missingArtifacts.push(rel);
10495
10820
  noteRefusal(refusal, `Required artifact missing: ${rel}.`);
10496
10821
  }
10497
10822
  }
10498
10823
  for (const rel of SYNTHESIS_FILES) {
10499
- if (!existsSync23(join23(packPath, rel))) {
10824
+ if (!existsSync24(join24(packPath, rel))) {
10500
10825
  refusal.missingArtifacts.push(rel);
10501
10826
  noteRefusal(refusal, `Synthesis artifact missing: ${rel}. Run \`research-os synth workspace\` after the pack reaches synthesis_ready.`);
10502
10827
  }
@@ -10530,22 +10855,22 @@ async function freeze(options) {
10530
10855
  if (handoff2 && handoff2.mode !== "synthesis_ready") {
10531
10856
  noteRefusal(refusal, `Cowork handoff mode is "${handoff2.mode}", not "synthesis_ready".`);
10532
10857
  }
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");
10858
+ const finalReportAbs = join24(packPath, "synthesis/final-report.md");
10859
+ const decisionBriefAbs = join24(packPath, "synthesis/decision-brief.md");
10860
+ const workingReportAbs = join24(packPath, "synthesis/working-report.md");
10536
10861
  let finalReportText = "";
10537
10862
  let decisionBriefText = "";
10538
10863
  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");
10864
+ if (existsSync24(finalReportAbs)) finalReportText = await readFile21(finalReportAbs, "utf8");
10865
+ if (existsSync24(decisionBriefAbs)) decisionBriefText = await readFile21(decisionBriefAbs, "utf8");
10866
+ if (existsSync24(workingReportAbs)) workingReportText = await readFile21(workingReportAbs, "utf8");
10542
10867
  const livePackClaimIds = /* @__PURE__ */ new Set();
10543
10868
  const liveLatestDecisionByClaim = /* @__PURE__ */ new Map();
10544
10869
  const liveLatestCreatedAtByClaim = /* @__PURE__ */ new Map();
10545
10870
  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");
10871
+ const claimsFile = join24(packPath, "sections", section.id, "claims.jsonl");
10872
+ if (existsSync24(claimsFile)) {
10873
+ const text = await readFile21(claimsFile, "utf8");
10549
10874
  for (const line of text.split(/\r?\n/)) {
10550
10875
  if (!line.trim()) continue;
10551
10876
  try {
@@ -10559,9 +10884,9 @@ async function freeze(options) {
10559
10884
  }
10560
10885
  }
10561
10886
  }
10562
- const reviewsFile = join23(packPath, "sections", section.id, "claim-reviews.jsonl");
10563
- if (existsSync23(reviewsFile)) {
10564
- const text = await readFile20(reviewsFile, "utf8");
10887
+ const reviewsFile = join24(packPath, "sections", section.id, "claim-reviews.jsonl");
10888
+ if (existsSync24(reviewsFile)) {
10889
+ const text = await readFile21(reviewsFile, "utf8");
10565
10890
  const reviews = [];
10566
10891
  for (const line of text.split(/\r?\n/)) {
10567
10892
  if (!line.trim()) continue;
@@ -10603,7 +10928,7 @@ async function freeze(options) {
10603
10928
  const unknownCitations = allCited.filter((c) => isWellFormedClaimId(c) && !allClaimIds.has(c));
10604
10929
  const repairCitations = allCited.filter((c) => repairOrRejected.has(c));
10605
10930
  const uncitedAccepted = acceptedClaimIds.filter((c) => !allCitedSet.has(c));
10606
- if (existsSync23(finalReportAbs) && finalReportText.trim().length > 0) {
10931
+ if (existsSync24(finalReportAbs) && finalReportText.trim().length > 0) {
10607
10932
  if (citationsInFinal.length === 0 && acceptedClaimIds.length > 0) {
10608
10933
  noteRefusal(refusal, "final-report.md contains no [claim:...] citations even though accepted claims exist.");
10609
10934
  }
@@ -10655,7 +10980,7 @@ async function freeze(options) {
10655
10980
  }
10656
10981
  }
10657
10982
  for (const section of research.sections) {
10658
- if (!existsSync23(join23(packPath, "audits", `${section.id}-gate.json`))) {
10983
+ if (!existsSync24(join24(packPath, "audits", `${section.id}-gate.json`))) {
10659
10984
  noteRefusal(refusal, `Section ${section.id} has no gate result on file.`);
10660
10985
  refusal.missingArtifacts.push(`audits/${section.id}-gate.json`);
10661
10986
  }
@@ -10695,16 +11020,16 @@ async function freeze(options) {
10695
11020
  }
10696
11021
  canonicalPaths.push("evidence/fetch-log.jsonl");
10697
11022
  canonicalPaths.push("evidence/citation-ledger.jsonl");
10698
- if (existsSync23(join23(packPath, "evidence", "source-cards"))) {
10699
- const entries = await readdir2(join23(packPath, "evidence", "source-cards"));
11023
+ if (existsSync24(join24(packPath, "evidence", "source-cards"))) {
11024
+ const entries = await readdir2(join24(packPath, "evidence", "source-cards"));
10700
11025
  for (const e of entries) {
10701
11026
  if (e.endsWith(".json")) canonicalPaths.push(`evidence/source-cards/${e}`);
10702
11027
  }
10703
11028
  }
10704
11029
  const canonicalHashes = [];
10705
11030
  for (const rel of canonicalPaths) {
10706
- const abs = join23(packPath, rel);
10707
- if (!existsSync23(abs)) continue;
11031
+ const abs = join24(packPath, rel);
11032
+ if (!existsSync24(abs)) continue;
10708
11033
  const h = await hashArtifact(ctx, rel);
10709
11034
  if (h) canonicalHashes.push(h);
10710
11035
  }
@@ -10758,7 +11083,7 @@ async function freeze(options) {
10758
11083
  claim_count: acceptedClaimIds.length,
10759
11084
  contradiction_count: unresolvedContradictionRefs.length,
10760
11085
  review_finding_count: 0,
10761
- gate_result_count: research.sections.filter((s) => existsSync23(join23(packPath, "audits", `${s.id}-gate.json`))).length,
11086
+ gate_result_count: research.sections.filter((s) => existsSync24(join24(packPath, "audits", `${s.id}-gate.json`))).length,
10762
11087
  integrity_checks: checks
10763
11088
  });
10764
11089
  const frozenResearch = {
@@ -10767,14 +11092,14 @@ async function freeze(options) {
10767
11092
  sections: research.sections.map((s) => ({ ...s, status: "frozen" }))
10768
11093
  };
10769
11094
  await writeFile18(yamlPath, yamlStringify6(frozenResearch, { lineWidth: 0 }), "utf8");
10770
- const auditsDir = join23(packPath, "audits");
10771
- await mkdir17(auditsDir, { recursive: true });
11095
+ const auditsDir = join24(packPath, "audits");
11096
+ await mkdir18(auditsDir, { recursive: true });
10772
11097
  for (const stale of ["freeze-refusal.json", "freeze-refusal.md"]) {
10773
- const p = join23(auditsDir, stale);
10774
- if (existsSync23(p)) await unlink(p);
11098
+ const p = join24(auditsDir, stale);
11099
+ if (existsSync24(p)) await unlink(p);
10775
11100
  }
10776
- const jsonPath = join23(auditsDir, "freeze-receipt.json");
10777
- const mdPath = join23(auditsDir, "freeze-receipt.md");
11101
+ const jsonPath = join24(auditsDir, "freeze-receipt.json");
11102
+ const mdPath = join24(auditsDir, "freeze-receipt.md");
10778
11103
  await writeFile18(jsonPath, JSON.stringify(receipt, null, 2), "utf8");
10779
11104
  await writeFile18(mdPath, renderFreezeReceiptMarkdown(receipt), "utf8");
10780
11105
  return {
@@ -10804,14 +11129,14 @@ async function writeRefusal(args) {
10804
11129
  next_actions: nextActions,
10805
11130
  would_freeze: false
10806
11131
  });
10807
- const auditsDir = join23(args.packPath, "audits");
10808
- await mkdir17(auditsDir, { recursive: true });
11132
+ const auditsDir = join24(args.packPath, "audits");
11133
+ await mkdir18(auditsDir, { recursive: true });
10809
11134
  for (const stale of ["freeze-receipt.json", "freeze-receipt.md"]) {
10810
- const p = join23(auditsDir, stale);
10811
- if (existsSync23(p)) await unlink(p);
11135
+ const p = join24(auditsDir, stale);
11136
+ if (existsSync24(p)) await unlink(p);
10812
11137
  }
10813
- const jsonPath = join23(auditsDir, "freeze-refusal.json");
10814
- const mdPath = join23(auditsDir, "freeze-refusal.md");
11138
+ const jsonPath = join24(auditsDir, "freeze-refusal.json");
11139
+ const mdPath = join24(auditsDir, "freeze-refusal.md");
10815
11140
  await writeFile18(jsonPath, JSON.stringify(payload, null, 2), "utf8");
10816
11141
  await writeFile18(mdPath, renderFreezeRefusalMarkdown(payload), "utf8");
10817
11142
  return {
@@ -10903,43 +11228,43 @@ var init_freeze = __esm({
10903
11228
  });
10904
11229
 
10905
11230
  // src/invalidate/schema.ts
10906
- import { z as z18 } from "zod";
11231
+ import { z as z19 } from "zod";
10907
11232
  var ArchivedArtifactSchema, SectionStatusChangeSchema, InvalidationReceiptSchema;
10908
11233
  var init_schema17 = __esm({
10909
11234
  "src/invalidate/schema.ts"() {
10910
11235
  "use strict";
10911
- ArchivedArtifactSchema = z18.object({
10912
- src: z18.string(),
11236
+ ArchivedArtifactSchema = z19.object({
11237
+ src: z19.string(),
10913
11238
  // path relative to packPath, before archival
10914
- dst: z18.string()
11239
+ dst: z19.string()
10915
11240
  // path relative to packPath, after archival
10916
11241
  });
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()
11242
+ SectionStatusChangeSchema = z19.object({
11243
+ section_id: z19.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
11244
+ before: z19.string(),
11245
+ after: z19.string()
11246
+ });
11247
+ InvalidationReceiptSchema = z19.object({
11248
+ receipt_id: z19.string().regex(/^inv_[0-9]+_[a-z0-9-]+$/),
11249
+ contract_label: z19.string().min(1),
11250
+ superseded_contract: z19.string().min(1).nullable(),
11251
+ new_contract: z19.string().min(1),
11252
+ reason: z19.string().min(8),
11253
+ invalidated_at: z19.string(),
11254
+ research_os_version: z19.string(),
11255
+ affected_sections: z19.array(z19.string().regex(/^[0-9]{2}-[a-z0-9-]+$/)),
11256
+ archived_artifacts: z19.array(ArchivedArtifactSchema),
11257
+ section_status_changes: z19.array(SectionStatusChangeSchema),
11258
+ frozen_at_cleared: z19.boolean(),
11259
+ notes: z19.string().nullable()
10935
11260
  });
10936
11261
  }
10937
11262
  });
10938
11263
 
10939
11264
  // 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";
11265
+ import { existsSync as existsSync25 } from "fs";
11266
+ import { mkdir as mkdir19, readFile as readFile22, readdir as readdir3, rename, writeFile as writeFile19 } from "fs/promises";
11267
+ import { dirname as dirname6, join as join25, posix, relative as relative3, resolve as resolve20, sep } from "path";
10943
11268
  import { parse as yamlParse10, stringify as yamlStringify7 } from "yaml";
10944
11269
  function posixify(p) {
10945
11270
  return p.split(sep).join("/");
@@ -10957,15 +11282,15 @@ function isLegacyClaimLine(line) {
10957
11282
  }
10958
11283
  }
10959
11284
  async function detectLegacyClaimSections(packPath) {
10960
- const sectionsDir = join24(packPath, "sections");
10961
- if (!existsSync24(sectionsDir)) return [];
11285
+ const sectionsDir = join25(packPath, "sections");
11286
+ if (!existsSync25(sectionsDir)) return [];
10962
11287
  const entries = await readdir3(sectionsDir, { withFileTypes: true });
10963
11288
  const affected = [];
10964
11289
  for (const entry of entries) {
10965
11290
  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");
11291
+ const claimsPath = join25(sectionsDir, entry.name, "claims.jsonl");
11292
+ if (!existsSync25(claimsPath)) continue;
11293
+ const text = await readFile22(claimsPath, "utf8");
10969
11294
  let hasLegacy = false;
10970
11295
  for (const line of text.split(/\r?\n/)) {
10971
11296
  if (isLegacyClaimLine(line)) {
@@ -10978,16 +11303,16 @@ async function detectLegacyClaimSections(packPath) {
10978
11303
  return affected.sort();
10979
11304
  }
10980
11305
  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 });
11306
+ const src = join25(packPath, rel);
11307
+ if (!existsSync25(src)) return null;
11308
+ const dst = join25(packPath, archiveRel, rel);
11309
+ await mkdir19(dirname6(dst), { recursive: true });
10985
11310
  await rename(src, dst);
10986
- return { src: posixify(rel), dst: posixify(join24(archiveRel, rel)) };
11311
+ return { src: posixify(rel), dst: posixify(join25(archiveRel, rel)) };
10987
11312
  }
10988
11313
  async function moveDirContentsIfExists(packPath, archiveRel, relDir, skipPrefix) {
10989
- const fullDir = join24(packPath, relDir);
10990
- if (!existsSync24(fullDir)) return [];
11314
+ const fullDir = join25(packPath, relDir);
11315
+ if (!existsSync25(fullDir)) return [];
10991
11316
  const entries = await readdir3(fullDir, { withFileTypes: true });
10992
11317
  const moved = [];
10993
11318
  for (const entry of entries) {
@@ -11065,8 +11390,8 @@ function buildReceiptMarkdown(receipt) {
11065
11390
  }
11066
11391
  async function invalidateExtraction(options) {
11067
11392
  const packPath = options.packPath ? resolve20(options.packPath) : process.cwd();
11068
- const yamlPath = join24(packPath, "research.yaml");
11069
- if (!existsSync24(yamlPath)) throw new PackNotFoundError(packPath);
11393
+ const yamlPath = join25(packPath, "research.yaml");
11394
+ if (!existsSync25(yamlPath)) throw new PackNotFoundError(packPath);
11070
11395
  const reason = options.reason.trim();
11071
11396
  if (reason.length < 8) {
11072
11397
  throw new Error("invalidation reason must be at least 8 characters");
@@ -11084,13 +11409,13 @@ async function invalidateExtraction(options) {
11084
11409
  const stampPath = stampIso.replace(/[:]/g, "").replace(/\.\d+Z$/, "Z");
11085
11410
  const archiveRel = posix.join("audits", "legacy", label, stampPath);
11086
11411
  const affectedSections = await detectLegacyClaimSections(packPath);
11087
- const research = ResearchYamlSchema.parse(yamlParse10(await readFile21(yamlPath, "utf8")));
11412
+ const research = ResearchYamlSchema.parse(yamlParse10(await readFile22(yamlPath, "utf8")));
11088
11413
  const packLevelDirs = ["handoffs", "synthesis", "audits"];
11089
11414
  let probeArtifactsExist = affectedSections.length > 0;
11090
11415
  if (!probeArtifactsExist) {
11091
11416
  for (const d of packLevelDirs) {
11092
- const full = join24(packPath, d);
11093
- if (!existsSync24(full)) continue;
11417
+ const full = join25(packPath, d);
11418
+ if (!existsSync25(full)) continue;
11094
11419
  const entries = await readdir3(full, { withFileTypes: true });
11095
11420
  const meaningful = entries.some(
11096
11421
  (e) => !(d === "audits" && e.isDirectory() && e.name === "legacy")
@@ -11112,7 +11437,7 @@ async function invalidateExtraction(options) {
11112
11437
  message: "No legacy artifacts found. Pack is on the current extraction contract."
11113
11438
  };
11114
11439
  }
11115
- await mkdir18(join24(packPath, archiveRel), { recursive: true });
11440
+ await mkdir19(join25(packPath, archiveRel), { recursive: true });
11116
11441
  const archived = [];
11117
11442
  for (const sectionId of affectedSections) {
11118
11443
  for (const filename of [
@@ -11161,14 +11486,14 @@ async function invalidateExtraction(options) {
11161
11486
  frozen_at_cleared: frozenAtCleared,
11162
11487
  notes: options.notes ?? null
11163
11488
  });
11164
- const receiptDir = join24(packPath, archiveRel);
11489
+ const receiptDir = join25(packPath, archiveRel);
11165
11490
  await writeFile19(
11166
- join24(receiptDir, "invalidation.json"),
11491
+ join25(receiptDir, "invalidation.json"),
11167
11492
  JSON.stringify(receipt, null, 2),
11168
11493
  "utf8"
11169
11494
  );
11170
11495
  await writeFile19(
11171
- join24(receiptDir, "invalidation.md"),
11496
+ join25(receiptDir, "invalidation.md"),
11172
11497
  buildReceiptMarkdown(receipt),
11173
11498
  "utf8"
11174
11499
  );
@@ -11194,20 +11519,20 @@ var init_run9 = __esm({
11194
11519
  });
11195
11520
 
11196
11521
  // 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";
11522
+ import { existsSync as existsSync26 } from "fs";
11523
+ import { mkdir as mkdir20, rename as rename2, writeFile as writeFile20 } from "fs/promises";
11524
+ import { dirname as dirname7, join as join26, posix as posix2, relative as relative4, resolve as resolve21, sep as sep2 } from "path";
11525
+ import { z as z20 } from "zod";
11201
11526
  function posixify2(p) {
11202
11527
  return p.split(sep2).join("/");
11203
11528
  }
11204
11529
  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 });
11530
+ const src = join26(packPath, rel);
11531
+ if (!existsSync26(src)) return null;
11532
+ const dst = join26(packPath, archiveRel, rel);
11533
+ await mkdir20(dirname7(dst), { recursive: true });
11209
11534
  await rename2(src, dst);
11210
- return { src: posixify2(rel), dst: posixify2(join25(archiveRel, rel)) };
11535
+ return { src: posixify2(rel), dst: posixify2(join26(archiveRel, rel)) };
11211
11536
  }
11212
11537
  function buildReceiptMarkdown2(receipt) {
11213
11538
  const lines = [];
@@ -11249,8 +11574,8 @@ function buildReceiptMarkdown2(receipt) {
11249
11574
  }
11250
11575
  async function invalidateReview(options) {
11251
11576
  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)))
11577
+ if (!existsSync26(join26(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
11578
+ if (!existsSync26(join26(packPath, "sections", options.sectionId)))
11254
11579
  throw new SectionNotFoundError(options.sectionId);
11255
11580
  const reason = options.reason.trim();
11256
11581
  if (reason.length < 8) {
@@ -11273,7 +11598,7 @@ async function invalidateReview(options) {
11273
11598
  // Also clear review-active.json so the next review run is unambiguous.
11274
11599
  posix2.join("sections", options.sectionId, "review-active.json")
11275
11600
  ];
11276
- const present = candidatePaths.filter((rel) => existsSync25(join25(packPath, rel)));
11601
+ const present = candidatePaths.filter((rel) => existsSync26(join26(packPath, rel)));
11277
11602
  if (present.length === 0) {
11278
11603
  return {
11279
11604
  performed: false,
@@ -11285,7 +11610,7 @@ async function invalidateReview(options) {
11285
11610
  message: "No canonical review artifacts found to invalidate. Section has no active review state."
11286
11611
  };
11287
11612
  }
11288
- await mkdir19(join25(packPath, archiveRel), { recursive: true });
11613
+ await mkdir20(join26(packPath, archiveRel), { recursive: true });
11289
11614
  const archived = [];
11290
11615
  for (const rel of present) {
11291
11616
  const a = await moveIfExists2(packPath, archiveRel, rel);
@@ -11301,14 +11626,14 @@ async function invalidateReview(options) {
11301
11626
  archived_artifacts: archived,
11302
11627
  notes: options.notes ?? null
11303
11628
  });
11304
- const receiptDir = join25(packPath, archiveRel);
11629
+ const receiptDir = join26(packPath, archiveRel);
11305
11630
  await writeFile20(
11306
- join25(receiptDir, "invalidation.json"),
11631
+ join26(receiptDir, "invalidation.json"),
11307
11632
  JSON.stringify(receipt, null, 2),
11308
11633
  "utf8"
11309
11634
  );
11310
11635
  await writeFile20(
11311
- join25(receiptDir, "invalidation.md"),
11636
+ join26(receiptDir, "invalidation.md"),
11312
11637
  buildReceiptMarkdown2(receipt),
11313
11638
  "utf8"
11314
11639
  );
@@ -11329,15 +11654,15 @@ var init_review2 = __esm({
11329
11654
  init_errors();
11330
11655
  init_src();
11331
11656
  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()
11657
+ ReviewInvalidationReceiptSchema = z20.object({
11658
+ receipt_id: z20.string().regex(/^invr_[0-9]+_[a-z0-9-]+$/),
11659
+ section_id: z20.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
11660
+ contract_label: z20.string().min(1),
11661
+ reason: z20.string().min(8),
11662
+ invalidated_at: z20.string(),
11663
+ research_os_version: z20.string(),
11664
+ archived_artifacts: z20.array(ArchivedArtifactSchema),
11665
+ notes: z20.string().nullable()
11341
11666
  });
11342
11667
  }
11343
11668
  });
@@ -11353,103 +11678,103 @@ var init_invalidate = __esm({
11353
11678
  });
11354
11679
 
11355
11680
  // src/section_report/schema.ts
11356
- import { z as z20 } from "zod";
11681
+ import { z as z21 } from "zod";
11357
11682
  var SectionReportSourcesSchema, SectionReportExtractionSchema, SectionReportContradictionsSchema, SectionReportReviewSchema, SectionReportAcceptanceSchema, SectionReportSchema;
11358
11683
  var init_schema18 = __esm({
11359
11684
  "src/section_report/schema.ts"() {
11360
11685
  "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())
11686
+ SectionReportSourcesSchema = z21.object({
11687
+ fetched_ok: z21.number().int().nonnegative(),
11688
+ source_cards: z21.number().int().nonnegative(),
11689
+ publishers: z21.array(z21.string()),
11690
+ primary_source_waiver: z21.object({
11691
+ status: z21.enum(["none", "requested", "granted"]),
11692
+ reason: z21.string().nullable(),
11693
+ compensating_controls: z21.array(z21.string())
11369
11694
  })
11370
11695
  });
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()
11696
+ SectionReportExtractionSchema = z21.object({
11697
+ candidate_claims: z21.number().int().nonnegative(),
11698
+ claims_per_source: z21.array(
11699
+ z21.object({
11700
+ source_id: z21.string(),
11701
+ claims: z21.number().int().nonnegative()
11377
11702
  })
11378
11703
  ),
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()
11704
+ claims_per_1k_words: z21.number(),
11705
+ excerpt_pages_processed: z21.number().int().nonnegative().nullable(),
11706
+ excerpt_id_failures: z21.number().int().nonnegative().nullable(),
11707
+ malformed_extractor_outputs: z21.number().int().nonnegative().nullable(),
11708
+ near_duplicate_clusters: z21.number().int().nonnegative(),
11709
+ weak_scope_count: z21.number().int().nonnegative(),
11710
+ generic_scope_count: z21.number().int().nonnegative(),
11711
+ density_flags: z21.number().int().nonnegative()
11712
+ });
11713
+ SectionReportContradictionsSchema = z21.object({
11714
+ pairs_compared: z21.number().int().nonnegative().nullable(),
11715
+ contradiction_candidates: z21.number().int().nonnegative(),
11716
+ overgeneralization_risks: z21.number().int().nonnegative()
11717
+ });
11718
+ SectionReportReviewSchema = z21.object({
11719
+ reviewed: z21.boolean(),
11720
+ accepted_for_synthesis: z21.number().int().nonnegative(),
11721
+ needs_scope_repair: z21.number().int().nonnegative(),
11722
+ needs_source_repair: z21.number().int().nonnegative(),
11723
+ needs_contradiction_mapping: z21.number().int().nonnegative(),
11724
+ rejected: z21.number().int().nonnegative(),
11725
+ needs_human_review: z21.number().int().nonnegative(),
11726
+ rejection_or_repair_by_category: z21.array(
11727
+ z21.object({
11728
+ category: z21.string(),
11729
+ count: z21.number().int().nonnegative()
11405
11730
  })
11406
11731
  )
11407
11732
  });
11408
- SectionReportAcceptanceSchema = z20.object({
11409
- candidate_claims: z20.number().int().nonnegative(),
11410
- accepted_for_synthesis: z20.number().int().nonnegative(),
11411
- acceptance_ratio: z20.number(),
11733
+ SectionReportAcceptanceSchema = z21.object({
11734
+ candidate_claims: z21.number().int().nonnegative(),
11735
+ accepted_for_synthesis: z21.number().int().nonnegative(),
11736
+ acceptance_ratio: z21.number(),
11412
11737
  // 0..1
11413
- accepted_per_source: z20.number(),
11738
+ accepted_per_source: z21.number(),
11414
11739
  // 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()
11740
+ accepted_per_1k_words: z21.number(),
11741
+ top_rejection_category: z21.string().nullable(),
11742
+ claim_overproduction_fired: z21.boolean(),
11743
+ synthesis_ready: z21.boolean()
11419
11744
  });
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(),
11745
+ SectionReportSchema = z21.object({
11746
+ report_id: z21.string().regex(/^secrep_[0-9]+_[a-z0-9-]+$/),
11747
+ section_id: z21.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
11748
+ reported_at: z21.string(),
11749
+ research_os_version: z21.string(),
11750
+ status: z21.string(),
11426
11751
  sources: SectionReportSourcesSchema,
11427
11752
  extraction: SectionReportExtractionSchema,
11428
11753
  contradictions: SectionReportContradictionsSchema,
11429
11754
  review: SectionReportReviewSchema,
11430
11755
  acceptance: SectionReportAcceptanceSchema,
11431
- gate_verdict: z20.string().nullable(),
11432
- gate_synthesis_eligible: z20.boolean().nullable()
11756
+ gate_verdict: z21.string().nullable(),
11757
+ gate_synthesis_eligible: z21.boolean().nullable()
11433
11758
  });
11434
11759
  }
11435
11760
  });
11436
11761
 
11437
11762
  // 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";
11763
+ import { existsSync as existsSync27 } from "fs";
11764
+ import { mkdir as mkdir21, readFile as readFile23, writeFile as writeFile21 } from "fs/promises";
11765
+ import { join as join27, resolve as resolve22 } from "path";
11441
11766
  import { parse as yamlParse11 } from "yaml";
11442
11767
  async function readJson(path, parser) {
11443
- if (!existsSync26(path)) return null;
11768
+ if (!existsSync27(path)) return null;
11444
11769
  try {
11445
- return parser(JSON.parse(await readFile22(path, "utf8")));
11770
+ return parser(JSON.parse(await readFile23(path, "utf8")));
11446
11771
  } catch {
11447
11772
  return null;
11448
11773
  }
11449
11774
  }
11450
11775
  async function readJsonl6(path, parse) {
11451
- if (!existsSync26(path)) return [];
11452
- const text = await readFile22(path, "utf8");
11776
+ if (!existsSync27(path)) return [];
11777
+ const text = await readFile23(path, "utf8");
11453
11778
  const out = [];
11454
11779
  for (const line of text.split(/\r?\n/)) {
11455
11780
  if (!line.trim()) continue;
@@ -11466,15 +11791,15 @@ function countWords2(text) {
11466
11791
  }
11467
11792
  async function reportSection(options) {
11468
11793
  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)))
11794
+ if (!existsSync27(join27(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
11795
+ if (!existsSync27(join27(packPath, "sections", options.sectionId)))
11471
11796
  throw new SectionNotFoundError(options.sectionId);
11472
11797
  const research = ResearchYamlSchema.parse(
11473
- yamlParse11(await readFile22(join26(packPath, "research.yaml"), "utf8"))
11798
+ yamlParse11(await readFile23(join27(packPath, "research.yaml"), "utf8"))
11474
11799
  );
11475
11800
  const section = research.sections.find((s) => s.id === options.sectionId);
11476
11801
  const allReceipts = await readJsonl6(
11477
- join26(packPath, "evidence", "fetch-log.jsonl"),
11802
+ join27(packPath, "evidence", "fetch-log.jsonl"),
11478
11803
  (raw) => FetchReceiptSchema.parse(raw)
11479
11804
  );
11480
11805
  const okReceipts = /* @__PURE__ */ new Map();
@@ -11485,9 +11810,9 @@ async function reportSection(options) {
11485
11810
  if (!prev || prev.fetched_at < r.fetched_at) okReceipts.set(r.source_id, r);
11486
11811
  }
11487
11812
  const sectionSourceIds = [];
11488
- const sourcesPath = join26(packPath, "sections", options.sectionId, "sources.jsonl");
11489
- if (existsSync26(sourcesPath)) {
11490
- const text = await readFile22(sourcesPath, "utf8");
11813
+ const sourcesPath = join27(packPath, "sections", options.sectionId, "sources.jsonl");
11814
+ if (existsSync27(sourcesPath)) {
11815
+ const text = await readFile23(sourcesPath, "utf8");
11491
11816
  for (const line of text.split(/\r?\n/)) {
11492
11817
  if (!line.trim()) continue;
11493
11818
  try {
@@ -11499,10 +11824,10 @@ async function reportSection(options) {
11499
11824
  }
11500
11825
  const cards = /* @__PURE__ */ new Map();
11501
11826
  for (const sid of sectionSourceIds) {
11502
- const cardPath = join26(packPath, "evidence", "source-cards", `${sid}.json`);
11503
- if (!existsSync26(cardPath)) continue;
11827
+ const cardPath = join27(packPath, "evidence", "source-cards", `${sid}.json`);
11828
+ if (!existsSync27(cardPath)) continue;
11504
11829
  try {
11505
- cards.set(sid, SourceCardSchema.parse(JSON.parse(await readFile22(cardPath, "utf8"))));
11830
+ cards.set(sid, SourceCardSchema.parse(JSON.parse(await readFile23(cardPath, "utf8"))));
11506
11831
  } catch {
11507
11832
  }
11508
11833
  }
@@ -11513,12 +11838,12 @@ async function reportSection(options) {
11513
11838
  for (const sid of sectionSourceIds) {
11514
11839
  const r = okReceipts.get(sid);
11515
11840
  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"));
11841
+ const p = join27(packPath, r.raw_text_path);
11842
+ if (!existsSync27(p)) continue;
11843
+ totalWords += countWords2(await readFile23(p, "utf8"));
11519
11844
  }
11520
11845
  const claims = await readJsonl6(
11521
- join26(packPath, "sections", options.sectionId, "claims.jsonl"),
11846
+ join27(packPath, "sections", options.sectionId, "claims.jsonl"),
11522
11847
  (raw) => ClaimSchema.parse(raw)
11523
11848
  );
11524
11849
  const claimsBySrc = /* @__PURE__ */ new Map();
@@ -11530,11 +11855,11 @@ async function reportSection(options) {
11530
11855
  }
11531
11856
  }
11532
11857
  const density = await readJson(
11533
- join26(packPath, "audits", `${options.sectionId}-claim-density.json`),
11858
+ join27(packPath, "audits", `${options.sectionId}-claim-density.json`),
11534
11859
  (raw) => raw
11535
11860
  );
11536
11861
  const extractReceipt = await readJson(
11537
- join26(packPath, "audits", `${options.sectionId}-claim-extract.json`),
11862
+ join27(packPath, "audits", `${options.sectionId}-claim-extract.json`),
11538
11863
  (raw) => raw
11539
11864
  );
11540
11865
  let excerptPagesProcessed = null;
@@ -11552,7 +11877,7 @@ async function reportSection(options) {
11552
11877
  ).length;
11553
11878
  }
11554
11879
  const contradictions = await readJsonl6(
11555
- join26(packPath, "sections", options.sectionId, "contradictions.jsonl"),
11880
+ join27(packPath, "sections", options.sectionId, "contradictions.jsonl"),
11556
11881
  (raw) => ContradictionSchema.parse(raw)
11557
11882
  );
11558
11883
  let pairsCompared = null;
@@ -11561,7 +11886,7 @@ async function reportSection(options) {
11561
11886
  (c) => c.type === "overgeneralization_risk"
11562
11887
  ).length;
11563
11888
  const reviews = await readJsonl6(
11564
- join26(packPath, "sections", options.sectionId, "claim-reviews.jsonl"),
11889
+ join27(packPath, "sections", options.sectionId, "claim-reviews.jsonl"),
11565
11890
  (raw) => ClaimReviewSchema.parse(raw)
11566
11891
  );
11567
11892
  const latestReviewByClaim = /* @__PURE__ */ new Map();
@@ -11570,7 +11895,7 @@ async function reportSection(options) {
11570
11895
  if (!prev || prev.created_at < r.created_at) latestReviewByClaim.set(r.claim_id, r);
11571
11896
  }
11572
11897
  const findings = await readJsonl6(
11573
- join26(packPath, "audits", `${options.sectionId}-findings.jsonl`),
11898
+ join27(packPath, "audits", `${options.sectionId}-findings.jsonl`),
11574
11899
  (raw) => ReviewFindingSchema.parse(raw)
11575
11900
  );
11576
11901
  const decisionCounts = {
@@ -11595,7 +11920,7 @@ async function reportSection(options) {
11595
11920
  const topRejectionCategory = rejectionByCategory[0]?.category ?? null;
11596
11921
  const claimOverproductionFired = (categoryCounts.get("claim_overproduction") ?? 0) > 0;
11597
11922
  const gate2 = await readJson(
11598
- join26(packPath, "audits", `${options.sectionId}-gate.json`),
11923
+ join27(packPath, "audits", `${options.sectionId}-gate.json`),
11599
11924
  (raw) => raw
11600
11925
  );
11601
11926
  const candidate = claims.length;
@@ -11731,10 +12056,10 @@ async function reportSection(options) {
11731
12056
  md.push("---");
11732
12057
  md.push("");
11733
12058
  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`);
12059
+ const auditsDir = join27(packPath, "audits");
12060
+ await mkdir21(auditsDir, { recursive: true });
12061
+ const jsonPath = join27(auditsDir, `${options.sectionId}-section-report.json`);
12062
+ const markdownPath = join27(auditsDir, `${options.sectionId}-section-report.md`);
11738
12063
  await writeFile21(jsonPath, JSON.stringify(report, null, 2), "utf8");
11739
12064
  await writeFile21(markdownPath, md.join("\n"), "utf8");
11740
12065
  return { report, jsonPath, markdownPath };
@@ -11772,17 +12097,17 @@ var init_triage = __esm({
11772
12097
  });
11773
12098
 
11774
12099
  // src/discover/schema.ts
11775
- import { z as z21 } from "zod";
12100
+ import { z as z22 } from "zod";
11776
12101
  var DiscoveryCandidateStatusSchema, SourceTypeGuessSchema, DiscoveryCandidateSchema, DiscoverySummarySchema;
11777
12102
  var init_schema19 = __esm({
11778
12103
  "src/discover/schema.ts"() {
11779
12104
  "use strict";
11780
- DiscoveryCandidateStatusSchema = z21.enum([
12105
+ DiscoveryCandidateStatusSchema = z22.enum([
11781
12106
  "candidate",
11782
12107
  "approved",
11783
12108
  "rejected"
11784
12109
  ]);
11785
- SourceTypeGuessSchema = z21.enum([
12110
+ SourceTypeGuessSchema = z22.enum([
11786
12111
  "primary",
11787
12112
  "docs",
11788
12113
  "paper",
@@ -11792,36 +12117,36 @@ var init_schema19 = __esm({
11792
12117
  "benchmark",
11793
12118
  "unknown"
11794
12119
  ]);
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(),
12120
+ DiscoveryCandidateSchema = z22.object({
12121
+ candidate_id: z22.string().regex(/^disc_[a-f0-9]{12}$/),
12122
+ section_id: z22.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
12123
+ url: z22.string().url(),
12124
+ title: z22.string().min(1),
12125
+ publisher: z22.string().nullable(),
11801
12126
  source_type_guess: SourceTypeGuessSchema,
11802
- why_relevant: z21.string().min(1),
12127
+ why_relevant: z22.string().min(1),
11803
12128
  // The free-text query that produced this candidate (for traceability).
11804
- query: z21.string().min(1),
12129
+ query: z22.string().min(1),
11805
12130
  // Lower rank = more central. Stable per-(query, provider) ordering.
11806
- rank: z21.number().int().positive(),
11807
- discovered_at: z21.string(),
12131
+ rank: z22.number().int().positive(),
12132
+ discovered_at: z22.string(),
11808
12133
  // 'candidate' | 'approved' | 'rejected'. Append-only: new entries with
11809
12134
  // same candidate_id supersede older ones; the latest entry's status wins.
11810
12135
  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())
12136
+ discovered_by: z22.string().min(1),
12137
+ reason: z22.string().nullable().default(null)
12138
+ });
12139
+ DiscoverySummarySchema = z22.object({
12140
+ summary_id: z22.string().regex(/^disum_[0-9]+_[a-z0-9-]+$/),
12141
+ section_id: z22.string().regex(/^[0-9]{2}-[a-z0-9-]+$/),
12142
+ ran_at: z22.string(),
12143
+ research_os_version: z22.string(),
12144
+ query: z22.string().min(1),
12145
+ provider: z22.string().min(1),
12146
+ candidates_proposed: z22.number().int().nonnegative(),
12147
+ candidates_validated: z22.number().int().nonnegative(),
12148
+ candidates_rejected_invalid_url: z22.number().int().nonnegative(),
12149
+ warnings: z22.array(z22.string())
11825
12150
  });
11826
12151
  }
11827
12152
  });
@@ -11973,10 +12298,10 @@ Propose source-URL candidates for this section. Quality over quantity.`;
11973
12298
  });
11974
12299
 
11975
12300
  // src/discover/run.ts
11976
- import { existsSync as existsSync27 } from "fs";
12301
+ import { existsSync as existsSync28 } from "fs";
11977
12302
  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";
12303
+ import { mkdir as mkdir22, readFile as readFile24, writeFile as writeFile22, appendFile as appendFile8 } from "fs/promises";
12304
+ import { join as join28, resolve as resolve23 } from "path";
11980
12305
  import { parse as yamlParse12 } from "yaml";
11981
12306
  function makeCandidateId(sectionId, url) {
11982
12307
  const hex = createHash10("sha256").update(`${sectionId}|${url}`).digest("hex").slice(0, 12);
@@ -11991,21 +12316,21 @@ function isHttpsUrl(s) {
11991
12316
  }
11992
12317
  }
11993
12318
  function candidatesPath(packPath, sectionId) {
11994
- return join27(packPath, "sections", sectionId, "discovery-candidates.jsonl");
12319
+ return join28(packPath, "sections", sectionId, "discovery-candidates.jsonl");
11995
12320
  }
11996
12321
  function reportPath(packPath, sectionId) {
11997
- return join27(packPath, "sections", sectionId, "discovery-report.md");
12322
+ return join28(packPath, "sections", sectionId, "discovery-report.md");
11998
12323
  }
11999
12324
  function summaryPath(packPath, sectionId) {
12000
- return join27(packPath, "audits", `${sectionId}-discovery.json`);
12325
+ return join28(packPath, "audits", `${sectionId}-discovery.json`);
12001
12326
  }
12002
12327
  function approvedUrlsPath(packPath, sectionId) {
12003
- return join27(packPath, "sections", sectionId, "urls.approved.txt");
12328
+ return join28(packPath, "sections", sectionId, "urls.approved.txt");
12004
12329
  }
12005
12330
  async function readCandidates(packPath, sectionId) {
12006
12331
  const path = candidatesPath(packPath, sectionId);
12007
- if (!existsSync27(path)) return [];
12008
- const text = await readFile23(path, "utf8");
12332
+ if (!existsSync28(path)) return [];
12333
+ const text = await readFile24(path, "utf8");
12009
12334
  const out = [];
12010
12335
  for (const line of text.split(/\r?\n/)) {
12011
12336
  if (!line.trim()) continue;
@@ -12035,7 +12360,7 @@ async function pickProvider(providers) {
12035
12360
  }
12036
12361
  async function loadSectionPurpose(packPath, sectionId) {
12037
12362
  const research = ResearchYamlSchema.parse(
12038
- yamlParse12(await readFile23(join27(packPath, "research.yaml"), "utf8"))
12363
+ yamlParse12(await readFile24(join28(packPath, "research.yaml"), "utf8"))
12039
12364
  );
12040
12365
  const section = research.sections.find((s) => s.id === sectionId);
12041
12366
  return section?.purpose ?? "";
@@ -12073,8 +12398,8 @@ function buildReportMarkdown(args) {
12073
12398
  }
12074
12399
  async function discover(options) {
12075
12400
  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)))
12401
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12402
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12078
12403
  throw new SectionNotFoundError(options.sectionId);
12079
12404
  const query2 = options.query.trim();
12080
12405
  if (query2.length < 4) throw new Error("discover query must be at least 4 characters");
@@ -12134,9 +12459,9 @@ async function discover(options) {
12134
12459
  newCandidates.push(candidate);
12135
12460
  }
12136
12461
  const ledgerPath = candidatesPath(packPath, options.sectionId);
12137
- await mkdir21(join27(packPath, "sections", options.sectionId), { recursive: true });
12462
+ await mkdir22(join28(packPath, "sections", options.sectionId), { recursive: true });
12138
12463
  for (const c of newCandidates) {
12139
- await appendFile7(ledgerPath, JSON.stringify(c) + "\n", "utf8");
12464
+ await appendFile8(ledgerPath, JSON.stringify(c) + "\n", "utf8");
12140
12465
  }
12141
12466
  const all = await readCandidates(packPath, options.sectionId);
12142
12467
  const latest = Array.from(latestPerCandidate(all).values());
@@ -12160,7 +12485,7 @@ async function discover(options) {
12160
12485
  candidates_rejected_invalid_url: invalidCount,
12161
12486
  warnings
12162
12487
  });
12163
- await mkdir21(join27(packPath, "audits"), { recursive: true });
12488
+ await mkdir22(join28(packPath, "audits"), { recursive: true });
12164
12489
  await writeFile22(summaryPath(packPath, options.sectionId), JSON.stringify(summary, null, 2), "utf8");
12165
12490
  return {
12166
12491
  candidatesAdded: newCandidates.length,
@@ -12180,13 +12505,13 @@ async function appendStatusUpdate(packPath, sectionId, candidate, newStatus, rea
12180
12505
  reason,
12181
12506
  discovered_at: stampIso
12182
12507
  });
12183
- await appendFile7(candidatesPath(packPath, sectionId), JSON.stringify(updated) + "\n", "utf8");
12508
+ await appendFile8(candidatesPath(packPath, sectionId), JSON.stringify(updated) + "\n", "utf8");
12184
12509
  return updated;
12185
12510
  }
12186
12511
  async function approve(options) {
12187
12512
  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)))
12513
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12514
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12190
12515
  throw new SectionNotFoundError(options.sectionId);
12191
12516
  const all = await readCandidates(packPath, options.sectionId);
12192
12517
  const latest = latestPerCandidate(all);
@@ -12219,8 +12544,8 @@ async function approve(options) {
12219
12544
  }
12220
12545
  async function reject(options) {
12221
12546
  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)))
12547
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12548
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12224
12549
  throw new SectionNotFoundError(options.sectionId);
12225
12550
  if (options.reason.trim().length < 4) {
12226
12551
  throw new Error("reject requires --reason of at least 4 characters");
@@ -12248,15 +12573,15 @@ async function reject(options) {
12248
12573
  }
12249
12574
  async function exportUrls(options) {
12250
12575
  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)))
12576
+ if (!existsSync28(join28(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
12577
+ if (!existsSync28(join28(packPath, "sections", options.sectionId)))
12253
12578
  throw new SectionNotFoundError(options.sectionId);
12254
12579
  const all = await readCandidates(packPath, options.sectionId);
12255
12580
  const latest = latestPerCandidate(all);
12256
12581
  const approved = Array.from(latest.values()).filter((c) => c.status === "approved").sort((a, b) => a.rank - b.rank);
12257
12582
  const lines = approved.map((c) => c.url).join("\n");
12258
12583
  const path = approvedUrlsPath(packPath, options.sectionId);
12259
- await mkdir21(join27(packPath, "sections", options.sectionId), { recursive: true });
12584
+ await mkdir22(join28(packPath, "sections", options.sectionId), { recursive: true });
12260
12585
  await writeFile22(path, lines + (lines.length > 0 ? "\n" : ""), "utf8");
12261
12586
  return { exportPath: path, approvedCount: approved.length };
12262
12587
  }
@@ -12304,7 +12629,7 @@ var init_src = __esm({
12304
12629
  init_triage();
12305
12630
  init_discover();
12306
12631
  init_errors();
12307
- RESEARCH_OS_VERSION = "0.3.3";
12632
+ RESEARCH_OS_VERSION = "0.5.0";
12308
12633
  }
12309
12634
  });
12310
12635
  init_src();
@@ -12411,6 +12736,7 @@ export {
12411
12736
  SectionStatusChangeSchema,
12412
12737
  SeveritySchema,
12413
12738
  SharedSourceSchema,
12739
+ SourceCardOverrideSchema,
12414
12740
  SourceCardSchema,
12415
12741
  SourceDiversityGapRowSchema,
12416
12742
  SourceTypeGuessSchema,
@@ -12430,6 +12756,7 @@ export {
12430
12756
  add,
12431
12757
  aggregate,
12432
12758
  appendFetchLog,
12759
+ appendOverride,
12433
12760
  appendSectionSourceId,
12434
12761
  applySchema,
12435
12762
  applyWaivers,
@@ -12470,6 +12797,8 @@ export {
12470
12797
  freeze,
12471
12798
  gate,
12472
12799
  gather,
12800
+ getEffectivePublisher,
12801
+ getEffectiveSourceType,
12473
12802
  handoff,
12474
12803
  hasNegation,
12475
12804
  indexDbPath,
@@ -12494,6 +12823,7 @@ export {
12494
12823
  promote,
12495
12824
  query,
12496
12825
  readActiveProfile,
12826
+ readOverrides,
12497
12827
  readTriagedClaimIds,
12498
12828
  readUrlsFile,
12499
12829
  reject,
@@ -12524,6 +12854,7 @@ export {
12524
12854
  syncRepoKnowledge,
12525
12855
  tokenize,
12526
12856
  triage,
12857
+ validateSourceCardOverride,
12527
12858
  workspace,
12528
12859
  writeActiveProfile,
12529
12860
  writeSourceCard