@dailephd/my-dev-kit-lab 0.4.3 → 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 (97) hide show
  1. package/CHANGELOG.md +468 -0
  2. package/README.md +8 -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/stageContextExpectations/producerReadinessExpectationTypes.js +8 -0
  10. package/dist/src/evaluation/stageContextExpectations/validation.js +200 -0
  11. package/dist/src/evaluation/stageContextMetrics/calculateAllocationMetrics.js +265 -0
  12. package/dist/src/evaluation/stageContextMetrics/calculateConditionCoverageMetrics.js +74 -0
  13. package/dist/src/evaluation/stageContextMetrics/calculateCriticalityMetrics.js +112 -0
  14. package/dist/src/evaluation/stageContextMetrics/calculateOwnerMetrics.js +84 -0
  15. package/dist/src/evaluation/stageContextMetrics/calculateProducerConditionAgreement.js +113 -0
  16. package/dist/src/evaluation/stageContextMetrics/calculateReadinessAgreement.js +212 -0
  17. package/dist/src/evaluation/stageContextMetrics/calculateRunIntegrityAgreement.js +309 -0
  18. package/dist/src/evaluation/stageContextMetrics/calculateSupplementalRawAgreement.js +60 -0
  19. package/dist/src/evaluation/stageContextMetrics/calculateTruncationClassification.js +144 -0
  20. package/dist/src/evaluation/stageContextMetrics/evaluateProducerReadinessBridge.js +198 -0
  21. package/dist/src/evaluation/stageContextMetrics/index.js +11 -0
  22. package/dist/src/evaluation/stageContextMetrics/producerReadinessMetricTypes.js +1 -0
  23. package/dist/src/evaluation/stageContextMetrics/runIntegrityAgreementTypes.js +1 -0
  24. package/dist/src/evaluation/stageContextSelectors/contextArtifactConsistency.js +17 -0
  25. package/dist/src/evaluation/stageContextSelectors/index.js +1 -0
  26. package/dist/src/evaluation/stageContextSelectors/orchestratorRunIntegritySelectors.js +64 -0
  27. package/dist/src/evaluation/upstreamArtifacts/index.js +8 -0
  28. package/dist/src/evaluation/upstreamArtifacts/orchestratorContextReadinessResultV1.js +247 -0
  29. package/dist/src/evaluation/upstreamArtifacts/orchestratorRunIntegrityV1.js +33 -0
  30. package/dist/src/evaluation/upstreamArtifacts/readImplementationContextPacketV1.js +9 -0
  31. package/dist/src/evaluation/upstreamArtifacts/readImplementationContextRetrievalReportV1.js +9 -0
  32. package/dist/src/evaluation/upstreamArtifacts/readOrchestratorRunIntegrityV1.js +9 -0
  33. package/dist/src/evaluation/upstreamArtifacts/readSupplementalContextTextFile.js +29 -0
  34. package/dist/src/evaluation/upstreamArtifacts/readTestContextPacketV1.js +9 -0
  35. package/dist/src/evaluation/upstreamArtifacts/readTestContextRetrievalReportV1.js +9 -0
  36. package/dist/src/evaluation/upstreamArtifacts/supplementalContextTextParser.js +75 -0
  37. package/dist/src/evaluation/upstreamArtifacts/supplementalContextTypes.js +152 -0
  38. package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitContextCapsuleV1.js +59 -2
  39. package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitRetrievalAuditRecordV1.js +15 -3
  40. package/dist/src/evaluation/upstreamArtifacts/validateOrchestratorRunIntegrityV1.js +177 -0
  41. package/dist/src/evaluation/upstreamArtifacts/validateSupplementalContextDocumentV1.js +148 -0
  42. package/dist/src/experiments/plugins/contextStrategyComparison/loadV043StrategyArtifacts.js +117 -6
  43. package/dist/src/experiments/plugins/contextStrategyComparison/runV043StageContextStrategyWithAssurance.js +18 -3
  44. package/dist/src/experiments/plugins/contextStrategyComparison/validateV043StrategyInput.js +17 -3
  45. package/dist/src/report/experiments/buildContextIntegrityReport.js +204 -0
  46. package/dist/src/report/experiments/buildContextStrategyComparisonV043Report.js +122 -0
  47. package/dist/src/report/experiments/contextIntegrityReportModel.js +1 -0
  48. package/dist/src/report/experiments/index.js +5 -0
  49. package/dist/src/report/experiments/renderContextIntegrityHtml.js +98 -0
  50. package/dist/src/report/experiments/renderContextIntegrityJsonReport.js +3 -0
  51. package/dist/src/report/experiments/renderContextIntegrityText.js +111 -0
  52. package/dist/src/report/experiments/renderContextStrategyComparisonV043Html.js +62 -0
  53. package/dist/src/report/experiments/renderPluginExperimentReportText.js +67 -0
  54. package/dist/src/securityValidation/packageChecks/requiredPackageContents.js +33 -0
  55. package/dist/src/securityValidation/packageChecks/runPackageChecks.js +11 -5
  56. package/docs/METRICS.md +111 -0
  57. package/package.json +5 -3
  58. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/derived/run-integrity-evidence.json +99 -0
  59. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/producer/implementation-audit.json +1585 -0
  60. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/producer/implementation-capsule.json +11455 -0
  61. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/00-request.txt +1 -0
  62. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/architecture-context-packet.txt +82 -0
  63. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/behavior-model.txt +104 -0
  64. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/final-report.txt +41 -0
  65. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/implementation-context-packet.txt +114 -0
  66. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/implementation-report.txt +92 -0
  67. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/judge-report.txt +34 -0
  68. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/pseudocode-packet.txt +244 -0
  69. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/request-brief.txt +40 -0
  70. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-context-packet.txt +93 -0
  71. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-implementation-report.txt +69 -0
  72. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-strategy-packet.txt +354 -0
  73. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/verification-report.txt +85 -0
  74. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/derived/run-integrity-evidence.json +98 -0
  75. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/architecture-audit.json +1169 -0
  76. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/architecture-request.json +29 -0
  77. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-audit.json +1494 -0
  78. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-capsule.json +11364 -0
  79. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-request.json +33 -0
  80. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/test-request.json +74 -0
  81. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/architecture-context-retrieval-report.txt +58 -0
  82. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/implementation-context-retrieval-report.txt +75 -0
  83. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/test-context-retrieval-report.txt +84 -0
  84. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/run.json +62 -0
  85. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/tool-versions/my-dev-kit-version.txt +1 -0
  86. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/tool-versions/orchestrator-version.txt +1 -0
  87. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/manifests/corrected-replay-manifest.json +19 -0
  88. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/manifests/failed-run-manifest.json +56 -0
  89. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/PROVENANCE.txt +18 -0
  90. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/context-capsule/complete-v1.0.0.json +556 -0
  91. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/retrieval-audit-record/complete-v1.0.0.json +238 -0
  92. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/PROVENANCE.txt +13 -0
  93. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/context-capsule.json +7874 -0
  94. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/retrieval-audit-record.json +1076 -0
  95. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/PROVENANCE.txt +31 -0
  96. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/ready-pass-final-pass.json +132 -0
  97. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/refresh-required-need-context.json +146 -0
