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

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 (35) hide show
  1. package/README.md +6 -1
  2. package/dist/src/evaluation/stageContextExpectations/producerReadinessExpectationTypes.js +8 -0
  3. package/dist/src/evaluation/stageContextExpectations/validation.js +200 -0
  4. package/dist/src/evaluation/stageContextMetrics/calculateAllocationMetrics.js +96 -0
  5. package/dist/src/evaluation/stageContextMetrics/calculateCriticalityMetrics.js +112 -0
  6. package/dist/src/evaluation/stageContextMetrics/calculateOwnerMetrics.js +84 -0
  7. package/dist/src/evaluation/stageContextMetrics/calculateReadinessAgreement.js +152 -0
  8. package/dist/src/evaluation/stageContextMetrics/calculateSupplementalRawAgreement.js +50 -0
  9. package/dist/src/evaluation/stageContextMetrics/calculateTruncationClassification.js +44 -0
  10. package/dist/src/evaluation/stageContextMetrics/evaluateProducerReadinessBridge.js +127 -0
  11. package/dist/src/evaluation/stageContextMetrics/index.js +7 -0
  12. package/dist/src/evaluation/stageContextMetrics/producerReadinessMetricTypes.js +1 -0
  13. package/dist/src/evaluation/stageContextSelectors/contextArtifactConsistency.js +10 -0
  14. package/dist/src/evaluation/upstreamArtifacts/index.js +6 -0
  15. package/dist/src/evaluation/upstreamArtifacts/orchestratorContextReadinessResultV1.js +247 -0
  16. package/dist/src/evaluation/upstreamArtifacts/readImplementationContextPacketV1.js +9 -0
  17. package/dist/src/evaluation/upstreamArtifacts/readImplementationContextRetrievalReportV1.js +9 -0
  18. package/dist/src/evaluation/upstreamArtifacts/readSupplementalContextTextFile.js +29 -0
  19. package/dist/src/evaluation/upstreamArtifacts/readTestContextPacketV1.js +9 -0
  20. package/dist/src/evaluation/upstreamArtifacts/readTestContextRetrievalReportV1.js +9 -0
  21. package/dist/src/evaluation/upstreamArtifacts/supplementalContextTextParser.js +75 -0
  22. package/dist/src/evaluation/upstreamArtifacts/supplementalContextTypes.js +152 -0
  23. package/dist/src/evaluation/upstreamArtifacts/validateMyDevKitRetrievalAuditRecordV1.js +9 -2
  24. package/dist/src/evaluation/upstreamArtifacts/validateSupplementalContextDocumentV1.js +148 -0
  25. package/dist/src/experiments/plugins/contextStrategyComparison/loadV043StrategyArtifacts.js +117 -6
  26. package/dist/src/experiments/plugins/contextStrategyComparison/runV043StageContextStrategyWithAssurance.js +18 -3
  27. package/dist/src/experiments/plugins/contextStrategyComparison/validateV043StrategyInput.js +17 -3
  28. package/dist/src/report/experiments/buildContextStrategyComparisonV043Report.js +122 -0
  29. package/dist/src/report/experiments/renderContextStrategyComparisonV043Html.js +62 -0
  30. package/dist/src/report/experiments/renderPluginExperimentReportText.js +67 -0
  31. package/docs/METRICS.md +28 -0
  32. package/package.json +2 -2
  33. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/PROVENANCE.txt +13 -0
  34. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/context-capsule.json +7874 -0
  35. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/retrieval-audit-record.json +1076 -0
