@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/index.js
CHANGED
|
@@ -112,7 +112,7 @@ var init_errors = __esm({
|
|
|
112
112
|
|
|
113
113
|
// src/intake/schema.ts
|
|
114
114
|
import { z } from "zod";
|
|
115
|
-
var SectionStatusSchema, SectionSchema, SourceFloorGateSchema, ClaimIntegrityGateSchema, FreshnessGateSchema, ContradictionGateSchema, SectionBudgetGateSchema, GateConfigSchema, PrimarySourceWaiverSchema, FreshnessRequirementsSchema, ReviewProfilePresetSchema, DEFAULT_REVIEW_PROFILES, ResearchYamlSchema;
|
|
115
|
+
var SectionStatusSchema, SectionSchema, SourceFloorGateSchema, ClaimIntegrityGateSchema, FreshnessGateSchema, ContradictionGateSchema, SectionBudgetGateSchema, GateConfigSchema, SectionScopedWaiverSchema, PrimarySourceWaiverSchema, FreshnessRequirementsSchema, ReviewProfilePresetSchema, DEFAULT_REVIEW_PROFILES, ResearchYamlSchema;
|
|
116
116
|
var init_schema = __esm({
|
|
117
117
|
"src/intake/schema.ts"() {
|
|
118
118
|
"use strict";
|
|
@@ -162,10 +162,20 @@ var init_schema = __esm({
|
|
|
162
162
|
contradiction: ContradictionGateSchema.default({}),
|
|
163
163
|
section_budget: SectionBudgetGateSchema.default({})
|
|
164
164
|
});
|
|
165
|
+
SectionScopedWaiverSchema = z.object({
|
|
166
|
+
section_id: z.string().regex(/^[0-9]{2}-[a-z0-9-]+$/, 'Section id must look like "01-landscape"'),
|
|
167
|
+
scope: z.enum(["min_independent_publishers", "primary_sources_required"]),
|
|
168
|
+
reason: z.string().min(1),
|
|
169
|
+
compensating_controls: z.array(z.string()).min(1)
|
|
170
|
+
});
|
|
165
171
|
PrimarySourceWaiverSchema = z.object({
|
|
166
172
|
status: z.enum(["none", "requested", "granted"]).default("none"),
|
|
167
173
|
reason: z.string().optional(),
|
|
168
|
-
compensating_controls: z.array(z.string()).default([])
|
|
174
|
+
compensating_controls: z.array(z.string()).default([]),
|
|
175
|
+
// Section-scoped waivers; each entry is its own waiver record. Independent
|
|
176
|
+
// of the pack-level status/reason/compensating_controls fields above.
|
|
177
|
+
// Defaults to [] for backward compatibility — existing packs unaffected.
|
|
178
|
+
section_waivers: z.array(SectionScopedWaiverSchema).default([])
|
|
169
179
|
});
|
|
170
180
|
FreshnessRequirementsSchema = z.object({
|
|
171
181
|
required: z.boolean().default(true),
|
|
@@ -3262,6 +3272,48 @@ function buildContradiction(args) {
|
|
|
3262
3272
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3263
3273
|
});
|
|
3264
3274
|
}
|
|
3275
|
+
async function resolveDetector(options) {
|
|
3276
|
+
const mode = options.detectorMode ?? "auto";
|
|
3277
|
+
if (!VALID_DETECTOR_MODES.includes(mode)) {
|
|
3278
|
+
throw new Error(
|
|
3279
|
+
`contradict map: invalid --detector value "${mode}"; valid values are: auto, heuristic, ollama-intern`
|
|
3280
|
+
);
|
|
3281
|
+
}
|
|
3282
|
+
if (mode === "heuristic") {
|
|
3283
|
+
return {
|
|
3284
|
+
detector: new HeuristicContradictionDetector(),
|
|
3285
|
+
announcement: "contradict map: using heuristic detector"
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
if (mode === "ollama-intern") {
|
|
3289
|
+
const d = new OllamaInternContradictionDetector(options.ollamaConfig ?? {});
|
|
3290
|
+
if (!await d.available()) {
|
|
3291
|
+
throw new Error(
|
|
3292
|
+
`contradict map: ollama-intern detector requested but model ${d.model} is unavailable; aborting (use --detector heuristic to bypass)`
|
|
3293
|
+
);
|
|
3294
|
+
}
|
|
3295
|
+
return {
|
|
3296
|
+
detector: d,
|
|
3297
|
+
announcement: `contradict map: using ollama-intern detector with model ${d.model}`
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
3300
|
+
const detectors = options.detectors ?? [
|
|
3301
|
+
new OllamaInternContradictionDetector(options.ollamaConfig ?? {}),
|
|
3302
|
+
new HeuristicContradictionDetector()
|
|
3303
|
+
];
|
|
3304
|
+
const detector = await pickContradictionDetector(detectors);
|
|
3305
|
+
if (detector.name === "ollama-intern") {
|
|
3306
|
+
const modelName = detector instanceof OllamaInternContradictionDetector ? detector.model : process.env.OLLAMA_INTERN_MODEL ?? "hermes3:8b";
|
|
3307
|
+
return {
|
|
3308
|
+
detector,
|
|
3309
|
+
announcement: `contradict map: using ollama-intern detector with model ${modelName}`
|
|
3310
|
+
};
|
|
3311
|
+
}
|
|
3312
|
+
return {
|
|
3313
|
+
detector,
|
|
3314
|
+
announcement: "contradict map: ollama-intern unavailable; using heuristic detector"
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3265
3317
|
async function map(options) {
|
|
3266
3318
|
const packPath = options.packPath ? resolve7(options.packPath) : process.cwd();
|
|
3267
3319
|
if (!existsSync9(join10(packPath, "research.yaml"))) throw new PackNotFoundError(packPath);
|
|
@@ -3273,8 +3325,7 @@ async function map(options) {
|
|
|
3273
3325
|
const allowed = await readTriagedClaimIds2(packPath, options.sectionId);
|
|
3274
3326
|
candidateClaims = candidateClaims.filter((c) => allowed.has(c.claim_id));
|
|
3275
3327
|
}
|
|
3276
|
-
const
|
|
3277
|
-
const detector = await pickContradictionDetector(adapters);
|
|
3328
|
+
const { detector, announcement } = await resolveDetector(options);
|
|
3278
3329
|
const summary = {
|
|
3279
3330
|
sectionId: options.sectionId,
|
|
3280
3331
|
detector: detector.name,
|
|
@@ -3284,7 +3335,8 @@ async function map(options) {
|
|
|
3284
3335
|
contradictionsAdded: 0,
|
|
3285
3336
|
contradictionsDeduped: 0,
|
|
3286
3337
|
contradictionIds: [],
|
|
3287
|
-
detectorError: null
|
|
3338
|
+
detectorError: null,
|
|
3339
|
+
detectorAnnouncement: announcement
|
|
3288
3340
|
};
|
|
3289
3341
|
const ledgerPath = join10(sectionDir, "contradictions.jsonl");
|
|
3290
3342
|
const mdPath = join10(sectionDir, "contradictions.md");
|
|
@@ -3348,7 +3400,7 @@ async function map(options) {
|
|
|
3348
3400
|
await writeFile9(mdPath, md, "utf8");
|
|
3349
3401
|
return summary;
|
|
3350
3402
|
}
|
|
3351
|
-
var DETECTOR_ID_PART;
|
|
3403
|
+
var DETECTOR_ID_PART, VALID_DETECTOR_MODES;
|
|
3352
3404
|
var init_map = __esm({
|
|
3353
3405
|
"src/contradictions/map.ts"() {
|
|
3354
3406
|
"use strict";
|
|
@@ -3356,11 +3408,14 @@ var init_map = __esm({
|
|
|
3356
3408
|
init_schema5();
|
|
3357
3409
|
init_schema7();
|
|
3358
3410
|
init_detectors();
|
|
3411
|
+
init_heuristic3();
|
|
3412
|
+
init_ollama_intern3();
|
|
3359
3413
|
init_markdown();
|
|
3360
3414
|
DETECTOR_ID_PART = {
|
|
3361
3415
|
heuristic: "heuristic",
|
|
3362
3416
|
"ollama-intern": "ollama_intern"
|
|
3363
3417
|
};
|
|
3418
|
+
VALID_DETECTOR_MODES = ["auto", "heuristic", "ollama-intern"];
|
|
3364
3419
|
}
|
|
3365
3420
|
});
|
|
3366
3421
|
|
|
@@ -4177,9 +4232,28 @@ function applyWaivers(input, results) {
|
|
|
4177
4232
|
const updated = results.map((r) => ({ ...r }));
|
|
4178
4233
|
const applied = [];
|
|
4179
4234
|
const validationFailures = [];
|
|
4180
|
-
|
|
4181
|
-
|
|
4235
|
+
applyPackLevelWaiver({
|
|
4236
|
+
waiver,
|
|
4237
|
+
cfg,
|
|
4238
|
+
updated,
|
|
4239
|
+
applied,
|
|
4240
|
+
validationFailures
|
|
4241
|
+
});
|
|
4242
|
+
for (const sw of waiver.section_waivers ?? []) {
|
|
4243
|
+
if (sw.section_id !== input.section.id) continue;
|
|
4244
|
+
applySectionScopedWaiver({
|
|
4245
|
+
waiver: sw,
|
|
4246
|
+
cfg,
|
|
4247
|
+
updated,
|
|
4248
|
+
applied,
|
|
4249
|
+
validationFailures
|
|
4250
|
+
});
|
|
4182
4251
|
}
|
|
4252
|
+
return { updatedResults: updated, waivers_applied: applied, waiver_validation_failures: validationFailures };
|
|
4253
|
+
}
|
|
4254
|
+
function applyPackLevelWaiver(args) {
|
|
4255
|
+
const { waiver, cfg, updated, applied, validationFailures } = args;
|
|
4256
|
+
if (waiver.status !== "granted") return;
|
|
4183
4257
|
if (!waiver.reason || waiver.reason.trim().length === 0) {
|
|
4184
4258
|
validationFailures.push({
|
|
4185
4259
|
family: "waivers",
|
|
@@ -4189,7 +4263,7 @@ function applyWaivers(input, results) {
|
|
|
4189
4263
|
evidence: [],
|
|
4190
4264
|
blocks_synthesis: true
|
|
4191
4265
|
});
|
|
4192
|
-
return
|
|
4266
|
+
return;
|
|
4193
4267
|
}
|
|
4194
4268
|
if (waiver.compensating_controls.length === 0) {
|
|
4195
4269
|
validationFailures.push({
|
|
@@ -4200,7 +4274,7 @@ function applyWaivers(input, results) {
|
|
|
4200
4274
|
evidence: [],
|
|
4201
4275
|
blocks_synthesis: true
|
|
4202
4276
|
});
|
|
4203
|
-
return
|
|
4277
|
+
return;
|
|
4204
4278
|
}
|
|
4205
4279
|
if (!cfg.primary_source_waiver_allowed) {
|
|
4206
4280
|
validationFailures.push({
|
|
@@ -4211,7 +4285,7 @@ function applyWaivers(input, results) {
|
|
|
4211
4285
|
evidence: [],
|
|
4212
4286
|
blocks_synthesis: true
|
|
4213
4287
|
});
|
|
4214
|
-
return
|
|
4288
|
+
return;
|
|
4215
4289
|
}
|
|
4216
4290
|
for (let i = 0; i < updated.length; i += 1) {
|
|
4217
4291
|
const r = updated[i];
|
|
@@ -4233,7 +4307,62 @@ function applyWaivers(input, results) {
|
|
|
4233
4307
|
});
|
|
4234
4308
|
}
|
|
4235
4309
|
}
|
|
4236
|
-
|
|
4310
|
+
}
|
|
4311
|
+
function applySectionScopedWaiver(args) {
|
|
4312
|
+
const { waiver, cfg, updated, applied, validationFailures } = args;
|
|
4313
|
+
if (!waiver.reason || waiver.reason.trim().length === 0) {
|
|
4314
|
+
validationFailures.push({
|
|
4315
|
+
family: "waivers",
|
|
4316
|
+
check: "section_scoped_waiver_reason_required",
|
|
4317
|
+
status: "fail",
|
|
4318
|
+
detail: `Section-scoped waiver for ${waiver.section_id}/${waiver.scope} is missing a reason. Waiver is invalid; original failure stands.`,
|
|
4319
|
+
evidence: [waiver.section_id],
|
|
4320
|
+
blocks_synthesis: true
|
|
4321
|
+
});
|
|
4322
|
+
return;
|
|
4323
|
+
}
|
|
4324
|
+
if (waiver.compensating_controls.length === 0) {
|
|
4325
|
+
validationFailures.push({
|
|
4326
|
+
family: "waivers",
|
|
4327
|
+
check: "section_scoped_waiver_compensating_controls_required",
|
|
4328
|
+
status: "fail",
|
|
4329
|
+
detail: `Section-scoped waiver for ${waiver.section_id}/${waiver.scope} has empty compensating_controls. Waiver is invalid; original failure stands.`,
|
|
4330
|
+
evidence: [waiver.section_id],
|
|
4331
|
+
blocks_synthesis: true
|
|
4332
|
+
});
|
|
4333
|
+
return;
|
|
4334
|
+
}
|
|
4335
|
+
if (!cfg.primary_source_waiver_allowed) {
|
|
4336
|
+
validationFailures.push({
|
|
4337
|
+
family: "waivers",
|
|
4338
|
+
check: "section_scoped_waiver_allowed_by_pack",
|
|
4339
|
+
status: "fail",
|
|
4340
|
+
detail: `Pack policy gates.source_floor.primary_source_waiver_allowed=false; section-scoped waiver for ${waiver.section_id}/${waiver.scope} cannot be applied.`,
|
|
4341
|
+
evidence: [waiver.section_id],
|
|
4342
|
+
blocks_synthesis: true
|
|
4343
|
+
});
|
|
4344
|
+
return;
|
|
4345
|
+
}
|
|
4346
|
+
for (let i = 0; i < updated.length; i += 1) {
|
|
4347
|
+
const r = updated[i];
|
|
4348
|
+
if (r.family === "source_floor" && r.check === waiver.scope && r.status === "fail") {
|
|
4349
|
+
const original = r.status;
|
|
4350
|
+
updated[i] = {
|
|
4351
|
+
...r,
|
|
4352
|
+
status: "pass_with_waiver",
|
|
4353
|
+
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.`,
|
|
4354
|
+
blocks_synthesis: false
|
|
4355
|
+
};
|
|
4356
|
+
applied.push({
|
|
4357
|
+
family: "source_floor",
|
|
4358
|
+
check: waiver.scope,
|
|
4359
|
+
reason: waiver.reason,
|
|
4360
|
+
compensating_controls: waiver.compensating_controls,
|
|
4361
|
+
original_status: original,
|
|
4362
|
+
new_status: "pass_with_waiver"
|
|
4363
|
+
});
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4237
4366
|
}
|
|
4238
4367
|
var init_waivers = __esm({
|
|
4239
4368
|
"src/gates/checks/waivers.ts"() {
|
|
@@ -5490,9 +5619,11 @@ function pickHighestPriority(decisions) {
|
|
|
5490
5619
|
return "accepted_for_synthesis";
|
|
5491
5620
|
}
|
|
5492
5621
|
function deriveClaimReviews(args) {
|
|
5493
|
-
const { claims, findings, reviewer, reviewMethod } = args;
|
|
5622
|
+
const { claims, findings, reviewer, reviewMethod, activeSectionWaivers } = args;
|
|
5494
5623
|
const reviews = [];
|
|
5495
5624
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5625
|
+
const monopolyWaived = Array.isArray(activeSectionWaivers) && activeSectionWaivers.some((w) => w.scope === "min_independent_publishers");
|
|
5626
|
+
const isWaivedFinding = (f) => monopolyWaived && f.category === "source_cluster_monopoly";
|
|
5496
5627
|
for (const claim of claims) {
|
|
5497
5628
|
const claimFindings = findings.filter((f) => f.claim_ids.includes(claim.claim_id));
|
|
5498
5629
|
if (claimFindings.length === 0) {
|
|
@@ -5509,6 +5640,7 @@ function deriveClaimReviews(args) {
|
|
|
5509
5640
|
}
|
|
5510
5641
|
let decisions = [];
|
|
5511
5642
|
for (const f of claimFindings) {
|
|
5643
|
+
if (isWaivedFinding(f)) continue;
|
|
5512
5644
|
if (f.severity === "block") {
|
|
5513
5645
|
decisions.push(BLOCK_TO_DECISION[f.category] ?? "rejected");
|
|
5514
5646
|
} else if (f.severity === "warn") {
|
|
@@ -5525,7 +5657,9 @@ function deriveClaimReviews(args) {
|
|
|
5525
5657
|
);
|
|
5526
5658
|
}
|
|
5527
5659
|
const decision = pickHighestPriority(decisions);
|
|
5528
|
-
const reasonParts = claimFindings.filter((f) => f.severity !== "info").map(
|
|
5660
|
+
const reasonParts = claimFindings.filter((f) => f.severity !== "info").map(
|
|
5661
|
+
(f) => isWaivedFinding(f) ? `${f.category} (${f.severity}, waived)` : `${f.category} (${f.severity})`
|
|
5662
|
+
);
|
|
5529
5663
|
const reason = reasonParts.length > 0 ? `Findings: ${reasonParts.join("; ")}.` : "Only info-level findings; accepted.";
|
|
5530
5664
|
reviews.push({
|
|
5531
5665
|
claim_id: claim.claim_id,
|
|
@@ -5906,7 +6040,8 @@ async function review(options) {
|
|
|
5906
6040
|
candidateClaims,
|
|
5907
6041
|
drafts: acceptedDrafts,
|
|
5908
6042
|
llmFindingsRejected,
|
|
5909
|
-
profile: options.profile ?? DEFAULT_PROFILE
|
|
6043
|
+
profile: options.profile ?? DEFAULT_PROFILE,
|
|
6044
|
+
research
|
|
5910
6045
|
});
|
|
5911
6046
|
}
|
|
5912
6047
|
async function runMultiPassReview(args) {
|
|
@@ -5968,7 +6103,8 @@ async function runMultiPassReview(args) {
|
|
|
5968
6103
|
candidateClaims: args.candidateClaims,
|
|
5969
6104
|
drafts: merged,
|
|
5970
6105
|
llmFindingsRejected,
|
|
5971
|
-
profile: args.options.profile ?? DEFAULT_PROFILE
|
|
6106
|
+
profile: args.options.profile ?? DEFAULT_PROFILE,
|
|
6107
|
+
research: args.research
|
|
5972
6108
|
});
|
|
5973
6109
|
}
|
|
5974
6110
|
async function reviewWithSpecificReviewer(args) {
|
|
@@ -5996,7 +6132,8 @@ async function reviewWithSpecificReviewer(args) {
|
|
|
5996
6132
|
candidateClaims: args.candidateClaims,
|
|
5997
6133
|
drafts: result.drafts,
|
|
5998
6134
|
llmFindingsRejected: 0,
|
|
5999
|
-
profile: args.options.profile ?? DEFAULT_PROFILE
|
|
6135
|
+
profile: args.options.profile ?? DEFAULT_PROFILE,
|
|
6136
|
+
research: args.research
|
|
6000
6137
|
});
|
|
6001
6138
|
}
|
|
6002
6139
|
async function finalizeReview(args) {
|
|
@@ -6019,11 +6156,15 @@ async function finalizeReview(args) {
|
|
|
6019
6156
|
seen.add(f.finding_id);
|
|
6020
6157
|
dedupedFindings.push(f);
|
|
6021
6158
|
}
|
|
6159
|
+
const activeSectionWaivers = args.research.primary_source_waiver.section_waivers.filter(
|
|
6160
|
+
(w) => w.section_id === args.sectionId
|
|
6161
|
+
);
|
|
6022
6162
|
const claimReviews = deriveClaimReviews({
|
|
6023
6163
|
claims: args.candidateClaims,
|
|
6024
6164
|
findings: dedupedFindings,
|
|
6025
6165
|
reviewer: args.reviewer,
|
|
6026
|
-
reviewMethod: args.reviewMethod
|
|
6166
|
+
reviewMethod: args.reviewMethod,
|
|
6167
|
+
activeSectionWaivers
|
|
6027
6168
|
});
|
|
6028
6169
|
const decisionCounts = {
|
|
6029
6170
|
accepted_for_synthesis: 0,
|
|
@@ -8750,6 +8891,19 @@ function buildStaleSources(input) {
|
|
|
8750
8891
|
}
|
|
8751
8892
|
return out;
|
|
8752
8893
|
}
|
|
8894
|
+
function findSectionWaiver(research, sectionId, scope) {
|
|
8895
|
+
return (research.primary_source_waiver.section_waivers ?? []).find(
|
|
8896
|
+
(w) => w.section_id === sectionId && w.scope === scope
|
|
8897
|
+
);
|
|
8898
|
+
}
|
|
8899
|
+
function annotateWeakSource(row, waiver) {
|
|
8900
|
+
if (!waiver) return row;
|
|
8901
|
+
return { ...row, waived: true, waiver_reason: waiver.reason };
|
|
8902
|
+
}
|
|
8903
|
+
function annotateDiversityGap(row, waiver) {
|
|
8904
|
+
if (!waiver) return row;
|
|
8905
|
+
return { ...row, waived: true, waiver_reason: waiver.reason };
|
|
8906
|
+
}
|
|
8753
8907
|
function buildWeakSources(input) {
|
|
8754
8908
|
const out = [];
|
|
8755
8909
|
const cfg = input.research.gates.source_floor;
|
|
@@ -8759,33 +8913,50 @@ function buildWeakSources(input) {
|
|
|
8759
8913
|
const publishers = new Set(
|
|
8760
8914
|
sectionSources.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
8761
8915
|
);
|
|
8916
|
+
const monopolyWaiver = findSectionWaiver(input.research, sid, "min_independent_publishers");
|
|
8917
|
+
const primaryWaiver = findSectionWaiver(input.research, sid, "primary_sources_required");
|
|
8762
8918
|
if (publishers.size === 1 && sectionSources.length >= 2) {
|
|
8763
|
-
out.push(
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8919
|
+
out.push(
|
|
8920
|
+
annotateWeakSource(
|
|
8921
|
+
{
|
|
8922
|
+
reason: "source_cluster_monopoly",
|
|
8923
|
+
section_id: sid,
|
|
8924
|
+
details: `Every source in this section traces to a single publisher (${[...publishers][0]}).`,
|
|
8925
|
+
evidence_ids: sectionSources.map((s) => s.source_id),
|
|
8926
|
+
artifact_path: `sections/${sid}/sources.jsonl`
|
|
8927
|
+
},
|
|
8928
|
+
monopolyWaiver
|
|
8929
|
+
)
|
|
8930
|
+
);
|
|
8770
8931
|
}
|
|
8771
8932
|
if (publishers.size < cfg.min_independent_publishers) {
|
|
8772
|
-
out.push(
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8933
|
+
out.push(
|
|
8934
|
+
annotateWeakSource(
|
|
8935
|
+
{
|
|
8936
|
+
reason: "low_independent_publishers",
|
|
8937
|
+
section_id: sid,
|
|
8938
|
+
details: `${publishers.size} independent publisher(s) \u2014 pack policy requires at least ${cfg.min_independent_publishers}.`,
|
|
8939
|
+
evidence_ids: [...publishers],
|
|
8940
|
+
artifact_path: `sections/${sid}/sources.jsonl`
|
|
8941
|
+
},
|
|
8942
|
+
monopolyWaiver
|
|
8943
|
+
)
|
|
8944
|
+
);
|
|
8779
8945
|
}
|
|
8780
8946
|
const primary = sectionSources.filter((c) => c.source_type === "primary").length;
|
|
8781
8947
|
if (primary < cfg.primary_sources_required) {
|
|
8782
|
-
out.push(
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8948
|
+
out.push(
|
|
8949
|
+
annotateWeakSource(
|
|
8950
|
+
{
|
|
8951
|
+
reason: "missing_primary_source",
|
|
8952
|
+
section_id: sid,
|
|
8953
|
+
details: `${primary} primary source(s) \u2014 pack policy requires at least ${cfg.primary_sources_required}.`,
|
|
8954
|
+
evidence_ids: sectionSources.filter((c) => c.source_type === "primary").map((c) => c.source_id),
|
|
8955
|
+
artifact_path: `sections/${sid}/sources.jsonl`
|
|
8956
|
+
},
|
|
8957
|
+
primaryWaiver
|
|
8958
|
+
)
|
|
8959
|
+
);
|
|
8789
8960
|
}
|
|
8790
8961
|
const types = /* @__PURE__ */ new Map();
|
|
8791
8962
|
for (const c of sectionSources) types.set(c.source_type, (types.get(c.source_type) ?? 0) + 1);
|
|
@@ -8900,20 +9071,31 @@ function buildSourceDiversityGaps(input) {
|
|
|
8900
9071
|
});
|
|
8901
9072
|
continue;
|
|
8902
9073
|
}
|
|
9074
|
+
const monopolyWaiver = findSectionWaiver(input.research, sid, "min_independent_publishers");
|
|
8903
9075
|
if (publishers.size === 1 && sectionSources.length >= 2) {
|
|
8904
|
-
out.push(
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
9076
|
+
out.push(
|
|
9077
|
+
annotateDiversityGap(
|
|
9078
|
+
{
|
|
9079
|
+
reason: "section_publisher_monopoly",
|
|
9080
|
+
section_id: sid,
|
|
9081
|
+
details: `Section sources monopolized by ${[...publishers][0]}.`,
|
|
9082
|
+
evidence_ids: sectionSources.map((s) => s.source_id)
|
|
9083
|
+
},
|
|
9084
|
+
monopolyWaiver
|
|
9085
|
+
)
|
|
9086
|
+
);
|
|
8910
9087
|
} else if (publishers.size < cfg.min_independent_publishers) {
|
|
8911
|
-
out.push(
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
9088
|
+
out.push(
|
|
9089
|
+
annotateDiversityGap(
|
|
9090
|
+
{
|
|
9091
|
+
reason: "low_section_publisher_count",
|
|
9092
|
+
section_id: sid,
|
|
9093
|
+
details: `${publishers.size} publisher(s); pack policy requires ${cfg.min_independent_publishers}.`,
|
|
9094
|
+
evidence_ids: [...publishers]
|
|
9095
|
+
},
|
|
9096
|
+
monopolyWaiver
|
|
9097
|
+
)
|
|
9098
|
+
);
|
|
8917
9099
|
}
|
|
8918
9100
|
}
|
|
8919
9101
|
const pubSectionCount = /* @__PURE__ */ new Map();
|
|
@@ -12120,7 +12302,7 @@ var init_src = __esm({
|
|
|
12120
12302
|
init_triage();
|
|
12121
12303
|
init_discover();
|
|
12122
12304
|
init_errors();
|
|
12123
|
-
RESEARCH_OS_VERSION = "0.
|
|
12305
|
+
RESEARCH_OS_VERSION = "0.3.1";
|
|
12124
12306
|
}
|
|
12125
12307
|
});
|
|
12126
12308
|
init_src();
|