@exaudeus/workrail 3.74.3 → 3.76.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 (32) hide show
  1. package/dist/console-ui/assets/index-DFZjlsUM.js +28 -0
  2. package/dist/console-ui/index.html +1 -1
  3. package/dist/coordinators/adaptive-pipeline.d.ts +8 -0
  4. package/dist/coordinators/context-assembly.d.ts +4 -0
  5. package/dist/coordinators/context-assembly.js +156 -0
  6. package/dist/coordinators/modes/full-pipeline.d.ts +1 -1
  7. package/dist/coordinators/modes/full-pipeline.js +140 -27
  8. package/dist/coordinators/modes/implement-shared.d.ts +3 -2
  9. package/dist/coordinators/modes/implement-shared.js +16 -6
  10. package/dist/coordinators/modes/implement.js +49 -3
  11. package/dist/coordinators/pipeline-run-context.d.ts +1811 -0
  12. package/dist/coordinators/pipeline-run-context.js +114 -0
  13. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +25 -2
  14. package/dist/manifest.json +54 -30
  15. package/dist/trigger/coordinator-deps.js +131 -0
  16. package/dist/v2/durable-core/domain/artifact-contract-validator.js +99 -0
  17. package/dist/v2/durable-core/schemas/artifacts/discovery-handoff.d.ts +39 -0
  18. package/dist/v2/durable-core/schemas/artifacts/discovery-handoff.js +10 -1
  19. package/dist/v2/durable-core/schemas/artifacts/index.d.ts +2 -1
  20. package/dist/v2/durable-core/schemas/artifacts/index.js +12 -1
  21. package/dist/v2/durable-core/schemas/artifacts/phase-handoff.d.ts +89 -0
  22. package/dist/v2/durable-core/schemas/artifacts/phase-handoff.js +56 -0
  23. package/docs/authoring-v2.md +12 -0
  24. package/docs/ideas/backlog.md +409 -1
  25. package/package.json +1 -1
  26. package/workflows/coding-task-workflow-agentic.json +9 -6
  27. package/workflows/mr-review-workflow.agentic.v2.json +2 -2
  28. package/workflows/routines/tension-driven-design.json +12 -12
  29. package/workflows/workflow-for-workflows.json +5 -11
  30. package/workflows/wr.discovery.json +20 -17
  31. package/workflows/wr.shaping.json +7 -4
  32. package/dist/console-ui/assets/index-ByqIsoyt.js +0 -28
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PipelineRunContextSchema = exports.ReviewPhaseRecordSchema = exports.CodingPhaseRecordSchema = exports.ShapingPhaseRecordSchema = exports.DiscoveryPhaseRecordSchema = exports.MIN_NOTES_LENGTH_FOR_PHASE_RESULT = void 0;
4
+ exports.buildPhaseResult = buildPhaseResult;
5
+ exports.parsePipelineRunContext = parsePipelineRunContext;
6
+ const zod_1 = require("zod");
7
+ const neverthrow_1 = require("neverthrow");
8
+ const index_js_1 = require("../v2/durable-core/schemas/artifacts/index.js");
9
+ exports.MIN_NOTES_LENGTH_FOR_PHASE_RESULT = 50;
10
+ function buildPhaseResult(artifact, recapMarkdown) {
11
+ if (artifact !== null) {
12
+ const maybeConf = artifact.confidenceBand;
13
+ const confidenceBand = maybeConf === 'high' || maybeConf === 'medium' || maybeConf === 'low'
14
+ ? maybeConf
15
+ : null;
16
+ return { kind: 'full', artifact, confidenceBand, recapMarkdown };
17
+ }
18
+ if (recapMarkdown !== null && recapMarkdown.trim().length > exports.MIN_NOTES_LENGTH_FOR_PHASE_RESULT) {
19
+ return { kind: 'partial', recapMarkdown: recapMarkdown.trim() };
20
+ }
21
+ return { kind: 'fallback', recapMarkdown };
22
+ }
23
+ const PhaseResultFullDiscoverySchema = zod_1.z.object({
24
+ kind: zod_1.z.literal('full'),
25
+ artifact: index_js_1.DiscoveryHandoffArtifactV1Schema,
26
+ confidenceBand: zod_1.z.enum(['high', 'medium', 'low']).nullable(),
27
+ recapMarkdown: zod_1.z.string().nullable(),
28
+ });
29
+ const PhaseResultFullShapingSchema = zod_1.z.object({
30
+ kind: zod_1.z.literal('full'),
31
+ artifact: index_js_1.ShapingHandoffArtifactV1Schema,
32
+ confidenceBand: zod_1.z.enum(['high', 'medium', 'low']).nullable(),
33
+ recapMarkdown: zod_1.z.string().nullable(),
34
+ });
35
+ const PhaseResultFullCodingSchema = zod_1.z.object({
36
+ kind: zod_1.z.literal('full'),
37
+ artifact: index_js_1.CodingHandoffArtifactV1Schema,
38
+ confidenceBand: zod_1.z.enum(['high', 'medium', 'low']).nullable(),
39
+ recapMarkdown: zod_1.z.string().nullable(),
40
+ });
41
+ const PhaseResultFullReviewSchema = zod_1.z.object({
42
+ kind: zod_1.z.literal('full'),
43
+ artifact: index_js_1.ReviewVerdictArtifactV1Schema,
44
+ confidenceBand: zod_1.z.enum(['high', 'medium', 'low']).nullable(),
45
+ recapMarkdown: zod_1.z.string().nullable(),
46
+ });
47
+ const PhaseResultPartialSchema = zod_1.z.object({
48
+ kind: zod_1.z.literal('partial'),
49
+ recapMarkdown: zod_1.z.string(),
50
+ });
51
+ const PhaseResultFallbackSchema = zod_1.z.object({
52
+ kind: zod_1.z.literal('fallback'),
53
+ recapMarkdown: zod_1.z.string().nullable(),
54
+ });
55
+ exports.DiscoveryPhaseRecordSchema = zod_1.z.object({
56
+ completedAt: zod_1.z.string(),
57
+ sessionHandle: zod_1.z.string().min(1),
58
+ result: zod_1.z.discriminatedUnion('kind', [
59
+ PhaseResultFullDiscoverySchema,
60
+ PhaseResultPartialSchema,
61
+ PhaseResultFallbackSchema,
62
+ ]),
63
+ });
64
+ exports.ShapingPhaseRecordSchema = zod_1.z.object({
65
+ completedAt: zod_1.z.string(),
66
+ sessionHandle: zod_1.z.string().min(1),
67
+ result: zod_1.z.discriminatedUnion('kind', [
68
+ PhaseResultFullShapingSchema,
69
+ PhaseResultPartialSchema,
70
+ PhaseResultFallbackSchema,
71
+ ]),
72
+ });
73
+ exports.CodingPhaseRecordSchema = zod_1.z.object({
74
+ completedAt: zod_1.z.string(),
75
+ sessionHandle: zod_1.z.string().min(1),
76
+ result: zod_1.z.discriminatedUnion('kind', [
77
+ PhaseResultFullCodingSchema,
78
+ PhaseResultPartialSchema,
79
+ PhaseResultFallbackSchema,
80
+ ]),
81
+ });
82
+ exports.ReviewPhaseRecordSchema = zod_1.z.object({
83
+ completedAt: zod_1.z.string(),
84
+ sessionHandle: zod_1.z.string().min(1),
85
+ result: zod_1.z.discriminatedUnion('kind', [
86
+ PhaseResultFullReviewSchema,
87
+ PhaseResultPartialSchema,
88
+ PhaseResultFallbackSchema,
89
+ ]),
90
+ });
91
+ exports.PipelineRunContextSchema = zod_1.z.object({
92
+ runId: zod_1.z.string().min(1),
93
+ goal: zod_1.z.string().min(1),
94
+ workspace: zod_1.z.string().min(1),
95
+ startedAt: zod_1.z.string(),
96
+ pipelineMode: zod_1.z.enum(['FULL', 'IMPLEMENT', 'REVIEW_ONLY', 'QUICK_REVIEW']),
97
+ status: zod_1.z.enum(['in_progress', 'completed']).optional(),
98
+ phases: zod_1.z.object({
99
+ discovery: exports.DiscoveryPhaseRecordSchema.optional(),
100
+ shaping: exports.ShapingPhaseRecordSchema.optional(),
101
+ coding: exports.CodingPhaseRecordSchema.optional(),
102
+ review: exports.ReviewPhaseRecordSchema.optional(),
103
+ }),
104
+ });
105
+ function parsePipelineRunContext(raw) {
106
+ const result = exports.PipelineRunContextSchema.safeParse(raw);
107
+ if (!result.success) {
108
+ const issues = result.error.issues
109
+ .map((i) => `${i.path.join('.')}: ${i.message}`)
110
+ .join('; ');
111
+ return (0, neverthrow_1.err)(`PipelineRunContext parse failed: ${issues}`);
112
+ }
113
+ return (0, neverthrow_1.ok)(result.data);
114
+ }
@@ -21,6 +21,29 @@ const VALIDATION_ERROR_PREFIX = '[ValidationError]';
21
21
  function reportValidationFailure(workflowId, sourceKind, error) {
22
22
  console.error(`${VALIDATION_ERROR_PREFIX} ${sourceKind}/${workflowId}: ${error}`);
23
23
  }
