@mcptoolshop/research-os 0.3.1 → 0.3.3
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 +181 -0
- package/README.es.md +101 -71
- package/README.fr.md +88 -58
- package/README.hi.md +85 -59
- package/README.it.md +99 -73
- package/README.ja.md +88 -58
- package/README.md +7 -3
- package/README.pt-BR.md +85 -59
- package/README.zh.md +104 -74
- package/dist/cli.js +123 -40
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +28 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3627,6 +3627,7 @@ function checkSourceFloor(input) {
|
|
|
3627
3627
|
const cfg = input.research.gates.source_floor;
|
|
3628
3628
|
const results = [];
|
|
3629
3629
|
const cards = input.sources;
|
|
3630
|
+
const sectionCards = cards.filter((c) => c.section_id === input.section.id);
|
|
3630
3631
|
const sourceCount = cards.length;
|
|
3631
3632
|
if (sourceCount < cfg.min_sources) {
|
|
3632
3633
|
results.push({
|
|
@@ -3650,12 +3651,15 @@ function checkSourceFloor(input) {
|
|
|
3650
3651
|
const publishers = new Set(
|
|
3651
3652
|
cards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
3652
3653
|
);
|
|
3654
|
+
const sectionPublishers = new Set(
|
|
3655
|
+
sectionCards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
3656
|
+
);
|
|
3653
3657
|
if (publishers.size < cfg.min_independent_publishers) {
|
|
3654
3658
|
results.push({
|
|
3655
3659
|
family: "source_floor",
|
|
3656
3660
|
check: "min_independent_publishers",
|
|
3657
3661
|
status: "fail",
|
|
3658
|
-
detail: `Found ${publishers.size} independent publisher(s); minimum ${cfg.min_independent_publishers} required
|
|
3662
|
+
detail: `Found ${publishers.size} independent publisher(s); minimum ${cfg.min_independent_publishers} required. (pack-wide=${publishers.size}, section-scoped=${sectionPublishers.size})`,
|
|
3659
3663
|
evidence: [...publishers],
|
|
3660
3664
|
blocks_synthesis: true
|
|
3661
3665
|
});
|
|
@@ -3664,18 +3668,19 @@ function checkSourceFloor(input) {
|
|
|
3664
3668
|
family: "source_floor",
|
|
3665
3669
|
check: "min_independent_publishers",
|
|
3666
3670
|
status: "pass",
|
|
3667
|
-
detail: `${publishers.size} independent publisher(s) >= minimum ${cfg.min_independent_publishers}
|
|
3671
|
+
detail: `${publishers.size} independent publisher(s) >= minimum ${cfg.min_independent_publishers}. (pack-wide=${publishers.size}, section-scoped=${sectionPublishers.size})`,
|
|
3668
3672
|
evidence: [],
|
|
3669
3673
|
blocks_synthesis: false
|
|
3670
3674
|
});
|
|
3671
3675
|
}
|
|
3672
3676
|
const primaryCount = cards.filter((c) => c.source_type === "primary").length;
|
|
3677
|
+
const sectionPrimaryCount = sectionCards.filter((c) => c.source_type === "primary").length;
|
|
3673
3678
|
if (primaryCount < cfg.primary_sources_required) {
|
|
3674
3679
|
results.push({
|
|
3675
3680
|
family: "source_floor",
|
|
3676
3681
|
check: "primary_sources_required",
|
|
3677
3682
|
status: "fail",
|
|
3678
|
-
detail: `Found ${primaryCount} primary source(s); minimum ${cfg.primary_sources_required} required. Pre-waiver
|
|
3683
|
+
detail: `Found ${primaryCount} primary source(s); minimum ${cfg.primary_sources_required} required. Pre-waiver. (pack-wide=${primaryCount}, section-scoped=${sectionPrimaryCount})`,
|
|
3679
3684
|
evidence: cards.filter((c) => c.source_type === "primary").map((c) => c.source_id),
|
|
3680
3685
|
blocks_synthesis: true
|
|
3681
3686
|
});
|
|
@@ -3684,7 +3689,7 @@ function checkSourceFloor(input) {
|
|
|
3684
3689
|
family: "source_floor",
|
|
3685
3690
|
check: "primary_sources_required",
|
|
3686
3691
|
status: "pass",
|
|
3687
|
-
detail: `${primaryCount} primary source(s) >= minimum ${cfg.primary_sources_required}
|
|
3692
|
+
detail: `${primaryCount} primary source(s) >= minimum ${cfg.primary_sources_required}. (pack-wide=${primaryCount}, section-scoped=${sectionPrimaryCount})`,
|
|
3688
3693
|
evidence: [],
|
|
3689
3694
|
blocks_synthesis: false
|
|
3690
3695
|
});
|
|
@@ -3838,25 +3843,14 @@ function checkClaimIntegrity(input) {
|
|
|
3838
3843
|
monoClaims.push(claim.claim_id);
|
|
3839
3844
|
}
|
|
3840
3845
|
}
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
});
|
|
3850
|
-
} else {
|
|
3851
|
-
results.push({
|
|
3852
|
-
family: "claim_integrity",
|
|
3853
|
-
check: "no_source_cluster_monopoly",
|
|
3854
|
-
status: "pass",
|
|
3855
|
-
detail: `${monoClaims.length}/${claims.length} claim(s) source from a single publisher (within tolerance).`,
|
|
3856
|
-
evidence: [],
|
|
3857
|
-
blocks_synthesis: false
|
|
3858
|
-
});
|
|
3859
|
-
}
|
|
3846
|
+
results.push({
|
|
3847
|
+
family: "claim_integrity",
|
|
3848
|
+
check: "no_source_cluster_monopoly",
|
|
3849
|
+
status: "pass",
|
|
3850
|
+
detail: `${monoClaims.length}/${claims.length} claim(s) are single-source by architecture (each claim references exactly one source). Publisher diversity is enforced at source-card level via min_independent_publishers.`,
|
|
3851
|
+
evidence: [],
|
|
3852
|
+
blocks_synthesis: false
|
|
3853
|
+
});
|
|
3860
3854
|
}
|
|
3861
3855
|
return results;
|
|
3862
3856
|
}
|
|
@@ -4580,7 +4574,9 @@ var init_schema10 = __esm({
|
|
|
4580
4574
|
docs: z11.number().int().nonnegative(),
|
|
4581
4575
|
unknown: z11.number().int().nonnegative(),
|
|
4582
4576
|
independent_publishers: z11.number().int().nonnegative(),
|
|
4583
|
-
failed_fetches: 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()
|
|
4584
4580
|
}),
|
|
4585
4581
|
contradiction_counts: z11.object({
|
|
4586
4582
|
total: z11.number().int().nonnegative(),
|
|
@@ -4653,10 +4649,14 @@ function summarizeClaimCounts(input) {
|
|
|
4653
4649
|
}
|
|
4654
4650
|
function summarizeSourceCounts(input) {
|
|
4655
4651
|
const cards = input.sources;
|
|
4652
|
+
const sectionCards = cards.filter((c) => c.section_id === input.section.id);
|
|
4656
4653
|
const failed = input.receipts.filter((r) => r.fetch_outcome !== "ok").length;
|
|
4657
4654
|
const publishers = new Set(
|
|
4658
4655
|
cards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
4659
4656
|
);
|
|
4657
|
+
const sectionPublishers = new Set(
|
|
4658
|
+
sectionCards.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
4659
|
+
);
|
|
4660
4660
|
return {
|
|
4661
4661
|
total: cards.length,
|
|
4662
4662
|
primary: cards.filter((c) => c.source_type === "primary").length,
|
|
@@ -4666,7 +4666,9 @@ function summarizeSourceCounts(input) {
|
|
|
4666
4666
|
docs: cards.filter((c) => c.source_type === "docs").length,
|
|
4667
4667
|
unknown: cards.filter((c) => c.source_type === "unknown").length,
|
|
4668
4668
|
independent_publishers: publishers.size,
|
|
4669
|
-
failed_fetches: failed
|
|
4669
|
+
failed_fetches: failed,
|
|
4670
|
+
section_primary: sectionCards.filter((c) => c.source_type === "primary").length,
|
|
4671
|
+
section_independent_publishers: sectionPublishers.size
|
|
4670
4672
|
};
|
|
4671
4673
|
}
|
|
4672
4674
|
function buildEffectiveStatuses3(resolutions) {
|
|
@@ -11872,7 +11874,7 @@ var init_src = __esm({
|
|
|
11872
11874
|
init_triage();
|
|
11873
11875
|
init_discover();
|
|
11874
11876
|
init_errors();
|
|
11875
|
-
RESEARCH_OS_VERSION = "0.3.
|
|
11877
|
+
RESEARCH_OS_VERSION = "0.3.3";
|
|
11876
11878
|
}
|
|
11877
11879
|
});
|
|
11878
11880
|
|
|
@@ -12311,12 +12313,55 @@ import { join as join31, basename, resolve as resolve24 } from "path";
|
|
|
12311
12313
|
|
|
12312
12314
|
// src/pack/publish/manifest.ts
|
|
12313
12315
|
init_schema();
|
|
12316
|
+
init_schema9();
|
|
12314
12317
|
import { createHash as createHash11 } from "crypto";
|
|
12315
12318
|
import { readFileSync, existsSync as existsSync28 } from "fs";
|
|
12316
12319
|
import { join as join28 } from "path";
|
|
12317
12320
|
import { parse as parseYaml } from "yaml";
|
|
12318
12321
|
import { z as z23 } from "zod";
|
|
12319
12322
|
|
|
12323
|
+
// src/closure-ledger/effective-accepted.ts
|
|
12324
|
+
function getEffectiveDecisionMap(reviews) {
|
|
12325
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
12326
|
+
for (const r of reviews) {
|
|
12327
|
+
const existing = map2.get(r.claim_id);
|
|
12328
|
+
if (!existing || r.created_at > existing.created_at) {
|
|
12329
|
+
map2.set(r.claim_id, r);
|
|
12330
|
+
}
|
|
12331
|
+
}
|
|
12332
|
+
return map2;
|
|
12333
|
+
}
|
|
12334
|
+
function getEffectiveAcceptedClaimIds(reviews) {
|
|
12335
|
+
const decisionMap = getEffectiveDecisionMap(reviews);
|
|
12336
|
+
const accepted = /* @__PURE__ */ new Set();
|
|
12337
|
+
for (const [claim_id, r] of decisionMap) {
|
|
12338
|
+
if (r.decision === "accepted_for_synthesis") accepted.add(claim_id);
|
|
12339
|
+
}
|
|
12340
|
+
return accepted;
|
|
12341
|
+
}
|
|
12342
|
+
function findIncompatibleDecisions(reviews) {
|
|
12343
|
+
const groups = /* @__PURE__ */ new Map();
|
|
12344
|
+
for (const r of reviews) {
|
|
12345
|
+
const key = `${r.claim_id}|${r.created_at}`;
|
|
12346
|
+
let set = groups.get(key);
|
|
12347
|
+
if (!set) {
|
|
12348
|
+
set = /* @__PURE__ */ new Set();
|
|
12349
|
+
groups.set(key, set);
|
|
12350
|
+
}
|
|
12351
|
+
set.add(r.decision);
|
|
12352
|
+
}
|
|
12353
|
+
const conflicts = [];
|
|
12354
|
+
for (const [key, decisions] of groups) {
|
|
12355
|
+
if (decisions.size > 1) {
|
|
12356
|
+
const sep3 = key.lastIndexOf("|");
|
|
12357
|
+
const claim_id = key.slice(0, sep3);
|
|
12358
|
+
const created_at = key.slice(sep3 + 1);
|
|
12359
|
+
conflicts.push({ claim_id, created_at, decisions: [...decisions] });
|
|
12360
|
+
}
|
|
12361
|
+
}
|
|
12362
|
+
return conflicts;
|
|
12363
|
+
}
|
|
12364
|
+
|
|
12320
12365
|
// src/pack/publish/schema.ts
|
|
12321
12366
|
import { z as z22 } from "zod";
|
|
12322
12367
|
var SectionSummarySchema = z22.object({
|
|
@@ -12348,6 +12393,7 @@ var GateResultMinimalSchema = z23.object({
|
|
|
12348
12393
|
verdict: z23.enum(["pass", "warn", "fail", "blocked"]),
|
|
12349
12394
|
synthesis_eligible: z23.boolean()
|
|
12350
12395
|
});
|
|
12396
|
+
var ClaimIdOnlySchema = z23.object({ claim_id: z23.string() });
|
|
12351
12397
|
function sha256Bytes(buf) {
|
|
12352
12398
|
return createHash11("sha256").update(buf).digest("hex");
|
|
12353
12399
|
}
|
|
@@ -12358,11 +12404,15 @@ function readJsonlSafe(filePath) {
|
|
|
12358
12404
|
if (!existsSync28(filePath)) return [];
|
|
12359
12405
|
return parseJsonl(readFileSync(filePath, "utf8"));
|
|
12360
12406
|
}
|
|
12361
|
-
function
|
|
12362
|
-
|
|
12363
|
-
const
|
|
12364
|
-
|
|
12365
|
-
|
|
12407
|
+
function readClaimReviews(filePath) {
|
|
12408
|
+
if (!existsSync28(filePath)) return [];
|
|
12409
|
+
const raw = parseJsonl(readFileSync(filePath, "utf8"));
|
|
12410
|
+
const valid = [];
|
|
12411
|
+
for (const r of raw) {
|
|
12412
|
+
const parsed = ClaimReviewSchema.safeParse(r);
|
|
12413
|
+
if (parsed.success) valid.push(parsed.data);
|
|
12414
|
+
}
|
|
12415
|
+
return valid;
|
|
12366
12416
|
}
|
|
12367
12417
|
function latestContradictionStatuses(resolutions) {
|
|
12368
12418
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -12376,7 +12426,7 @@ function latestDispositionStatuses(dispositions) {
|
|
|
12376
12426
|
for (const d of sorted) m.set(d.claim_id, "dispositioned");
|
|
12377
12427
|
return m;
|
|
12378
12428
|
}
|
|
12379
|
-
function deriveManifest(packDir, packageName, operatorNotes = "") {
|
|
12429
|
+
function deriveManifest(packDir, packageName, operatorNotes = "", warnings = []) {
|
|
12380
12430
|
const yamlPath = join28(packDir, "research.yaml");
|
|
12381
12431
|
if (!existsSync28(yamlPath)) throw new Error(`research.yaml not found in ${packDir}`);
|
|
12382
12432
|
const research = ResearchYamlSchema.parse(parseYaml(readFileSync(yamlPath, "utf8")));
|
|
@@ -12404,15 +12454,43 @@ function deriveManifest(packDir, packageName, operatorNotes = "") {
|
|
|
12404
12454
|
let totalPreserved = 0;
|
|
12405
12455
|
for (const sectionId of sectionIds) {
|
|
12406
12456
|
const sectionDir = join28(packDir, "sections", sectionId);
|
|
12407
|
-
const
|
|
12408
|
-
const
|
|
12409
|
-
const
|
|
12410
|
-
|
|
12411
|
-
|
|
12457
|
+
const reviewsPath = join28(sectionDir, "claim-reviews.jsonl");
|
|
12458
|
+
const reviews = readClaimReviews(reviewsPath);
|
|
12459
|
+
const conflicts = findIncompatibleDecisions(reviews);
|
|
12460
|
+
if (conflicts.length > 0) {
|
|
12461
|
+
const first = conflicts[0];
|
|
12462
|
+
throw new Error(
|
|
12463
|
+
`Section ${sectionId}: claim-reviews.jsonl has incompatible decisions for claim_id=${first.claim_id} at created_at=${first.created_at} (decisions seen: ${first.decisions.join(", ")}). Latest-decision-wins tie-breaker undefined \u2014 investigate the review pipeline state.` + (conflicts.length > 1 ? ` (${conflicts.length - 1} other claim_id(s) similarly affected.)` : "")
|
|
12464
|
+
);
|
|
12465
|
+
}
|
|
12466
|
+
const effectiveAccepted = getEffectiveAcceptedClaimIds(reviews);
|
|
12467
|
+
const acceptedCount = effectiveAccepted.size;
|
|
12468
|
+
const claimsPath = join28(sectionDir, "claims.jsonl");
|
|
12469
|
+
if (existsSync28(claimsPath)) {
|
|
12470
|
+
const claimRows = parseJsonl(readFileSync(claimsPath, "utf8"));
|
|
12471
|
+
const claimIds = /* @__PURE__ */ new Set();
|
|
12472
|
+
for (const c of claimRows) {
|
|
12473
|
+
const parsed = ClaimIdOnlySchema.safeParse(c);
|
|
12474
|
+
if (parsed.success) claimIds.add(parsed.data.claim_id);
|
|
12475
|
+
}
|
|
12476
|
+
const phantoms = [];
|
|
12477
|
+
for (const cid of effectiveAccepted) {
|
|
12478
|
+
if (!claimIds.has(cid)) phantoms.push(cid);
|
|
12479
|
+
}
|
|
12480
|
+
if (phantoms.length > 0) {
|
|
12481
|
+
throw new Error(
|
|
12482
|
+
`Section ${sectionId}: ${phantoms.length} effective accepted claim_id(s) absent from claims.jsonl \u2014 phantom claims violate the closure-ledger subset invariant. Examples: ${phantoms.slice(0, 3).join(", ")}` + (phantoms.length > 3 ? ` (+${phantoms.length - 3} more)` : "")
|
|
12483
|
+
);
|
|
12484
|
+
}
|
|
12485
|
+
} else {
|
|
12486
|
+
warnings.push(
|
|
12487
|
+
`section ${sectionId}: claims.jsonl absent \u2014 phantom-claim integrity check skipped`
|
|
12488
|
+
);
|
|
12489
|
+
}
|
|
12412
12490
|
const auditAccepted = auditSectionMap.get(sectionId);
|
|
12413
12491
|
if (auditAccepted !== void 0 && auditAccepted !== acceptedCount) {
|
|
12414
|
-
|
|
12415
|
-
`
|
|
12492
|
+
warnings.push(
|
|
12493
|
+
`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).`
|
|
12416
12494
|
);
|
|
12417
12495
|
}
|
|
12418
12496
|
const gateResultPath = join28(packDir, "audits", `${sectionId}-gate.json`);
|
|
@@ -12422,6 +12500,11 @@ function deriveManifest(packDir, packageName, operatorNotes = "") {
|
|
|
12422
12500
|
const gateResult = GateResultMinimalSchema.parse(
|
|
12423
12501
|
JSON.parse(readFileSync(gateResultPath, "utf8"))
|
|
12424
12502
|
);
|
|
12503
|
+
if (!gateResult.synthesis_eligible) {
|
|
12504
|
+
throw new Error(
|
|
12505
|
+
`Section ${sectionId}: gate is not synthesis_eligible (verdict=${gateResult.verdict}). Pack admission requires every section to be synthesis-eligible.`
|
|
12506
|
+
);
|
|
12507
|
+
}
|
|
12425
12508
|
const dispositions = readJsonlSafe(
|
|
12426
12509
|
join28(sectionDir, "claim-synthesis-dispositions.jsonl")
|
|
12427
12510
|
);
|
|
@@ -12730,7 +12813,7 @@ async function publish(input) {
|
|
|
12730
12813
|
);
|
|
12731
12814
|
}
|
|
12732
12815
|
}
|
|
12733
|
-
const manifest = deriveManifest(fromDir, packageName, input.operatorNotes ?? "");
|
|
12816
|
+
const manifest = deriveManifest(fromDir, packageName, input.operatorNotes ?? "", warnings);
|
|
12734
12817
|
if (input.dryRun) {
|
|
12735
12818
|
const finalReportPath2 = join31(fromDir, "synthesis/final-report.md");
|
|
12736
12819
|
const finalReport2 = existsSync30(finalReportPath2) ? readFileSync3(finalReportPath2, "utf8") : "";
|