@@ -0,0 +1,148 @@
1
+ import { makeFailure } from "./runtimeValidation.js";
2
+ import { parseSupplementalContextText } from "./supplementalContextTextParser.js";
3
+ import { REQUIRED_METADATA_BY_DOCUMENT_KIND, REQUIRED_SECTIONS_BY_DOCUMENT_KIND, ROLE_BY_DOCUMENT_KIND, SUPPLEMENTAL_CONTEXT_PACKET_SCHEMA_SUPPORTED_MAJOR, SUPPLEMENTAL_CONTEXT_RETRIEVAL_REPORT_SCHEMA_SUPPORTED_MAJOR, VALID_ADEQUACY_VALUES, VALID_DECLARED_STATUSES, VALID_FRESHNESS_VALUES, VALID_REPOSITORY_SCOPES, VALID_TRUNCATION_VALUES, isRetrievalReportKind, isTestKind } from "./supplementalContextTypes.js";
4
+ const SCHEMA_VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+$/;
5
+ function supportedMajorForKind(kind) {
6
+ return isRetrievalReportKind(kind)
7
+ ? SUPPLEMENTAL_CONTEXT_RETRIEVAL_REPORT_SCHEMA_SUPPORTED_MAJOR
8
+ : SUPPLEMENTAL_CONTEXT_PACKET_SCHEMA_SUPPORTED_MAJOR;
9
+ }
10
+ function literalOrFail(artifactKind, sourcePath, fieldPath, value, allowed, errorCode = "INVALID_LITERAL_VALUE") {
11
+ if (allowed.includes(value))
12
+ return value;
13
+ return {
14
+ ok: false,
15
+ artifactKind,
16
+ sourcePath,
17
+ code: errorCode,
18
+ message: `Field "${fieldPath}" in artifact "${artifactKind}" at "${sourcePath}" has invalid value "${value}". Expected one of: ${allowed.join(", ")}.`,
19
+ fieldPath,
20
+ expected: allowed.join(" | "),
21
+ actual: value
22
+ };
23
+ }
24
+ function isFailure(value) {
25
+ return typeof value === "object" && value !== null && value.ok === false;
26
+ }
27
+ export function validateSupplementalContextDocumentV1(artifactKind, expectedKind, text, sourcePath) {
28
+ const parsed = parseSupplementalContextText(text);
29
+ if (parsed.metadataOrder.length === 0 && parsed.sectionOrder.length === 0) {
30
+ return makeFailure(artifactKind, sourcePath, "MALFORMED_TEXT_ARTIFACT", `Artifact "${artifactKind}" at "${sourcePath}" contains no recognizable "Key: value" metadata or "## Heading" sections.`);
31
+ }
32
+ // Kind/role identity is checked before general required-metadata/section completeness so
33
+ // that feeding one document kind's file to another kind's reader is reported as a
34
+ // structural identity mismatch (DOCUMENT_KIND_MISMATCH / DOCUMENT_ROLE_MISMATCH) rather
35
+ // than as an incidental "missing metadata key" from the wrong kind's required-field list.
36
+ const declaredKind = parsed.metadata["Document kind"];
37
+ if (declaredKind !== expectedKind) {
38
+ return makeFailure(artifactKind, sourcePath, "DOCUMENT_KIND_MISMATCH", `Artifact "${artifactKind}" at "${sourcePath}" declares document kind "${declaredKind}" but this reader expects "${expectedKind}".`, "Document kind", expectedKind, declaredKind);
39
+ }
40
+ const expectedRole = ROLE_BY_DOCUMENT_KIND[expectedKind];
41
+ const declaredRole = parsed.metadata["Role"];
42
+ if (declaredRole !== expectedRole) {
43
+ return makeFailure(artifactKind, sourcePath, "DOCUMENT_ROLE_MISMATCH", `Artifact "${artifactKind}" at "${sourcePath}" declares role "${declaredRole}" but this reader expects "${expectedRole}".`, "Role", expectedRole, declaredRole);
44
+ }
45
+ const requiredMetadata = REQUIRED_METADATA_BY_DOCUMENT_KIND[expectedKind];
46
+ for (const key of requiredMetadata) {
47
+ if (!(key in parsed.metadata)) {
48
+ return makeFailure(artifactKind, sourcePath, "MISSING_REQUIRED_METADATA", `Artifact "${artifactKind}" at "${sourcePath}" is missing required metadata key "${key}".`, key, "present");
49
+ }
50
+ }
51
+ const requiredSections = REQUIRED_SECTIONS_BY_DOCUMENT_KIND[expectedKind];
52
+ for (const heading of requiredSections) {
53
+ if (!(heading in parsed.sections)) {
54
+ return makeFailure(artifactKind, sourcePath, "MISSING_REQUIRED_SECTION", `Artifact "${artifactKind}" at "${sourcePath}" is missing required section heading "## ${heading}".`, heading, "present");
55
+ }
56
+ }
57
+ const schemaVersion = parsed.metadata["Schema version"];
58
+ if (!SCHEMA_VERSION_PATTERN.test(schemaVersion)) {
59
+ return makeFailure(artifactKind, sourcePath, "INVALID_SCHEMA_VERSION", `Artifact "${artifactKind}" at "${sourcePath}" has an invalid "Schema version": expected strict "x.y.z" semantic version.`, "Schema version", "^[0-9]+\\.[0-9]+\\.[0-9]+$", schemaVersion);
60
+ }
61
+ const major = Number(schemaVersion.split(".")[0]);
62
+ const supportedMajor = supportedMajorForKind(expectedKind);
63
+ if (major !== supportedMajor) {
64
+ return makeFailure(artifactKind, sourcePath, "UNSUPPORTED_SCHEMA_MAJOR", `Artifact "${artifactKind}" at "${sourcePath}" has unsupported schema major "${major}" in "Schema version": supported major is ${supportedMajor}.`, "Schema version", String(supportedMajor), schemaVersion);
65
+ }
66
+ const status = literalOrFail(artifactKind, sourcePath, "Status", parsed.metadata["Status"], VALID_DECLARED_STATUSES, "UNKNOWN_STATUS_VALUE");
67
+ if (isFailure(status))
68
+ return status;
69
+ const repositoryScope = literalOrFail(artifactKind, sourcePath, "Repository scope", parsed.metadata["Repository scope"], VALID_REPOSITORY_SCOPES);
70
+ if (isFailure(repositoryScope))
71
+ return repositoryScope;
72
+ const freshness = literalOrFail(artifactKind, sourcePath, "Freshness", parsed.metadata["Freshness"], VALID_FRESHNESS_VALUES);
73
+ if (isFailure(freshness))
74
+ return freshness;
75
+ const adequacy = literalOrFail(artifactKind, sourcePath, "Adequacy", parsed.metadata["Adequacy"], VALID_ADEQUACY_VALUES);
76
+ if (isFailure(adequacy))
77
+ return adequacy;
78
+ const requiredEvidenceTruncated = literalOrFail(artifactKind, sourcePath, "Required evidence truncated", parsed.metadata["Required evidence truncated"], VALID_TRUNCATION_VALUES);
79
+ if (isFailure(requiredEvidenceTruncated))
80
+ return requiredEvidenceTruncated;
81
+ const indexIdentity = parsed.metadata["Index identity"];
82
+ if (!indexIdentity || indexIdentity.length === 0) {
83
+ return makeFailure(artifactKind, sourcePath, "MISSING_CANONICAL_IDENTITY", `Artifact "${artifactKind}" at "${sourcePath}" is missing a non-empty canonical "Index identity".`, "Index identity", "present");
84
+ }
85
+ const document = {
86
+ documentKind: expectedKind,
87
+ role: expectedRole,
88
+ schemaVersion,
89
+ schemaMajor: 1,
90
+ status,
91
+ repositoryScope,
92
+ freshness,
93
+ adequacy,
94
+ requiredEvidenceTruncated,
95
+ contextCapsuleSchemaVersion: parsed.metadata["Context capsule schema version"],
96
+ retrievalAuditSchemaVersion: parsed.metadata["Retrieval audit schema version"],
97
+ toolName: parsed.metadata["Tool name"],
98
+ toolVersion: parsed.metadata["Tool version"],
99
+ indexIdentity,
100
+ rawMetadata: parsed.metadata,
101
+ sections: parsed.sections,
102
+ sectionOrder: parsed.sectionOrder,
103
+ metadataOrder: parsed.metadataOrder,
104
+ warnings: []
105
+ };
106
+ if (isRetrievalReportKind(expectedKind)) {
107
+ document.requestSchemaVersion = parsed.metadata["Request schema version"];
108
+ const fullFileFallbackUsed = literalOrFail(artifactKind, sourcePath, "Full-file fallback used", parsed.metadata["Full-file fallback used"], VALID_TRUNCATION_VALUES);
109
+ if (isFailure(fullFileFallbackUsed))
110
+ return fullFileFallbackUsed;
111
+ document.fullFileFallbackUsed = fullFileFallbackUsed;
112
+ const determinismChecked = literalOrFail(artifactKind, sourcePath, "Determinism checked", parsed.metadata["Determinism checked"], VALID_TRUNCATION_VALUES);
113
+ if (isFailure(determinismChecked))
114
+ return determinismChecked;
115
+ document.determinismChecked = determinismChecked;
116
+ }
117
+ if (isTestKind(expectedKind)) {
118
+ const responsibilityMappingsTruncated = literalOrFail(artifactKind, sourcePath, "Responsibility mappings truncated", parsed.metadata["Responsibility mappings truncated"], VALID_TRUNCATION_VALUES);
119
+ if (isFailure(responsibilityMappingsTruncated))
120
+ return responsibilityMappingsTruncated;
121
+ document.responsibilityMappingsTruncated = responsibilityMappingsTruncated;
122
+ // Preserved verbatim, never validated against the orchestrator's internal
123
+ // CriticalResponsibilityMappingState enum -- see supplementalContextTypes.ts.
124
+ const criticalStatus = parsed.metadata["Critical responsibility mapping status"];
125
+ if (!criticalStatus || criticalStatus.length === 0) {
126
+ return makeFailure(artifactKind, sourcePath, "MISSING_REQUIRED_METADATA", `Artifact "${artifactKind}" at "${sourcePath}" is missing a non-empty "Critical responsibility mapping status".`, "Critical responsibility mapping status", "present");
127
+ }
128
+ document.criticalResponsibilityMappingStatus = criticalStatus;
129
+ }
130
+ if (parsed.duplicateMetadataKeys.length > 0) {
131
+ document.warnings.push(`Duplicate metadata keys were ignored (first occurrence kept): ${parsed.duplicateMetadataKeys.join(", ")}.`);
132
+ }
133
+ if (parsed.duplicateSectionHeadings.length > 0) {
134
+ document.warnings.push(`Duplicate section headings were ignored (first occurrence kept): ${parsed.duplicateSectionHeadings.join(", ")}.`);
135
+ }
136
+ if (parsed.emptyMetadataKeys.length > 0) {
137
+ document.warnings.push(`Metadata keys had empty values: ${parsed.emptyMetadataKeys.join(", ")}.`);
138
+ }
139
+ return {
140
+ ok: true,
141
+ artifactKind,
142
+ sourcePath,
143
+ schemaVersion,
144
+ schemaMajor: 1,
145
+ artifact: document,
146
+ rawArtifact: { metadata: parsed.metadata, sections: parsed.sections }
147
+ };
148
+ }
@@ -1,4 +1,4 @@
1
- import { readMyDevKitContextCapsuleV1, readMyDevKitRetrievalAuditRecordV1, readOrchestratorWorkflowInstructionPacketV1 } from "../../../evaluation/upstreamArtifacts/index.js";
1
+ import { readImplementationContextPacketV1, readImplementationContextRetrievalReportV1, readMyDevKitContextCapsuleV1, readMyDevKitRetrievalAuditRecordV1, readOrchestratorWorkflowInstructionPacketV1, readTestContextPacketV1, readTestContextRetrievalReportV1, validateOrchestratorContextReadinessResultV1 } from "../../../evaluation/upstreamArtifacts/index.js";
2
2
  import { checkMyDevKitContextArtifactConsistency } from "../../../evaluation/stageContextSelectors/index.js";
