@mcptoolshop/research-os 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +194 -0
- package/README.md +9 -5
- package/dist/cli.js +245 -57
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +185 -86
- package/dist/index.js +234 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -106,11 +106,12 @@ __export(schema_exports, {
|
|
|
106
106
|
ReviewProfilePresetSchema: () => ReviewProfilePresetSchema,
|
|
107
107
|
SectionBudgetGateSchema: () => SectionBudgetGateSchema,
|
|
108
108
|
SectionSchema: () => SectionSchema,
|
|
109
|
+
SectionScopedWaiverSchema: () => SectionScopedWaiverSchema,
|
|
109
110
|
SectionStatusSchema: () => SectionStatusSchema,
|
|
110
111
|
SourceFloorGateSchema: () => SourceFloorGateSchema
|
|
111
112
|
});
|
|
112
113
|
import { z } from "zod";
|
|
113
|
-
var SectionStatusSchema, SectionSchema, SourceFloorGateSchema, ClaimIntegrityGateSchema, FreshnessGateSchema, ContradictionGateSchema, SectionBudgetGateSchema, GateConfigSchema, PrimarySourceWaiverSchema, FreshnessRequirementsSchema, ReviewProfilePresetSchema, DEFAULT_REVIEW_PROFILES, ResearchYamlSchema;
|
|
114
|
+
var SectionStatusSchema, SectionSchema, SourceFloorGateSchema, ClaimIntegrityGateSchema, FreshnessGateSchema, ContradictionGateSchema, SectionBudgetGateSchema, GateConfigSchema, SectionScopedWaiverSchema, PrimarySourceWaiverSchema, FreshnessRequirementsSchema, ReviewProfilePresetSchema, DEFAULT_REVIEW_PROFILES, ResearchYamlSchema;
|
|
114
115
|
var init_schema = __esm({
|
|
115
116
|
"src/intake/schema.ts"() {
|
|
116
117
|
"use strict";
|
|
@@ -160,10 +161,20 @@ var init_schema = __esm({
|
|
|
160
161
|
contradiction: ContradictionGateSchema.default({}),
|
|
161
162
|
section_budget: SectionBudgetGateSchema.default({})
|
|
162
163
|
});
|
|
164
|
+
SectionScopedWaiverSchema = z.object({
|
|
165
|
+
section_id: z.string().regex(/^[0-9]{2}-[a-z0-9-]+$/, 'Section id must look like "01-landscape"'),
|
|
166
|
+
scope: z.enum(["min_independent_publishers", "primary_sources_required"]),
|
|
167
|
+
reason: z.string().min(1),
|
|
168
|
+
compensating_controls: z.array(z.string()).min(1)
|
|
169
|
+
});
|
|
163
170
|
PrimarySourceWaiverSchema = z.object({
|
|
164
171
|
status: z.enum(["none", "requested", "granted"]).default("none"),
|
|
165
172
|
reason: z.string().optional(),
|
|
166
|
-
compensating_controls: z.array(z.string()).default([])
|
|
173
|
+
compensating_controls: z.array(z.string()).default([]),
|
|
174
|
+
// Section-scoped waivers; each entry is its own waiver record. Independent
|
|
175
|
+
// of the pack-level status/reason/compensating_controls fields above.
|
|
176
|
+
// Defaults to [] for backward compatibility — existing packs unaffected.
|
|
177
|
+
section_waivers: z.array(SectionScopedWaiverSchema).default([])
|
|
167
178
|
});
|
|
168
179
|
FreshnessRequirementsSchema = z.object({
|
|
169
180
|
required: z.boolean().default(true),
|
|
@@ -2774,9 +2785,6 @@ Hard rules:
|
|
|
2774
2785
|
});
|
|
2775
2786
|
|
|
2776
2787
|
// src/contradictions/detectors/index.ts
|
|
2777
|
-
function defaultContradictionDetectors() {
|
|
2778
|
-
return [new OllamaInternContradictionDetector(), new HeuristicContradictionDetector()];
|
|
2779
|
-
}
|
|
2780
2788
|
async function pickContradictionDetector(detectors) {
|
|
2781
2789
|
for (const d of detectors) {
|
|
2782
2790
|
if (await d.available()) return d;
|
|
@@ -3250,6 +3258,48 @@ function buildContradiction(args) {
|
|
|
3250
3258
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3251
3259
|
});
|
|
3252
3260
|
}
|
|
3261
|
+
async function resolveDetector(options) {
|
|
3262
|
+
const mode = options.detectorMode ?? "auto";
|
|
3263
|
+
if (!VALID_DETECTOR_MODES.includes(mode)) {
|
|
3264
|
+
throw new Error(
|
|
3265
|
+
`contradict map: invalid --detector value "${mode}"; valid values are: auto, heuristic, ollama-intern`
|
|
3266
|
+
);
|
|
3267
|
+
}
|
|
3268
|
+
if (mode === "heuristic") {
|
|
3269
|
+
return {
|
|
3270
|
+
detector: new HeuristicContradictionDetector(),
|
|
3271
|
+
announcement: "contradict map: using heuristic detector"
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
if (mode === "ollama-intern") {
|
|
3275
|
+
const d = new OllamaInternContradictionDetector(options.ollamaConfig ?? {});
|
|
3276
|
+
if (!await d.available()) {
|
|
3277
|
+
throw new Error(
|
|
3278
|
+
`contradict map: ollama-intern detector requested but model ${d.model} is unavailable; aborting (use --detector heuristic to bypass)`
|
|
3279
|
+
);
|
|
3280
|
+
}
|
|
3281
|
+
return {
|
|
3282
|
+
detector: d,
|
|
3283
|
+
announcement: `contradict map: using ollama-intern detector with model ${d.model}`
|
|
3284
|
+
};
|
|
3285
|
+
}
|
|
3286
|
+
const detectors = options.detectors ?? [
|
|
3287
|
+
new OllamaInternContradictionDetector(options.ollamaConfig ?? {}),
|
|
3288
|
+
new HeuristicContradictionDetector()
|
|
3289
|
+
];
|
|
3290
|
+
const detector = await pickContradictionDetector(detectors);
|
|
3291
|
+
if (detector.name === "ollama-intern") {
|
|
3292
|
+
const modelName = detector instanceof OllamaInternContradictionDetector ? detector.model : process.env.OLLAMA_INTERN_MODEL ?? "hermes3:8b";
|
|
3293
|
+
return {
|
|
3294
|
+
detector,
|
|
3295
|
+
announcement: `contradict map: using ollama-intern detector with model ${modelName}`
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
return {
|
|
3299
|
+
detector,
|
|
3300
|
+
announcement: "contradict map: ollama-intern unavailable; using heuristic detector"
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3253
3303
|
async function map(options) {
|
|
3254
3304
|
const packPath = options.packPath ? resolve7(options.packPath) : process.cwd();
|
|
3255
3305
|
if (!existsSync9(join10(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
@@ -3261,8 +3311,7 @@ async function map(options) {
|
|
|
3261
3311
|
const allowed = await readTriagedClaimIds2(packPath, options.sectionId);
|
|
3262
3312
|
candidateClaims = candidateClaims.filter((c) => allowed.has(c.claim_id));
|
|
3263
3313
|
}
|
|
3264
|
-
const
|
|
3265
|
-
const detector = await pickContradictionDetector(adapters);
|
|
3314
|
+
const { detector, announcement } = await resolveDetector(options);
|
|
3266
3315
|
const summary = {
|
|
3267
3316
|
sectionId: options.sectionId,
|
|
3268
3317
|
detector: detector.name,
|
|
@@ -3272,7 +3321,8 @@ async function map(options) {
|
|
|
3272
3321
|
contradictionsAdded: 0,
|
|
3273
3322
|
contradictionsDeduped: 0,
|
|
3274
3323
|
contradictionIds: [],
|
|
3275
|
-
detectorError: null
|
|
3324
|
+
detectorError: null,
|
|
3325
|
+
detectorAnnouncement: announcement
|
|
3276
3326
|
};
|
|
3277
3327
|
const ledgerPath = join10(sectionDir, "contradictions.jsonl");
|
|
3278
3328
|
const mdPath = join10(sectionDir, "contradictions.md");
|
|
@@ -3336,7 +3386,7 @@ async function map(options) {
|
|
|
3336
3386
|
await writeFile9(mdPath, md, "utf8");
|
|
3337
3387
|
return summary;
|
|
3338
3388
|
}
|
|
3339
|
-
var DETECTOR_ID_PART;
|
|
3389
|
+
var DETECTOR_ID_PART, VALID_DETECTOR_MODES;
|
|
3340
3390
|
var init_map = __esm({
|
|
3341
3391
|
"src/contradictions/map.ts"() {
|
|
3342
3392
|
"use strict";
|
|
@@ -3344,11 +3394,14 @@ var init_map = __esm({
|
|
|
3344
3394
|
init_schema5();
|
|
3345
3395
|
init_schema7();
|
|
3346
3396
|
init_detectors();
|
|
3397
|
+
init_heuristic3();
|
|
3398
|
+
init_ollama_intern3();
|
|
3347
3399
|
init_markdown();
|
|
3348
3400
|
DETECTOR_ID_PART = {
|
|
3349
3401
|
heuristic: "heuristic",
|
|
3350
3402
|
"ollama-intern": "ollama_intern"
|
|
3351
3403
|
};
|
|
3404
|
+
VALID_DETECTOR_MODES = ["auto", "heuristic", "ollama-intern"];
|
|
3352
3405
|
}
|
|
3353
3406
|
});
|
|
3354
3407
|
|
|
@@ -4165,9 +4218,28 @@ function applyWaivers(input, results) {
|
|
|
4165
4218
|
const updated = results.map((r) => ({ ...r }));
|
|
4166
4219
|
const applied = [];
|
|
4167
4220
|
const validationFailures = [];
|
|
4168
|
-
|
|
4169
|
-
|
|
4221
|
+
applyPackLevelWaiver({
|
|
4222
|
+
waiver,
|
|
4223
|
+
cfg,
|
|
4224
|
+
updated,
|
|
4225
|
+
applied,
|
|
4226
|
+
validationFailures
|
|
4227
|
+
});
|
|
4228
|
+
for (const sw of waiver.section_waivers ?? []) {
|
|
4229
|
+
if (sw.section_id !== input.section.id) continue;
|
|
4230
|
+
applySectionScopedWaiver({
|
|
4231
|
+
waiver: sw,
|
|
4232
|
+
cfg,
|
|
4233
|
+
updated,
|
|
4234
|
+
applied,
|
|
4235
|
+
validationFailures
|
|
4236
|
+
});
|
|
4170
4237
|
}
|
|
4238
|
+
return { updatedResults: updated, waivers_applied: applied, waiver_validation_failures: validationFailures };
|
|
4239
|
+
}
|
|
4240
|
+
function applyPackLevelWaiver(args) {
|
|
4241
|
+
const { waiver, cfg, updated, applied, validationFailures } = args;
|
|
4242
|
+
if (waiver.status !== "granted") return;
|
|
4171
4243
|
if (!waiver.reason || waiver.reason.trim().length === 0) {
|
|
4172
4244
|
validationFailures.push({
|
|
4173
4245
|
family: "waivers",
|
|
@@ -4177,7 +4249,7 @@ function applyWaivers(input, results) {
|
|
|
4177
4249
|
evidence: [],
|
|
4178
4250
|
blocks_synthesis: true
|
|
4179
4251
|
});
|
|
4180
|
-
return
|
|
4252
|
+
return;
|
|
4181
4253
|
}
|
|
4182
4254
|
if (waiver.compensating_controls.length === 0) {
|
|
4183
4255
|
validationFailures.push({
|
|
@@ -4188,7 +4260,7 @@ function applyWaivers(input, results) {
|
|
|
4188
4260
|
evidence: [],
|
|
4189
4261
|
blocks_synthesis: true
|
|
4190
4262
|
});
|
|
4191
|
-
return
|
|
4263
|
+
return;
|
|
4192
4264
|
}
|
|
4193
4265
|
if (!cfg.primary_source_waiver_allowed) {
|
|
4194
4266
|
validationFailures.push({
|
|
@@ -4199,7 +4271,7 @@ function applyWaivers(input, results) {
|
|
|
4199
4271
|
evidence: [],
|
|
4200
4272
|
blocks_synthesis: true
|
|
4201
4273
|
});
|
|
4202
|
-
return
|
|
4274
|
+
return;
|
|
4203
4275
|
}
|
|
4204
4276
|
for (let i = 0; i < updated.length; i += 1) {
|
|
4205
4277
|
const r = updated[i];
|
|
@@ -4221,7 +4293,62 @@ function applyWaivers(input, results) {
|
|
|
4221
4293
|
});
|
|
4222
4294
|
}
|
|
4223
4295
|
}
|
|
4224
|
-
|
|
4296
|
+
}
|
|
4297
|
+
function applySectionScopedWaiver(args) {
|
|
4298
|
+
const { waiver, cfg, updated, applied, validationFailures } = args;
|
|
4299
|
+
if (!waiver.reason || waiver.reason.trim().length === 0) {
|
|
4300
|
+
validationFailures.push({
|
|
4301
|
+
family: "waivers",
|
|
4302
|
+
check: "section_scoped_waiver_reason_required",
|
|
4303
|
+
status: "fail",
|
|
4304
|
+
detail: `Section-scoped waiver for ${waiver.section_id}/${waiver.scope} is missing a reason. Waiver is invalid; original failure stands.`,
|
|
4305
|
+
evidence: [waiver.section_id],
|
|
4306
|
+
blocks_synthesis: true
|
|
4307
|
+
});
|
|
4308
|
+
return;
|
|
4309
|
+
}
|
|
4310
|
+
if (waiver.compensating_controls.length === 0) {
|
|
4311
|
+
validationFailures.push({
|
|
4312
|
+
family: "waivers",
|
|
4313
|
+
check: "section_scoped_waiver_compensating_controls_required",
|
|
4314
|
+
status: "fail",
|
|
4315
|
+
detail: `Section-scoped waiver for ${waiver.section_id}/${waiver.scope} has empty compensating_controls. Waiver is invalid; original failure stands.`,
|
|
4316
|
+
evidence: [waiver.section_id],
|
|
4317
|
+
blocks_synthesis: true
|
|
4318
|
+
});
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4321
|
+
if (!cfg.primary_source_waiver_allowed) {
|
|
4322
|
+
validationFailures.push({
|
|
4323
|
+
family: "waivers",
|
|
4324
|
+
check: "section_scoped_waiver_allowed_by_pack",
|
|
4325
|
+
status: "fail",
|
|
4326
|
+
detail: `Pack policy gates.source_floor.primary_source_waiver_allowed=false; section-scoped waiver for ${waiver.section_id}/${waiver.scope} cannot be applied.`,
|
|
4327
|
+
evidence: [waiver.section_id],
|
|
4328
|
+
blocks_synthesis: true
|
|
4329
|
+
});
|
|
4330
|
+
return;
|
|
4331
|
+
}
|
|
4332
|
+
for (let i = 0; i < updated.length; i += 1) {
|
|
4333
|
+
const r = updated[i];
|
|
4334
|
+
if (r.family === "source_floor" && r.check === waiver.scope && r.status === "fail") {
|
|
4335
|
+
const original = r.status;
|
|
4336
|
+
updated[i] = {
|
|
4337
|
+
...r,
|
|
4338
|
+
status: "pass_with_waiver",
|
|
4339
|
+
detail: `${r.detail} Section-scoped waiver granted for ${waiver.section_id} with ${waiver.compensating_controls.length} compensating control(s); converted from fail to pass_with_waiver.`,
|
|
4340
|
+
blocks_synthesis: false
|
|
4341
|
+
};
|
|
4342
|
+
applied.push({
|
|
4343
|
+
family: "source_floor",
|
|
4344
|
+
check: waiver.scope,
|
|
4345
|
+
reason: waiver.reason,
|
|
4346
|
+
compensating_controls: waiver.compensating_controls,
|
|
4347
|
+
original_status: original,
|
|
4348
|
+
new_status: "pass_with_waiver"
|
|
4349
|
+
});
|
|
4350
|
+
}
|
|
4351
|
+
}
|
|
4225
4352
|
}
|
|
4226
4353
|
var init_waivers = __esm({
|
|
4227
4354
|
"src/gates/checks/waivers.ts"() {
|
|
@@ -5478,9 +5605,11 @@ function pickHighestPriority(decisions) {
|
|
|
5478
5605
|
return "accepted_for_synthesis";
|
|
5479
5606
|
}
|
|
5480
5607
|
function deriveClaimReviews(args) {
|
|
5481
|
-
const { claims, findings, reviewer, reviewMethod } = args;
|
|
5608
|
+
const { claims, findings, reviewer, reviewMethod, activeSectionWaivers } = args;
|
|
5482
5609
|
const reviews = [];
|
|
5483
5610
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5611
|
+
const monopolyWaived = Array.isArray(activeSectionWaivers) && activeSectionWaivers.some((w) => w.scope === "min_independent_publishers");
|
|
5612
|
+
const isWaivedFinding = (f) => monopolyWaived && f.category === "source_cluster_monopoly";
|
|
5484
5613
|
for (const claim of claims) {
|
|
5485
5614
|
const claimFindings = findings.filter((f) => f.claim_ids.includes(claim.claim_id));
|
|
5486
5615
|
if (claimFindings.length === 0) {
|
|
@@ -5497,6 +5626,7 @@ function deriveClaimReviews(args) {
|
|
|
5497
5626
|
}
|
|
5498
5627
|
let decisions = [];
|
|
5499
5628
|
for (const f of claimFindings) {
|
|
5629
|
+
if (isWaivedFinding(f)) continue;
|
|
5500
5630
|
if (f.severity === "block") {
|
|
5501
5631
|
decisions.push(BLOCK_TO_DECISION[f.category] ?? "rejected");
|
|
5502
5632
|
} else if (f.severity === "warn") {
|
|
@@ -5513,7 +5643,9 @@ function deriveClaimReviews(args) {
|
|
|
5513
5643
|
);
|
|
5514
5644
|
}
|
|
5515
5645
|
const decision = pickHighestPriority(decisions);
|
|
5516
|
-
const reasonParts = claimFindings.filter((f) => f.severity !== "info").map(
|
|
5646
|
+
const reasonParts = claimFindings.filter((f) => f.severity !== "info").map(
|
|
5647
|
+
(f) => isWaivedFinding(f) ? `${f.category} (${f.severity}, waived)` : `${f.category} (${f.severity})`
|
|
5648
|
+
);
|
|
5517
5649
|
const reason = reasonParts.length > 0 ? `Findings: ${reasonParts.join("; ")}.` : "Only info-level findings; accepted.";
|
|
5518
5650
|
reviews.push({
|
|
5519
5651
|
claim_id: claim.claim_id,
|
|
@@ -5894,7 +6026,8 @@ async function review(options) {
|
|
|
5894
6026
|
candidateClaims,
|
|
5895
6027
|
drafts: acceptedDrafts,
|
|
5896
6028
|
llmFindingsRejected,
|
|
5897
|
-
profile: options.profile ?? DEFAULT_PROFILE
|
|
6029
|
+
profile: options.profile ?? DEFAULT_PROFILE,
|
|
6030
|
+
research
|
|
5898
6031
|
});
|
|
5899
6032
|
}
|
|
5900
6033
|
async function runMultiPassReview(args) {
|
|
@@ -5956,7 +6089,8 @@ async function runMultiPassReview(args) {
|
|
|
5956
6089
|
candidateClaims: args.candidateClaims,
|
|
5957
6090
|
drafts: merged,
|
|
5958
6091
|
llmFindingsRejected,
|
|
5959
|
-
profile: args.options.profile ?? DEFAULT_PROFILE
|
|
6092
|
+
profile: args.options.profile ?? DEFAULT_PROFILE,
|
|
6093
|
+
research: args.research
|
|
5960
6094
|
});
|
|
5961
6095
|
}
|
|
5962
6096
|
async function reviewWithSpecificReviewer(args) {
|
|
@@ -5984,7 +6118,8 @@ async function reviewWithSpecificReviewer(args) {
|
|
|
5984
6118
|
candidateClaims: args.candidateClaims,
|
|
5985
6119
|
drafts: result.drafts,
|
|
5986
6120
|
llmFindingsRejected: 0,
|
|
5987
|
-
profile: args.options.profile ?? DEFAULT_PROFILE
|
|
6121
|
+
profile: args.options.profile ?? DEFAULT_PROFILE,
|
|
6122
|
+
research: args.research
|
|
5988
6123
|
});
|
|
5989
6124
|
}
|
|
5990
6125
|
async function finalizeReview(args) {
|
|
@@ -6007,11 +6142,15 @@ async function finalizeReview(args) {
|
|
|
6007
6142
|
seen.add(f.finding_id);
|
|
6008
6143
|
dedupedFindings.push(f);
|
|
6009
6144
|
}
|
|
6145
|
+
const activeSectionWaivers = args.research.primary_source_waiver.section_waivers.filter(
|
|
6146
|
+
(w) => w.section_id === args.sectionId
|
|
6147
|
+
);
|
|
6010
6148
|
const claimReviews = deriveClaimReviews({
|
|
6011
6149
|
claims: args.candidateClaims,
|
|
6012
6150
|
findings: dedupedFindings,
|
|
6013
6151
|
reviewer: args.reviewer,
|
|
6014
|
-
reviewMethod: args.reviewMethod
|
|
6152
|
+
reviewMethod: args.reviewMethod,
|
|
6153
|
+
activeSectionWaivers
|
|
6015
6154
|
});
|
|
6016
6155
|
const decisionCounts = {
|
|
6017
6156
|
accepted_for_synthesis: 0,
|
|
@@ -8738,6 +8877,19 @@ function buildStaleSources(input) {
|
|
|
8738
8877
|
}
|
|
8739
8878
|
return out;
|
|
8740
8879
|
}
|
|
8880
|
+
function findSectionWaiver(research, sectionId, scope) {
|
|
8881
|
+
return (research.primary_source_waiver.section_waivers ?? []).find(
|
|
8882
|
+
(w) => w.section_id === sectionId && w.scope === scope
|
|
8883
|
+
);
|
|
8884
|
+
}
|
|
8885
|
+
function annotateWeakSource(row, waiver) {
|
|
8886
|
+
if (!waiver) return row;
|
|
8887
|
+
return { ...row, waived: true, waiver_reason: waiver.reason };
|
|
8888
|
+
}
|
|
8889
|
+
function annotateDiversityGap(row, waiver) {
|
|
8890
|
+
if (!waiver) return row;
|
|
8891
|
+
return { ...row, waived: true, waiver_reason: waiver.reason };
|
|
8892
|
+
}
|
|
8741
8893
|
function buildWeakSources(input) {
|
|
8742
8894
|
const out = [];
|
|
8743
8895
|
const cfg = input.research.gates.source_floor;
|
|
@@ -8747,33 +8899,50 @@ function buildWeakSources(input) {
|
|
|
8747
8899
|
const publishers = new Set(
|
|
8748
8900
|
sectionSources.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
8749
8901
|
);
|
|
8902
|
+
const monopolyWaiver = findSectionWaiver(input.research, sid, "min_independent_publishers");
|
|
8903
|
+
const primaryWaiver = findSectionWaiver(input.research, sid, "primary_sources_required");
|
|
8750
8904
|
if (publishers.size === 1 && sectionSources.length >= 2) {
|
|
8751
|
-
out.push(
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8905
|
+
out.push(
|
|
8906
|
+
annotateWeakSource(
|
|
8907
|
+
{
|
|
8908
|
+
reason: "source_cluster_monopoly",
|
|
8909
|
+
section_id: sid,
|
|
8910
|
+
details: `Every source in this section traces to a single publisher (${[...publishers][0]}).`,
|
|
8911
|
+
evidence_ids: sectionSources.map((s) => s.source_id),
|
|
8912
|
+
artifact_path: `sections/${sid}/sources.jsonl`
|
|
8913
|
+
},
|
|
8914
|
+
monopolyWaiver
|
|
8915
|
+
)
|
|
8916
|
+
);
|
|
8758
8917
|
}
|
|
8759
8918
|
if (publishers.size < cfg.min_independent_publishers) {
|
|
8760
|
-
out.push(
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8919
|
+
out.push(
|
|
8920
|
+
annotateWeakSource(
|
|
8921
|
+
{
|
|
8922
|
+
reason: "low_independent_publishers",
|
|
8923
|
+
section_id: sid,
|
|
8924
|
+
details: `${publishers.size} independent publisher(s) \u2014 pack policy requires at least ${cfg.min_independent_publishers}.`,
|
|
8925
|
+
evidence_ids: [...publishers],
|
|
8926
|
+
artifact_path: `sections/${sid}/sources.jsonl`
|
|
8927
|
+
},
|
|
8928
|
+
monopolyWaiver
|
|
8929
|
+
)
|
|
8930
|
+
);
|
|
8767
8931
|
}
|
|
8768
8932
|
const primary = sectionSources.filter((c) => c.source_type === "primary").length;
|
|
8769
8933
|
if (primary < cfg.primary_sources_required) {
|
|
8770
|
-
out.push(
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8934
|
+
out.push(
|
|
8935
|
+
annotateWeakSource(
|
|
8936
|
+
{
|
|
8937
|
+
reason: "missing_primary_source",
|
|
8938
|
+
section_id: sid,
|
|
8939
|
+
details: `${primary} primary source(s) \u2014 pack policy requires at least ${cfg.primary_sources_required}.`,
|
|
8940
|
+
evidence_ids: sectionSources.filter((c) => c.source_type === "primary").map((c) => c.source_id),
|
|
8941
|
+
artifact_path: `sections/${sid}/sources.jsonl`
|
|
8942
|
+
},
|
|
8943
|
+
primaryWaiver
|
|
8944
|
+
)
|
|
8945
|
+
);
|
|
8777
8946
|
}
|
|
8778
8947
|
const types = /* @__PURE__ */ new Map();
|
|
8779
8948
|
for (const c of sectionSources) types.set(c.source_type, (types.get(c.source_type) ?? 0) + 1);
|
|
@@ -8888,20 +9057,31 @@ function buildSourceDiversityGaps(input) {
|
|
|
8888
9057
|
});
|
|
8889
9058
|
continue;
|
|
8890
9059
|
}
|
|
9060
|
+
const monopolyWaiver = findSectionWaiver(input.research, sid, "min_independent_publishers");
|
|
8891
9061
|
if (publishers.size === 1 && sectionSources.length >= 2) {
|
|
8892
|
-
out.push(
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
9062
|
+
out.push(
|
|
9063
|
+
annotateDiversityGap(
|
|
9064
|
+
{
|
|
9065
|
+
reason: "section_publisher_monopoly",
|
|
9066
|
+
section_id: sid,
|
|
9067
|
+
details: `Section sources monopolized by ${[...publishers][0]}.`,
|
|
9068
|
+
evidence_ids: sectionSources.map((s) => s.source_id)
|
|
9069
|
+
},
|
|
9070
|
+
monopolyWaiver
|
|
9071
|
+
)
|
|
9072
|
+
);
|
|
8898
9073
|
} else if (publishers.size < cfg.min_independent_publishers) {
|
|
8899
|
-
out.push(
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
9074
|
+
out.push(
|
|
9075
|
+
annotateDiversityGap(
|
|
9076
|
+
{
|
|
9077
|
+
reason: "low_section_publisher_count",
|
|
9078
|
+
section_id: sid,
|
|
9079
|
+
details: `${publishers.size} publisher(s); pack policy requires ${cfg.min_independent_publishers}.`,
|
|
9080
|
+
evidence_ids: [...publishers]
|
|
9081
|
+
},
|
|
9082
|
+
monopolyWaiver
|
|
9083
|
+
)
|
|
9084
|
+
);
|
|
8905
9085
|
}
|
|
8906
9086
|
}
|
|
8907
9087
|
const pubSectionCount = /* @__PURE__ */ new Map();
|
|
@@ -11692,7 +11872,7 @@ var init_src = __esm({
|
|
|
11692
11872
|
init_triage();
|
|
11693
11873
|
init_discover();
|
|
11694
11874
|
init_errors();
|
|
11695
|
-
RESEARCH_OS_VERSION = "0.
|
|
11875
|
+
RESEARCH_OS_VERSION = "0.3.1";
|
|
11696
11876
|
}
|
|
11697
11877
|
});
|
|
11698
11878
|
|
|
@@ -12123,7 +12303,7 @@ init_synth();
|
|
|
12123
12303
|
init_audit();
|
|
12124
12304
|
init_freeze();
|
|
12125
12305
|
init_invalidate();
|
|
12126
|
-
import { Command } from "commander";
|
|
12306
|
+
import { Command, Option } from "commander";
|
|
12127
12307
|
|
|
12128
12308
|
// src/pack/publish/index.ts
|
|
12129
12309
|
import { existsSync as existsSync30, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
@@ -13054,13 +13234,21 @@ contradictCmd.command("map").description("Detect contradiction candidates among
|
|
|
13054
13234
|
"--triaged-only",
|
|
13055
13235
|
"Only consider claims that triage selected_for_review; reduces N\xB2 pair classification on dense sections",
|
|
13056
13236
|
false
|
|
13237
|
+
).addOption(
|
|
13238
|
+
new Option(
|
|
13239
|
+
"--detector <mode>",
|
|
13240
|
+
"Detector to use: auto (default, env-var-driven), heuristic (always fast, no LLM), ollama-intern (require LLM, fail visibly if unavailable)"
|
|
13241
|
+
).choices(["auto", "heuristic", "ollama-intern"]).default("auto")
|
|
13057
13242
|
).action(async (section, opts) => {
|
|
13058
13243
|
try {
|
|
13059
13244
|
const result = await map({
|
|
13060
13245
|
sectionId: section,
|
|
13061
13246
|
packPath: opts.pack,
|
|
13062
|
-
triagedOnly: opts.triagedOnly
|
|
13247
|
+
triagedOnly: opts.triagedOnly,
|
|
13248
|
+
detectorMode: opts.detector
|
|
13063
13249
|
});
|
|
13250
|
+
process.stdout.write(`${result.detectorAnnouncement}
|
|
13251
|
+
`);
|
|
13064
13252
|
process.stdout.write(`contradiction map complete
|
|
13065
13253
|
`);
|
|
13066
13254
|
process.stdout.write(` section: ${result.sectionId}
|