@dailephd/my-dev-kit-lab 0.4.4 → 0.4.5

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +468 -0
  2. package/README.md +3 -1
  3. package/dist/scripts/report-context-integrity-smoke.js +70 -0
  4. package/dist/src/evaluation/ecosystemFixtures/index.js +3 -0
  5. package/dist/src/evaluation/ecosystemFixtures/loadContextIntegrityFixture.js +102 -0
  6. package/dist/src/evaluation/ecosystemFixtures/manifestTypes.js +1 -0
  7. package/dist/src/evaluation/ecosystemFixtures/validateManifest.js +82 -0
  8. package/dist/src/evaluation/ecosystemFixtures/verifyFixtureHashes.js +45 -0
  9. package/dist/src/evaluation/stageContextMetrics/calculateAllocationMetrics.js +169 -0
  10. package/dist/src/evaluation/stageContextMetrics/calculateConditionCoverageMetrics.js +74 -0
  11. package/dist/src/evaluation/stageContextMetrics/calculateProducerConditionAgreement.js +113 -0
  12. package/dist/src/evaluation/stageContextMetrics/calculateReadinessAgreement.js +60 -0
  13. package/dist/src/evaluation/stageContextMetrics/calculateRunIntegrityAgreement.js +309 -0
  14. package/dist/src/evaluation/stageContextMetrics/calculateSupplementalRawAgreement.js +12 -2
  15. package/dist/src/evaluation/stageContextMetrics/calculateTruncationClassification.js +100 -0
  16. package/dist/src/evaluation/stageContextMetrics/evaluateProducerReadinessBridge.js +76 -5
  17. package/dist/src/evaluation/stageContextMetrics/index.js +4 -0
  18. package/dist/src/evaluation/stageContextMetrics/runIntegrityAgreementTypes.js +1 -0
  19. package/dist/src/evaluation/stageContextSelectors/contextArtifactConsistency.js +7 -0
  20. package/dist/src/evaluation/stageContextSelectors/index.js +1 -0
  21. package/dist/src/evaluation/stageContextSelectors/orchestratorRunIntegritySelectors.js +64 -0
  22. package/dist/src/evaluation/upstreamArtifacts/index.js +2 -0
  23. package/dist/src/evaluation/upstreamArtifacts/orchestratorRunIntegrityV1.js +33 -0
  24. package/dist/src/evaluation/upstreamArtifacts/readOrchestratorRunIntegrityV1.js +9 -0
  25. package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitContextCapsuleV1.js +59 -2
  26. package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitRetrievalAuditRecordV1.js +6 -1
  27. package/dist/src/evaluation/upstreamArtifacts/validateOrchestratorRunIntegrityV1.js +177 -0
  28. package/dist/src/report/experiments/buildContextIntegrityReport.js +204 -0
  29. package/dist/src/report/experiments/contextIntegrityReportModel.js +1 -0
  30. package/dist/src/report/experiments/index.js +5 -0
  31. package/dist/src/report/experiments/renderContextIntegrityHtml.js +98 -0
  32. package/dist/src/report/experiments/renderContextIntegrityJsonReport.js +3 -0
  33. package/dist/src/report/experiments/renderContextIntegrityText.js +111 -0
  34. package/dist/src/securityValidation/packageChecks/requiredPackageContents.js +33 -0
  35. package/dist/src/securityValidation/packageChecks/runPackageChecks.js +11 -5
  36. package/docs/METRICS.md +83 -0
  37. package/package.json +4 -2
  38. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/derived/run-integrity-evidence.json +99 -0
  39. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/producer/implementation-audit.json +1585 -0
  40. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/producer/implementation-capsule.json +11455 -0
  41. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/00-request.txt +1 -0
  42. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/architecture-context-packet.txt +82 -0
  43. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/behavior-model.txt +104 -0
  44. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/final-report.txt +41 -0
  45. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/implementation-context-packet.txt +114 -0
  46. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/implementation-report.txt +92 -0
  47. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/judge-report.txt +34 -0
  48. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/pseudocode-packet.txt +244 -0
  49. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/request-brief.txt +40 -0
  50. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-context-packet.txt +93 -0
  51. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-implementation-report.txt +69 -0
  52. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-strategy-packet.txt +354 -0
  53. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/verification-report.txt +85 -0
  54. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/derived/run-integrity-evidence.json +98 -0
  55. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/architecture-audit.json +1169 -0
  56. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/architecture-request.json +29 -0
  57. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-audit.json +1494 -0
  58. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-capsule.json +11364 -0
  59. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-request.json +33 -0
  60. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/test-request.json +74 -0
  61. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/architecture-context-retrieval-report.txt +58 -0
  62. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/implementation-context-retrieval-report.txt +75 -0
  63. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/test-context-retrieval-report.txt +84 -0
  64. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/run.json +62 -0
  65. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/tool-versions/my-dev-kit-version.txt +1 -0
  66. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/tool-versions/orchestrator-version.txt +1 -0
  67. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/manifests/corrected-replay-manifest.json +19 -0
  68. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/manifests/failed-run-manifest.json +56 -0
  69. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/PROVENANCE.txt +18 -0
  70. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/context-capsule/complete-v1.0.0.json +556 -0
  71. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/retrieval-audit-record/complete-v1.0.0.json +238 -0
  72. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/PROVENANCE.txt +31 -0
  73. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/ready-pass-final-pass.json +132 -0
  74. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/refresh-required-need-context.json +146 -0
