@mcptoolshop/research-os 0.3.0 → 0.3.2
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 +214 -0
- package/README.es.md +99 -71
- package/README.fr.md +86 -58
- package/README.hi.md +83 -59
- package/README.it.md +97 -73
- package/README.ja.md +86 -58
- package/README.md +8 -4
- package/README.pt-BR.md +83 -59
- package/README.zh.md +102 -74
- package/dist/cli.js +281 -62
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +174 -84
- package/dist/index.js +185 -48
- 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),
|
|
@@ -4207,9 +4218,28 @@ function applyWaivers(input, results) {
|
|
|
4207
4218
|
const updated = results.map((r) => ({ ...r }));
|
|
4208
4219
|
const applied = [];
|
|
4209
4220
|
const validationFailures = [];
|
|
4210
|
-
|
|
4211
|
-
|
|
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
|
+
});
|
|
4212
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;
|
|
4213
4243
|
if (!waiver.reason || waiver.reason.trim().length === 0) {
|
|
4214
4244
|
validationFailures.push({
|
|
4215
4245
|
family: "waivers",
|
|
@@ -4219,7 +4249,7 @@ function applyWaivers(input, results) {
|
|
|
4219
4249
|
evidence: [],
|
|
4220
4250
|
blocks_synthesis: true
|
|
4221
4251
|
});
|
|
4222
|
-
return
|
|
4252
|
+
return;
|
|
4223
4253
|
}
|
|
4224
4254
|
if (waiver.compensating_controls.length === 0) {
|
|
4225
4255
|
validationFailures.push({
|
|
@@ -4230,7 +4260,7 @@ function applyWaivers(input, results) {
|
|
|
4230
4260
|
evidence: [],
|
|
4231
4261
|
blocks_synthesis: true
|
|
4232
4262
|
});
|
|
4233
|
-
return
|
|
4263
|
+
return;
|
|
4234
4264
|
}
|
|
4235
4265
|
if (!cfg.primary_source_waiver_allowed) {
|
|
4236
4266
|
validationFailures.push({
|
|
@@ -4241,7 +4271,7 @@ function applyWaivers(input, results) {
|
|
|
4241
4271
|
evidence: [],
|
|
4242
4272
|
blocks_synthesis: true
|
|
4243
4273
|
});
|
|
4244
|
-
return
|
|
4274
|
+
return;
|
|
4245
4275
|
}
|
|
4246
4276
|
for (let i = 0; i < updated.length; i += 1) {
|
|
4247
4277
|
const r = updated[i];
|
|
@@ -4263,7 +4293,62 @@ function applyWaivers(input, results) {
|
|
|
4263
4293
|
});
|
|
4264
4294
|
}
|
|
4265
4295
|
}
|
|
4266
|
-
|
|
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
|
+
}
|
|
4267
4352
|
}
|
|
4268
4353
|
var init_waivers = __esm({
|
|
4269
4354
|
"src/gates/checks/waivers.ts"() {
|
|
@@ -5520,9 +5605,11 @@ function pickHighestPriority(decisions) {
|
|
|
5520
5605
|
return "accepted_for_synthesis";
|
|
5521
5606
|
}
|
|
5522
5607
|
function deriveClaimReviews(args) {
|
|
5523
|
-
const { claims, findings, reviewer, reviewMethod } = args;
|
|
5608
|
+
const { claims, findings, reviewer, reviewMethod, activeSectionWaivers } = args;
|
|
5524
5609
|
const reviews = [];
|
|
5525
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";
|
|
5526
5613
|
for (const claim of claims) {
|
|
5527
5614
|
const claimFindings = findings.filter((f) => f.claim_ids.includes(claim.claim_id));
|
|
5528
5615
|
if (claimFindings.length === 0) {
|
|
@@ -5539,6 +5626,7 @@ function deriveClaimReviews(args) {
|
|
|
5539
5626
|
}
|
|
5540
5627
|
let decisions = [];
|
|
5541
5628
|
for (const f of claimFindings) {
|
|
5629
|
+
if (isWaivedFinding(f)) continue;
|
|
5542
5630
|
if (f.severity === "block") {
|
|
5543
5631
|
decisions.push(BLOCK_TO_DECISION[f.category] ?? "rejected");
|
|
5544
5632
|
} else if (f.severity === "warn") {
|
|
@@ -5555,7 +5643,9 @@ function deriveClaimReviews(args) {
|
|
|
5555
5643
|
);
|
|
5556
5644
|
}
|
|
5557
5645
|
const decision = pickHighestPriority(decisions);
|
|
5558
|
-
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
|
+
);
|
|
5559
5649
|
const reason = reasonParts.length > 0 ? `Findings: ${reasonParts.join("; ")}.` : "Only info-level findings; accepted.";
|
|
5560
5650
|
reviews.push({
|
|
5561
5651
|
claim_id: claim.claim_id,
|
|
@@ -5936,7 +6026,8 @@ async function review(options) {
|
|
|
5936
6026
|
candidateClaims,
|
|
5937
6027
|
drafts: acceptedDrafts,
|
|
5938
6028
|
llmFindingsRejected,
|
|
5939
|
-
profile: options.profile ?? DEFAULT_PROFILE
|
|
6029
|
+
profile: options.profile ?? DEFAULT_PROFILE,
|
|
6030
|
+
research
|
|
5940
6031
|
});
|
|
5941
6032
|
}
|
|
5942
6033
|
async function runMultiPassReview(args) {
|
|
@@ -5998,7 +6089,8 @@ async function runMultiPassReview(args) {
|
|
|
5998
6089
|
candidateClaims: args.candidateClaims,
|
|
5999
6090
|
drafts: merged,
|
|
6000
6091
|
llmFindingsRejected,
|
|
6001
|
-
profile: args.options.profile ?? DEFAULT_PROFILE
|
|
6092
|
+
profile: args.options.profile ?? DEFAULT_PROFILE,
|
|
6093
|
+
research: args.research
|
|
6002
6094
|
});
|
|
6003
6095
|
}
|
|
6004
6096
|
async function reviewWithSpecificReviewer(args) {
|
|
@@ -6026,7 +6118,8 @@ async function reviewWithSpecificReviewer(args) {
|
|
|
6026
6118
|
candidateClaims: args.candidateClaims,
|
|
6027
6119
|
drafts: result.drafts,
|
|
6028
6120
|
llmFindingsRejected: 0,
|
|
6029
|
-
profile: args.options.profile ?? DEFAULT_PROFILE
|
|
6121
|
+
profile: args.options.profile ?? DEFAULT_PROFILE,
|
|
6122
|
+
research: args.research
|
|
6030
6123
|
});
|
|
6031
6124
|
}
|
|
6032
6125
|
async function finalizeReview(args) {
|
|
@@ -6049,11 +6142,15 @@ async function finalizeReview(args) {
|
|
|
6049
6142
|
seen.add(f.finding_id);
|
|
6050
6143
|
dedupedFindings.push(f);
|
|
6051
6144
|
}
|
|
6145
|
+
const activeSectionWaivers = args.research.primary_source_waiver.section_waivers.filter(
|
|
6146
|
+
(w) => w.section_id === args.sectionId
|
|
6147
|
+
);
|
|
6052
6148
|
const claimReviews = deriveClaimReviews({
|
|
6053
6149
|
claims: args.candidateClaims,
|
|
6054
6150
|
findings: dedupedFindings,
|
|
6055
6151
|
reviewer: args.reviewer,
|
|
6056
|
-
reviewMethod: args.reviewMethod
|
|
6152
|
+
reviewMethod: args.reviewMethod,
|
|
6153
|
+
activeSectionWaivers
|
|
6057
6154
|
});
|
|
6058
6155
|
const decisionCounts = {
|
|
6059
6156
|
accepted_for_synthesis: 0,
|
|
@@ -8780,6 +8877,19 @@ function buildStaleSources(input) {
|
|
|
8780
8877
|
}
|
|
8781
8878
|
return out;
|
|
8782
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
|
+
}
|
|
8783
8893
|
function buildWeakSources(input) {
|
|
8784
8894
|
const out = [];
|
|
8785
8895
|
const cfg = input.research.gates.source_floor;
|
|
@@ -8789,33 +8899,50 @@ function buildWeakSources(input) {
|
|
|
8789
8899
|
const publishers = new Set(
|
|
8790
8900
|
sectionSources.map((c) => c.publisher).filter((p) => typeof p === "string")
|
|
8791
8901
|
);
|
|
8902
|
+
const monopolyWaiver = findSectionWaiver(input.research, sid, "min_independent_publishers");
|
|
8903
|
+
const primaryWaiver = findSectionWaiver(input.research, sid, "primary_sources_required");
|
|
8792
8904
|
if (publishers.size === 1 && sectionSources.length >= 2) {
|
|
8793
|
-
out.push(
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
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
|
+
);
|
|
8800
8917
|
}
|
|
8801
8918
|
if (publishers.size < cfg.min_independent_publishers) {
|
|
8802
|
-
out.push(
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
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
|
+
);
|
|
8809
8931
|
}
|
|
8810
8932
|
const primary = sectionSources.filter((c) => c.source_type === "primary").length;
|
|
8811
8933
|
if (primary < cfg.primary_sources_required) {
|
|
8812
|
-
out.push(
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
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
|
+
);
|
|
8819
8946
|
}
|
|
8820
8947
|
const types = /* @__PURE__ */ new Map();
|
|
8821
8948
|
for (const c of sectionSources) types.set(c.source_type, (types.get(c.source_type) ?? 0) + 1);
|
|
@@ -8930,20 +9057,31 @@ function buildSourceDiversityGaps(input) {
|
|
|
8930
9057
|
});
|
|
8931
9058
|
continue;
|
|
8932
9059
|
}
|
|
9060
|
+
const monopolyWaiver = findSectionWaiver(input.research, sid, "min_independent_publishers");
|
|
8933
9061
|
if (publishers.size === 1 && sectionSources.length >= 2) {
|
|
8934
|
-
out.push(
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
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
|
+
);
|
|
8940
9073
|
} else if (publishers.size < cfg.min_independent_publishers) {
|
|
8941
|
-
out.push(
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
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
|
+
);
|
|
8947
9085
|
}
|
|
8948
9086
|
}
|
|
8949
9087
|
const pubSectionCount = /* @__PURE__ */ new Map();
|
|
@@ -11734,7 +11872,7 @@ var init_src = __esm({
|
|
|
11734
11872
|
init_triage();
|
|
11735
11873
|
init_discover();
|
|
11736
11874
|
init_errors();
|
|
11737
|
-
RESEARCH_OS_VERSION = "0.3.
|
|
11875
|
+
RESEARCH_OS_VERSION = "0.3.2";
|
|
11738
11876
|
}
|
|
11739
11877
|
});
|
|
11740
11878
|
|
|
@@ -12173,12 +12311,55 @@ import { join as join31, basename, resolve as resolve24 } from "path";
|
|
|
12173
12311
|
|
|
12174
12312
|
// src/pack/publish/manifest.ts
|
|
12175
12313
|
init_schema();
|
|
12314
|
+
init_schema9();
|
|
12176
12315
|
import { createHash as createHash11 } from "crypto";
|
|
12177
12316
|
import { readFileSync, existsSync as existsSync28 } from "fs";
|
|
12178
12317
|
import { join as join28 } from "path";
|
|
12179
12318
|
import { parse as parseYaml } from "yaml";
|
|
12180
12319
|
import { z as z23 } from "zod";
|
|
12181
12320
|
|
|
12321
|
+
// src/closure-ledger/effective-accepted.ts
|
|
12322
|
+
function getEffectiveDecisionMap(reviews) {
|
|
12323
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
12324
|
+
for (const r of reviews) {
|
|
12325
|
+
const existing = map2.get(r.claim_id);
|
|
12326
|
+
if (!existing || r.created_at > existing.created_at) {
|
|
12327
|
+
map2.set(r.claim_id, r);
|
|
12328
|
+
}
|
|
12329
|
+
}
|
|
12330
|
+
return map2;
|
|
12331
|
+
}
|
|
12332
|
+
function getEffectiveAcceptedClaimIds(reviews) {
|
|
12333
|
+
const decisionMap = getEffectiveDecisionMap(reviews);
|
|
12334
|
+
const accepted = /* @__PURE__ */ new Set();
|
|
12335
|
+
for (const [claim_id, r] of decisionMap) {
|
|
12336
|
+
if (r.decision === "accepted_for_synthesis") accepted.add(claim_id);
|
|
12337
|
+
}
|
|
12338
|
+
return accepted;
|
|
12339
|
+
}
|
|
12340
|
+
function findIncompatibleDecisions(reviews) {
|
|
12341
|
+
const groups = /* @__PURE__ */ new Map();
|
|
12342
|
+
for (const r of reviews) {
|
|
12343
|
+
const key = `${r.claim_id}|${r.created_at}`;
|
|
12344
|
+
let set = groups.get(key);
|
|
12345
|
+
if (!set) {
|
|
12346
|
+
set = /* @__PURE__ */ new Set();
|
|
12347
|
+
groups.set(key, set);
|
|
12348
|
+
}
|
|
12349
|
+
set.add(r.decision);
|
|
12350
|
+
}
|
|
12351
|
+
const conflicts = [];
|
|
12352
|
+
for (const [key, decisions] of groups) {
|
|
12353
|
+
if (decisions.size > 1) {
|
|
12354
|
+
const sep3 = key.lastIndexOf("|");
|
|
12355
|
+
const claim_id = key.slice(0, sep3);
|
|
12356
|
+
const created_at = key.slice(sep3 + 1);
|
|
12357
|
+
conflicts.push({ claim_id, created_at, decisions: [...decisions] });
|
|
12358
|
+
}
|
|
12359
|
+
}
|
|
12360
|
+
return conflicts;
|
|
12361
|
+
}
|
|
12362
|
+
|
|
12182
12363
|
// src/pack/publish/schema.ts
|
|
12183
12364
|
import { z as z22 } from "zod";
|
|
12184
12365
|
var SectionSummarySchema = z22.object({
|
|
@@ -12210,6 +12391,7 @@ var GateResultMinimalSchema = z23.object({
|
|
|
12210
12391
|
verdict: z23.enum(["pass", "warn", "fail", "blocked"]),
|
|
12211
12392
|
synthesis_eligible: z23.boolean()
|
|
12212
12393
|
});
|
|
12394
|
+
var ClaimIdOnlySchema = z23.object({ claim_id: z23.string() });
|
|
12213
12395
|
function sha256Bytes(buf) {
|
|
12214
12396
|
return createHash11("sha256").update(buf).digest("hex");
|
|
12215
12397
|
}
|
|
@@ -12220,11 +12402,15 @@ function readJsonlSafe(filePath) {
|
|
|
12220
12402
|
if (!existsSync28(filePath)) return [];
|
|
12221
12403
|
return parseJsonl(readFileSync(filePath, "utf8"));
|
|
12222
12404
|
}
|
|
12223
|
-
function
|
|
12224
|
-
|
|
12225
|
-
const
|
|
12226
|
-
|
|
12227
|
-
|
|
12405
|
+
function readClaimReviews(filePath) {
|
|
12406
|
+
if (!existsSync28(filePath)) return [];
|
|
12407
|
+
const raw = parseJsonl(readFileSync(filePath, "utf8"));
|
|
12408
|
+
const valid = [];
|
|
12409
|
+
for (const r of raw) {
|
|
12410
|
+
const parsed = ClaimReviewSchema.safeParse(r);
|
|
12411
|
+
if (parsed.success) valid.push(parsed.data);
|
|
12412
|
+
}
|
|
12413
|
+
return valid;
|
|
12228
12414
|
}
|
|
12229
12415
|
function latestContradictionStatuses(resolutions) {
|
|
12230
12416
|
const m = /* @__PURE__ */ new Map();
|
|
@@ -12238,7 +12424,7 @@ function latestDispositionStatuses(dispositions) {
|
|
|
12238
12424
|
for (const d of sorted) m.set(d.claim_id, "dispositioned");
|
|
12239
12425
|
return m;
|
|
12240
12426
|
}
|
|
12241
|
-
function deriveManifest(packDir, packageName, operatorNotes = "") {
|
|
12427
|
+
function deriveManifest(packDir, packageName, operatorNotes = "", warnings = []) {
|
|
12242
12428
|
const yamlPath = join28(packDir, "research.yaml");
|
|
12243
12429
|
if (!existsSync28(yamlPath)) throw new Error(`research.yaml not found in ${packDir}`);
|
|
12244
12430
|
const research = ResearchYamlSchema.parse(parseYaml(readFileSync(yamlPath, "utf8")));
|
|
@@ -12266,15 +12452,43 @@ function deriveManifest(packDir, packageName, operatorNotes = "") {
|
|
|
12266
12452
|
let totalPreserved = 0;
|
|
12267
12453
|
for (const sectionId of sectionIds) {
|
|
12268
12454
|
const sectionDir = join28(packDir, "sections", sectionId);
|
|
12269
|
-
const
|
|
12270
|
-
const
|
|
12271
|
-
const
|
|
12272
|
-
|
|
12273
|
-
|
|
12455
|
+
const reviewsPath = join28(sectionDir, "claim-reviews.jsonl");
|
|
12456
|
+
const reviews = readClaimReviews(reviewsPath);
|
|
12457
|
+
const conflicts = findIncompatibleDecisions(reviews);
|
|
12458
|
+
if (conflicts.length > 0) {
|
|
12459
|
+
const first = conflicts[0];
|
|
12460
|
+
throw new Error(
|
|
12461
|
+
`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.)` : "")
|
|
12462
|
+
);
|
|
12463
|
+
}
|
|
12464
|
+
const effectiveAccepted = getEffectiveAcceptedClaimIds(reviews);
|
|
12465
|
+
const acceptedCount = effectiveAccepted.size;
|
|
12466
|
+
const claimsPath = join28(sectionDir, "claims.jsonl");
|
|
12467
|
+
if (existsSync28(claimsPath)) {
|
|
12468
|
+
const claimRows = parseJsonl(readFileSync(claimsPath, "utf8"));
|
|
12469
|
+
const claimIds = /* @__PURE__ */ new Set();
|
|
12470
|
+
for (const c of claimRows) {
|
|
12471
|
+
const parsed = ClaimIdOnlySchema.safeParse(c);
|
|
12472
|
+
if (parsed.success) claimIds.add(parsed.data.claim_id);
|
|
12473
|
+
}
|
|
12474
|
+
const phantoms = [];
|
|
12475
|
+
for (const cid of effectiveAccepted) {
|
|
12476
|
+
if (!claimIds.has(cid)) phantoms.push(cid);
|
|
12477
|
+
}
|
|
12478
|
+
if (phantoms.length > 0) {
|
|
12479
|
+
throw new Error(
|
|
12480
|
+
`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)` : "")
|
|
12481
|
+
);
|
|
12482
|
+
}
|
|
12483
|
+
} else {
|
|
12484
|
+
warnings.push(
|
|
12485
|
+
`section ${sectionId}: claims.jsonl absent \u2014 phantom-claim integrity check skipped`
|
|
12486
|
+
);
|
|
12487
|
+
}
|
|
12274
12488
|
const auditAccepted = auditSectionMap.get(sectionId);
|
|
12275
12489
|
if (auditAccepted !== void 0 && auditAccepted !== acceptedCount) {
|
|
12276
|
-
|
|
12277
|
-
`
|
|
12490
|
+
warnings.push(
|
|
12491
|
+
`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).`
|
|
12278
12492
|
);
|
|
12279
12493
|
}
|
|
12280
12494
|
const gateResultPath = join28(packDir, "audits", `${sectionId}-gate.json`);
|
|
@@ -12284,6 +12498,11 @@ function deriveManifest(packDir, packageName, operatorNotes = "") {
|
|
|
12284
12498
|
const gateResult = GateResultMinimalSchema.parse(
|
|
12285
12499
|
JSON.parse(readFileSync(gateResultPath, "utf8"))
|
|
12286
12500
|
);
|
|
12501
|
+
if (!gateResult.synthesis_eligible) {
|
|
12502
|
+
throw new Error(
|
|
12503
|
+
`Section ${sectionId}: gate is not synthesis_eligible (verdict=${gateResult.verdict}). Pack admission requires every section to be synthesis-eligible.`
|
|
12504
|
+
);
|
|
12505
|
+
}
|
|
12287
12506
|
const dispositions = readJsonlSafe(
|
|
12288
12507
|
join28(sectionDir, "claim-synthesis-dispositions.jsonl")
|
|
12289
12508
|
);
|
|
@@ -12592,7 +12811,7 @@ async function publish(input) {
|
|
|
12592
12811
|
);
|
|
12593
12812
|
}
|
|
12594
12813
|
}
|
|
12595
|
-
const manifest = deriveManifest(fromDir, packageName, input.operatorNotes ?? "");
|
|
12814
|
+
const manifest = deriveManifest(fromDir, packageName, input.operatorNotes ?? "", warnings);
|
|
12596
12815
|
if (input.dryRun) {
|
|
12597
12816
|
const finalReportPath2 = join31(fromDir, "synthesis/final-report.md");
|
|
12598
12817
|
const finalReport2 = existsSync30(finalReportPath2) ? readFileSync3(finalReportPath2, "utf8") : "";
|