24
+ function extractValidationErrors(err) {
25
+ if (err instanceof error_handler_1.MCPError && typeof err.data?.details === 'string') {
26
+ try {
27
+ const ajvErrors = JSON.parse(err.data.details);
28
+ if (Array.isArray(ajvErrors) && ajvErrors.length > 0) {
29
+ return ajvErrors.map((e) => {
30
+ const location = e.instancePath ? `at '${e.instancePath}'` : 'at root';
31
+ const detail = (() => {
32
+ if (e.keyword === 'additionalProperties' && e.params && typeof e.params === 'object') {
33
+ const prop = e.params.additionalProperty;
34
+ return `additional property '${String(prop)}' is not allowed`;
35
+ }
36
+ return e.message ?? e.keyword ?? 'unknown error';
37
+ })();
38
+ return `${location}: ${detail}`;
39
+ });
40
+ }
41
+ }
42
+ catch {
43
+ }
44
+ }
45
+ return [err instanceof Error ? err.message : String(err)];
46
+ }
24
47
  class SchemaValidatingWorkflowStorage {
25
48
  constructor(inner) {
26
49
  this.inner = inner;
@@ -71,7 +94,7 @@ class SchemaValidatingWorkflowStorage {
71
94
  warnings.push({
72
95
  workflowId: workflow.definition.id,
73
96
  sourceKind: workflow.source.kind,
74
- errors: [errorMessage],
97
+ errors: extractValidationErrors(err),
75
98
  });
76
99
  }
77
100
  }
@@ -158,7 +181,7 @@ class SchemaValidatingCompositeWorkflowStorage {
158
181
  warnings.push({
159
182
  workflowId: workflow.definition.id,
160
183
  sourceKind: workflow.source.kind,
161
- errors: [errorMessage],
184
+ errors: extractValidationErrors(err),
162
185
  });
163
186
  }
164
187
  }
