@exaudeus/workrail 3.10.0 → 3.11.0

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 (59) hide show
  1. package/dist/application/services/validation-engine.js +134 -0
  2. package/dist/application/services/workflow-compiler.js +54 -0
  3. package/dist/manifest.json +138 -74
  4. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.d.ts +14 -0
  5. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.js +27 -0
  6. package/dist/mcp/handlers/v2-advance-core/assessment-validation.d.ts +16 -0
  7. package/dist/mcp/handlers/v2-advance-core/assessment-validation.js +213 -0
  8. package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +1 -0
  9. package/dist/mcp/handlers/v2-advance-core/event-builders.js +3 -2
  10. package/dist/mcp/handlers/v2-advance-core/index.js +23 -8
  11. package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +9 -1
  12. package/dist/mcp/handlers/v2-advance-core/input-validation.js +22 -2
  13. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
  14. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +69 -19
  15. package/dist/mcp/handlers/v2-advance-core/outcome-success.js +22 -0
  16. package/dist/mcp/handlers/v2-execution/replay.js +7 -0
  17. package/dist/mcp/output-schemas.d.ts +156 -42
  18. package/dist/mcp/output-schemas.js +15 -0
  19. package/dist/mcp/v2-response-formatter.js +7 -1
  20. package/dist/types/workflow-definition.d.ts +26 -0
  21. package/dist/types/workflow-definition.js +16 -1
  22. package/dist/v2/durable-core/constants.d.ts +2 -0
  23. package/dist/v2/durable-core/constants.js +2 -0
  24. package/dist/v2/durable-core/domain/assessment-consequence-event-builder.d.ts +23 -0
  25. package/dist/v2/durable-core/domain/assessment-consequence-event-builder.js +36 -0
  26. package/dist/v2/durable-core/domain/assessment-record.d.ts +12 -0
  27. package/dist/v2/durable-core/domain/assessment-record.js +2 -0
  28. package/dist/v2/durable-core/domain/assessment-recorded-event-builder.d.ts +22 -0
  29. package/dist/v2/durable-core/domain/assessment-recorded-event-builder.js +38 -0
  30. package/dist/v2/durable-core/domain/blocked-node-builder.d.ts +1 -1
  31. package/dist/v2/durable-core/domain/blocked-node-builder.js +8 -0
  32. package/dist/v2/durable-core/domain/blocking-decision.d.ts +6 -0
  33. package/dist/v2/durable-core/domain/blocking-decision.js +15 -0
  34. package/dist/v2/durable-core/domain/prompt-renderer.js +25 -1
  35. package/dist/v2/durable-core/domain/reason-model.d.ts +12 -2
  36. package/dist/v2/durable-core/domain/reason-model.js +27 -2
  37. package/dist/v2/durable-core/domain/risk-policy-guardrails.js +1 -0
  38. package/dist/v2/durable-core/domain/validation-criteria-validator.d.ts +1 -0
  39. package/dist/v2/durable-core/domain/validation-criteria-validator.js +11 -0
  40. package/dist/v2/durable-core/schemas/artifacts/assessment.d.ts +55 -0
  41. package/dist/v2/durable-core/schemas/artifacts/assessment.js +29 -0
  42. package/dist/v2/durable-core/schemas/artifacts/index.d.ts +2 -1
  43. package/dist/v2/durable-core/schemas/artifacts/index.js +8 -1
  44. package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +24 -24
  45. package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.d.ts +141 -21
  46. package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.js +10 -1
  47. package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +729 -171
  48. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +1442 -202
  49. package/dist/v2/durable-core/schemas/session/events.d.ts +231 -8
  50. package/dist/v2/durable-core/schemas/session/events.js +36 -0
  51. package/dist/v2/durable-core/schemas/session/gaps.d.ts +2 -2
  52. package/dist/v2/projections/assessment-consequences.d.ts +19 -0
  53. package/dist/v2/projections/assessment-consequences.js +33 -0
  54. package/dist/v2/projections/assessments.d.ts +21 -0
  55. package/dist/v2/projections/assessments.js +35 -0
  56. package/package.json +1 -1
  57. package/spec/workflow.schema.json +110 -0
  58. package/workflows/bug-investigation.agentic.v2.json +28 -2
  59. package/workflows/test-artifact-loop-control.json +28 -2