3
3
  import { readStageContextExpectationFixtureV1 } from "../../../evaluation/stageContextExpectations/index.js";
4
4
  import { readV043FullWorkflowLibraryFixture } from "./readV043FullWorkflowLibraryFixture.js";
@@ -265,20 +265,131 @@ export async function loadV043StrategyArtifacts(input) {
265
265
  details: packetResult
266
266
  });
267
267
  }
268
+ // v0.4.4 Batch 3: producer-readiness bridge inputs -- every one is optional (section
269
+ // 11.2). Only a supplied-but-unreadable source becomes a load failure; an absent
270
+ // source is simply left off the payload for the bridge evaluator to treat as
271
+ // unavailable/not-applicable.
272
+ let implementationContextPacketSourcePath;
273
+ let implementationContextPacket;
274
+ if (input.implementationContextPacketPath !== undefined) {
275
+ const result = await readImplementationContextPacketV1(input.implementationContextPacketPath);
276
+ if (!result.ok) {
277
+ issues.push({
278
+ code: "IMPLEMENTATION_CONTEXT_PACKET_READ_FAILED",
279
+ fieldPath: "implementationContextPacket",
280
+ message: `Failed to read implementation-context packet: ${result.message}`,
281
+ sourcePath: result.sourcePath,
282
+ details: result
283
+ });
284
+ }
285
+ else {
286
+ implementationContextPacketSourcePath = result.sourcePath;
287
+ implementationContextPacket = result.artifact;
288
+ }
289
+ }
290
+ let implementationContextRetrievalReportSourcePath;
291
+ let implementationContextRetrievalReport;
292
+ if (input.implementationContextRetrievalReportPath !== undefined) {
293
+ const result = await readImplementationContextRetrievalReportV1(input.implementationContextRetrievalReportPath);
294
+ if (!result.ok) {
295
+ issues.push({
296
+ code: "IMPLEMENTATION_CONTEXT_RETRIEVAL_REPORT_READ_FAILED",
297
+ fieldPath: "implementationContextRetrievalReport",
298
+ message: `Failed to read implementation-context retrieval report: ${result.message}`,
299
+ sourcePath: result.sourcePath,
300
+ details: result
301
+ });
302
+ }
303
+ else {
304
+ implementationContextRetrievalReportSourcePath = result.sourcePath;
305
+ implementationContextRetrievalReport = result.artifact;
306
+ }
307
+ }
308
+ let testContextPacketSourcePath;
309
+ let testContextPacket;
310
+ if (input.testContextPacketPath !== undefined) {
311
+ const result = await readTestContextPacketV1(input.testContextPacketPath);
312
+ if (!result.ok) {
313
+ issues.push({
314
+ code: "TEST_CONTEXT_PACKET_READ_FAILED",
315
+ fieldPath: "testContextPacket",
316
+ message: `Failed to read test-context packet: ${result.message}`,
317
+ sourcePath: result.sourcePath,
318
+ details: result
319
+ });
320
+ }
321
+ else {
322
+ testContextPacketSourcePath = result.sourcePath;
323
+ testContextPacket = result.artifact;
324
+ }
325
+ }
326
+ let testContextRetrievalReportSourcePath;
327
+ let testContextRetrievalReport;
328
+ if (input.testContextRetrievalReportPath !== undefined) {
329
+ const result = await readTestContextRetrievalReportV1(input.testContextRetrievalReportPath);
330
+ if (!result.ok) {
331
+ issues.push({
332
+ code: "TEST_CONTEXT_RETRIEVAL_REPORT_READ_FAILED",
333
+ fieldPath: "testContextRetrievalReport",
334
+ message: `Failed to read test-context retrieval report: ${result.message}`,
335
+ sourcePath: result.sourcePath,
336
+ details: result
337
+ });
338
+ }
339
+ else {
340
+ testContextRetrievalReportSourcePath = result.sourcePath;
341
+ testContextRetrievalReport = result.artifact;
342
+ }
343
+ }
344
+ let readiness;
345
+ if (input.readiness !== undefined) {
346
+ const readinessResult = validateOrchestratorContextReadinessResultV1(input.readiness, "programmatic-input:readiness");
347
+ if (!readinessResult.ok) {
348
+ issues.push({
349
+ code: "READINESS_INPUT_INVALID",
350
+ fieldPath: "readiness",
351
+ message: `Readiness programmatic input is invalid: ${readinessResult.message}`,
352
+ details: readinessResult
353
+ });
354
+ }
355
+ else {
356
+ readiness = readinessResult.artifact;
357
+ }
358
+ }
268
359
  if (issues.length > 0 || !expectationsResult.ok || !packetResult.ok || pairs.length !== input.contextArtifacts.length) {
269
360
  return buildFailureResult(input, issues);
270
361
  }