@@ -0,0 +1,212 @@
1
+ // v0.4.4 Batch 2: readiness/raw-evidence agreement. Readiness stays observed consumer
2
+ // output (Batch 1) -- this module only compares it against an explicit fixture-declared
3
+ // expectation. It never calls, imports, or reimplements contextReadiness.ts.
4
+ import { checkSupplementalReadinessIdentityConsistency } from "../upstreamArtifacts/index.js";
5
+ const READINESS_ADEQUATE_STATUSES = [
6
+ "context sufficient for implementation",
7
+ "context sufficient with listed assumptions"
8
+ ];
9
+ function unavailableStructural(field, reason) {
10
+ return { field, expectedValue: null, observedValue: null, agreement: null, availability: "unavailable", reason };
11
+ }
12
+ export function calculateReadinessStructuralAgreement(readiness, supplementalPacket, packetSourcePath) {
13
+ const results = [];
14
+ if (readiness === undefined) {
15
+ results.push(unavailableStructural("canonicalRepositoryIdentity", "No readiness result was supplied."));
16
+ results.push(unavailableStructural("packetPath", "No readiness result was supplied."));
17
+ return results;
18
+ }
19
+ if (supplementalPacket === undefined) {
20
+ results.push(unavailableStructural("canonicalRepositoryIdentity", "No supplemental document was supplied to compare against readiness."));
21
+ }
22
+ else {
23
+ const diagnostic = checkSupplementalReadinessIdentityConsistency(supplementalPacket, readiness);
24
+ results.push({
25
+ field: "canonicalRepositoryIdentity",
26
+ expectedValue: supplementalPacket.indexIdentity,
27
+ observedValue: readiness.indexIdentity ?? null,
28
+ agreement: diagnostic === null,
29
+ availability: readiness.indexIdentity === undefined ? "unavailable" : "available",
30
+ reason: readiness.indexIdentity === undefined ? "The readiness result does not declare indexIdentity." : diagnostic?.message ?? null
31
+ });
32
+ }
33
+ if (packetSourcePath === undefined) {
34
+ results.push(unavailableStructural("packetPath", "No packet source path was supplied to compare against readiness.packetPath."));
35
+ }
36
+ else {
37
+ results.push({
38
+ field: "packetPath",
39
+ expectedValue: packetSourcePath,
40
+ observedValue: readiness.packetPath,
41
+ agreement: packetSourcePath === readiness.packetPath,
42
+ availability: "available",
43
+ reason: null
44
+ });
45
+ }
46
+ return results;
47
+ }
48
+ export function calculateReadinessDecisionAgreement(readiness, expectation) {
49
+ if (expectation === undefined) {
50
+ return {
51
+ availability: "not-applicable",
52
+ observedDecision: readiness?.decision ?? null,
53
+ allowedDecisions: null,
54
+ decisionAgreement: null,
55
+ observedClassification: readiness?.classification ?? null,
56
+ expectedClassification: null,
57
+ classificationAgreement: null,
58
+ observedIssueCodes: readiness?.blockingIssueCodes ?? [],
59
+ expectedIssueCodes: null,
60
+ issueCodesAgreement: null,
61
+ observedPrimaryIssueCode: readiness?.primaryIssue?.code ?? null,
62
+ expectedPrimaryIssueCode: null,
63
+ primaryIssueAgreement: null,
64
+ reason: "The expectation fixture declares no readiness expectation."
65
+ };
66
+ }
67
+ if (readiness === undefined) {
68
+ return {
69
+ availability: "unavailable",
70
+ observedDecision: null,
71
+ allowedDecisions: expectation.allowedDecisions ?? null,
72
+ decisionAgreement: null,
73
+ observedClassification: null,
74
+ expectedClassification: expectation.expectedClassification ?? null,
75
+ classificationAgreement: null,
76
+ observedIssueCodes: [],
77
+ expectedIssueCodes: expectation.expectedIssueCodes ?? null,
78
+ issueCodesAgreement: null,
79
+ observedPrimaryIssueCode: null,
80
+ expectedPrimaryIssueCode: expectation.expectedPrimaryIssueCode ?? null,
81
+ primaryIssueAgreement: null,
82
+ reason: "No readiness result was supplied."
83
+ };
84
+ }
85
+ const decisionAgreement = expectation.allowedDecisions === undefined ? null : expectation.allowedDecisions.includes(readiness.decision);
86
+ const classificationAgreement = expectation.expectedClassification === undefined ? null : expectation.expectedClassification === readiness.classification;
87
+ const issueCodesAgreement = expectation.expectedIssueCodes === undefined
88
+ ? null
89
+ : expectation.expectedIssueCodes.length === readiness.blockingIssueCodes.length &&
90
+ expectation.expectedIssueCodes.every((code) => readiness.blockingIssueCodes.includes(code));
91
+ const primaryIssueAgreement = expectation.expectedPrimaryIssueCode === undefined
92
+ ? null
93
+ : expectation.expectedPrimaryIssueCode === (readiness.primaryIssue?.code ?? null);
94
+ return {
95
+ availability: "available",
96
+ observedDecision: readiness.decision,
97
+ allowedDecisions: expectation.allowedDecisions ?? null,
98
+ decisionAgreement,
99
+ observedClassification: readiness.classification,
100
+ expectedClassification: expectation.expectedClassification ?? null,
101
+ classificationAgreement,
102
+ observedIssueCodes: readiness.blockingIssueCodes,
103
+ expectedIssueCodes: expectation.expectedIssueCodes ?? null,
104
+ issueCodesAgreement,
105
+ observedPrimaryIssueCode: readiness.primaryIssue?.code ?? null,
106
+ expectedPrimaryIssueCode: expectation.expectedPrimaryIssueCode ?? null,
107
+ primaryIssueAgreement,
108
+ reason: null
109
+ };
110
+ }
111
+ export function calculateInvalidReady(readiness, expectation) {
112
+ if (readiness === undefined) {
113
+ return { availability: "unavailable", invalidReady: null, observedDecision: null, expectedBlockingDecision: false, missingExpectedIssueCodes: [], reason: "No readiness result was supplied." };
114
+ }
115
+ if (expectation === undefined) {
116
+ return { availability: "not-applicable", invalidReady: null, observedDecision: readiness.decision, expectedBlockingDecision: false, missingExpectedIssueCodes: [], reason: "The expectation fixture declares no readiness expectation." };
117
+ }
118
+ const expectsBlocking = (expectation.allowedDecisions !== undefined &&
119
+ !expectation.allowedDecisions.includes("ready") &&
120
+ (expectation.allowedDecisions.includes("refresh-required") || expectation.allowedDecisions.length > 0)) ||
121
+ (expectation.expectedIssueCodes !== undefined && expectation.expectedIssueCodes.length > 0);
122
+ if (!expectsBlocking) {
123
+ return { availability: "not-applicable", invalidReady: null, observedDecision: readiness.decision, expectedBlockingDecision: false, missingExpectedIssueCodes: [], reason: "The expectation fixture does not require blocked or refresh-required behavior." };
124
+ }
125
+ if (readiness.decision !== "ready") {
126
+ return { availability: "available", invalidReady: false, observedDecision: readiness.decision, expectedBlockingDecision: true, missingExpectedIssueCodes: [], reason: null };
127
+ }
128
+ const expectedCodes = expectation.expectedIssueCodes ?? [];
129
+ const missing = expectedCodes.filter((code) => !readiness.blockingIssueCodes.includes(code));
130
+ return { availability: "available", invalidReady: true, observedDecision: readiness.decision, expectedBlockingDecision: true, missingExpectedIssueCodes: missing, reason: null };
131
+ }
132
+ export function calculateValidBlocked(readiness, expectation) {
133
+ if (readiness === undefined) {
134
+ return { availability: "unavailable", validBlocked: null, validRefreshRequired: null, observedDecision: null, reason: "No readiness result was supplied." };
135
+ }
136
+ if (expectation === undefined) {
137
+ return { availability: "not-applicable", validBlocked: null, validRefreshRequired: null, observedDecision: readiness.decision, reason: "The expectation fixture declares no readiness expectation." };
138
+ }
139
+ if (readiness.decision !== "refresh-required") {
140
+ return { availability: "available", validBlocked: false, validRefreshRequired: false, observedDecision: readiness.decision, reason: null };
141
+ }
142
+ const allowsRefreshRequired = expectation.allowedDecisions === undefined || expectation.allowedDecisions.includes("refresh-required");
143
+ const issueCodesMatch = expectation.expectedIssueCodes === undefined ||
144
+ expectation.expectedIssueCodes.every((code) => readiness.blockingIssueCodes.includes(code));
145
+ const primaryMatches = expectation.expectedPrimaryIssueCode === undefined || expectation.expectedPrimaryIssueCode === (readiness.primaryIssue?.code ?? null);
146
+ const valid = allowsRefreshRequired && issueCodesMatch && primaryMatches;
147
+ return { availability: "available", validBlocked: valid, validRefreshRequired: valid, observedDecision: readiness.decision, reason: null };
148
+ }
149
+ // v0.4.5 Batch 2 (section 18): compares producer role adequacy (as already reconciled with
150
+ // condition-coverage evidence by calculateProducerConditionAgreement) against the supplied
151
+ // orchestrator readiness decision already accepted by the v0.4.4 bridge. This never decides
152
+ // what readiness "should" have been -- it only reports the observed relationship.
153
+ export function calculateProducerReadinessRelationship(roleAdequacyStatus, producerConditionAgreement, readiness) {
154
+ if (roleAdequacyStatus === undefined || readiness === undefined) {
155
+ return {
156
+ availability: "unavailable",
157
+ outcome: "insufficient-evidence",
158
+ observedRoleAdequacyStatus: roleAdequacyStatus ?? null,
159
+ observedReadinessDecision: readiness?.decision ?? null,
160
+ contradictionCodes: [],
161
+ reason: roleAdequacyStatus === undefined && readiness === undefined
162
+ ? "Neither roleAdequacy.status nor a readiness result was supplied."
163
+ : roleAdequacyStatus === undefined
164
+ ? "No roleAdequacy.status was supplied."
165
+ : "No readiness result was supplied."
166
+ };
167
+ }
168
+ if (producerConditionAgreement.outcome === "contradiction") {
169
+ return {
170
+ availability: "available",
171
+ outcome: "insufficient-evidence",
172
+ observedRoleAdequacyStatus: roleAdequacyStatus,
173
+ observedReadinessDecision: readiness.decision,
174
+ contradictionCodes: [],
175
+ reason: "Producer role-adequacy already contradicts its own condition-coverage evidence; the readiness relationship cannot be attributed to either side."
176
+ };
177
+ }
178
+ if (producerConditionAgreement.outcome === "unsupported-legacy-evidence" || producerConditionAgreement.outcome === "insufficient-evidence") {
179
+ return {
180
+ availability: "unavailable",
181
+ outcome: producerConditionAgreement.outcome,
182
+ observedRoleAdequacyStatus: roleAdequacyStatus,
183
+ observedReadinessDecision: readiness.decision,
184
+ contradictionCodes: [],
185
+ reason: producerConditionAgreement.reason
186
+ };
187
+ }
188
+ const producerAdequate = READINESS_ADEQUATE_STATUSES.includes(roleAdequacyStatus);
189
+ const readinessReady = readiness.decision === "ready";
190
+ const contradictionCodes = [];
191
+ if (producerAdequate && !readinessReady)
192
+ contradictionCodes.push("PRODUCER_ADEQUATE_BUT_READINESS_NOT_READY");
193
+ if (!producerAdequate && readinessReady)
194
+ contradictionCodes.push("PRODUCER_INADEQUATE_BUT_READINESS_READY");
195
+ const outcome = contradictionCodes.length > 0 ? "contradiction" : "agreement";
196
+ return {
197
+ availability: "available",
198
+ outcome,
199
+ observedRoleAdequacyStatus: roleAdequacyStatus,
200
+ observedReadinessDecision: readiness.decision,
201
+ contradictionCodes,
202
+ reason: outcome === "contradiction" ? "Producer role adequacy and supplied orchestrator readiness disagree; both sides are retained without a lab-owned verdict." : null
203
+ };
204
+ }
205
+ export function calculateReadinessAgreementMetrics(readiness, expectation, supplementalPacket, packetSourcePath) {
206
+ return {
207
+ structuralAgreement: calculateReadinessStructuralAgreement(readiness, supplementalPacket, packetSourcePath),
208
+ decisionAgreement: calculateReadinessDecisionAgreement(readiness, expectation),
209
+ invalidReady: calculateInvalidReady(readiness, expectation),
210
+ validBlocked: calculateValidBlocked(readiness, expectation)
211
+ };
212
+ }
@@ -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
+ }
@@ -0,0 +1,60 @@
1
+ function extractRawIdentity(artifact) {
2
+ return {
3
+ projectRoot: artifact.index.projectRoot,
4
+ role: artifact.request.role,
5
+ freshnessState: artifact.freshness.state,
6
+ truncated: artifact.truncation.truncated,
7
+ requiredEvidenceLost: artifact.truncation.requiredEvidenceLost
8
+ };
9
+ }
10
+ function truncationToYesNo(value) {
11
+ return value ? "yes" : "no";
12
+ }
13
+ function buildField(field, rawValue, supplementalValue, unavailableReason) {
14
+ if (unavailableReason !== null) {
15
+ return { field, rawValue, supplementalValue, agreement: null, availability: "unavailable", reason: unavailableReason };
16
+ }
17
+ return { field, rawValue, supplementalValue, agreement: rawValue === supplementalValue, availability: "available", reason: null };
18
+ }
19
+ export function calculateSupplementalRawAgreement(supplemental, raw) {
20
+ if (supplemental === undefined || raw === undefined) {
21
+ const reason = supplemental === undefined && raw === undefined
22
+ ? "Neither a supplemental document nor a raw artifact was supplied."
23
+ : supplemental === undefined
24
+ ? "No supplemental document was supplied."
25
+ : "No raw context-capsule or retrieval-audit artifact was supplied.";
26
+ const fields = [
27
+ buildField("canonicalRepositoryIdentity", null, supplemental?.indexIdentity ?? null, reason),
28
+ buildField("role", null, supplemental?.role ?? null, reason),
29
+ buildField("freshness", null, supplemental?.freshness ?? null, reason),
30
+ buildField("requiredEvidenceTruncated", null, supplemental?.requiredEvidenceTruncated ?? null, reason),
31
+ buildField("requiredEvidenceLost", null, supplemental?.requiredEvidenceTruncated ?? null, reason)
32
+ ];
33
+ return { fields, contradictions: [], upstreamProducerParityPreserved: null };
34
+ }
35
+ const rawIdentity = extractRawIdentity(raw);
36
+ const fields = [];
37
+ fields.push(buildField("canonicalRepositoryIdentity", rawIdentity.projectRoot ?? null, supplemental.indexIdentity, rawIdentity.projectRoot === undefined ? "The raw artifact does not declare index.projectRoot." : null));
38
+ const roleComparable = rawIdentity.role === "implementation" || rawIdentity.role === "test-implementation";
39
+ fields.push(buildField("role", rawIdentity.role, supplemental.role, roleComparable ? null : `Raw role "${rawIdentity.role}" has no supplemental-document counterpart in this vocabulary.`));
40
+ fields.push(buildField("freshness", rawIdentity.freshnessState, supplemental.freshness, null));
41
+ fields.push(buildField("requiredEvidenceTruncated", truncationToYesNo(rawIdentity.truncated), supplemental.requiredEvidenceTruncated, supplemental.requiredEvidenceTruncated === "unknown"
42
+ ? "The supplemental document declares truncation as \"unknown\", so agreement with the raw boolean cannot be determined."
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));
52
+ const contradictions = fields.filter((f) => f.availability === "available" && f.agreement === false);
53
+ return {
54
+ fields,
55
+ contradictions,
56
+ // A successfully read raw pair is itself the observed outcome of the frozen
57
+ // assertRawEvidenceParity() write-time guard; see file header. Never recomputed here.
58
+ upstreamProducerParityPreserved: true
59
+ };
60
+ }