@mcptoolshop/research-os 0.3.3 → 0.4.0

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