@@ -473,16 +473,16 @@
473
473
  "sha256": "5fe866e54f796975dec5d8ba9983aefd86074db212d3fccd64eed04bc9f0b3da",
474
474
  "bytes": 8011
475
475
  },
476
- "console-ui/assets/index-ByqIsoyt.js": {
477
- "sha256": "86f5c9d133d4c98fef157087edd26d9a811fd0e68018a797b52a9a44eb501ead",
478
- "bytes": 768234
476
+ "console-ui/assets/index-DFZjlsUM.js": {
477
+ "sha256": "32444c14f8151326508fdfd00dd46abffc2034064849a411fac1e779da707eca",
478
+ "bytes": 767524
479
479
  },
480
480
  "console-ui/assets/index-DHrKiMCf.css": {
481
481
  "sha256": "40290b50e21ee7e82433efe13b1aa31c1ea608bd057a5c4e324982f284bc928b",
482
482
  "bytes": 60673
483
483
  },
484
484
  "console-ui/index.html": {
485
- "sha256": "9f03a646bde4274beaade4ded2699065b901624e5173627ebd20babed3f2e7e3",
485
+ "sha256": "1c656cb35c24495d9db4392e51d43d0f07f800e724c1e901df5fd48a43e2418d",
486
486
  "bytes": 417
487
487
  },
488
488
  "console/standalone-console.d.ts": {
@@ -526,36 +526,44 @@
526
526
  "bytes": 77
527
527
  },
528
528
  "coordinators/adaptive-pipeline.d.ts": {
529
- "sha256": "8f1e9ee8e14fe847d57ab65d75dab48b08afd376af1fb70c65f760f4a26ff855",
530
- "bytes": 2811
529
+ "sha256": "1ab143720fd019202368715d774a045b55cf953827571570d1396a62d9cacda0",
530
+ "bytes": 3512
531
531
  },
532
532
  "coordinators/adaptive-pipeline.js": {
533
533
  "sha256": "afac18f8fc5fd75bc5ddd32cb3aaf8e02c22210c4ece001a4dd3c69ac2c10c8a",
534
534
  "bytes": 4588
535
535
  },
536
+ "coordinators/context-assembly.d.ts": {
537
+ "sha256": "ce8e19d9c2902329a96d2447d7d7c6489bafdafc9c8cfdb23423994524640d14",
538
+ "bytes": 466
539
+ },
540
+ "coordinators/context-assembly.js": {
541
+ "sha256": "6f8f3480b4cd42af02e279d9a67587eb0ce55b32611a94ce8dccf57a6589b71d",
542
+ "bytes": 7937
543
+ },
536
544
  "coordinators/modes/full-pipeline.d.ts": {
537
- "sha256": "94735f14cbacab0c37f4474b636646b7bd6b9e3b2304181f14b3939157ebe3dd",
538
- "bytes": 462
545
+ "sha256": "88230513db280be8b63106fef82a408499ba16a7acfbe5fa69f86af0a0c3ae04",
546
+ "bytes": 450
539
547
  },
540
548
  "coordinators/modes/full-pipeline.js": {
541
- "sha256": "040b412e33ecb46dcc27c9968c9c0cc5d416880a03dab6f93ed09b8f447c21aa",
542
- "bytes": 13161
549
+ "sha256": "99dc73260d66bb5c9e5c8f477467f5f934aad81b171d0e1cbe661109e939f9e7",
550
+ "bytes": 20365
543
551
  },
544
552
  "coordinators/modes/implement-shared.d.ts": {
545
- "sha256": "fbad9d91d84d2112b273175618686489a7f106385e0e62d6cab80804d6d0f2d7",
546
- "bytes": 708
553
+ "sha256": "3203d8cb8a51dfe0cf88f3ab29d2dd5e0e60ae3b9c9dcc9f426a8581f55e71ff",
554
+ "bytes": 918
547
555
  },
548
556
  "coordinators/modes/implement-shared.js": {
549
- "sha256": "96f95d9fca1c511eb1b12247148d0e0de3dd3add5aacd72e2a8bba4b36f5995b",
550
- "bytes": 13636
557
+ "sha256": "ef9385ab3881aafcf677a9a8823b139f1b197436276c448ff67cc3b4500acf98",
558
+ "bytes": 14254
551
559
  },
552
560
  "coordinators/modes/implement.d.ts": {
553
561
  "sha256": "23919c24d62a0bf15296a52fbc594cca8b1b34e6f8d98dcf7dede8d97ad4cabb",
554
562
  "bytes": 347
555
563
  },
556
564
  "coordinators/modes/implement.js": {
557
- "sha256": "59b0dddf00587db1d565c1e460cdad1f75b4ca32687e3649452b0f4de90a3061",
558
- "bytes": 5482
565
+ "sha256": "e779f11315f4e911aa71318271070d7ffc7f2cc243c33669285b6f10b7c0d901",
566
+ "bytes": 8265
559
567
  },
560
568
  "coordinators/modes/quick-review.d.ts": {
561
569
  "sha256": "03a4f29a07047b0bf788d84f8e0ebab63d64c8eb98aa57087943a8fb84563998",
@@ -573,6 +581,14 @@
573
581
  "sha256": "a7da0b06ea4b16ae71b3d08a559dade91b01ffe1270633eb8ac1c355d57126eb",
574
582
  "bytes": 1198
575
583
  },
584
+ "coordinators/pipeline-run-context.d.ts": {
585
+ "sha256": "8fc743a1458e4d7a0971c968d51749d55a3bc114b286efe6b2030eb062809589",
586
+ "bytes": 68469
587
+ },
588
+ "coordinators/pipeline-run-context.js": {
589
+ "sha256": "5489319764a0dbd1b037521e014784fab518c4ff4f9137e045129e6845793e55",
590
+ "bytes": 4790
591
+ },
576
592
  "coordinators/pr-review.d.ts": {
577
593
  "sha256": "0dba830dd29cd82c58300ca9fdfb4c29d0acd0b257740ce3e65f2360239a106b",
578
594
  "bytes": 4501
@@ -1026,8 +1042,8 @@
1026
1042
  "bytes": 2023
1027
1043
  },
1028
1044
  "infrastructure/storage/schema-validating-workflow-storage.js": {
1029
- "sha256": "4004fac4f8cb1b5cadbe060a389fe6a97377eedc6d4209b92da0d82d36a5e1b0",
1030
- "bytes": 7801
1045
+ "sha256": "06a8dd9b05f3186dc305d39436b49c6c13e08b30b1fa9ae1f3d6161789c3b993",
1046
+ "bytes": 8878
1031
1047
  },
1032
1048
  "infrastructure/storage/storage.d.ts": {
1033
1049
  "sha256": "481c5c0ef797baa7f18cff6a468a1de6d1ef34dd4b35f53e318e30b825b31e63",
@@ -1794,8 +1810,8 @@
1794
1810
  "bytes": 854
1795
1811
  },
1796
1812
  "trigger/coordinator-deps.js": {
1797
- "sha256": "1b400abbd6158e900d4559c74b9f3069a46b60fd02d046f36e30cc40a58acb51",
1798
- "bytes": 22626
1813
+ "sha256": "ceff71f1fd76b8ce7c490eee80fb2c13496d53adc321b85be525784eeafe930a",
1814
+ "bytes": 29393
1799
1815
  },
1800
1816
  "trigger/delivery-action.d.ts": {
1801
1817
  "sha256": "bba98a08e35653304b604cd3ec126374cb731620db27ee2c8d6782d5b5b31207",
@@ -2058,8 +2074,8 @@
2058
2074
  "bytes": 1343
2059
2075
  },
2060
2076
  "v2/durable-core/domain/artifact-contract-validator.js": {
2061
- "sha256": "d7ad3b9c3c3bbb54616682640be23bb04b6256bb96f8fad5daf56cc0df43ec36",
2062
- "bytes": 6500
2077
+ "sha256": "f3b3e7505a820ab3870b19994d9a4d8e61d9051452a28d86feef737d24c144a3",
2078
+ "bytes": 10619
2063
2079
  },
2064
2080
  "v2/durable-core/domain/assessment-consequence-event-builder.d.ts": {
2065
2081
  "sha256": "be91245e615888ae4675c093a9f323500099dbe6d1250ced4726764e1029e9ad",
@@ -2414,20 +2430,20 @@
2414
2430
  "bytes": 1242
2415
2431
  },
2416
2432
  "v2/durable-core/schemas/artifacts/discovery-handoff.d.ts": {
2417
- "sha256": "ccf5b234bc4e1d98a688ecdf555cb8a0cc4e171209f31845d13ac89ef747bbe9",
2418
- "bytes": 1174
2433
+ "sha256": "36b933327a0aed2b767834c4567c7bc51209f9f39e844f3a2503ddccc70ec93e",
2434
+ "bytes": 2323
2419
2435
  },
2420
2436
  "v2/durable-core/schemas/artifacts/discovery-handoff.js": {
2421
- "sha256": "78b5ce8abef29a1f15d8e10f33a9174f8d027550ffdfc419ddad03db8d0fc7d6",
2422
- "bytes": 1138
2437
+ "sha256": "2c8e2f51df1691a34e64bfb4ecfbfd8b4447ca961824612c366f9a30092bfe9b",
2438
+ "bytes": 1624
2423
2439
  },
2424
2440
  "v2/durable-core/schemas/artifacts/index.d.ts": {
2425
- "sha256": "7821aa8564f8900b81396497e26ad3718805c64daf02147f1427d35dfe6c957d",
2426
- "bytes": 1557
2441
+ "sha256": "016e3d46d2eac61e12caf851f8b9d46512b2a3a186bbab7d672127f7f48eb168",
2442
+ "bytes": 1976
2427
2443
  },
2428
2444
  "v2/durable-core/schemas/artifacts/index.js": {
2429
- "sha256": "6755939280959c6a8ad70a5141a0a8803ad136eb1064f6238bb08e294c2330d0",
2430
- "bytes": 5772
2445
+ "sha256": "55392c70481c4a89aebf42ad7b1becb17e128d575b3bd832f7fd8f2b85ae89b9",
2446
+ "bytes": 7493
2431
2447
  },
2432
2448
  "v2/durable-core/schemas/artifacts/loop-control.d.ts": {
2433
2449
  "sha256": "95dabfdcedb1a71c58c38a805dcf24c76254783bc0b2c7d429a3b8ffb94202ed",
@@ -2437,6 +2453,14 @@
2437
2453
  "sha256": "c4b2ed38f9fdda3fbe3e4b68b0a228890fd9a7c73117a6d202928acabe6fc0f9",
2438
2454
  "bytes": 2115
2439
2455
  },
2456
+ "v2/durable-core/schemas/artifacts/phase-handoff.d.ts": {
2457
+ "sha256": "e56371b1e7e5ca83c2867c04d9e1c4b9f24ca654905c5c24d2577acc48a005c4",
2458
+ "bytes": 3228
2459
+ },
2460
+ "v2/durable-core/schemas/artifacts/phase-handoff.js": {
2461
+ "sha256": "05e038285bcd4a140a22b71e7bf1a53ee123de8f23b021b4e9cb9164e266b02a",
2462
+ "bytes": 2651
2463
+ },
2440
2464
  "v2/durable-core/schemas/artifacts/review-verdict.d.ts": {
2441
2465
  "sha256": "2b9f7ae6b3fafe6c26f266bb249c8c286144eb1f753c803d4358ef81e30d1736",
2442
2466
  "bytes": 2211
@@ -38,10 +38,12 @@ const fs = __importStar(require("node:fs"));
38
38
  const os = __importStar(require("node:os"));
39
39
  const path = __importStar(require("node:path"));
40
40
  const node_crypto_1 = require("node:crypto");
41
+ const neverthrow_1 = require("neverthrow");
41
42
  const start_js_1 = require("../mcp/handlers/v2-execution/start.js");
42
43
  const v2_token_ops_js_1 = require("../mcp/handlers/v2-token-ops.js");
43
44
  const index_js_1 = require("../context-assembly/index.js");
44
45
  const infra_js_1 = require("../context-assembly/infra.js");
46
+ const pipeline_run_context_js_1 = require("../coordinators/pipeline-run-context.js");
45
47
  function createCoordinatorDeps(deps) {
46
48
  const { ctx, execFileAsync, consoleService } = deps;
47
49
  let dispatch = null;
@@ -485,5 +487,134 @@ function createCoordinatorDeps(deps) {
485
487
  }
486
488
  return 'timeout';
487
489
  },
490
+ generateRunId: () => (0, node_crypto_1.randomUUID)(),
491
+ readActiveRunId: async (workspace) => {
492
+ const runsDir = path.join(workspace, '.workrail', 'pipeline-runs');
493
+ try {
494
+ const entries = await fs.promises.readdir(runsDir);
495
+ const candidates = [];
496
+ for (const entry of entries) {
497
+ if (!entry.endsWith('-context.json'))
498
+ continue;
499
+ try {
500
+ const raw = await fs.promises.readFile(path.join(runsDir, entry), 'utf-8');
501
+ const ctx = JSON.parse(raw);
502
+ if (typeof ctx !== 'object' || ctx === null)
503
+ continue;
504
+ const c = ctx;
505
+ if (typeof c['runId'] !== 'string')
506
+ continue;
507
+ if (c['status'] === 'completed')
508
+ continue;
509
+ candidates.push({ runId: c['runId'], startedAt: String(c['startedAt'] ?? '') });
510
+ }
511
+ catch {
512
+ continue;
513
+ }
514
+ }
515
+ if (candidates.length === 0)
516
+ return (0, neverthrow_1.ok)(null);
517
+ candidates.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
518
+ if (candidates.length > 1) {
519
+ process.stderr.write(`[WARN coordinator] ${candidates.length} in-progress pipeline runs found -- resuming newest (${candidates[0].runId}). ` +
520
+ `Others: ${candidates.slice(1).map(c => c.runId).join(', ')}. Run 'worktrain cleanup' to clear stale runs.\n`);
521
+ }
522
+ return (0, neverthrow_1.ok)(candidates[0].runId);
523
+ }
524
+ catch (e) {
525
+ if (e.code === 'ENOENT')
526
+ return (0, neverthrow_1.ok)(null);
527
+ const msg = e instanceof Error ? e.message : String(e);
528
+ process.stderr.write(`[WARN coordinator] readActiveRunId failed -- crash recovery skipped: ${msg}\n`);
529
+ return (0, neverthrow_1.err)(`readActiveRunId failed: ${msg}`);
530
+ }
531
+ },
532
+ markPipelineRunComplete: async (workspace, runId) => {
533
+ const runsDir = path.join(workspace, '.workrail', 'pipeline-runs');
534
+ const filePath = path.join(runsDir, `${runId}-context.json`);
535
+ const tmpPath = filePath + '.tmp';
536
+ try {
537
+ const raw = await fs.promises.readFile(filePath, 'utf-8');
538
+ const existing = JSON.parse(raw);
539
+ const updated = { ...existing, status: 'completed' };
540
+ await fs.promises.writeFile(tmpPath, JSON.stringify(updated, null, 2) + '\n', 'utf-8');
541
+ await fs.promises.rename(tmpPath, filePath);
542
+ return (0, neverthrow_1.ok)(undefined);
543
+ }
544
+ catch (e) {
545
+ try {
546
+ await fs.promises.unlink(tmpPath);
547
+ }
548
+ catch { }
549
+ return (0, neverthrow_1.err)(`markPipelineRunComplete failed: ${e instanceof Error ? e.message : String(e)}`);
550
+ }
551
+ },
552
+ readPipelineContext: async (workspace, runId) => {
553
+ const runsDir = path.join(workspace, '.workrail', 'pipeline-runs');
554
+ const filePath = path.join(runsDir, `${runId}-context.json`);
555
+ try {
556
+ const raw = await fs.promises.readFile(filePath, 'utf-8');
557
+ const parsed = JSON.parse(raw);
558
+ return (0, pipeline_run_context_js_1.parsePipelineRunContext)(parsed);
559
+ }
560
+ catch (e) {
561
+ if (e.code === 'ENOENT') {
562
+ return (0, neverthrow_1.ok)(null);
563
+ }
564
+ const msg = e instanceof Error ? e.message : String(e);
565
+ return (0, neverthrow_1.err)(`readPipelineContext failed: ${msg}`);
566
+ }
567
+ },
568
+ createPipelineContext: async (workspace, runId, goal, pipelineMode) => {
569
+ const runsDir = path.join(workspace, '.workrail', 'pipeline-runs');
570
+ const filePath = path.join(runsDir, `${runId}-context.json`);
571
+ const tmpPath = filePath + '.tmp';
572
+ try {
573
+ await fs.promises.mkdir(runsDir, { recursive: true });
574
+ const initial = { runId, goal, workspace, startedAt: new Date().toISOString(), pipelineMode, phases: {} };
575
+ await fs.promises.writeFile(tmpPath, JSON.stringify(initial, null, 2) + '\n', 'utf-8');
576
+ await fs.promises.rename(tmpPath, filePath);
577
+ return (0, neverthrow_1.ok)(undefined);
578
+ }
579
+ catch (e) {
580
+ try {
581
+ await fs.promises.unlink(tmpPath);
582
+ }
583
+ catch { }
584
+ return (0, neverthrow_1.err)(`createPipelineContext failed: ${e instanceof Error ? e.message : String(e)}`);
585
+ }
586
+ },
587
+ writePhaseRecord: async (workspace, runId, entry) => {
588
+ const runsDir = path.join(workspace, '.workrail', 'pipeline-runs');
589
+ const filePath = path.join(runsDir, `${runId}-context.json`);
590
+ const tmpPath = filePath + '.tmp';
591
+ try {
592
+ await fs.promises.mkdir(runsDir, { recursive: true });
593
+ const raw = await fs.promises.readFile(filePath, 'utf-8');
594
+ const parsed = JSON.parse(raw);
595
+ const existing = (0, pipeline_run_context_js_1.parsePipelineRunContext)(parsed);
596
+ if (existing.isErr() || existing.value === null) {
597
+ return (0, neverthrow_1.err)(`writePhaseRecord: context file missing or invalid for runId=${runId}`);
598
+ }
599
+ const updated = {
600
+ ...existing.value,
601
+ phases: {
602
+ ...existing.value.phases,
603
+ [entry.phase]: entry.record,
604
+ },
605
+ };
606
+ await fs.promises.writeFile(tmpPath, JSON.stringify(updated, null, 2) + '\n', 'utf-8');
607
+ await fs.promises.rename(tmpPath, filePath);
608
+ return (0, neverthrow_1.ok)(undefined);
609
+ }
610
+ catch (e) {
611
+ const msg = e instanceof Error ? e.message : String(e);
612
+ try {
613
+ await fs.promises.unlink(tmpPath);
614
+ }
615
+ catch { }
616
+ return (0, neverthrow_1.err)(`writePhaseRecord failed: ${msg}`);
617
+ }
618
+ },
488
619
  };
489
620
  }