362
+ const payload = {
363
+ contextArtifacts: pairs,
364
+ workflowInstructionPacketSourcePath: packetResult.sourcePath,
365
+ workflowInstructionPacket: packetResult.artifact
366
+ };
367
+ if (implementationContextPacket !== undefined) {
368
+ payload.implementationContextPacketSourcePath = implementationContextPacketSourcePath;
369
+ payload.implementationContextPacket = implementationContextPacket;
370
+ }
371
+ if (implementationContextRetrievalReport !== undefined) {
372
+ payload.implementationContextRetrievalReportSourcePath = implementationContextRetrievalReportSourcePath;
373
+ payload.implementationContextRetrievalReport = implementationContextRetrievalReport;
374
+ }
375
+ if (testContextPacket !== undefined) {
376
+ payload.testContextPacketSourcePath = testContextPacketSourcePath;
377
+ payload.testContextPacket = testContextPacket;
378
+ }
379
+ if (testContextRetrievalReport !== undefined) {
380
+ payload.testContextRetrievalReportSourcePath = testContextRetrievalReportSourcePath;
381
+ payload.testContextRetrievalReport = testContextRetrievalReport;
382
+ }
383
+ if (readiness !== undefined) {
384
+ payload.readiness = readiness;
385
+ }
271
386
  return {
272
387
  status: "completed",
273
388
  strategyId: input.strategyId,
274
389
  input,
275
390
  expectationsSourcePath: expectationsResult.sourcePath,
276
391
  expectations: expectationsResult.fixture,
277
- payload: {
278
- contextArtifacts: pairs,
279
- workflowInstructionPacketSourcePath: packetResult.sourcePath,
280
- workflowInstructionPacket: packetResult.artifact
281
- },
392
+ payload,
282
393
  warnings: []
283
394
  };