@@ -144,6 +144,11 @@ const V2BlockerPointerSchema = zod_1.z.discriminatedUnion('kind', [
144
144
  zod_1.z.object({ kind: zod_1.z.literal('context_budget') }),
145
145
  zod_1.z.object({ kind: zod_1.z.literal('output_contract'), contractRef: zod_1.z.string().min(1) }),
146
146
  zod_1.z.object({ kind: zod_1.z.literal('capability'), capability: zod_1.z.enum(['delegation', 'web_browsing']) }),
147
+ zod_1.z.object({
148
+ kind: zod_1.z.literal('assessment_dimension'),
149
+ assessmentId: zod_1.z.string().min(1),
150
+ dimensionId: zod_1.z.string().min(1).regex(DELIMITER_SAFE_ID_PATTERN, 'dimensionId must be delimiter-safe: [a-z0-9_-]+'),
151
+ }),
147
152
  zod_1.z.object({
148
153
  kind: zod_1.z.literal('workflow_step'),
149
154
  stepId: zod_1.z.string().min(1).regex(DELIMITER_SAFE_ID_PATTERN, 'stepId must be delimiter-safe: [a-z0-9_-]+'),
@@ -154,6 +159,7 @@ const V2BlockerSchema = zod_1.z.object({
154
159
  'USER_ONLY_DEPENDENCY',
155
160
  'MISSING_REQUIRED_OUTPUT',
156
161
  'INVALID_REQUIRED_OUTPUT',
162
+ 'ASSESSMENT_FOLLOWUP_REQUIRED',
157
163
  'MISSING_REQUIRED_NOTES',
158
164
  'MISSING_CONTEXT_KEY',
159
165
  'CONTEXT_BUDGET_EXCEEDED',
@@ -195,6 +201,9 @@ exports.V2BlockerReportSchema = zod_1.z
195
201
  case 'capability':
196
202
  ptrStable = p.capability;
197
203
  break;
204
+ case 'assessment_dimension':
205
+ ptrStable = `${p.assessmentId}|${p.dimensionId}`;
206
+ break;
198
207
  case 'workflow_step':
199
208
  ptrStable = p.stepId;
200
209
  break;
@@ -252,6 +261,12 @@ const V2ContinueWorkflowBlockedSchema = zod_1.z.object({
252
261
  suggestions: zod_1.z.array(zod_1.z.string()),
253
262
  })
254
263
  .optional(),
264
+ assessmentFollowup: zod_1.z
265
+ .object({
266
+ title: zod_1.z.string().min(1),
267
+ guidance: zod_1.z.string().min(1),
268
+ })
269
+ .optional(),
255
270
  });
256
271
  exports.V2ContinueWorkflowOutputSchema = zod_1.z.discriminatedUnion('kind', [
257
272
  V2ContinueWorkflowOkSchema,
@@ -126,7 +126,13 @@ function formatBlockedRetryable(data) {
126
126
  lines.push('');
127
127
  }
128
128
  }
129
- lines.push('Retry with corrected output:');
129
+ if (data.assessmentFollowup) {
130
+ lines.push('**Follow-up required before retrying this step:**');
131
+ lines.push(`- ${data.assessmentFollowup.title}`);
132
+ lines.push(`- ${data.assessmentFollowup.guidance}`);
133
+ lines.push('');
134
+ }
135
+ lines.push('Retry the same step with improved output:');
130
136
  lines.push('');
131
137
  lines.push(formatTokenBlock(data));
132
138
  return lines.join('\n');
@@ -11,6 +11,29 @@ export interface PromptFragment {
11
11
  readonly when?: Condition;
12
12
  readonly text: string;
13
13
  }
14
+ export interface AssessmentDimensionDefinition {
15
+ readonly id: string;
16
+ readonly purpose: string;
17
+ readonly levels: readonly string[];
18
+ readonly required?: boolean;
19
+ }
20
+ export interface AssessmentDefinition {
21
+ readonly id: string;
22
+ readonly purpose: string;
23
+ readonly dimensions: readonly AssessmentDimensionDefinition[];
24
+ }
25
+ export interface AssessmentConsequenceTriggerDefinition {
26
+ readonly dimensionId: string;
27
+ readonly equalsLevel: string;
28
+ }
29
+ export interface AssessmentFollowupRequiredEffectDefinition {
30
+ readonly kind: 'require_followup';
31
+ readonly guidance: string;
32
+ }
33
+ export interface AssessmentConsequenceDefinition {
34
+ readonly when: AssessmentConsequenceTriggerDefinition;
35
+ readonly effect: AssessmentFollowupRequiredEffectDefinition;
36
+ }
14
37
  export interface WorkflowStepDefinition {
15
38
  readonly id: string;
16
39
  readonly title: string;
@@ -23,6 +46,8 @@ export interface WorkflowStepDefinition {
23
46
  readonly runCondition?: Readonly<Record<string, unknown>>;
24
47
  readonly validationCriteria?: ValidationCriteria;
25
48
  readonly outputContract?: OutputContract;
49
+ readonly assessmentRefs?: readonly string[];
50
+ readonly assessmentConsequences?: readonly AssessmentConsequenceDefinition[];
26
51
  readonly notesOptional?: boolean;
27
52
  readonly templateCall?: TemplateCall;
28
53
  readonly promptFragments?: readonly PromptFragment[];
@@ -106,6 +131,7 @@ export interface WorkflowDefinition {
106
131
  readonly functionDefinitions?: readonly FunctionDefinition[];
107
132
  readonly recommendedPreferences?: WorkflowRecommendedPreferences;
108
133
  readonly features?: readonly string[];
134
+ readonly assessments?: readonly AssessmentDefinition[];
109
135
  readonly extensionPoints?: readonly ExtensionPoint[];
110
136
  readonly references?: readonly WorkflowReference[];
111
137
  }
@@ -28,11 +28,26 @@ function hasWorkflowDefinitionShape(obj) {
28
28
  function createWorkflowDefinition(definition) {
29
29
  return Object.freeze({
30
30
  ...definition,
31
- steps: Object.freeze(definition.steps.map(step => Object.freeze({ ...step }))),
31
+ steps: Object.freeze(definition.steps.map(step => Object.freeze({
32
+ ...step,
33
+ assessmentConsequences: 'assessmentConsequences' in step && step.assessmentConsequences
34
+ ? Object.freeze(step.assessmentConsequences.map(consequence => Object.freeze({
35
+ ...consequence,
36
+ when: Object.freeze({ ...consequence.when }),
37
+ effect: Object.freeze({ ...consequence.effect }),
38
+ })))
39
+ : undefined,
40
+ }))),
32
41
  preconditions: definition.preconditions ? Object.freeze([...definition.preconditions]) : undefined,
33
42
  clarificationPrompts: definition.clarificationPrompts ? Object.freeze([...definition.clarificationPrompts]) : undefined,
34
43
  metaGuidance: definition.metaGuidance ? Object.freeze([...definition.metaGuidance]) : undefined,
35
44
  functionDefinitions: definition.functionDefinitions ? Object.freeze([...definition.functionDefinitions]) : undefined,
45
+ assessments: definition.assessments
46
+ ? Object.freeze(definition.assessments.map(assessment => Object.freeze({
47
+ ...assessment,
48
+ dimensions: Object.freeze(assessment.dimensions.map(dimension => Object.freeze({ ...dimension }))),
49
+ })))
50
+ : undefined,
36
51
  extensionPoints: definition.extensionPoints ? Object.freeze([...definition.extensionPoints]) : undefined,
37
52
  references: definition.references ? Object.freeze(definition.references.map(ref => Object.freeze({ ...ref }))) : undefined,
38
53
  });
@@ -28,6 +28,8 @@ export declare const EVENT_KIND: {
28
28
  readonly ADVANCE_RECORDED: "advance_recorded";
29
29
  readonly VALIDATION_PERFORMED: "validation_performed";
30
30
  readonly NODE_OUTPUT_APPENDED: "node_output_appended";
31
+ readonly ASSESSMENT_RECORDED: "assessment_recorded";
32
+ readonly ASSESSMENT_CONSEQUENCE_APPLIED: "assessment_consequence_applied";
31
33
  readonly PREFERENCES_CHANGED: "preferences_changed";
32
34
  readonly CAPABILITY_OBSERVED: "capability_observed";
33
35
  readonly GAP_RECORDED: "gap_recorded";
@@ -31,6 +31,8 @@ exports.EVENT_KIND = {
31
31
  ADVANCE_RECORDED: 'advance_recorded',
32
32
  VALIDATION_PERFORMED: 'validation_performed',
33
33
  NODE_OUTPUT_APPENDED: 'node_output_appended',
34
+ ASSESSMENT_RECORDED: 'assessment_recorded',
35
+ ASSESSMENT_CONSEQUENCE_APPLIED: 'assessment_consequence_applied',
34
36
  PREFERENCES_CHANGED: 'preferences_changed',
35
37
  CAPABILITY_OBSERVED: 'capability_observed',
36
38
  GAP_RECORDED: 'gap_recorded',
@@ -0,0 +1,23 @@
1
+ import { type Result } from 'neverthrow';
2
+ import type { DomainEventV1 } from '../schemas/session/index.js';
3
+ type EventToAppendV1 = Omit<DomainEventV1, 'eventIndex' | 'sessionId'>;
4
+ export type AssessmentConsequenceEventError = {
5
+ readonly code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION';
6
+ readonly message: string;
7
+ };
8
+ export declare function buildAssessmentConsequenceAppliedEvent(args: {
9
+ readonly sessionId: string;
10
+ readonly attemptId: string;
11
+ readonly scope: {
12
+ readonly runId: string;
13
+ readonly nodeId: string;
14
+ };
15
+ readonly assessmentId: string;
16
+ readonly dimensionId: string;
17
+ readonly level: string;
18
+ readonly guidance: string;
19
+ readonly minted: {
20
+ readonly eventId: string;
21
+ };
22
+ }): Result<EventToAppendV1, AssessmentConsequenceEventError>;
23
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAssessmentConsequenceAppliedEvent = buildAssessmentConsequenceAppliedEvent;
4
+ const neverthrow_1 = require("neverthrow");
5
+ const constants_js_1 = require("../constants.js");
6
+ function buildAssessmentConsequenceAppliedEvent(args) {
7
+ if (!args.sessionId)
8
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'sessionId is required' });
9
+ if (!args.attemptId)
10
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'attemptId is required' });
11
+ if (!args.scope.runId || !args.scope.nodeId) {
12
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'scope.runId and scope.nodeId are required' });
13
+ }
14
+ if (!args.assessmentId || !args.dimensionId || !args.level || !args.guidance) {
15
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_CONSEQUENCE_EVENT_INVARIANT_VIOLATION', message: 'assessment consequence fields are required' });
16
+ }
17
+ return (0, neverthrow_1.ok)({
18
+ v: 1,
19
+ eventId: args.minted.eventId,
20
+ kind: constants_js_1.EVENT_KIND.ASSESSMENT_CONSEQUENCE_APPLIED,
21
+ dedupeKey: `assessment_consequence_applied:${args.sessionId}:${args.scope.nodeId}:${args.attemptId}`,
22
+ scope: { runId: args.scope.runId, nodeId: args.scope.nodeId },
23
+ data: {
24
+ attemptId: args.attemptId,
25
+ assessmentId: args.assessmentId,
26
+ trigger: {
27
+ dimensionId: args.dimensionId,
28
+ level: args.level,
29
+ },
30
+ effect: {
31
+ kind: 'require_followup',
32
+ guidance: args.guidance,
33
+ },
34
+ },
35
+ });
36
+ }
@@ -0,0 +1,12 @@
1
+ export interface RecordedAssessmentDimensionV1 {
2
+ readonly dimensionId: string;
3
+ readonly level: string;
4
+ readonly rationale?: string;
5
+ readonly normalization: 'exact' | 'normalized';
6
+ }
7
+ export interface RecordedAssessmentV1 {
8
+ readonly assessmentId: string;
9
+ readonly summary?: string;
10
+ readonly dimensions: readonly RecordedAssessmentDimensionV1[];
11
+ readonly normalizationNotes: readonly string[];
12
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,22 @@
1
+ import { type Result } from 'neverthrow';
2
+ import type { DomainEventV1 } from '../schemas/session/index.js';
3
+ import type { RecordedAssessmentV1 } from './assessment-record.js';
4
+ type EventToAppendV1 = Omit<DomainEventV1, 'eventIndex' | 'sessionId'>;
5
+ export type AssessmentRecordedEventError = {
6
+ readonly code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION';
7
+ readonly message: string;
8
+ };
9
+ export declare function buildAssessmentRecordedEvent(args: {
10
+ readonly sessionId: string;
11
+ readonly attemptId: string;
12
+ readonly artifactOutputId: string;
13
+ readonly scope: {
14
+ readonly runId: string;
15
+ readonly nodeId: string;
16
+ };
17
+ readonly assessment: RecordedAssessmentV1;
18
+ readonly minted: {
19
+ readonly eventId: string;
20
+ };
21
+ }): Result<EventToAppendV1, AssessmentRecordedEventError>;
22
+ export {};
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAssessmentRecordedEvent = buildAssessmentRecordedEvent;
4
+ const neverthrow_1 = require("neverthrow");
5
+ const constants_js_1 = require("../constants.js");
6
+ function buildAssessmentRecordedEvent(args) {
7
+ if (!args.sessionId)
8
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'sessionId is required' });
9
+ if (!args.attemptId)
10
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'attemptId is required' });
11
+ if (!args.artifactOutputId)
12
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'artifactOutputId is required' });
13
+ if (!args.scope.runId || !args.scope.nodeId) {
14
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'scope.runId and scope.nodeId are required' });
15
+ }
16
+ if (!args.minted.eventId)
17
+ return (0, neverthrow_1.err)({ code: 'ASSESSMENT_EVENT_INVARIANT_VIOLATION', message: 'minted.eventId is required' });
18
+ return (0, neverthrow_1.ok)({
19
+ v: 1,
20
+ eventId: args.minted.eventId,
21
+ kind: constants_js_1.EVENT_KIND.ASSESSMENT_RECORDED,
22
+ dedupeKey: `assessment_recorded:${args.sessionId}:${args.scope.nodeId}:${args.attemptId}`,
23
+ scope: { runId: args.scope.runId, nodeId: args.scope.nodeId },
24
+ data: {
25
+ assessmentId: args.assessment.assessmentId,
26
+ attemptId: args.attemptId,
27
+ artifactOutputId: args.artifactOutputId,
28
+ summary: args.assessment.summary,
29
+ normalizationNotes: [...args.assessment.normalizationNotes],
30
+ dimensions: args.assessment.dimensions.map((dimension) => ({
31
+ dimensionId: dimension.dimensionId,
32
+ level: dimension.level,
33
+ normalization: dimension.normalization,
34
+ ...(dimension.rationale ? { rationale: dimension.rationale } : {}),
35
+ })),
36
+ },
37
+ });
38
+ }
@@ -14,7 +14,7 @@ export declare function buildBlockedNodeSnapshot(args: {
14
14
  readonly priorSnapshot: ExecutionSnapshotFileV1;
15
15
  readonly primaryReason: ReasonV1;
16
16
  readonly attemptId: AttemptId;
17
- readonly validationRef: string;
17
+ readonly validationRef?: string;
18
18
  readonly blockers: BlockerReportV1;
19
19
  readonly sha256: Sha256PortV2;
20
20
  }): Result<ExecutionSnapshotFileV1, BlockedNodeBuildError>;