@@ -25,6 +25,12 @@ function validateArtifactContract(artifacts, contract) {
25
25
  return validateCoordinatorSignalContract(artifacts, contractRef, required);
26
26
  case index_js_1.REVIEW_VERDICT_CONTRACT_REF:
27
27
  return validateReviewVerdictContract(artifacts, contractRef, required);
28
+ case index_js_1.DISCOVERY_HANDOFF_CONTRACT_REF:
29
+ return validateDiscoveryHandoffContract(artifacts, contractRef, required);
30
+ case index_js_1.SHAPING_HANDOFF_CONTRACT_REF:
31
+ return validateShapingHandoffContract(artifacts, contractRef, required);
32
+ case index_js_1.CODING_HANDOFF_CONTRACT_REF:
33
+ return validateCodingHandoffContract(artifacts, contractRef, required);
28
34
  default:
29
35
  return {
30
36
  valid: false,
@@ -129,6 +135,99 @@ function validateReviewVerdictContract(artifacts, contractRef, required) {
129
135
  }
130
136
  return { valid: true, artifact: parseResult.data };
131
137
  }
138
+ function validateDiscoveryHandoffContract(artifacts, contractRef, required) {
139
+ const handoffArtifacts = artifacts.filter(index_js_1.isDiscoveryHandoffArtifact);
140
+ if (handoffArtifacts.length === 0) {
141
+ if (required) {
142
+ return {
143
+ valid: false,
144
+ error: {
145
+ code: 'MISSING_REQUIRED_ARTIFACT',
146
+ contractRef,
147
+ message: `Required artifact missing: ${contractRef}. Agent must provide an artifact with kind='wr.discovery_handoff'.`,
148
+ },
149
+ };
150
+ }
151
+ return { valid: true, artifact: null };
152
+ }
153
+ const artifact = handoffArtifacts[0];
154
+ const parseResult = index_js_1.DiscoveryHandoffArtifactV1Schema.safeParse(artifact);
155
+ if (!parseResult.success) {
156
+ const issues = parseResult.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
157
+ return {
158
+ valid: false,
159
+ error: {
160
+ code: 'INVALID_ARTIFACT_SCHEMA',
161
+ contractRef,
162
+ message: `Artifact schema validation failed for ${contractRef}`,
163
+ issues,
164
+ },
165
+ };
166
+ }
167
+ return { valid: true, artifact: parseResult.data };
168
+ }
169
+ function validateShapingHandoffContract(artifacts, contractRef, required) {
170
+ const handoffArtifacts = artifacts.filter(index_js_1.isShapingHandoffArtifact);
171
+ if (handoffArtifacts.length === 0) {
172
+ if (required) {
173
+ return {
174
+ valid: false,
175
+ error: {
176
+ code: 'MISSING_REQUIRED_ARTIFACT',
177
+ contractRef,
178
+ message: `Required artifact missing: ${contractRef}. Agent must provide an artifact with kind='wr.shaping_handoff'.`,
179
+ },
180
+ };
181
+ }
182
+ return { valid: true, artifact: null };
183
+ }
184
+ const artifact = handoffArtifacts[0];
185
+ const parseResult = index_js_1.ShapingHandoffArtifactV1Schema.safeParse(artifact);
186
+ if (!parseResult.success) {
187
+ const issues = parseResult.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
188
+ return {
189
+ valid: false,
190
+ error: {
191
+ code: 'INVALID_ARTIFACT_SCHEMA',
192
+ contractRef,
193
+ message: `Artifact schema validation failed for ${contractRef}`,
194
+ issues,
195
+ },
196
+ };
197
+ }
198
+ return { valid: true, artifact: parseResult.data };
199
+ }
200
+ function validateCodingHandoffContract(artifacts, contractRef, required) {
201
+ const handoffArtifacts = artifacts.filter(index_js_1.isCodingHandoffArtifact);
202
+ if (handoffArtifacts.length === 0) {
203
+ if (required) {
204
+ return {
205
+ valid: false,
206
+ error: {
207
+ code: 'MISSING_REQUIRED_ARTIFACT',
208
+ contractRef,
209
+ message: `Required artifact missing: ${contractRef}. Agent must provide an artifact with kind='wr.coding_handoff'.`,
210
+ },
211
+ };
212
+ }
213
+ return { valid: true, artifact: null };
214
+ }
215
+ const artifact = handoffArtifacts[0];
216
+ const parseResult = index_js_1.CodingHandoffArtifactV1Schema.safeParse(artifact);
217
+ if (!parseResult.success) {
218
+ const issues = parseResult.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
219
+ return {
220
+ valid: false,
221
+ error: {
222
+ code: 'INVALID_ARTIFACT_SCHEMA',
223
+ contractRef,
224
+ message: `Artifact schema validation failed for ${contractRef}`,
225
+ issues,
226
+ },
227
+ };
228
+ }
229
+ return { valid: true, artifact: parseResult.data };
230
+ }
132
231
  function requiresArtifactValidation(outputContract) {
133
232
  if (!outputContract)
134
233
  return false;
@@ -7,6 +7,27 @@ export declare const DiscoveryHandoffArtifactV1Schema: z.ZodObject<{
7
7
  designDocPath: z.ZodString;
8
8
  confidenceBand: z.ZodEnum<["high", "medium", "low"]>;
9
9
  keyInvariants: z.ZodArray<z.ZodString, "many">;
10
+ rejectedDirections: z.ZodOptional<z.ZodArray<z.ZodObject<{
11
+ direction: z.ZodString;
12
+ reason: z.ZodString;
13
+ }, "strip", z.ZodTypeAny, {
14
+ reason: string;
15
+ direction: string;
16
+ }, {
17
+ reason: string;
18
+ direction: string;
19
+ }>, "many">>;
20
+ implementationConstraints: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
21
+ keyCodebaseLocations: z.ZodOptional<z.ZodArray<z.ZodObject<{
22
+ path: z.ZodString;
23
+ relevance: z.ZodString;
24
+ }, "strip", z.ZodTypeAny, {
25
+ path: string;
26
+ relevance: string;
27
+ }, {
28
+ path: string;
29
+ relevance: string;
30
+ }>, "many">>;
10
31
  }, "strict", z.ZodTypeAny, {
11
32
  kind: "wr.discovery_handoff";
12
33
  version: 1;
@@ -14,6 +35,15 @@ export declare const DiscoveryHandoffArtifactV1Schema: z.ZodObject<{
14
35
  designDocPath: string;
15
36
  confidenceBand: "low" | "high" | "medium";
16
37
  keyInvariants: string[];
38
+ rejectedDirections?: {
39
+ reason: string;
40
+ direction: string;
41
+ }[] | undefined;
42
+ implementationConstraints?: string[] | undefined;
43
+ keyCodebaseLocations?: {
44
+ path: string;
45
+ relevance: string;
46
+ }[] | undefined;
17
47
  }, {
18
48
  kind: "wr.discovery_handoff";
19
49
  version: 1;
@@ -21,6 +51,15 @@ export declare const DiscoveryHandoffArtifactV1Schema: z.ZodObject<{
21
51
  designDocPath: string;
22
52
  confidenceBand: "low" | "high" | "medium";
23
53
  keyInvariants: string[];
54
+ rejectedDirections?: {
55
+ reason: string;
56
+ direction: string;
57
+ }[] | undefined;
58
+ implementationConstraints?: string[] | undefined;
59
+ keyCodebaseLocations?: {
60
+ path: string;
61
+ relevance: string;
62
+ }[] | undefined;
24
63
  }>;
25
64
  export type DiscoveryHandoffArtifactV1 = z.infer<typeof DiscoveryHandoffArtifactV1Schema>;
26
65
  export declare function isDiscoveryHandoffArtifact(artifact: unknown): artifact is {
@@ -12,7 +12,16 @@ exports.DiscoveryHandoffArtifactV1Schema = zod_1.z
12
12
  selectedDirection: zod_1.z.string().min(1),
13
13
  designDocPath: zod_1.z.string(),
14
14
  confidenceBand: zod_1.z.enum(['high', 'medium', 'low']),
15
- keyInvariants: zod_1.z.array(zod_1.z.string().min(1)),
15
+ keyInvariants: zod_1.z.array(zod_1.z.string().min(1).max(200)).max(12),
16
+ rejectedDirections: zod_1.z.array(zod_1.z.object({
17
+ direction: zod_1.z.string().min(1).max(200),
18
+ reason: zod_1.z.string().min(1).max(300),
19
+ })).max(5).optional(),
20
+ implementationConstraints: zod_1.z.array(zod_1.z.string().min(1).max(200)).max(8).optional(),
21
+ keyCodebaseLocations: zod_1.z.array(zod_1.z.object({
22
+ path: zod_1.z.string().min(1).max(300),
23
+ relevance: zod_1.z.string().min(1).max(150),
24
+ })).max(10).optional(),
16
25
  })
17
26
  .strict();
18
27
  function isDiscoveryHandoffArtifact(artifact) {