@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,265 @@
1
+ const NO_BORROW_EVIDENCE_REASON = "The frozen my-dev-kit artifacts do not expose cross-group capacity borrowing evidence.";
2
+ export function calculateAllocationFactsForGroups(evidenceGroups, sourceArtifact, sourceInstance) {
3
+ const requiredGroupCapacity = [];
4
+ const usedReservation = [];
5
+ const borrowedCapacity = [];
6
+ const unusedCapacity = [];
7
+ for (const group of evidenceGroups) {
8
+ requiredGroupCapacity.push({
9
+ groupId: group.id,
10
+ sourceArtifact,
11
+ sourceInstance,
12
+ value: group.limit,
13
+ unit: "items",
14
+ availability: group.limit === null ? "unavailable" : "available",
15
+ reason: group.limit === null ? "No hard limit is declared for this evidence group." : null
16
+ });
17
+ usedReservation.push({
18
+ groupId: group.id,
19
+ sourceArtifact,
20
+ sourceInstance,
21
+ value: group.usedCount,
22
+ unit: "items",
23
+ availability: "available",
24
+ reason: null
25
+ });
26
+ borrowedCapacity.push({
27
+ groupId: group.id,
28
+ sourceArtifact,
29
+ sourceInstance,
30
+ value: null,
31
+ unit: "items",
32
+ availability: "unavailable",
33
+ reason: NO_BORROW_EVIDENCE_REASON
34
+ });
35
+ if (group.limit === null) {
36
+ unusedCapacity.push({
37
+ groupId: group.id,
38
+ sourceArtifact,
39
+ sourceInstance,
40
+ value: null,
41
+ unit: "items",
42
+ availability: "unavailable",
43
+ reason: "No hard limit is declared for this evidence group, so unused capacity cannot be computed."
44
+ });
45
+ continue;
46
+ }
47
+ const unused = group.limit - group.usedCount;
48
+ if (unused < 0) {
49
+ unusedCapacity.push({
50
+ groupId: group.id,
51
+ sourceArtifact,
52
+ sourceInstance,
53
+ value: null,
54
+ unit: "items",
55
+ availability: "unavailable",
56
+ reason: `Computed unused capacity was negative (used ${group.usedCount} exceeds limit ${group.limit}); treated as a data inconsistency rather than a valid value.`
57
+ });
58
+ continue;
59
+ }
60
+ unusedCapacity.push({
61
+ groupId: group.id,
62
+ sourceArtifact,
63
+ sourceInstance,
64
+ value: unused,
65
+ unit: "items",
66
+ availability: "available",
67
+ reason: null
68
+ });
69
+ }
70
+ return { requiredGroupCapacity, usedReservation, borrowedCapacity, unusedCapacity };
71
+ }
72
+ export function calculateRequiredEvidenceOmitted(matches, sourceArtifactTruncated) {
73
+ const missing = matches.filter((m) => m.inclusion === "required" && m.outcome === "missing");
74
+ if (missing.length === 0) {
75
+ return {
76
+ metric: { availability: "not-applicable", count: null, evidenceKeys: [], reason: "No required expectation is missing from observed evidence." },
77
+ entries: []
78
+ };
79
+ }
80
+ const entries = missing.map((m) => ({
81
+ evidenceKey: m.targetKey,
82
+ groupId: null,
83
+ truncationReported: sourceArtifactTruncated[m.sourceArtifact] ?? false,
84
+ sourceArtifact: m.sourceArtifact
85
+ }));
86
+ return {
87
+ metric: { availability: "available", count: entries.length, evidenceKeys: entries.map((e) => e.evidenceKey), reason: null },
88
+ entries
89
+ };
90
+ }
91
+ // Not currently consumed by the calculators above, but kept available for callers that
92
+ // want to cross-check allocation expectations directly (bounded, deterministic; the lab
93
+ // still never recalculates the producer's allocation decision).
94
+ export function findAllocationExpectationForGroup(expectations, groupId) {
95
+ return (expectations ?? []).find((e) => e.groupId === groupId);
96
+ }
97
+ // v0.4.5 Batch 2: per-group/aggregate allocation and spillover evidence, read directly from
98
+ // ContextCapsule.groupTruncation (the Batch 1 mirror of the v1.10.3/v1.10.4 required-first
99
+ // allocator diagnostics). A group entry that lacks every additive allocation field is
100
+ // legacy/unavailable, never fabricated as zero. RetrievalAuditRecord has no groupTruncation
101
+ // field, so this evidence is context-capsule-only (sourceArtifact is always fixed).
102
+ function hasAnyAllocationField(entry) {
103
+ return (entry.required !== undefined ||
104
+ entry.reservation !== undefined ||
105
+ entry.initiallySelectedCount !== undefined ||
106
+ entry.unusedReservationContributed !== undefined ||
107
+ entry.borrowedCapacity !== undefined ||
108
+ entry.requiredOmittedCount !== undefined ||
109
+ entry.optionalOmittedCount !== undefined ||
110
+ entry.adequacyAffected !== undefined ||
111
+ entry.governingHardBound !== undefined ||
112
+ entry.aggregateCapacityUsed !== undefined ||
113
+ entry.aggregateCapacityRemaining !== undefined);
114
+ }
115
+ function calculatePerGroupAllocation(entries, sourceInstance) {
116
+ return entries.map((entry) => {
117
+ const available = hasAnyAllocationField(entry);
118
+ return {
119
+ groupId: entry.groupId,
120
+ sourceArtifact: "context-capsule",
121
+ sourceInstance,
122
+ availability: available ? "available" : "unavailable",
123
+ required: entry.required ?? null,
124
+ reservation: entry.reservation ?? null,
125
+ initiallySelectedCount: entry.initiallySelectedCount ?? null,
126
+ unusedReservationContributed: entry.unusedReservationContributed ?? null,
127
+ borrowedCapacity: entry.borrowedCapacity ?? null,
128
+ governingHardBound: entry.governingHardBound ?? null,
129
+ requiredOmittedCount: entry.requiredOmittedCount ?? null,
130
+ optionalOmittedCount: entry.optionalOmittedCount ?? null,
131
+ droppedCount: entry.droppedCount,
132
+ droppedEvidenceIds: entry.droppedEvidenceIds ?? [],
133
+ adequacyAffected: entry.adequacyAffected ?? null,
134
+ aggregateCapacityUsed: entry.aggregateCapacityUsed ?? null,
135
+ aggregateCapacityRemaining: entry.aggregateCapacityRemaining ?? null,
136
+ reason: available ? null : `Group "${entry.groupId}" exposes no v1.10.3/v1.10.4 allocation diagnostics (legacy schema-major-1 evidence).`
137
+ };
138
+ });
139
+ }
140
+ function sumIfComplete(entries, field) {
141
+ if (entries.length === 0)
142
+ return null;
143
+ let total = 0;
144
+ for (const entry of entries) {
145
+ const value = entry[field];
146
+ if (typeof value !== "number")
147
+ return null;
148
+ total += value;
149
+ }
150
+ return total;
151
+ }
152
+ function calculateAggregateAllocation(entries) {
153
+ if (entries.length === 0) {
154
+ return {
155
+ availability: "not-applicable",
156
+ groupCount: 0,
157
+ totalReservation: null,
158
+ totalInitiallySelected: null,
159
+ totalUnusedReservationContributed: null,
160
+ totalBorrowedCapacity: null,
161
+ totalRequiredOmitted: null,
162
+ totalOptionalOmitted: null,
163
+ totalDropped: null,
164
+ groupsContributingUnusedReservation: [],
165
+ groupsBorrowingCapacity: [],
166
+ groupsWithRequiredOmission: [],
167
+ groupsWithOptionalOnlyOmission: [],
168
+ groupsWithAdequacyAffected: [],
169
+ partial: false,
170
+ reason: "No evidence groups were supplied."
171
+ };
172
+ }
173
+ const anyAllocationEvidence = entries.some(hasAnyAllocationField);
174
+ if (!anyAllocationEvidence) {
175
+ return {
176
+ availability: "unavailable",
177
+ groupCount: entries.length,
178
+ totalReservation: null,
179
+ totalInitiallySelected: null,
180
+ totalUnusedReservationContributed: null,
181
+ totalBorrowedCapacity: null,
182
+ totalRequiredOmitted: null,
183
+ totalOptionalOmitted: null,
184
+ totalDropped: null,
185
+ groupsContributingUnusedReservation: [],
186
+ groupsBorrowingCapacity: [],
187
+ groupsWithRequiredOmission: [],
188
+ groupsWithOptionalOnlyOmission: [],
189
+ groupsWithAdequacyAffected: [],
190
+ partial: false,
191
+ reason: "No group exposes v1.10.3/v1.10.4 allocation diagnostics (legacy schema-major-1 evidence)."
192
+ };
193
+ }
194
+ const totalReservation = sumIfComplete(entries, "reservation");
195
+ const totalInitiallySelected = sumIfComplete(entries, "initiallySelectedCount");
196
+ const totalUnusedReservationContributed = sumIfComplete(entries, "unusedReservationContributed");
197
+ const totalBorrowedCapacity = sumIfComplete(entries, "borrowedCapacity");
198
+ const totalRequiredOmitted = sumIfComplete(entries, "requiredOmittedCount");
199
+ const totalOptionalOmitted = sumIfComplete(entries, "optionalOmittedCount");
200
+ const totalDropped = sumIfComplete(entries, "droppedCount");
201
+ const totals = [
202
+ totalReservation,
203
+ totalInitiallySelected,
204
+ totalUnusedReservationContributed,
205
+ totalBorrowedCapacity,
206
+ totalRequiredOmitted,
207
+ totalOptionalOmitted
208
+ ];
209
+ const partial = totals.some((t) => t === null);
210
+ return {
211
+ availability: "available",
212
+ groupCount: entries.length,
213
+ totalReservation,
214
+ totalInitiallySelected,
215
+ totalUnusedReservationContributed,
216
+ totalBorrowedCapacity,
217
+ totalRequiredOmitted,
218
+ totalOptionalOmitted,
219
+ totalDropped,
220
+ groupsContributingUnusedReservation: entries.filter((e) => (e.unusedReservationContributed ?? 0) > 0).map((e) => e.groupId),
221
+ groupsBorrowingCapacity: entries.filter((e) => (e.borrowedCapacity ?? 0) > 0).map((e) => e.groupId),
222
+ groupsWithRequiredOmission: entries.filter((e) => (e.requiredOmittedCount ?? 0) > 0).map((e) => e.groupId),
223
+ groupsWithOptionalOnlyOmission: entries
224
+ .filter((e) => (e.optionalOmittedCount ?? 0) > 0 && (e.requiredOmittedCount ?? 0) === 0)
225
+ .map((e) => e.groupId),
226
+ groupsWithAdequacyAffected: entries.filter((e) => e.adequacyAffected === true).map((e) => e.groupId),
227
+ partial,
228
+ reason: partial ? "At least one group lacks the full allocation field set; incomplete totals are reported as null rather than partially summed." : null
229
+ };
230
+ }
231
+ function calculateSpillover(entries) {
232
+ const anyAllocationEvidence = entries.some(hasAnyAllocationField);
233
+ if (!anyAllocationEvidence) {
234
+ return {
235
+ availability: "unavailable",
236
+ groupsContributing: [],
237
+ groupsBorrowing: [],
238
+ totalContributed: null,
239
+ totalBorrowed: null,
240
+ contributionCoversBorrowing: null,
241
+ reason: "No group exposes v1.10.3/v1.10.4 allocation diagnostics (legacy schema-major-1 evidence)."
242
+ };
243
+ }
244
+ const groupsContributing = entries.filter((e) => (e.unusedReservationContributed ?? 0) > 0).map((e) => e.groupId);
245
+ const groupsBorrowing = entries.filter((e) => (e.borrowedCapacity ?? 0) > 0).map((e) => e.groupId);
246
+ const totalContributed = sumIfComplete(entries, "unusedReservationContributed");
247
+ const totalBorrowed = sumIfComplete(entries, "borrowedCapacity");
248
+ return {
249
+ availability: "available",
250
+ groupsContributing,
251
+ groupsBorrowing,
252
+ totalContributed,
253
+ totalBorrowed,
254
+ contributionCoversBorrowing: totalContributed !== null && totalBorrowed !== null ? totalContributed >= totalBorrowed : null,
255
+ reason: null
256
+ };
257
+ }
258
+ export function calculateGroupAllocationMetrics(groupTruncation, sourceInstance) {
259
+ const entries = groupTruncation ?? [];
260
+ return {
261
+ perGroup: calculatePerGroupAllocation(entries, sourceInstance),
262
+ aggregate: calculateAggregateAllocation(entries),
263
+ spillover: calculateSpillover(entries)
264
+ };
265
+ }
@@ -0,0 +1,74 @@
1
+ function unavailableCoverageMetrics(reason) {
2
+ const unavailableCount = { availability: "unavailable", count: null, evidenceKeys: [], reason };
3
+ return {
4
+ availability: "unavailable",
5
+ requiredConditionsTotal: unavailableCount,
6
+ requiredConditionsSatisfied: unavailableCount,
7
+ requiredConditionsMissing: unavailableCount,
8
+ requiredConditionsLost: unavailableCount,
9
+ optionalConditionsTotal: unavailableCount,
10
+ optionalConditionsSatisfied: unavailableCount,
11
+ optionalConditionsMissing: unavailableCount,
12
+ witnessEvidence: [],
13
+ lastWitnessLoss: unavailableCount,
14
+ conditionToGroupMapping: [],
15
+ reason
16
+ };
17
+ }
18
+ function countMetric(ids) {
19
+ return { availability: "available", count: ids.length, evidenceKeys: ids, reason: null };
20
+ }
21
+ function coverageState(condition) {
22
+ if (condition.conditionSatisfied)
23
+ return "satisfied";
24
+ if (condition.lostRequiredCondition)
25
+ return "lost-to-allocation";
26
+ return "missing-evidence";
27
+ }
28
+ export function calculateConditionCoverageMetrics(roleConditionCoverage, availableEvidenceGroupIds) {
29
+ if (roleConditionCoverage === undefined) {
30
+ return unavailableCoverageMetrics("No roleConditionCoverage evidence was supplied (legacy schema-major-1 artifact, or the producer did not emit condition-level diagnostics).");
31
+ }
32
+ const required = roleConditionCoverage.filter((c) => c.required);
33
+ const optional = roleConditionCoverage.filter((c) => !c.required);
34
+ const requiredSatisfied = required.filter((c) => c.conditionSatisfied);
35
+ const requiredLost = required.filter((c) => c.lostRequiredCondition);
36
+ const requiredMissing = required.filter((c) => !c.conditionSatisfied && !c.lostRequiredCondition);
37
+ const optionalSatisfied = optional.filter((c) => c.conditionSatisfied);
38
+ const optionalMissing = optional.filter((c) => !c.conditionSatisfied);
39
+ const witnessEvidence = roleConditionCoverage.map((c) => ({
40
+ conditionId: c.conditionId,
41
+ role: c.role,
42
+ required: c.required,
43
+ witnessPolicy: c.witnessPolicy,
44
+ requiredWitnessCount: c.requiredWitnessCount,
45
+ availableWitnessCount: c.availableWitnessCount,
46
+ retainedWitnessCount: c.retainedWitnessCount,
47
+ retainedWitnessIds: c.retainedWitnessIds,
48
+ adequateWitnessRemains: c.conditionSatisfied,
49
+ coverageState: coverageState(c),
50
+ lossReason: c.lossReason,
51
+ evidenceGroupIds: c.evidenceGroupIds
52
+ }));
53
+ const conditionToGroupMapping = roleConditionCoverage.map((c) => ({
54
+ conditionId: c.conditionId,
55
+ required: c.required,
56
+ evidenceGroupIds: c.evidenceGroupIds,
57
+ mapped: c.evidenceGroupIds.length > 0,
58
+ unknownGroupIds: availableEvidenceGroupIds === undefined ? [] : c.evidenceGroupIds.filter((id) => !availableEvidenceGroupIds.includes(id))
59
+ }));
60
+ return {
61
+ availability: "available",
62
+ requiredConditionsTotal: countMetric(required.map((c) => c.conditionId)),
63
+ requiredConditionsSatisfied: countMetric(requiredSatisfied.map((c) => c.conditionId)),
64
+ requiredConditionsMissing: countMetric(requiredMissing.map((c) => c.conditionId)),
65
+ requiredConditionsLost: countMetric(requiredLost.map((c) => c.conditionId)),
66
+ optionalConditionsTotal: countMetric(optional.map((c) => c.conditionId)),
67
+ optionalConditionsSatisfied: countMetric(optionalSatisfied.map((c) => c.conditionId)),
68
+ optionalConditionsMissing: countMetric(optionalMissing.map((c) => c.conditionId)),
69
+ witnessEvidence,
70
+ lastWitnessLoss: countMetric(requiredLost.map((c) => c.conditionId)),
71
+ conditionToGroupMapping,
72
+ reason: null
73
+ };
74
+ }
@@ -0,0 +1,112 @@
1
+ function findMapping(mappings, responsibilityId) {
2
+ return mappings?.find((m) => m.responsibilityId === responsibilityId);
3
+ }
4
+ export function calculateCriticalityMetrics(mappings, expectations) {
5
+ const list = expectations ?? [];
6
+ const overlayAgreement = [];
7
+ const missingCriticalityResponsibilityIds = [];
8
+ const conflictingCriticalityResponsibilityIds = [];
9
+ for (const e of list) {
10
+ if (!e.applicable) {
11
+ overlayAgreement.push({
12
+ responsibilityId: e.responsibilityId,
13
+ expectedCriticality: e.expectedCriticality,
14
+ observedCriticality: findMapping(mappings, e.responsibilityId)?.criticality ?? null,
15
+ agreement: null,
16
+ availability: "not-applicable",
17
+ reason: "Expectation is marked not applicable."
18
+ });
19
+ continue;
20
+ }
21
+ if (mappings === undefined) {
22
+ overlayAgreement.push({
23
+ responsibilityId: e.responsibilityId,
24
+ expectedCriticality: e.expectedCriticality,
25
+ observedCriticality: null,
26
+ agreement: null,
27
+ availability: "unavailable",
28
+ reason: "No raw responsibility-mapping evidence was supplied."
29
+ });
30
+ missingCriticalityResponsibilityIds.push(e.responsibilityId);
31
+ continue;
32
+ }
33
+ const mapping = findMapping(mappings, e.responsibilityId);
34
+ if (mapping === undefined) {
35
+ overlayAgreement.push({
36
+ responsibilityId: e.responsibilityId,
37
+ expectedCriticality: e.expectedCriticality,
38
+ observedCriticality: null,
39
+ agreement: null,
40
+ availability: "unavailable",
41
+ reason: "No raw mapping entry exists for this responsibility ID."
42
+ });
43
+ missingCriticalityResponsibilityIds.push(e.responsibilityId);
44
+ continue;
45
+ }
46
+ const agreement = mapping.criticality === e.expectedCriticality;
47
+ overlayAgreement.push({
48
+ responsibilityId: e.responsibilityId,
49
+ expectedCriticality: e.expectedCriticality,
50
+ observedCriticality: mapping.criticality,
51
+ agreement,
52
+ availability: "available",
53
+ reason: null
54
+ });
55
+ if (!agreement)
56
+ conflictingCriticalityResponsibilityIds.push(e.responsibilityId);
57
+ }
58
+ const unexpectedCriticalityResponsibilityIds = list.length === 0 || mappings === undefined
59
+ ? []
60
+ : mappings.filter((m) => !list.some((e) => e.responsibilityId === m.responsibilityId)).map((m) => m.responsibilityId);
61
+ const fullyMappedCriticalIds = [];
62
+ const partiallyMappedCriticalIds = [];
63
+ const unmappedCriticalIds = [];
64
+ const missingMappingEvidenceCriticalIds = [];
65
+ for (const e of list) {
66
+ if (!e.applicable || !e.requiresFullMapping || e.expectedCriticality !== "critical")
67
+ continue;
68
+ const mapping = findMapping(mappings, e.responsibilityId);
69
+ if (mapping === undefined) {
70
+ missingMappingEvidenceCriticalIds.push(e.responsibilityId);
71
+ continue;
72
+ }
73
+ if (mapping.mappingStatus === "mapped")
74
+ fullyMappedCriticalIds.push(e.responsibilityId);
75
+ else if (mapping.mappingStatus === "partially-mapped")
76
+ partiallyMappedCriticalIds.push(e.responsibilityId);
77
+ else if (mapping.mappingStatus === "unmapped")
78
+ unmappedCriticalIds.push(e.responsibilityId);
79
+ // mapping.mappingStatus === "not-applicable": excluded from the denominator entirely.
80
+ }
81
+ const denominator = fullyMappedCriticalIds.length + partiallyMappedCriticalIds.length + unmappedCriticalIds.length;
82
+ const mappedCriticalCompleteness = denominator === 0
83
+ ? {
84
+ availability: "not-applicable",
85
+ numerator: null,
86
+ denominator: null,
87
+ rate: null,
88
+ matchedExpectationIds: [],
89
+ missingExpectationIds: [],
90
+ reason: "No applicable critical responsibility requiring full mapping has resolvable mapping status."
91
+ }
92
+ : {
93
+ availability: "available",
94
+ numerator: fullyMappedCriticalIds.length,
95
+ denominator,
96
+ rate: fullyMappedCriticalIds.length / denominator,
97
+ matchedExpectationIds: fullyMappedCriticalIds,
98
+ missingExpectationIds: [...partiallyMappedCriticalIds, ...unmappedCriticalIds],
99
+ reason: null
100
+ };
101
+ return {
102
+ overlayAgreement,
103
+ missingCriticalityResponsibilityIds,
104
+ conflictingCriticalityResponsibilityIds,
105
+ unexpectedCriticalityResponsibilityIds,
106
+ mappedCriticalCompleteness,
107
+ fullyMappedCriticalIds,
108
+ partiallyMappedCriticalIds,
109
+ unmappedCriticalIds,
110
+ missingMappingEvidenceCriticalIds
111
+ };
112
+ }
@@ -0,0 +1,84 @@
1
+ export function collectSelectedOwnerEvidence(selectedOwners, sourceArtifact, sourceInstance) {
2
+ return selectedOwners.map((item) => ({
3
+ ownerId: item.id,
4
+ sourceArtifact,
5
+ sourceInstance,
6
+ itemKind: item.itemKind,
7
+ path: item.path ?? null,
8
+ symbolId: item.symbolId ?? null,
9
+ sourceFieldPath: `${sourceInstance}.selectedOwners`
10
+ }));
11
+ }
12
+ function notApplicableRatio(reason) {
13
+ return { availability: "not-applicable", numerator: null, denominator: null, rate: null, matchedExpectationIds: [], missingExpectationIds: [], reason };
14
+ }
15
+ function unavailableRatio(reason) {
16
+ return { availability: "unavailable", numerator: null, denominator: null, rate: null, matchedExpectationIds: [], missingExpectationIds: [], reason };
17
+ }
18
+ function notApplicableCount(reason) {
19
+ return { availability: "not-applicable", count: null, evidenceKeys: [], reason };
20
+ }
21
+ function unavailableCount(reason) {
22
+ return { availability: "unavailable", count: null, evidenceKeys: [], reason };
23
+ }
24
+ export function calculateOwnerMetrics(selectedOwnerEvidence, ownerExpectations) {
25
+ const evidence = selectedOwnerEvidence ?? [];
26
+ const evidenceUnavailable = selectedOwnerEvidence === undefined;
27
+ const unavailableReason = "Producer selected-owner evidence was not supplied to this metric.";
28
+ const requiredIds = new Set((ownerExpectations ?? []).filter((e) => e.inclusion === "required").map((e) => e.ownerId));
29
+ const allowedIds = new Set((ownerExpectations ?? []).filter((e) => e.inclusion === "allowed").map((e) => e.ownerId));
30
+ const forbiddenIds = new Set((ownerExpectations ?? []).filter((e) => e.inclusion === "forbidden").map((e) => e.ownerId));
31
+ const hasClosedSet = requiredIds.size > 0 || allowedIds.size > 0;
32
+ const observedIds = new Set(evidence.map((e) => e.ownerId));
33
+ const expectedOwnerPresent = evidenceUnavailable
34
+ ? unavailableRatio(unavailableReason)
35
+ : requiredIds.size === 0
36
+ ? notApplicableRatio("The expectation fixture contains no required owner expectations.")
37
+ : (() => {
38
+ const matched = [...requiredIds].filter((id) => observedIds.has(id));
39
+ const missing = [...requiredIds].filter((id) => !observedIds.has(id));
40
+ return {
41
+ availability: "available",
42
+ numerator: matched.length,
43
+ denominator: requiredIds.size,
44
+ rate: matched.length / requiredIds.size,
45
+ matchedExpectationIds: matched,
46
+ missingExpectationIds: missing,
47
+ reason: null
48
+ };
49
+ })();
50
+ const forbiddenOwnerPresent = evidenceUnavailable
51
+ ? unavailableRatio(unavailableReason)
52
+ : forbiddenIds.size === 0
53
+ ? notApplicableRatio("The expectation fixture contains no forbidden owner expectations.")
54
+ : (() => {
55
+ const present = [...forbiddenIds].filter((id) => observedIds.has(id));
56
+ const absent = [...forbiddenIds].filter((id) => !observedIds.has(id));
57
+ return {
58
+ availability: "available",
59
+ numerator: present.length,
60
+ denominator: forbiddenIds.size,
61
+ rate: present.length / forbiddenIds.size,
62
+ matchedExpectationIds: present,
63
+ missingExpectationIds: absent,
64
+ reason: null
65
+ };
66
+ })();
67
+ const falsePositiveCount = evidenceUnavailable
68
+ ? unavailableCount(unavailableReason)
69
+ : !hasClosedSet && forbiddenIds.size === 0
70
+ ? notApplicableCount("The expectation fixture defines neither a closed owner set nor forbidden owners.")
71
+ : (() => {
72
+ const falsePositives = [...observedIds].filter((id) => forbiddenIds.has(id) || (hasClosedSet && !requiredIds.has(id) && !allowedIds.has(id)));
73
+ return { availability: "available", count: falsePositives.length, evidenceKeys: falsePositives, reason: null };
74
+ })();
75
+ const falseNegativeCount = evidenceUnavailable
76
+ ? unavailableCount(unavailableReason)
77
+ : requiredIds.size === 0
78
+ ? notApplicableCount("The expectation fixture contains no required owner expectations.")
79
+ : (() => {
80
+ const falseNegatives = [...requiredIds].filter((id) => !observedIds.has(id));
81
+ return { availability: "available", count: falseNegatives.length, evidenceKeys: falseNegatives, reason: null };
82
+ })();
83
+ return { selectedOwnerEvidence: [...evidence], expectedOwnerPresent, forbiddenOwnerPresent, falsePositiveCount, falseNegativeCount };
84
+ }
@@ -0,0 +1,113 @@
1
+ import { checkMyDevKitContextArtifactConsistency } from "../stageContextSelectors/contextArtifactConsistency.js";
2
+ const ADEQUATE_STATUSES = [
3
+ "context sufficient for implementation",
4
+ "context sufficient with listed assumptions"
5
+ ];
6
+ const INADEQUATE_STATUSES = [
7
+ "context insufficient and more retrieval required",
8
+ "context conflict found and user or upstream stage decision required"
9
+ ];
10
+ export function calculateProducerConditionAgreement(roleAdequacyStatus, conditionCoverage) {
11
+ if (roleAdequacyStatus === undefined) {
12
+ return {
13
+ availability: "unavailable",
14
+ outcome: "insufficient-evidence",
15
+ observedRoleAdequacyStatus: null,
16
+ contradictionCodes: [],
17
+ reason: "No roleAdequacy.status was supplied."
18
+ };
19
+ }
20
+ if (conditionCoverage.availability !== "available") {
21
+ return {
22
+ availability: "unavailable",
23
+ outcome: "unsupported-legacy-evidence",
24
+ observedRoleAdequacyStatus: roleAdequacyStatus,
25
+ contradictionCodes: [],
26
+ reason: conditionCoverage.reason ?? "No condition-coverage evidence was available to compare against roleAdequacy.status."
27
+ };
28
+ }
29
+ const requiredTotal = conditionCoverage.requiredConditionsTotal.count ?? 0;
30
+ const requiredSatisfied = conditionCoverage.requiredConditionsSatisfied.count ?? 0;
31
+ const requiredLost = conditionCoverage.requiredConditionsLost.count ?? 0;
32
+ const allRequiredSatisfied = requiredTotal > 0 && requiredSatisfied === requiredTotal;
33
+ const producerSaysAdequate = ADEQUATE_STATUSES.includes(roleAdequacyStatus);
34
+ const producerSaysInadequate = INADEQUATE_STATUSES.includes(roleAdequacyStatus);
35
+ const contradictionCodes = [];
36
+ if (producerSaysInadequate && allRequiredSatisfied && requiredLost === 0) {
37
+ contradictionCodes.push("PRODUCER_INADEQUATE_BUT_ALL_REQUIRED_CONDITIONS_RETAINED");
38
+ }
39
+ if (producerSaysAdequate && requiredLost > 0) {
40
+ contradictionCodes.push("PRODUCER_ADEQUATE_BUT_REQUIRED_CONDITION_LOST");
41
+ }
42
+ const outcome = contradictionCodes.length > 0 ? "contradiction" : "agreement";
43
+ return {
44
+ availability: "available",
45
+ outcome,
46
+ observedRoleAdequacyStatus: roleAdequacyStatus,
47
+ contradictionCodes,
48
+ reason: outcome === "contradiction"
49
+ ? "Producer role-adequacy status contradicts its own explicit condition-coverage evidence; both sides are retained."
50
+ : null
51
+ };
52
+ }
53
+ export function calculateRequiredEvidenceLossAgreement(requiredEvidenceLost, conditionCoverage, requiredOmittedCount) {
54
+ if (requiredEvidenceLost === undefined) {
55
+ return {
56
+ availability: "unavailable",
57
+ outcome: "insufficient-evidence",
58
+ requiredEvidenceLost: null,
59
+ explicitConditionLossDetected: null,
60
+ requiredOmittedCount: requiredOmittedCount ?? null,
61
+ contradictionCodes: [],
62
+ reason: "No truncation.requiredEvidenceLost rollup was supplied."
63
+ };
64
+ }
65
+ if (conditionCoverage.availability !== "available") {
66
+ return {
67
+ availability: "unavailable",
68
+ outcome: "unsupported-legacy-evidence",
69
+ requiredEvidenceLost,
70
+ explicitConditionLossDetected: null,
71
+ requiredOmittedCount: requiredOmittedCount ?? null,
72
+ contradictionCodes: [],
73
+ reason: conditionCoverage.reason ?? "No condition-coverage evidence was available to compare against requiredEvidenceLost."
74
+ };
75
+ }
76
+ const explicitConditionLossDetected = (conditionCoverage.requiredConditionsLost.count ?? 0) > 0;
77
+ const contradictionCodes = [];
78
+ if (!requiredEvidenceLost && explicitConditionLossDetected) {
79
+ contradictionCodes.push("REQUIRED_EVIDENCE_LOST_FALSE_BUT_CONDITION_LOSS_DETECTED");
80
+ }
81
+ if (requiredEvidenceLost && !explicitConditionLossDetected && (requiredOmittedCount ?? 0) === 0) {
82
+ contradictionCodes.push("REQUIRED_EVIDENCE_LOST_TRUE_BUT_NO_CONDITION_OR_GROUP_LOSS_DETECTED");
83
+ }
84
+ const outcome = contradictionCodes.length > 0 ? "contradiction" : "agreement";
85
+ return {
86
+ availability: "available",
87
+ outcome,
88
+ requiredEvidenceLost,
89
+ explicitConditionLossDetected,
90
+ requiredOmittedCount: requiredOmittedCount ?? null,
91
+ contradictionCodes,
92
+ reason: outcome === "contradiction" ? "truncation.requiredEvidenceLost contradicts explicit condition-loss/required-omission evidence; both sides are retained." : null
93
+ };
94
+ }
95
+ const RELEVANT_CAPSULE_AUDIT_FIELDS = new Set(["roleConditionCoverage", "truncation", "roleAdequacy"]);
96
+ export function calculateCapsuleAuditConditionAgreement(capsule, audit) {
97
+ if (capsule === undefined || audit === undefined) {
98
+ const reason = capsule === undefined && audit === undefined
99
+ ? "Neither a context capsule nor a retrieval audit record was supplied."
100
+ : capsule === undefined
101
+ ? "No context capsule was supplied."
102
+ : "No retrieval audit record was supplied.";
103
+ return { availability: "unavailable", consistent: null, contradictingFieldPaths: [], reason };
104
+ }
105
+ const result = checkMyDevKitContextArtifactConsistency(capsule, audit);
106
+ const contradictingFieldPaths = result.issues.filter((issue) => RELEVANT_CAPSULE_AUDIT_FIELDS.has(issue.fieldPath)).map((issue) => issue.fieldPath);
107
+ return {
108
+ availability: "available",
109
+ consistent: contradictingFieldPaths.length === 0,
110
+ contradictingFieldPaths,
111
+ reason: null
112
+ };
113
+ }