@@ -9,6 +9,14 @@ function toContractViolationReason(reason) {
9
9
  return { kind: 'invalid_required_output', contractRef: reason.contractRef };
10
10
  case 'missing_required_output':
11
11
  return { kind: 'missing_required_output', contractRef: reason.contractRef };
12
+ case 'assessment_followup_required':
13
+ return {
14
+ kind: 'assessment_followup_required',
15
+ assessmentId: reason.assessmentId,
16
+ dimensionId: reason.dimensionId,
17
+ level: reason.level,
18
+ guidance: reason.guidance,
19
+ };
12
20
  case 'missing_context_key':
13
21
  return { kind: 'missing_context_key', key: reason.key };
14
22
  case 'context_budget_exceeded':
@@ -34,4 +34,10 @@ export declare function detectBlockingReasonsV1(args: {
34
34
  readonly missingNotes?: {
35
35
  readonly stepId: string;
36
36
  };
37
+ readonly assessmentFollowupRequired?: {
38
+ readonly assessmentId: string;
39
+ readonly dimensionId: string;
40
+ readonly level: string;
41
+ readonly guidance: string;
42
+ };
37
43
  }): Result<readonly ReasonV1[], BlockingDecisionError>;
@@ -63,5 +63,20 @@ function detectBlockingReasonsV1(args) {
63
63
  }
64
64
  reasons.push({ kind: 'missing_notes', stepId: args.missingNotes.stepId });
65
65
  }