@@ -0,0 +1,309 @@
1
+ import { selectStageMayRenderNormalPrompt } from "../stageContextSelectors/orchestratorRunIntegritySelectors.js";
2
+ const CONTEXT_SENSITIVE_STAGE_NAMES = ["implementation", "test-implementation"];
3
+ function contradiction(code, fieldPath, expected, observed, reason) {
4
+ return { code, fieldPath, expected, observed, reason };
5
+ }
6
+ // --- section 19: readiness vs prompt mode ----------------------------------------------
7
+ export function calculateReadinessPromptAgreement(evidence, stageName) {
8
+ const readinessClassification = evidence.gate.readinessClassification;
9
+ const stageMayRenderNormalPrompt = selectStageMayRenderNormalPrompt(evidence, stageName) ?? null;
10
+ if (!CONTEXT_SENSITIVE_STAGE_NAMES.includes(stageName)) {
11
+ return {
12
+ availability: "not-applicable",
13
+ outcome: "not-applicable",
14
+ readinessClassification,
15
+ stageMayRenderNormalPrompt,
16
+ contradictions: [],
17
+ reason: `Stage "${stageName}" is not a context-sensitive stage; the gate does not attach a prompt-mode decision to it.`
18
+ };
19
+ }
20
+ if (stageMayRenderNormalPrompt === null) {
21
+ return {
22
+ availability: "unavailable",
23
+ outcome: "insufficient-evidence",
24
+ readinessClassification,
25
+ stageMayRenderNormalPrompt: null,
26
+ contradictions: [],
27
+ reason: "No prompt-mode decision was derivable for this stage."
28
+ };
29
+ }
30
+ const contradictions = [];
31
+ if (readinessClassification === "ready" && !stageMayRenderNormalPrompt) {
32
+ contradictions.push(contradiction("READY_BUT_PROMPT_NOT_NORMAL", "gate.readinessClassification", "ready", "refresh-only", "Readiness is ready but this stage may not render its normal prompt."));
33
+ }
34
+ if (readinessClassification === "refresh-required" && stageMayRenderNormalPrompt) {
35
+ contradictions.push(contradiction("REFRESH_REQUIRED_BUT_PROMPT_NORMAL", "gate.readinessClassification", "refresh-required", "normal", "Readiness is refresh-required but this stage may still render its normal prompt."));
36
+ }
37
+ return {
38
+ availability: "available",
39
+ outcome: contradictions.length > 0 ? "contradiction" : "agreement",
40
+ readinessClassification,
41
+ stageMayRenderNormalPrompt,
42
+ contradictions,
43
+ reason: contradictions.length > 0 ? "Readiness classification and stage prompt-mode evidence disagree." : null
44
+ };
45
+ }
46
+ // --- section 20: readiness vs expected judge verdict ------------------------------------
47
+ export function calculateReadinessExpectedJudgeAgreement(evidence) {
48
+ const { contextReady, expectedJudgeVerdict } = evidence.gate;
49
+ const contradictions = [];
50
+ if (contextReady && expectedJudgeVerdict !== "PASS") {
51
+ contradictions.push(contradiction("CONTEXT_READY_BUT_EXPECTED_NOT_PASS", "gate.expectedJudgeVerdict", "PASS", expectedJudgeVerdict, "contextReady is true but expectedJudgeVerdict is not PASS."));
52
+ }
53
+ if (!contextReady && expectedJudgeVerdict !== "NEED_CONTEXT") {
54
+ contradictions.push(contradiction("CONTEXT_NOT_READY_BUT_EXPECTED_NOT_NEED_CONTEXT", "gate.expectedJudgeVerdict", "NEED_CONTEXT", expectedJudgeVerdict, "contextReady is false but expectedJudgeVerdict is not NEED_CONTEXT."));
55
+ }
56
+ return {
57
+ availability: "available",
58
+ outcome: contradictions.length > 0 ? "contradiction" : "agreement",
59
+ contextReady,
60
+ expectedJudgeVerdict,
61
+ contradictions,
62
+ reason: contradictions.length > 0 ? "The gate's own contextReady/expectedJudgeVerdict invariant does not hold for this evidence." : null
63
+ };
64
+ }
65
+ // --- section 21: expected vs parsed (actual) judge verdict -------------------------------
66
+ export function calculateExpectedActualJudgeAgreement(evidence) {
67
+ const judgeIntegrity = evidence.judgeIntegrity;
68
+ if (judgeIntegrity === undefined) {
69
+ return {
70
+ availability: "unavailable",
71
+ outcome: "insufficient-evidence",
72
+ expectedJudgeVerdict: evidence.gate.expectedJudgeVerdict,
73
+ authoredJudgeVerdict: null,
74
+ judgeVerdictParseStatus: null,
75
+ judgeVerdictAccepted: null,
76
+ contradictions: [],
77
+ reason: "No judgeIntegrity evidence was supplied."
78
+ };
79
+ }
80
+ const contradictions = [];
81
+ const { expectedJudgeVerdict, authoredJudgeVerdict, judgeVerdictParseStatus, judgeVerdictAccepted, judgeVerdictMatchesExpected, blockingCodes } = judgeIntegrity;
82
+ if (judgeVerdictParseStatus === "unknown-verdict") {
83
+ contradictions.push(contradiction("JUDGE_VERDICT_UNKNOWN", "judgeIntegrity.judgeVerdictParseStatus", "parsed", "unknown-verdict", "Judge report declares an unrecognized verdict."));
84
+ }
85
+ else if (judgeVerdictParseStatus === "missing-verdict") {
86
+ contradictions.push(contradiction("JUDGE_VERDICT_MISSING", "judgeIntegrity.judgeVerdictParseStatus", "parsed", "missing-verdict", "Judge report does not declare a verdict."));
87
+ }
88
+ else if (judgeVerdictParseStatus === "missing-artifact") {
89
+ contradictions.push(contradiction("JUDGE_REPORT_MISSING", "judgeIntegrity.judgeArtifactPresent", "true", "false", "No judge report artifact exists."));
90
+ }
91
+ else if (authoredJudgeVerdict === "PASS" && expectedJudgeVerdict === "NEED_CONTEXT") {
92
+ // The exact upstream contradiction code (src/judgeIntegrity.ts
93
+ // JUDGE_VERDICT_CONTRADICTS_RUN_INTEGRITY), preserved verbatim when present.
94
+ contradictions.push(contradiction(blockingCodes.includes("JUDGE_VERDICT_CONTRADICTS_RUN_INTEGRITY") ? "JUDGE_VERDICT_CONTRADICTS_RUN_INTEGRITY" : "AUTHORED_PASS_CONTRADICTS_EXPECTED_NEED_CONTEXT", "judgeIntegrity.authoredJudgeVerdict", "NEED_CONTEXT", "PASS", 'Judge report authored "PASS" but canonical readiness still requires NEED_CONTEXT.'));
95
+ }
96
+ else if (judgeVerdictAccepted && judgeVerdictMatchesExpected === false && authoredJudgeVerdict !== null) {
97
+ // A syntactically valid, accepted verdict that legitimately differs from the expected
98
+ // one without being the PASS/NEED_CONTEXT contradiction above (e.g. a correction-route
99
+ // verdict, or a terminal BLOCKED/SCOPE_VIOLATION verdict) is not itself an integrity
100
+ // contradiction -- it is the normal correction-required or blocked shape.
101
+ }
102
+ return {
103
+ availability: "available",
104
+ outcome: contradictions.length > 0 ? "contradiction" : "agreement",
105
+ expectedJudgeVerdict,
106
+ authoredJudgeVerdict,
107
+ judgeVerdictParseStatus,
108
+ judgeVerdictAccepted,
109
+ contradictions,
110
+ reason: contradictions.length > 0 ? contradictions[0].reason : null
111
+ };
112
+ }
113
+ // --- section 22: judge verdict vs correction route ---------------------------------------
114
+ const TERMINAL_BLOCKED_VERDICTS = ["SCOPE_VIOLATION", "BLOCKED"];
115
+ export function calculateJudgeCorrectionAgreement(evidence) {
116
+ const judgeIntegrity = evidence.judgeIntegrity;
117
+ if (judgeIntegrity === undefined) {
118
+ return {
119
+ availability: "unavailable",
120
+ outcome: "insufficient-evidence",
121
+ authoredJudgeVerdict: null,
122
+ correctionRequired: null,
123
+ acceptedCorrectionStage: null,
124
+ routedStage: null,
125
+ contradictions: [],
126
+ reason: "No judgeIntegrity evidence was supplied."
127
+ };
128
+ }
129
+ const { authoredJudgeVerdict, correctionRequired, correctionBlocked, acceptedCorrectionStage, acceptedCorrectionRoute } = judgeIntegrity;
130
+ const routedStage = acceptedCorrectionRoute?.routedStage ?? null;
131
+ const contradictions = [];
132
+ if (authoredJudgeVerdict === "PASS" && acceptedCorrectionRoute !== null) {
133
+ contradictions.push(contradiction("PASS_WITH_ACTIVE_CORRECTION_ROUTE", "judgeIntegrity.acceptedCorrectionRoute", "null", "active", "Judge verdict is PASS but an active correction route is present."));
134
+ }
135
+ if (authoredJudgeVerdict !== null &&
136
+ authoredJudgeVerdict !== "PASS" &&
137
+ !TERMINAL_BLOCKED_VERDICTS.includes(authoredJudgeVerdict) &&
138
+ !correctionBlocked &&
139
+ correctionRequired &&
140
+ acceptedCorrectionRoute === null) {
141
+ contradictions.push(contradiction("CORRECTIVE_VERDICT_WITHOUT_ROUTE", "judgeIntegrity.acceptedCorrectionRoute", "active", "null", `Judge verdict "${authoredJudgeVerdict}" requires correction but no correction route is present.`));
142
+ }
143
+ if (acceptedCorrectionStage !== null && routedStage !== null && acceptedCorrectionStage !== routedStage) {
144
+ contradictions.push(contradiction("CORRECTION_DESTINATION_MISMATCH", "judgeIntegrity.acceptedCorrectionStage", routedStage, acceptedCorrectionStage, "acceptedCorrectionStage does not match the structured correction route's routedStage."));
145
+ }
146
+ return {
147
+ availability: "available",
148
+ outcome: contradictions.length > 0 ? "contradiction" : "agreement",
149
+ authoredJudgeVerdict,
150
+ correctionRequired,
151
+ acceptedCorrectionStage,
152
+ routedStage,
153
+ contradictions,
154
+ reason: contradictions.length > 0 ? contradictions[0].reason : null
155
+ };
156
+ }
157
+ // --- section 23: readiness/judge vs final-report eligibility -----------------------------
158
+ export function calculateJudgeFinalEligibilityAgreement(evidence) {
159
+ const judgeIntegrity = evidence.judgeIntegrity;
160
+ if (judgeIntegrity === undefined) {
161
+ return {
162
+ availability: "unavailable",
163
+ outcome: "insufficient-evidence",
164
+ authoredJudgeVerdict: null,
165
+ judgeVerdictAccepted: null,
166
+ judgeVerdictMatchesExpected: null,
167
+ finalReportEligible: null,
168
+ contradictions: [],
169
+ reason: "No judgeIntegrity evidence was supplied."
170
+ };
171
+ }
172
+ const { authoredJudgeVerdict, judgeVerdictAccepted, judgeVerdictMatchesExpected, finalReportEligible, judgeVerdictParseStatus } = judgeIntegrity;
173
+ const contradictions = [];
174
+ if (finalReportEligible && authoredJudgeVerdict !== "PASS") {
175
+ contradictions.push(contradiction("ELIGIBLE_WITH_NON_PASS_JUDGE", "judgeIntegrity.finalReportEligible", "PASS", authoredJudgeVerdict, "finalReportEligible is true but the authored judge verdict is not PASS."));
176
+ }
177
+ if (finalReportEligible && judgeVerdictParseStatus !== "parsed") {
178
+ contradictions.push(contradiction("ELIGIBLE_WITH_MALFORMED_OR_MISSING_JUDGE", "judgeIntegrity.finalReportEligible", "parsed", judgeVerdictParseStatus, "finalReportEligible is true but the judge verdict was not successfully parsed."));
179
+ }
180
+ if (finalReportEligible && !judgeVerdictAccepted) {
181
+ contradictions.push(contradiction("ELIGIBLE_WITH_UNACCEPTED_JUDGE", "judgeIntegrity.finalReportEligible", "true", "false", "finalReportEligible is true but judgeVerdictAccepted is false."));
182
+ }
183
+ if (finalReportEligible && judgeVerdictMatchesExpected === false) {
184
+ contradictions.push(contradiction("ELIGIBLE_WITH_UNMATCHED_EXPECTED_VERDICT", "judgeIntegrity.finalReportEligible", "true", "false", "finalReportEligible is true but judgeVerdictMatchesExpected is false."));
185
+ }
186
+ return {
187
+ availability: "available",
188
+ outcome: contradictions.length > 0 ? "contradiction" : "agreement",
189
+ authoredJudgeVerdict,
190
+ judgeVerdictAccepted,
191
+ judgeVerdictMatchesExpected,
192
+ finalReportEligible,
193
+ contradictions,
194
+ reason: contradictions.length > 0 ? contradictions[0].reason : null
195
+ };
196
+ }
197
+ // --- section 24: final eligibility vs final artifact and verdict -------------------------
198
+ export function calculateEligibilityFinalArtifactAgreement(evidence) {
199
+ const eligibility = evidence.finalReportEligibility;
200
+ const finalArtifactPresent = evidence.finalArtifactPresent;
201
+ const finalArtifactVerdict = evidence.finalArtifactVerdict ?? null;
202
+ if (eligibility === undefined || finalArtifactPresent === undefined) {
203
+ return {
204
+ availability: "unavailable",
205
+ outcome: "insufficient-evidence",
206
+ eligible: eligibility?.eligible ?? null,
207
+ finalArtifactPresent: finalArtifactPresent ?? null,
208
+ finalArtifactVerdict,
209
+ contradictions: [],
210
+ reason: eligibility === undefined ? "No finalReportEligibility evidence was supplied." : "No finalArtifactPresent evidence was supplied."
211
+ };
212
+ }
213
+ const { eligible } = eligibility;
214
+ const contradictions = [];
215
+ if (eligible && !finalArtifactPresent) {
216
+ contradictions.push(contradiction("ELIGIBLE_BUT_ARTIFACT_MISSING", "finalArtifactPresent", "true", "false", "The run is final-report eligible but no final artifact is present."));
217
+ }
218
+ if (!eligible && finalArtifactPresent) {
219
+ contradictions.push(contradiction("INELIGIBLE_BUT_ARTIFACT_PRESENT", "finalArtifactPresent", "false", "true", "The run is not final-report eligible but a final artifact is present."));
220
+ }
221
+ if (!eligible && finalArtifactVerdict === "PASS") {
222
+ // The required v0.4.5 failure detection: NEED_CONTEXT (or any other ineligible state)
223
+ // followed by a normal PASS final report must report an integrity contradiction.
224
+ const expectedJudge = evidence.gate.expectedJudgeVerdict;
225
+ contradictions.push(contradiction(expectedJudge === "NEED_CONTEXT" ? "NEED_CONTEXT_FOLLOWED_BY_FINAL_PASS" : "INELIGIBLE_WITH_FINAL_PASS", "finalArtifactVerdict", "not PASS", "PASS", expectedJudge === "NEED_CONTEXT"
226
+ ? "Canonical readiness required NEED_CONTEXT, yet a final report with verdict PASS exists."
227
+ : "The run is not final-report eligible, yet a final report with verdict PASS exists."));
228
+ }
229
+ if (finalArtifactPresent && evidence.finalArtifactVerdict === undefined) {
230
+ contradictions.push(contradiction("FINAL_ARTIFACT_MALFORMED_VERDICT", "finalArtifactVerdict", "parsed", "undefined", "A final artifact is present but its verdict was not supplied."));
231
+ }
232
+ return {
233
+ availability: "available",
234
+ outcome: contradictions.length > 0 ? "contradiction" : "agreement",
235
+ eligible,
236
+ finalArtifactPresent,
237
+ finalArtifactVerdict,
238
+ contradictions,
239
+ reason: contradictions.length > 0 ? contradictions[0].reason : null
240
+ };
241
+ }
242
+ // --- section 25: lifecycle integrity -----------------------------------------------------
243
+ export function calculateLifecycleIntegrityAgreement(evidence) {
244
+ if (evidence.lifecycle.length === 0) {
245
+ return { availability: "not-applicable", outcome: "not-applicable", entries: [], contradictions: [], reason: "No lifecycle evidence was supplied." };
246
+ }
247
+ const contradictions = [];
248
+ const entries = evidence.lifecycle.map((entry) => {
249
+ const completionBasis = entry.resolvedState !== "complete" ? "not-complete" : entry.manualRecord === null ? "artifact-presence-only" : "manual-record";
250
+ const markCompleteRejected = entry.manualRecord?.state === "complete" && entry.resolvedState !== "complete";
251
+ const stageContextBlocked = CONTEXT_SENSITIVE_STAGE_NAMES.includes(entry.stageName) &&
252
+ evidence.gate.blockedStageNames.includes(entry.stageName);
253
+ const finalReportIneligible = entry.stageName === "final-report" && evidence.finalReportEligibility?.eligible === false;
254
+ const shouldBeBlocked = stageContextBlocked || finalReportIneligible;
255
+ const bypassSucceeded = shouldBeBlocked && entry.resolvedState === "complete";
256
+ if (bypassSucceeded) {
257
+ contradictions.push(contradiction("MANUAL_MARK_COMPLETE_BYPASS_SUCCEEDED", `lifecycle[${entry.stageName}].resolvedState`, "blocked", "complete", `Stage "${entry.stageName}" should be run-integrity-blocked but resolved to "complete"${completionBasis === "artifact-presence-only" ? " through artifact presence alone" : " despite a manual mark-complete record"}.`));
258
+ }
259
+ return { artifactFile: entry.artifactFile, stageName: entry.stageName, resolvedState: entry.resolvedState, completionBasis, markCompleteRejected, bypassSucceeded };
260
+ });
261
+ return {
262
+ availability: "available",
263
+ outcome: contradictions.length > 0 ? "contradiction" : "agreement",
264
+ entries,
265
+ contradictions,
266
+ reason: contradictions.length > 0 ? "At least one lifecycle entry resolved to complete despite required run-integrity blocking." : null
267
+ };
268
+ }
269
+ // --- section 26: end-to-end aggregate summary ---------------------------------------------
270
+ function summarize(producerReadinessOutcome, readinessPrompt, readinessExpectedJudge, expectedActualJudge, judgeCorrection, judgeFinalEligibility, eligibilityFinalArtifact, lifecycleIntegrity) {
271
+ const componentOutcomes = {
272
+ producerReadiness: producerReadinessOutcome,
273
+ readinessPrompt,
274
+ readinessExpectedJudge,
275
+ expectedActualJudge,
276
+ judgeCorrection,
277
+ judgeFinalEligibility,
278
+ eligibilityFinalArtifact,
279
+ lifecycleIntegrity
280
+ };
281
+ const contradictingComponents = Object.keys(componentOutcomes).filter((key) => componentOutcomes[key] === "contradiction");
282
+ const category = contradictingComponents.length > 0
283
+ ? "contradiction-present"
284
+ : Object.values(componentOutcomes).some((o) => o === "insufficient-evidence")
285
+ ? "insufficient-evidence"
286
+ : Object.values(componentOutcomes).some((o) => o === "unsupported-legacy-evidence")
287
+ ? "unsupported-legacy-evidence"
288
+ : "full-agreement";
289
+ return { category, componentOutcomes, contradictingComponents };
290
+ }
291
+ export function evaluateRunIntegrity(evidence, stageName, producerReadinessOutcome = "not-applicable") {
292
+ const readinessPrompt = calculateReadinessPromptAgreement(evidence, stageName);
293
+ const readinessExpectedJudge = calculateReadinessExpectedJudgeAgreement(evidence);
294
+ const expectedActualJudge = calculateExpectedActualJudgeAgreement(evidence);
295
+ const judgeCorrection = calculateJudgeCorrectionAgreement(evidence);
296
+ const judgeFinalEligibility = calculateJudgeFinalEligibilityAgreement(evidence);
297
+ const eligibilityFinalArtifact = calculateEligibilityFinalArtifactAgreement(evidence);
298
+ const lifecycleIntegrity = calculateLifecycleIntegrityAgreement(evidence);
299
+ return {
300
+ readinessPrompt,
301
+ readinessExpectedJudge,
302
+ expectedActualJudge,
303
+ judgeCorrection,
304
+ judgeFinalEligibility,
305
+ eligibilityFinalArtifact,
306
+ lifecycleIntegrity,
307
+ endToEndSummary: summarize(producerReadinessOutcome, readinessPrompt.outcome, readinessExpectedJudge.outcome, expectedActualJudge.outcome, judgeCorrection.outcome, judgeFinalEligibility.outcome, eligibilityFinalArtifact.outcome, lifecycleIntegrity.outcome)
308
+ };
309
+ }
@@ -3,7 +3,8 @@ function extractRawIdentity(artifact) {
3
3
  projectRoot: artifact.index.projectRoot,
4
4
  role: artifact.request.role,
5
5
  freshnessState: artifact.freshness.state,
6
- truncated: artifact.truncation.truncated
6
+ truncated: artifact.truncation.truncated,
7
+ requiredEvidenceLost: artifact.truncation.requiredEvidenceLost
7
8
  };