284
395
  }
@@ -1,6 +1,6 @@
1
1
  import { captureTargetSnapshot, compareTargetSnapshots } from "../../../evaluation/targetImmutability/index.js";
2
2
  import { calculateStageContextDeterminism } from "../../../evaluation/stageContextDeterminism/index.js";
3
- import { evaluateV043StageContextExecution } from "../../../evaluation/stageContextMetrics/index.js";
3
+ import { buildProducerReadinessBridgeInputFromCombinedPayload, evaluateProducerReadinessBridge, evaluateV043StageContextExecution } from "../../../evaluation/stageContextMetrics/index.js";
4
4
  import { executeV043StageContextStrategy } from "./executeV043StageContextStrategy.js";
5
5
  async function buildRunWithTargetImmutability(targetConfig, input) {
6
6
  if (targetConfig === undefined) {
@@ -57,6 +57,15 @@ async function buildRunWithTargetImmutability(targetConfig, input) {
57
57
  targetImmutability: { availability: "available", comparison, reason: null }
58
58
  };
59
59
  }
60
+ // v0.4.4 Batch 3: computed exactly once per run, immediately after execution, from the
61
+ // already-loaded execution payload -- never rereads artifacts, never reruns the strategy.
62
+ function buildProducerReadinessBridgeResult(execution) {
63
+ if (execution.status !== "completed" || execution.strategyId !== "combined-bounded-stage-context")
64
+ return null;
65
+ const payload = execution.payload;
66
+ const bridgeInput = buildProducerReadinessBridgeInputFromCombinedPayload(payload, execution.expectations);
67
+ return evaluateProducerReadinessBridge(bridgeInput);
68
+ }
60
69
  function computeStatus(issues, config) {
61
70
  if (issues.length > 0)
62
71
  return "failed";
@@ -69,23 +78,27 @@ export async function runV043StageContextStrategyWithAssurance(input, config) {
69
78
  const issues = [];
70
79
  let primaryExecution;
71
80
  let primaryEvaluation;
81
+ let primaryProducerReadinessBridge = null;
72
82
  try {
73
83
  for (let runNumber = 1; runNumber <= config.repeatCount; runNumber += 1) {
74
84
  const { execution, targetImmutability } = await buildRunWithTargetImmutability(config.targetImmutability, input);
75
85
  const evaluation = evaluateV043StageContextExecution(execution, { targetImmutability });
86
+ const producerReadinessBridge = buildProducerReadinessBridgeResult(execution);
76
87
  if (runNumber === 1) {
77
88
  primaryExecution = execution;
78
89
  primaryEvaluation = evaluation;
90
+ primaryProducerReadinessBridge = producerReadinessBridge;
79
91
  }
80
92
  runRecords.push({
81
93
  runNumber,
82
94
  executionStatus: execution.status,
83
95
  evaluationStatus: evaluation.status,
84
- targetImmutability
96
+ targetImmutability,
97
+ producerReadinessBridge
85
98
  });
86
99
  determinismCandidates.push({
87
100
  runNumber,
88
- value: { execution, evaluation, targetImmutability }
101
+ value: { execution, evaluation, targetImmutability, producerReadinessBridge }
89
102
  });
90
103
  if (execution.status !== "completed") {
91
104
  issues.push({
@@ -147,6 +160,7 @@ export async function runV043StageContextStrategyWithAssurance(input, config) {
147
160
  repeatCount: config.repeatCount,
148
161
  primaryExecution: primaryExecution,
149
162
  primaryEvaluation: primaryEvaluation,
163
+ primaryProducerReadinessBridge,
150
164
  runRecords,
151
165
  determinism,
152
166
  issues
@@ -175,6 +189,7 @@ export async function runV043StageContextStrategyWithAssurance(input, config) {
175
189
  repeatCount: config.repeatCount,
176
190
  primaryExecution: fallbackExecution,
177
191
  primaryEvaluation: fallbackEvaluation,
192
+ primaryProducerReadinessBridge,
178
193
  runRecords,
179
194
  determinism: fallbackDeterminism,
180
195
  issues: [
@@ -45,7 +45,12 @@ const STRATEGY_CONTRACTS = {
45
45
  fields: [
46
46
  { name: "expectationsPath", kind: "required-path" },
47
47
  { name: "contextArtifacts", kind: "required-array" },
48
- { name: "workflowInstructionPacketPath", kind: "required-path" }
48
+ { name: "workflowInstructionPacketPath", kind: "required-path" },
49
+ { name: "implementationContextPacketPath", kind: "optional-path" },
50
+ { name: "implementationContextRetrievalReportPath", kind: "optional-path" },
51
+ { name: "testContextPacketPath", kind: "optional-path" },
52
+ { name: "testContextRetrievalReportPath", kind: "optional-path" },
53
+ { name: "readiness", kind: "optional-object" }
49
54
  ]
50
55
  }
51
56
  };
@@ -93,7 +98,7 @@ export function validateV043StrategyInput(value) {
93
98
  }
94
99
  }
95
100
  for (const field of contract.fields) {
96
- if (field.kind !== "optional-path" && root[field.name] === undefined) {
101
+ if (field.kind !== "optional-path" && field.kind !== "optional-object" && root[field.name] === undefined) {
97
102
  issues.push({
98
103
  code: "MISSING_REQUIRED_FIELD",
99
104
  fieldPath: field.name,
@@ -114,6 +119,15 @@ export function validateV043StrategyInput(value) {
114
119
  });
115
120
  }
116
121
  }
122
+ else if (field.kind === "optional-object") {
123
+ if (!isPlainObject(fieldValue)) {
124
+ issues.push({
125
+ code: "INVALID_FIELD_TYPE",
126
+ fieldPath: field.name,
127
+ message: `"${field.name}" must be an object.`
128
+ });
129
+ }
130
+ }
117
131
  else if (typeof fieldValue !== "string") {
118
132
  issues.push({
119
133
  code: "INVALID_FIELD_TYPE",
@@ -123,7 +137,7 @@ export function validateV043StrategyInput(value) {
123
137
  }
124
138
  }
125
139
  for (const field of contract.fields) {
126
- if (field.kind === "required-array")
140
+ if (field.kind === "required-array" || field.kind === "optional-object")
127
141
  continue;
128
142
  const fieldValue = root[field.name];
129
143
  if (typeof fieldValue === "string" && fieldValue.length === 0) {
@@ -69,12 +69,134 @@ function buildSummary(executions, evaluations, assurances) {
69
69
  };
70
70
  }
71
71
  function buildStrategyReport(execution, evaluation, assurance) {
72
+ const producerReadinessBridge = buildProducerReadinessBridgeReport(assurance.primaryProducerReadinessBridge);
72
73
  return {
73
74
  strategyId: assurance.strategyId,
74
75
  artifacts: buildArtifactMetadata(execution),
75
76
  execution: buildExecutionSummary(execution),
76
77
  evaluation: buildEvaluationSummary(evaluation),
77
78
  assurance: buildAssuranceSummary(assurance),
79
+ ...(producerReadinessBridge ? { producerReadinessBridge } : {}),
80
+ };
81
+ }
82
+ function buildAllocationFactList(source) {
83
+ return boundItems(source.map((f) => ({ groupId: f.groupId, value: f.value, unit: f.unit, availability: f.availability, reason: f.reason })));
84
+ }
85
+ function buildOwnerEvaluation(owner) {
86
+ return {
87
+ selectedOwnerEvidence: boundItems(owner.selectedOwnerEvidence.map((e) => ({ ownerId: e.ownerId, sourceArtifact: e.sourceArtifact, sourceInstance: e.sourceInstance }))),
88
+ expectedOwnerPresent: buildRatioMetric(owner.expectedOwnerPresent),
89
+ forbiddenOwnerPresent: buildRatioMetric(owner.forbiddenOwnerPresent),
90
+ falsePositiveCount: buildCountMetric(owner.falsePositiveCount),
91
+ falseNegativeCount: buildCountMetric(owner.falseNegativeCount),
92
+ };
93
+ }
94
+ function buildTruncationList(source) {
95
+ return boundItems(source.map((t) => ({
96
+ groupId: t.groupId,
97
+ cause: t.cause,
98
+ availability: t.availability,
99
+ reason: t.reason,
100
+ omittedRequiredEvidenceIds: t.omittedRequiredEvidenceIds,
101
+ limit: t.limit,
102
+ used: t.used,
103
+ available: t.available,
104
+ })));
105
+ }
106
+ function buildSupplementalRawAgreement(agreement) {
107
+ if (agreement === null)
108
+ return null;
109
+ return {
110
+ fields: agreement.fields.map((f) => ({ field: f.field, rawValue: f.rawValue, supplementalValue: f.supplementalValue, agreement: f.agreement, availability: f.availability, reason: f.reason })),
111
+ contradictions: agreement.contradictions.map((f) => ({ field: f.field, rawValue: f.rawValue, supplementalValue: f.supplementalValue, agreement: f.agreement, availability: f.availability, reason: f.reason })),
112
+ upstreamProducerParityPreserved: agreement.upstreamProducerParityPreserved,
113
+ };
114
+ }
115
+ function buildProducerReadinessSide(side) {
116
+ return {
117
+ role: side.role,
118
+ ownerEvaluation: buildOwnerEvaluation(side.ownerEvaluation),
119
+ allocationEvaluation: {
120
+ requiredGroupCapacity: buildAllocationFactList(side.allocationEvaluation.requiredGroupCapacity),
121
+ usedReservation: buildAllocationFactList(side.allocationEvaluation.usedReservation),
122
+ borrowedCapacity: buildAllocationFactList(side.allocationEvaluation.borrowedCapacity),
123
+ unusedCapacity: buildAllocationFactList(side.allocationEvaluation.unusedCapacity),
124
+ },
125
+ truncationEvaluation: buildTruncationList(side.truncationEvaluation),
126
+ packetAgreement: buildSupplementalRawAgreement(side.packetAgreement),
127
+ reportAgreement: buildSupplementalRawAgreement(side.reportAgreement),
128
+ };
129
+ }
130
+ function buildReadinessAgreement(agreement) {
131
+ return {
132
+ structuralAgreement: agreement.structuralAgreement.map((s) => ({
133
+ field: s.field,
134
+ expectedValue: s.expectedValue,
135
+ observedValue: s.observedValue,
136
+ agreement: s.agreement,
137
+ availability: s.availability,
138
+ reason: s.reason,
139
+ })),
140
+ decisionAgreement: {
141
+ availability: agreement.decisionAgreement.availability,
142
+ observedDecision: agreement.decisionAgreement.observedDecision,
143
+ allowedDecisions: agreement.decisionAgreement.allowedDecisions,
144
+ decisionAgreement: agreement.decisionAgreement.decisionAgreement,
145
+ observedIssueCodes: agreement.decisionAgreement.observedIssueCodes,
146
+ expectedIssueCodes: agreement.decisionAgreement.expectedIssueCodes,
147
+ issueCodesAgreement: agreement.decisionAgreement.issueCodesAgreement,
148
+ observedPrimaryIssueCode: agreement.decisionAgreement.observedPrimaryIssueCode,
149
+ expectedPrimaryIssueCode: agreement.decisionAgreement.expectedPrimaryIssueCode,
150
+ primaryIssueAgreement: agreement.decisionAgreement.primaryIssueAgreement,
151
+ reason: agreement.decisionAgreement.reason,
152
+ },
153
+ invalidReady: {
154
+ availability: agreement.invalidReady.availability,
155
+ invalidReady: agreement.invalidReady.invalidReady,
156
+ missingExpectedIssueCodes: agreement.invalidReady.missingExpectedIssueCodes,
157
+ reason: agreement.invalidReady.reason,
158
+ },
159
+ validBlocked: {
160
+ availability: agreement.validBlocked.availability,
161
+ validBlocked: agreement.validBlocked.validBlocked,
162
+ validRefreshRequired: agreement.validBlocked.validRefreshRequired,
163
+ observedDecision: agreement.validBlocked.observedDecision,
164
+ reason: agreement.validBlocked.reason,
165
+ },
166
+ };
167
+ }
168
+ function buildCriticalityEvaluation(criticality) {
169
+ return {
170
+ overlayAgreement: boundItems(criticality.overlayAgreement.map((e) => ({
171
+ responsibilityId: e.responsibilityId,
172
+ expectedCriticality: e.expectedCriticality,
173
+ observedCriticality: e.observedCriticality,
174
+ agreement: e.agreement,
175
+ availability: e.availability,
176
+ reason: e.reason,
177
+ }))),
178
+ missingCriticalityResponsibilityIds: criticality.missingCriticalityResponsibilityIds,
179
+ conflictingCriticalityResponsibilityIds: criticality.conflictingCriticalityResponsibilityIds,
180
+ unexpectedCriticalityResponsibilityIds: criticality.unexpectedCriticalityResponsibilityIds,
181
+ mappedCriticalCompleteness: buildRatioMetric(criticality.mappedCriticalCompleteness),
182
+ fullyMappedCriticalIds: criticality.fullyMappedCriticalIds,
183
+ partiallyMappedCriticalIds: criticality.partiallyMappedCriticalIds,
184
+ unmappedCriticalIds: criticality.unmappedCriticalIds,
185
+ missingMappingEvidenceCriticalIds: criticality.missingMappingEvidenceCriticalIds,
186
+ };
187
+ }
188
+ function buildProducerReadinessBridgeReport(bridge) {
189
+ if (bridge === null || bridge === undefined)
190
+ return null;
191
+ return {
192
+ status: bridge.status,
193
+ reason: bridge.reason,
194
+ implementation: bridge.implementation ? buildProducerReadinessSide(bridge.implementation) : null,
195
+ testImplementation: bridge.testImplementation ? buildProducerReadinessSide(bridge.testImplementation) : null,
196
+ readinessAgreement: bridge.readinessAgreement ? buildReadinessAgreement(bridge.readinessAgreement) : null,
197
+ criticalityEvaluation: bridge.criticalityEvaluation ? buildCriticalityEvaluation(bridge.criticalityEvaluation) : null,
198
+ warnings: boundItems(bridge.warnings),
199
+ evidenceReferences: boundItems(bridge.evidenceReferences),
78
200
  };
79
201
  }
80
202
  function buildExecutionSummary(execution) {
@@ -187,8 +187,70 @@ function renderStrategySection(strategy, index) {
187
187
  ${table(["Status", "Repeat Count"], [[strategy.assurance.status, String(strategy.assurance.repeatCount)]])}
188
188
  ${boundedListNote(strategy.assurance.issues)}
189
189
  ${table(["Code", "Run", "Field Path", "Message"], assuranceIssueRows)}
190
+ ${strategy.producerReadinessBridge ? renderProducerReadinessBridgeSection(strategy.producerReadinessBridge) : ""}
190
191
  </section>`;
191
192
  }
193
+ function renderProducerReadinessSideSection(label, side) {
194
+ if (side === null) {
195
+ return `<h4>${escapeHtml(label)}</h4><p class="muted">Not supplied.</p>`;
196
+ }
197
+ const truncationRows = side.truncationEvaluation.items.map((t) => [
198
+ t.groupId,
199
+ t.cause,
200
+ t.availability,
201
+ String(t.limit),
202
+ String(t.used),
203
+ t.reason ?? "",
204
+ ]);
205
+ function agreementSection(agreementLabel, agreement) {
206
+ if (agreement === null) {
207
+ return `<h5>${escapeHtml(agreementLabel)}</h5><p class="muted">Unavailable.</p>`;
208
+ }
209
+ const rows = agreement.contradictions.map((c) => [c.field, String(c.rawValue), String(c.supplementalValue), c.availability]);
210
+ return `<h5>${escapeHtml(agreementLabel)}</h5>
211
+ <p>Producer parity preserved: ${escapeHtml(agreement.upstreamProducerParityPreserved)}</p>
212
+ ${table(["Field", "Raw Value", "Supplemental Value", "Availability"], rows)}`;
213
+ }
214
+ return `<h4>${escapeHtml(label)}</h4>
215
+ ${renderRatioMetricTable("Expected Owner Present", side.ownerEvaluation.expectedOwnerPresent)}
216
+ ${renderRatioMetricTable("Forbidden Owner Present", side.ownerEvaluation.forbiddenOwnerPresent)}
217
+ ${renderCountMetricTable("Owner False Positives", side.ownerEvaluation.falsePositiveCount)}
218
+ ${renderCountMetricTable("Owner False Negatives", side.ownerEvaluation.falseNegativeCount)}
219
+ <h5>Truncation Classification</h5>
220
+ ${boundedListNote(side.truncationEvaluation)}
221
+ ${table(["Group", "Cause", "Availability", "Limit", "Used", "Reason"], truncationRows)}
222
+ ${agreementSection("Packet/Raw Agreement", side.packetAgreement)}
223
+ ${agreementSection("Report/Raw Agreement", side.reportAgreement)}`;
224
+ }
225
+ function renderProducerReadinessBridgeSection(bridge) {
226
+ if (bridge.status === "not-applicable") {
227
+ return `<h4>Producer-Readiness Bridge</h4><p class="muted">${escapeHtml(bridge.reason)}</p>`;
228
+ }
229
+ const criticality = bridge.criticalityEvaluation;
230
+ return `<h4>Producer-Readiness Bridge</h4>
231
+ ${renderProducerReadinessSideSection("Implementation", bridge.implementation)}
232
+ ${renderProducerReadinessSideSection("Test", bridge.testImplementation)}
233
+ <h5>Readiness Agreement</h5>
234
+ ${bridge.readinessAgreement === null
235
+ ? `<p class="muted">Not supplied.</p>`
236
+ : table(["Observed Decision", "Decision Agreement", "Issue-Code Agreement", "Invalid Ready", "Valid Blocked", "Valid Refresh-Required"], [
237
+ [
238
+ String(bridge.readinessAgreement.decisionAgreement.observedDecision),
239
+ String(bridge.readinessAgreement.decisionAgreement.decisionAgreement),
240
+ String(bridge.readinessAgreement.decisionAgreement.issueCodesAgreement),
241
+ String(bridge.readinessAgreement.invalidReady.invalidReady),
242
+ String(bridge.readinessAgreement.validBlocked.validBlocked),
243
+ String(bridge.readinessAgreement.validBlocked.validRefreshRequired),
244
+ ],
245
+ ])}
246
+ <h5>Criticality Evaluation</h5>
247
+ ${criticality === null
248
+ ? `<p class="muted">Not supplied.</p>`
249
+ : `${renderRatioMetricTable("Mapped Critical Completeness", criticality.mappedCriticalCompleteness)}
250
+ <p>Partially mapped (excluded from complete): ${escapeHtml(criticality.partiallyMappedCriticalIds.join(", ") || "none")}</p>
251
+ <p>Unmapped: ${escapeHtml(criticality.unmappedCriticalIds.join(", ") || "none")}</p>
252
+ <p>Conflicting: ${escapeHtml(criticality.conflictingCriticalityResponsibilityIds.join(", ") || "none")}</p>`}`;
253
+ }
192
254
  export function renderContextStrategyComparisonV043Html(report) {
193
255
  if (report === null) {
194
256
  return `<section><h2>V0.4.3 Stage-Context Evidence</h2><p>Not applicable to this plugin.</p></section>`;