66
+ if (args.assessmentFollowupRequired) {
67
+ if (!constants_js_1.DELIMITER_SAFE_ID_PATTERN.test(args.assessmentFollowupRequired.dimensionId)) {
68
+ return (0, neverthrow_1.err)({
69
+ code: 'INVALID_DELIMITER_SAFE_ID',
70
+ message: `assessment dimension ID must be delimiter-safe: [a-z0-9_-]+ (got: ${args.assessmentFollowupRequired.dimensionId})`,
71
+ });
72
+ }
73
+ reasons.push({
74
+ kind: 'assessment_followup_required',
75
+ assessmentId: args.assessmentFollowupRequired.assessmentId,
76
+ dimensionId: args.assessmentFollowupRequired.dimensionId,
77
+ level: args.assessmentFollowupRequired.level,
78
+ guidance: args.assessmentFollowupRequired.guidance,
79
+ });
80
+ }
66
81
  return (0, neverthrow_1.ok)(reasons);
67
82
  }
@@ -181,6 +181,18 @@ function formatOutputContractRequirements(outputContract) {
181
181
  ];
182
182
  }
183
183
  }
184
+ function formatAssessmentRequirements(assessments) {
185
+ if (assessments.length === 0)
186
+ return [];
187
+ const requirements = [];
188
+ for (const assessment of assessments) {
189
+ requirements.push('Provide an artifact with kind: "wr.assessment"');
190
+ requirements.push(`Assessment target: "${assessment.id}"`);
191
+ requirements.push(`Dimensions: ${assessment.dimensions.map((dimension) => `${dimension.id} (${dimension.levels.join(' | ')})`).join(', ')}`);
192
+ requirements.push('Use only canonical dimension levels. If the engine rejects the artifact, correct the submitted levels instead of inventing new ones.');
193
+ }
194
+ return requirements;
195
+ }
184
196
  function assembleFragmentedPrompt(fragments, context) {
185
197
  return fragments
186
198
  .filter(f => (0, condition_evaluator_js_1.evaluateCondition)(f.when, context))
@@ -217,6 +229,12 @@ function renderPendingPrompt(args) {
217
229
  const outputContract = 'outputContract' in step
218
230
  ? step.outputContract
219
231
  : undefined;
232
+ const stepAssessmentRefs = 'assessmentRefs' in step
233
+ ? step.assessmentRefs
234
+ : undefined;
235
+ const stepAssessments = stepAssessmentRefs && stepAssessmentRefs.length > 0
236
+ ? (args.workflow.definition.assessments ?? []).filter((assessment) => stepAssessmentRefs.includes(assessment.id))
237
+ : [];
220
238
  const isExitStep = outputContract?.contractRef === index_js_2.LOOP_CONTROL_CONTRACT_REF;
221
239
  const loopStep = resolveParentLoopStep(args.workflow, args.stepId);
222
240
  const maxIterations = loopStep?.loop.maxIterations;
@@ -247,6 +265,12 @@ function renderPendingPrompt(args) {
247
265
  ? `\n\n${contractRequirements.map(r => `- ${r}`).join('\n')}`
248
266
  : `\n\n**OUTPUT REQUIREMENTS (System):**\n${contractRequirements.map(r => `- ${r}`).join('\n')}`
249
267
  : '';
268
+ const assessmentRequirements = formatAssessmentRequirements(stepAssessments);
269
+ const assessmentSection = assessmentRequirements.length > 0
270
+ ? cleanResponseFormat
271
+ ? `\n\n${assessmentRequirements.map(r => `- ${r}`).join('\n')}`
272
+ : `\n\n**ASSESSMENT REQUIREMENTS (System):**\n${assessmentRequirements.map(r => `- ${r}`).join('\n')}`
273
+ : '';
250
274
  const isNotesOptional = outputContract !== undefined ||
251
275
  ('notesOptional' in step && step.notesOptional === true);
252
276
  const notesSection = (() => {
@@ -297,7 +321,7 @@ function renderPendingPrompt(args) {
297
321
  const fragmentSuffix = promptFragments && promptFragments.length > 0
298
322
  ? assembleFragmentedPrompt(promptFragments, renderContext)
299
323
  : '';
300
- const enhancedPrompt = loopBanner + basePrompt + requirementsSection + contractSection + notesSection
324
+ const enhancedPrompt = loopBanner + basePrompt + requirementsSection + contractSection + assessmentSection + notesSection
301
325
  + (fragmentSuffix ? '\n\n' + fragmentSuffix : '');
302
326
  if (!args.rehydrateOnly) {
303
327
  return (0, neverthrow_1.ok)({ stepId: args.stepId, title: baseTitle, prompt: enhancedPrompt, agentRole, requireConfirmation });
@@ -8,7 +8,7 @@ export type GapReasonV1 = {
8
8
  readonly detail: UserOnlyDependencyReasonV1;
9
9
  } | {
10
10
  readonly category: 'contract_violation';
11
- readonly detail: 'missing_required_output' | 'invalid_required_output' | 'missing_required_notes';
11
+ readonly detail: 'missing_required_output' | 'invalid_required_output' | 'missing_required_notes' | 'assessment_followup_required';
12
12
  } | {
13
13
  readonly category: 'capability_missing';
14
14
  readonly detail: 'required_capability_unavailable' | 'required_capability_unknown';
@@ -16,7 +16,7 @@ export type GapReasonV1 = {
16
16
  readonly category: 'unexpected';
17
17
  readonly detail: 'invariant_violation' | 'storage_corruption_detected' | 'evaluation_error';
18
18
  };
19
- export type BlockerCodeV1 = 'USER_ONLY_DEPENDENCY' | 'MISSING_REQUIRED_OUTPUT' | 'INVALID_REQUIRED_OUTPUT' | 'MISSING_REQUIRED_NOTES' | 'MISSING_CONTEXT_KEY' | 'CONTEXT_BUDGET_EXCEEDED' | 'REQUIRED_CAPABILITY_UNKNOWN' | 'REQUIRED_CAPABILITY_UNAVAILABLE' | 'INVARIANT_VIOLATION' | 'STORAGE_CORRUPTION_DETECTED';
19
+ export type BlockerCodeV1 = 'USER_ONLY_DEPENDENCY' | 'MISSING_REQUIRED_OUTPUT' | 'INVALID_REQUIRED_OUTPUT' | 'ASSESSMENT_FOLLOWUP_REQUIRED' | 'MISSING_REQUIRED_NOTES' | 'MISSING_CONTEXT_KEY' | 'CONTEXT_BUDGET_EXCEEDED' | 'REQUIRED_CAPABILITY_UNKNOWN' | 'REQUIRED_CAPABILITY_UNAVAILABLE' | 'INVARIANT_VIOLATION' | 'STORAGE_CORRUPTION_DETECTED';
20
20
  export type BlockerPointerV1 = {
21
21
  readonly kind: 'context_key';
22
22
  readonly key: string;
@@ -28,6 +28,10 @@ export type BlockerPointerV1 = {
28
28
  } | {
29
29
  readonly kind: 'capability';
30
30
  readonly capability: CapabilityV2;
31
+ } | {
32
+ readonly kind: 'assessment_dimension';
33
+ readonly assessmentId: string;
34
+ readonly dimensionId: string;
31
35
  } | {
32
36
  readonly kind: 'workflow_step';
33
37
  readonly stepId: string;
@@ -52,6 +56,12 @@ export type ReasonV1 = {
52
56
  } | {
53
57
  readonly kind: 'invalid_required_output';
54
58
  readonly contractRef: string;
59
+ } | {
60
+ readonly kind: 'assessment_followup_required';
61
+ readonly assessmentId: string;
62
+ readonly dimensionId: string;
63
+ readonly level: string;
64
+ readonly guidance: string;
55
65
  } | {
56
66
  readonly kind: 'missing_notes';
57
67
  readonly stepId: string;
@@ -8,6 +8,7 @@ exports.shouldBlock = shouldBlock;
8
8
  const constants_js_1 = require("../constants.js");
9
9
  const neverthrow_1 = require("neverthrow");
10
10
  const utf8_byte_length_js_1 = require("../schemas/lib/utf8-byte-length.js");
11
+ const index_js_1 = require("../schemas/artifacts/index.js");
11
12
  function ensureDelimiterSafeId(label, value) {
12
13
  if (!constants_js_1.DELIMITER_SAFE_ID_PATTERN.test(value)) {
13
14
  return (0, neverthrow_1.err)({
@@ -58,6 +59,12 @@ function reasonToGap(reason) {
58
59
  reason: { category: 'contract_violation', detail: 'invalid_required_output' },
59
60
  summary: `Invalid required output for contractRef=${reason.contractRef}`,
60
61
  };
62
+ case 'assessment_followup_required':
63
+ return {
64
+ severity: 'critical',
65
+ reason: { category: 'contract_violation', detail: 'assessment_followup_required' },
66
+ summary: `Assessment follow-up required: ${reason.assessmentId}.${reason.dimensionId} == ${reason.level}`,
67
+ };
61
68
  case 'missing_notes':
62
69
  return {
63
70
  severity: 'critical',
@@ -125,6 +132,9 @@ function blockerSortKey(b) {
125
132
  case 'capability':
126
133
  ptrStable = p.capability;
127
134
  break;
135
+ case 'assessment_dimension':
136
+ ptrStable = `${p.assessmentId}|${p.dimensionId}`;
137
+ break;
128
138
  case 'workflow_step':
129
139
  ptrStable = p.stepId;
130
140
  break;
@@ -162,7 +172,9 @@ function reasonToBlocker(reason) {
162
172
  code: 'MISSING_REQUIRED_OUTPUT',
163
173
  pointer: { kind: 'output_contract', contractRef },
164
174
  message: `Missing required output (contractRef=${contractRef}).`,
165
- suggestedFix: 'Call continue_workflow without output to rehydrate the current step, then retry with output.notesMarkdown that satisfies the step output requirements.',
175
+ suggestedFix: contractRef === index_js_1.ASSESSMENT_CONTRACT_REF
176
+ ? 'Call continue_workflow without output to rehydrate the current step, then retry with an assessment artifact under output.artifacts using kind "wr.assessment" and the required canonical dimension levels.'
177
+ : 'Call continue_workflow without output to rehydrate the current step, then retry with output.notesMarkdown that satisfies the step output requirements.',
166
178
  }))
167
179
  .andThen(ensureBlockerTextBudgets);
168
180
  case 'invalid_required_output':
@@ -171,9 +183,22 @@ function reasonToBlocker(reason) {
171
183
  code: 'INVALID_REQUIRED_OUTPUT',
172
184
  pointer: { kind: 'output_contract', contractRef },
173
185
  message: `Invalid output for contractRef=${contractRef}.`,
174
- suggestedFix: 'Update output.notesMarkdown to satisfy validation. Then call continue_workflow without output to rehydrate the current step and retry advance with the corrected output. Replaying the same invalid advance will keep returning this blocked result.',
186
+ suggestedFix: contractRef === index_js_1.ASSESSMENT_CONTRACT_REF
187
+ ? 'Update the assessment artifact in output.artifacts so it uses the expected assessment ID and canonical dimension levels. Then call continue_workflow without output to rehydrate the current step and retry with the corrected artifact.'
188
+ : 'Update output.notesMarkdown to satisfy validation. Then call continue_workflow without output to rehydrate the current step and retry advance with the corrected output. Replaying the same invalid advance will keep returning this blocked result.',
175
189
  }))
176
190
  .andThen(ensureBlockerTextBudgets);
191
+ case 'assessment_followup_required':
192
+ return ensureBlockerTextBudgets({
193
+ code: 'ASSESSMENT_FOLLOWUP_REQUIRED',
194
+ pointer: {
195
+ kind: 'assessment_dimension',
196
+ assessmentId: reason.assessmentId,
197
+ dimensionId: reason.dimensionId,
198
+ },
199
+ message: `Follow-up required before this step can proceed: ${reason.assessmentId}.${reason.dimensionId} matched "${reason.level}".`,
200
+ suggestedFix: `Stay on this step, address this follow-up, and retry with updated output: ${reason.guidance}`,
201
+ });
177
202
  case 'missing_notes':
178
203
  return ensureDelimiterSafeId('workflow_step.stepId', reason.stepId)
179
204
  .map((stepId) => ({
@@ -6,6 +6,7 @@ function isNeverDowngradable(reason) {
6
6
  switch (reason.kind) {
7
7
  case 'missing_required_output':
8
8
  case 'invalid_required_output':
9
+ case 'assessment_followup_required':
9
10
  case 'missing_notes':
10
11
  return true;
11
12
  case 'user_only_dependency':
@@ -11,6 +11,7 @@ export declare function getOutputRequirementStatusWithArtifactsV1(args: {
11
11
  readonly outputContract: OutputContract | undefined;
12
12
  readonly artifacts: readonly unknown[];
13
13
  readonly validationCriteria: ValidationCriteria | undefined;
14
+ readonly assessmentValidation?: ValidationResult;
14
15
  readonly notesMarkdown: string | undefined;
15
16
  readonly validation: ValidationResult | undefined;
16
17
  }): OutputRequirementStatus;
@@ -4,6 +4,7 @@ exports.VALIDATION_CRITERIA_CONTRACT_REF = void 0;
4
4
  exports.getOutputRequirementStatusV1 = getOutputRequirementStatusV1;
5
5
  exports.getOutputRequirementStatusWithArtifactsV1 = getOutputRequirementStatusWithArtifactsV1;
6
6
  const artifact_contract_validator_js_1 = require("./artifact-contract-validator.js");
7
+ const index_js_1 = require("../schemas/artifacts/index.js");
7
8
  exports.VALIDATION_CRITERIA_CONTRACT_REF = 'wr.validationCriteria';
8
9
  function getOutputRequirementStatusV1(args) {
9
10
  if (!args.validationCriteria)
@@ -38,6 +39,16 @@ function getOutputRequirementStatusWithArtifactsV1(args) {
38
39
  }
39
40
  return { kind: 'satisfied' };
40
41
  }
42
+ if (args.assessmentValidation) {
43
+ if (!args.assessmentValidation.valid) {
44
+ return {
45
+ kind: 'invalid',
46
+ contractRef: index_js_1.ASSESSMENT_CONTRACT_REF,
47
+ validation: args.assessmentValidation,
48
+ };
49
+ }
50
+ return { kind: 'satisfied' };
51
+ }
41
52
  return getOutputRequirementStatusV1({
42
53
  validationCriteria: args.validationCriteria,
43
54
  notesMarkdown: args.notesMarkdown,
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ export declare const ASSESSMENT_CONTRACT_REF: "wr.contracts.assessment";
3
+ export declare const AssessmentDimensionSubmissionSchema: z.ZodUnion<[z.ZodString, z.ZodObject<{
4
+ level: z.ZodString;
5
+ rationale: z.ZodOptional<z.ZodString>;
6
+ }, "strict", z.ZodTypeAny, {
7
+ level: string;
8
+ rationale?: string | undefined;
9
+ }, {
10
+ level: string;
11
+ rationale?: string | undefined;
12
+ }>]>;
13
+ export type AssessmentDimensionSubmission = z.infer<typeof AssessmentDimensionSubmissionSchema>;
14
+ export declare const AssessmentArtifactV1Schema: z.ZodObject<{
15
+ kind: z.ZodLiteral<"wr.assessment">;
16
+ assessmentId: z.ZodOptional<z.ZodString>;
17
+ dimensions: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
18
+ level: z.ZodString;
19
+ rationale: z.ZodOptional<z.ZodString>;
20
+ }, "strict", z.ZodTypeAny, {
21
+ level: string;
22
+ rationale?: string | undefined;
23
+ }, {
24
+ level: string;
25
+ rationale?: string | undefined;
26
+ }>]>>, Record<string, string | {
27
+ level: string;
28
+ rationale?: string | undefined;
29
+ }>, Record<string, string | {
30
+ level: string;
31
+ rationale?: string | undefined;
32
+ }>>;
33
+ summary: z.ZodOptional<z.ZodString>;
34
+ }, "strict", z.ZodTypeAny, {
35
+ kind: "wr.assessment";
36
+ dimensions: Record<string, string | {
37
+ level: string;
38
+ rationale?: string | undefined;
39
+ }>;
40
+ assessmentId?: string | undefined;
41
+ summary?: string | undefined;
42
+ }, {
43
+ kind: "wr.assessment";
44
+ dimensions: Record<string, string | {
45
+ level: string;
46
+ rationale?: string | undefined;
47
+ }>;
48
+ assessmentId?: string | undefined;
49
+ summary?: string | undefined;
50
+ }>;
51
+ export type AssessmentArtifactV1 = z.infer<typeof AssessmentArtifactV1Schema>;
52
+ export declare function isAssessmentArtifact(artifact: unknown): artifact is {
53
+ readonly kind: 'wr.assessment';
54
+ };
55
+ export declare function parseAssessmentArtifact(artifact: unknown): AssessmentArtifactV1 | null;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AssessmentArtifactV1Schema = exports.AssessmentDimensionSubmissionSchema = exports.ASSESSMENT_CONTRACT_REF = void 0;
4
+ exports.isAssessmentArtifact = isAssessmentArtifact;
5
+ exports.parseAssessmentArtifact = parseAssessmentArtifact;
6
+ const zod_1 = require("zod");
7
+ const MAX_ASSESSMENT_SUMMARY_LENGTH = 1024;
8
+ const MAX_DIMENSION_RATIONALE_LENGTH = 512;
9
+ exports.ASSESSMENT_CONTRACT_REF = 'wr.contracts.assessment';
10
+ exports.AssessmentDimensionSubmissionSchema = zod_1.z.union([
11
+ zod_1.z.string().min(1),
12
+ zod_1.z.object({
13
+ level: zod_1.z.string().min(1),
14
+ rationale: zod_1.z.string().min(1).max(MAX_DIMENSION_RATIONALE_LENGTH).optional(),
15
+ }).strict(),
16
+ ]);
17
+ exports.AssessmentArtifactV1Schema = zod_1.z.object({
18
+ kind: zod_1.z.literal('wr.assessment'),
19
+ assessmentId: zod_1.z.string().min(1).max(64).optional(),
20
+ dimensions: zod_1.z.record(exports.AssessmentDimensionSubmissionSchema).refine((value) => Object.keys(value).length > 0, 'dimensions must contain at least one entry'),
21
+ summary: zod_1.z.string().max(MAX_ASSESSMENT_SUMMARY_LENGTH).optional(),
22
+ }).strict();
23
+ function isAssessmentArtifact(artifact) {
24
+ return typeof artifact === 'object' && artifact !== null && artifact.kind === 'wr.assessment';
25
+ }
26
+ function parseAssessmentArtifact(artifact) {
27
+ const result = exports.AssessmentArtifactV1Schema.safeParse(artifact);
28
+ return result.success ? result.data : null;
29
+ }