8
9
  }
9
10
  function truncationToYesNo(value) {
@@ -26,7 +27,8 @@ export function calculateSupplementalRawAgreement(supplemental, raw) {
26
27
  buildField("canonicalRepositoryIdentity", null, supplemental?.indexIdentity ?? null, reason),
27
28
  buildField("role", null, supplemental?.role ?? null, reason),
28
29
  buildField("freshness", null, supplemental?.freshness ?? null, reason),
29
- buildField("requiredEvidenceTruncated", null, supplemental?.requiredEvidenceTruncated ?? null, reason)
30
+ buildField("requiredEvidenceTruncated", null, supplemental?.requiredEvidenceTruncated ?? null, reason),
31
+ buildField("requiredEvidenceLost", null, supplemental?.requiredEvidenceTruncated ?? null, reason)
30
32
  ];
31
33
  return { fields, contradictions: [], upstreamProducerParityPreserved: null };
32
34
  }
@@ -39,6 +41,14 @@ export function calculateSupplementalRawAgreement(supplemental, raw) {
39
41
  fields.push(buildField("requiredEvidenceTruncated", truncationToYesNo(rawIdentity.truncated), supplemental.requiredEvidenceTruncated, supplemental.requiredEvidenceTruncated === "unknown"
40
42
  ? "The supplemental document declares truncation as \"unknown\", so agreement with the raw boolean cannot be determined."
41
43
  : null));
44
+ // v0.4.5 Batch 2 (section 17): the stricter comparison against the Batch 1
45
+ // truncation.requiredEvidenceLost rollup, additive to the general "truncated" comparison
46
+ // above. Absent on legacy raw artifacts, never fabricated.
47
+ fields.push(buildField("requiredEvidenceLost", rawIdentity.requiredEvidenceLost === undefined ? null : truncationToYesNo(rawIdentity.requiredEvidenceLost), supplemental.requiredEvidenceTruncated, rawIdentity.requiredEvidenceLost === undefined
48
+ ? "The raw artifact does not declare truncation.requiredEvidenceLost (legacy schema-major-1 evidence)."
49
+ : supplemental.requiredEvidenceTruncated === "unknown"
50
+ ? "The supplemental document declares truncation as \"unknown\", so agreement with the raw boolean cannot be determined."
51
+ : null));
42
52
  const contradictions = fields.filter((f) => f.availability === "available" && f.agreement === false);
43
53
  return {
44
54
  fields,
@@ -42,3 +42,103 @@ export function classifyTruncationRecord(record, sourceArtifact, sourceInstance)
42
42
  export function classifyTruncationRecords(records, sourceArtifact, sourceInstance) {
43
43
  return records.map((record) => classifyTruncationRecord(record, sourceArtifact, sourceInstance));
44
44
  }
45
+ // v0.4.5 Batch 2: condition-aware truncation classification. Distinguishes the seven states
46
+ // required by the Batch 2 plan using only explicit upstream evidence (truncation.truncated,
47
+ // truncation.requiredEvidenceLost, ContextCapsule.groupTruncation's required/optional omitted
48
+ // counts, and roleConditionCoverage's lostRequiredCondition/lossReason). Never independently
49
+ // decides that a condition lost its last witness from a raw file count.
50
+ export function classifyConditionAwareTruncation(artifact, sourceArtifact, sourceInstance) {
51
+ const requiredEvidenceLost = artifact.truncation.requiredEvidenceLost;
52
+ const roleConditionCoverage = artifact.roleConditionCoverage;
53
+ const groupTruncation = "groupTruncation" in artifact ? artifact.groupTruncation : undefined;
54
+ const lostConditions = roleConditionCoverage?.filter((c) => c.lostRequiredCondition) ?? undefined;
55
+ const lostRequiredConditionIds = lostConditions?.map((c) => c.conditionId) ?? [];
56
+ const groupsWithBothOmissionFields = groupTruncation?.filter((g) => g.requiredOmittedCount !== undefined && g.optionalOmittedCount !== undefined);
57
+ const requiredOmittedTotal = groupsWithBothOmissionFields !== undefined && groupsWithBothOmissionFields.length === (groupTruncation?.length ?? -1) && groupTruncation.length > 0
58
+ ? groupsWithBothOmissionFields.reduce((sum, g) => sum + (g.requiredOmittedCount ?? 0), 0)
59
+ : null;
60
+ const optionalOmittedTotal = groupsWithBothOmissionFields !== undefined && groupsWithBothOmissionFields.length === (groupTruncation?.length ?? -1) && groupTruncation.length > 0
61
+ ? groupsWithBothOmissionFields.reduce((sum, g) => sum + (g.optionalOmittedCount ?? 0), 0)
62
+ : null;
63
+ const base = {
64
+ sourceArtifact,
65
+ sourceInstance,
66
+ requiredEvidenceLost: requiredEvidenceLost ?? null,
67
+ requiredOmittedTotal,
68
+ optionalOmittedTotal,
69
+ lostRequiredConditionIds
70
+ };
71
+ const hasAnyCurrentDiagnostics = requiredEvidenceLost !== undefined || roleConditionCoverage !== undefined || requiredOmittedTotal !== null;
72
+ if (!hasAnyCurrentDiagnostics) {
73
+ return {
74
+ ...base,
75
+ availability: "unavailable",
76
+ state: "unsupported-legacy-diagnostics",
77
+ contradictionCodes: [],
78
+ reason: "No v1.10.3/v1.10.4 truncation or condition-coverage diagnostics were supplied (legacy schema-major-1 evidence)."
79
+ };
80
+ }
81
+ const contradictionCodes = [];
82
+ if (requiredEvidenceLost === false && lostRequiredConditionIds.length > 0) {
83
+ contradictionCodes.push("REQUIRED_EVIDENCE_LOST_FALSE_BUT_CONDITION_LOSS_REPORTED");
84
+ }
85
+ if (requiredEvidenceLost === true &&
86
+ roleConditionCoverage !== undefined &&
87
+ lostRequiredConditionIds.length === 0 &&
88
+ roleConditionCoverage.filter((c) => c.required).every((c) => c.conditionSatisfied) &&
89
+ (requiredOmittedTotal === null || requiredOmittedTotal === 0)) {
90
+ contradictionCodes.push("REQUIRED_EVIDENCE_LOST_TRUE_BUT_ALL_REQUIRED_CONDITIONS_RETAINED");
91
+ }
92
+ if (contradictionCodes.length > 0) {
93
+ return {
94
+ ...base,
95
+ availability: "available",
96
+ state: "contradictory-producer-evidence",
97
+ contradictionCodes,
98
+ reason: "Producer evidence is internally contradictory; both sides are retained rather than resolved by the lab."
99
+ };
100
+ }
101
+ if (lostRequiredConditionIds.length > 0) {
102
+ return {
103
+ ...base,
104
+ availability: "available",
105
+ state: "required-condition-or-last-witness-loss",
106
+ contradictionCodes: [],
107
+ reason: `Explicit lostRequiredCondition evidence exists for: ${lostRequiredConditionIds.join(", ")}.`
108
+ };
109
+ }
110
+ if (requiredEvidenceLost === true || (requiredOmittedTotal !== null && requiredOmittedTotal > 0)) {
111
+ return {
112
+ ...base,
113
+ availability: "available",
114
+ state: "required-evidence-loss",
115
+ contradictionCodes: [],
116
+ reason: "Required evidence loss is explicitly reported without condition-level detail."
117
+ };
118
+ }
119
+ if (requiredEvidenceLost === undefined && artifact.truncation.truncated) {
120
+ return {
121
+ ...base,
122
+ availability: "available",
123
+ state: "unknown-criticality",
124
+ contradictionCodes: [],
125
+ reason: "General truncation occurred but the rollup requiredEvidenceLost field is absent, so required-versus-optional criticality cannot be determined."
126
+ };
127
+ }
128
+ if (artifact.truncation.truncated) {
129
+ return {
130
+ ...base,
131
+ availability: "available",
132
+ state: "optional-only-truncation",
133
+ contradictionCodes: [],
134
+ reason: "Truncation occurred but no required evidence loss or condition loss was reported."
135
+ };
136
+ }
137
+ return {
138
+ ...base,
139
+ availability: "available",
140
+ state: "no-truncation",
141
+ contradictionCodes: [],
142
+ reason: null
143
+ };
144
+ }
@@ -1,9 +1,12 @@
1
1
  import { collectSelectedOwnerEvidence, calculateOwnerMetrics } from "./calculateOwnerMetrics.js";
2
- import { calculateAllocationFactsForGroups } from "./calculateAllocationMetrics.js";
3
- import { classifyTruncationRecords } from "./calculateTruncationClassification.js";
2
+ import { calculateAllocationFactsForGroups, calculateGroupAllocationMetrics } from "./calculateAllocationMetrics.js";
3
+ import { classifyTruncationRecords, classifyConditionAwareTruncation } from "./calculateTruncationClassification.js";
4
+ import { calculateConditionCoverageMetrics } from "./calculateConditionCoverageMetrics.js";
5
+ import { calculateCapsuleAuditConditionAgreement, calculateProducerConditionAgreement, calculateRequiredEvidenceLossAgreement } from "./calculateProducerConditionAgreement.js";
4
6
  import { calculateSupplementalRawAgreement } from "./calculateSupplementalRawAgreement.js";
5
- import { calculateReadinessAgreementMetrics } from "./calculateReadinessAgreement.js";
7
+ import { calculateReadinessAgreementMetrics, calculateProducerReadinessRelationship } from "./calculateReadinessAgreement.js";
6
8
  import { calculateCriticalityMetrics } from "./calculateCriticalityMetrics.js";
9
+ import { evaluateRunIntegrity } from "./calculateRunIntegrityAgreement.js";
7
10
  function ownerExpectationsFor(expectations, sourceArtifact) {
8
11
  return expectations?.ownerExpectations?.filter((e) => e.sourceArtifact === sourceArtifact);
9
12
  }
@@ -36,7 +39,32 @@ function evaluateSide(input, expectations) {
36
39
  : [];
37
40
  const packetAgreement = input.packet !== undefined || rawForAgreement !== undefined ? calculateSupplementalRawAgreement(input.packet, rawForAgreement) : null;
38
41
  const reportAgreement = input.report !== undefined || rawForAgreement !== undefined ? calculateSupplementalRawAgreement(input.report, rawForAgreement) : null;
39
- return { ownerEvaluation, allocationEvaluation, truncationEvaluation, packetAgreement, reportAgreement };
42
+ // v0.4.5 Batch 2: allocation/condition-coverage evidence, computed once per side and
43
+ // carried through the result unchanged (never recomputed by a renderer or later batch).
44
+ const groupAllocationEvaluation = calculateGroupAllocationMetrics(capsule?.groupTruncation, input.role);
45
+ const conditionAwareTruncationEvaluation = truncationArtifact
46
+ ? classifyConditionAwareTruncation(truncationArtifact, truncationSourceArtifact, input.role)
47
+ : null;
48
+ const roleConditionCoverage = capsule?.roleConditionCoverage ?? input.retrievalAuditRecord?.roleConditionCoverage;
49
+ const availableEvidenceGroupIds = capsule?.evidenceGroups.map((g) => g.id);
50
+ const conditionCoverageEvaluation = calculateConditionCoverageMetrics(roleConditionCoverage, availableEvidenceGroupIds);
51
+ const roleAdequacyStatus = (capsule ?? input.retrievalAuditRecord)?.roleAdequacy.status;
52
+ const producerConditionAgreement = calculateProducerConditionAgreement(roleAdequacyStatus, conditionCoverageEvaluation);
53
+ const requiredEvidenceLossAgreement = calculateRequiredEvidenceLossAgreement(truncationArtifact?.truncation.requiredEvidenceLost, conditionCoverageEvaluation, groupAllocationEvaluation.aggregate.totalRequiredOmitted);
54
+ const capsuleAuditConditionAgreement = calculateCapsuleAuditConditionAgreement(capsule, input.retrievalAuditRecord);
55
+ return {
56
+ ownerEvaluation,
57
+ allocationEvaluation,
58
+ truncationEvaluation,
59
+ packetAgreement,
60
+ reportAgreement,
61
+ groupAllocationEvaluation,
62
+ conditionAwareTruncationEvaluation,
63
+ conditionCoverageEvaluation,
64
+ producerConditionAgreement,
65
+ requiredEvidenceLossAgreement,
66
+ capsuleAuditConditionAgreement
67
+ };
40
68
  }
41
69
  // Builds bridge-evaluator input from an already-loaded combined-bounded-stage-context
42
70
  // execution payload (Batch 3 section 12.1: "accept already-read raw, supplemental,
@@ -72,7 +100,10 @@ export function buildProducerReadinessBridgeInputFromCombinedPayload(payload, ex
72
100
  };
73
101
  }
74
102
  export function evaluateProducerReadinessBridge(input) {
75
- const hasAnyInput = input.implementation !== undefined || input.testImplementation !== undefined || input.readiness !== undefined;
103
+ const hasAnyInput = input.implementation !== undefined ||
104
+ input.testImplementation !== undefined ||
105
+ input.readiness !== undefined ||
106
+ input.runIntegrityEvidence !== undefined;
76
107
  if (!hasAnyInput) {
77
108
  return {
78
109
  status: "not-applicable",
@@ -81,6 +112,8 @@ export function evaluateProducerReadinessBridge(input) {
81
112
  testImplementation: null,
82
113
  readinessAgreement: null,
83
114
  criticalityEvaluation: null,
115
+ producerReadinessRelationship: null,
116
+ runIntegrityEvaluation: null,
84
117
  warnings: [],
85
118
  evidenceReferences: []
86
119
  };
@@ -114,6 +147,42 @@ export function evaluateProducerReadinessBridge(input) {
114
147
  input.implementation?.retrievalAuditRecord?.responsibilityMappings.mappings ??
115
148
  input.implementation?.contextCapsule?.responsibilityMappings.mappings;
116
149
  const criticalityEvaluation = calculateCriticalityMetrics(criticalityMappings, input.expectations.producerReadinessExpectations?.criticalityExpectations);
150
+ // v0.4.5 Batch 2: producer role adequacy vs supplied orchestrator readiness. Selects the
151
+ // side input.readiness.kind actually applies to when available, otherwise falls back to
152
+ // the same test-implementation-preferred precedence as criticalityEvaluation above.
153
+ const readinessSideRole = input.readiness?.kind === "implementation" || input.readiness?.kind === "test" ? input.readiness.kind : undefined;
154
+ const readinessSideEvaluation = readinessSideRole === "implementation" ? implementation : readinessSideRole === "test" ? testImplementation : (testImplementation ?? implementation);
155
+ const readinessSideRawArtifact = readinessSideEvaluation?.role === "implementation"
156
+ ? (input.implementation?.contextCapsule ?? input.implementation?.retrievalAuditRecord)
157
+ : readinessSideEvaluation?.role === "test-implementation"
158
+ ? (input.testImplementation?.contextCapsule ?? input.testImplementation?.retrievalAuditRecord)
159
+ : undefined;
160
+ // calculateProducerReadinessRelationship short-circuits before touching this argument
161
+ // whenever roleAdequacyStatus is undefined, so a placeholder is safe when no side applies.
162
+ const placeholderProducerConditionAgreement = {
163
+ availability: "unavailable",
164
+ outcome: "insufficient-evidence",
165
+ observedRoleAdequacyStatus: null,
166
+ contradictionCodes: [],
167
+ reason: "No producer side applies to the supplied readiness result."
168
+ };
169
+ const producerReadinessRelationship = calculateProducerReadinessRelationship(readinessSideRawArtifact?.roleAdequacy.status, readinessSideEvaluation?.producerConditionAgreement ?? placeholderProducerConditionAgreement, input.readiness);
170
+ // v0.4.5 Batch 3: bounded orchestrator run-integrity evidence and agreement, computed
171
+ // once here and carried through unchanged. Stage selection follows the same
172
+ // applicableContextKinds the gate itself declares (implementation preferred, since the
173
+ // gate blocks implementation before test-implementation in workflow order); the
174
+ // producerReadinessRelationship outcome already computed above feeds the end-to-end
175
+ // summary's "producerReadiness" component rather than being recalculated.
176
+ let runIntegrityEvaluation = null;
177
+ if (input.runIntegrityEvidence !== undefined) {
178
+ const gate = input.runIntegrityEvidence.gate;
179
+ const stageName = gate.applicableContextKinds.includes("implementation")
180
+ ? "implementation"
181
+ : gate.applicableContextKinds.includes("test")
182
+ ? "test-implementation"
183
+ : gate.mode;
184
+ runIntegrityEvaluation = evaluateRunIntegrity(input.runIntegrityEvidence, stageName, producerReadinessRelationship.outcome);
185
+ }
117
186
  return {
118
187
  status: "evaluated",
119
188
  reason: null,
@@ -121,6 +190,8 @@ export function evaluateProducerReadinessBridge(input) {
121
190
  testImplementation,
122
191
  readinessAgreement,
123
192
  criticalityEvaluation,
193
+ producerReadinessRelationship,
194
+ runIntegrityEvaluation,
124
195
  warnings,
125
196
  evidenceReferences
126
197
  };
@@ -15,3 +15,7 @@ export { calculateSupplementalRawAgreement } from "./calculateSupplementalRawAgr
15
15
  export { calculateReadinessStructuralAgreement, calculateReadinessDecisionAgreement, calculateInvalidReady, calculateValidBlocked, calculateReadinessAgreementMetrics } from "./calculateReadinessAgreement.js";
16
16
  export { calculateCriticalityMetrics } from "./calculateCriticalityMetrics.js";
17
17
  export { evaluateProducerReadinessBridge, buildProducerReadinessBridgeInputFromCombinedPayload } from "./evaluateProducerReadinessBridge.js";
18
+ export { calculateGroupAllocationMetrics } from "./calculateAllocationMetrics.js";
19
+ export { calculateConditionCoverageMetrics } from "./calculateConditionCoverageMetrics.js";
20
+ export { calculateProducerConditionAgreement, calculateRequiredEvidenceLossAgreement, calculateCapsuleAuditConditionAgreement } from "./calculateProducerConditionAgreement.js";
21
+ export { calculateReadinessPromptAgreement, calculateReadinessExpectedJudgeAgreement, calculateExpectedActualJudgeAgreement, calculateJudgeCorrectionAgreement, calculateJudgeFinalEligibilityAgreement, calculateEligibilityFinalArtifactAgreement, calculateLifecycleIntegrityAgreement, evaluateRunIntegrity } from "./calculateRunIntegrityAgreement.js";
@@ -38,6 +38,13 @@ export function checkMyDevKitContextArtifactConsistency(capsule, audit) {
38
38
  capsuleValue: capsule.roleContext,
39
39
  auditValue: audit.roleContext
40
40
  },
41
+ // Both absent is a compatible legacy pair. One absent and one present is a
42
+ // contradiction; absence is never normalized to an empty current result.
43
+ {
44
+ fieldPath: "roleConditionCoverage",
45
+ capsuleValue: (capsule.roleConditionCoverage ?? null),
46
+ auditValue: (audit.roleConditionCoverage ?? null)
47
+ },
41
48
  {
42
49
  fieldPath: "responsibilityMappings",
43
50
  capsuleValue: capsule.responsibilityMappings,
@@ -2,3 +2,4 @@ export * from "./contextCapsuleSelectors.js";
2
2
  export * from "./retrievalAuditRecordSelectors.js";
3
3
  export * from "./workflowInstructionPacketSelectors.js";
4
4
  export * from "./contextArtifactConsistency.js";
5
+ export * from "./orchestratorRunIntegritySelectors.js";