@cleocode/contracts 2026.5.122 → 2026.5.124

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 (108) hide show
  1. package/dist/__tests__/completion-operation-contracts.test.d.ts +8 -0
  2. package/dist/__tests__/completion-operation-contracts.test.d.ts.map +1 -0
  3. package/dist/__tests__/completion-operation-contracts.test.js +114 -0
  4. package/dist/__tests__/completion-operation-contracts.test.js.map +1 -0
  5. package/dist/__tests__/operation-envelope-validation.test.d.ts +8 -0
  6. package/dist/__tests__/operation-envelope-validation.test.d.ts.map +1 -0
  7. package/dist/__tests__/operation-envelope-validation.test.js +105 -0
  8. package/dist/__tests__/operation-envelope-validation.test.js.map +1 -0
  9. package/dist/__tests__/satisfies-atom.test.js +21 -3
  10. package/dist/__tests__/satisfies-atom.test.js.map +1 -1
  11. package/dist/__tests__/workgraph-context-scaffold-contracts.test.d.ts +8 -0
  12. package/dist/__tests__/workgraph-context-scaffold-contracts.test.d.ts.map +1 -0
  13. package/dist/__tests__/workgraph-context-scaffold-contracts.test.js +188 -0
  14. package/dist/__tests__/workgraph-context-scaffold-contracts.test.js.map +1 -0
  15. package/dist/__tests__/workgraph-operation-contracts.test.d.ts +8 -0
  16. package/dist/__tests__/workgraph-operation-contracts.test.d.ts.map +1 -0
  17. package/dist/__tests__/workgraph-operation-contracts.test.js +115 -0
  18. package/dist/__tests__/workgraph-operation-contracts.test.js.map +1 -0
  19. package/dist/data-accessor.d.ts +77 -0
  20. package/dist/data-accessor.d.ts.map +1 -1
  21. package/dist/data-accessor.js.map +1 -1
  22. package/dist/dispatch/__tests__/docs-update-registry.test.d.ts +2 -0
  23. package/dist/dispatch/__tests__/docs-update-registry.test.d.ts.map +1 -0
  24. package/dist/dispatch/__tests__/docs-update-registry.test.js +27 -0
  25. package/dist/dispatch/__tests__/docs-update-registry.test.js.map +1 -0
  26. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.d.ts +2 -0
  27. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.d.ts.map +1 -0
  28. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.js +271 -0
  29. package/dist/dispatch/__tests__/operation-envelope-snapshots.test.js.map +1 -0
  30. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  31. package/dist/dispatch/operations-registry.js +487 -7
  32. package/dist/dispatch/operations-registry.js.map +1 -1
  33. package/dist/evidence-atom-schema.d.ts +4 -4
  34. package/dist/evidence-atom-schema.d.ts.map +1 -1
  35. package/dist/evidence-atom-schema.js +9 -9
  36. package/dist/evidence-atom-schema.js.map +1 -1
  37. package/dist/index.d.ts +12 -6
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +8 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/operation-envelope-validation.d.ts +79 -0
  42. package/dist/operation-envelope-validation.d.ts.map +1 -0
  43. package/dist/operation-envelope-validation.js +219 -0
  44. package/dist/operation-envelope-validation.js.map +1 -0
  45. package/dist/operations/__tests__/agent-operation-contract.test.d.ts +2 -0
  46. package/dist/operations/__tests__/agent-operation-contract.test.d.ts.map +1 -0
  47. package/dist/operations/__tests__/agent-operation-contract.test.js +128 -0
  48. package/dist/operations/__tests__/agent-operation-contract.test.js.map +1 -0
  49. package/dist/operations/__tests__/tasks-input-contracts.test.js +15 -0
  50. package/dist/operations/__tests__/tasks-input-contracts.test.js.map +1 -1
  51. package/dist/operations/agent-operation-contract.d.ts +227 -0
  52. package/dist/operations/agent-operation-contract.d.ts.map +1 -0
  53. package/dist/operations/agent-operation-contract.js +38 -0
  54. package/dist/operations/agent-operation-contract.js.map +1 -0
  55. package/dist/operations/docs.d.ts +19 -1
  56. package/dist/operations/docs.d.ts.map +1 -1
  57. package/dist/operations/docs.js +20 -0
  58. package/dist/operations/docs.js.map +1 -1
  59. package/dist/operations/focus.d.ts +1 -1
  60. package/dist/operations/index.d.ts +1 -0
  61. package/dist/operations/index.d.ts.map +1 -1
  62. package/dist/operations/index.js +1 -0
  63. package/dist/operations/index.js.map +1 -1
  64. package/dist/operations/orchestrate.d.ts +66 -0
  65. package/dist/operations/orchestrate.d.ts.map +1 -1
  66. package/dist/operations/tasks.d.ts +568 -11
  67. package/dist/operations/tasks.d.ts.map +1 -1
  68. package/dist/operations/tasks.js +22 -3
  69. package/dist/operations/tasks.js.map +1 -1
  70. package/dist/task-record.d.ts +15 -0
  71. package/dist/task-record.d.ts.map +1 -1
  72. package/dist/task.d.ts +15 -6
  73. package/dist/task.d.ts.map +1 -1
  74. package/dist/task.js.map +1 -1
  75. package/dist/tasks.d.ts +944 -0
  76. package/dist/tasks.d.ts.map +1 -1
  77. package/dist/tasks.js +198 -1
  78. package/dist/tasks.js.map +1 -1
  79. package/dist/workgraph.d.ts +1950 -0
  80. package/dist/workgraph.d.ts.map +1 -0
  81. package/dist/workgraph.js +449 -0
  82. package/dist/workgraph.js.map +1 -0
  83. package/package.json +10 -2
  84. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  85. package/src/__tests__/completion-operation-contracts.test.ts +154 -0
  86. package/src/__tests__/operation-envelope-validation.test.ts +140 -0
  87. package/src/__tests__/satisfies-atom.test.ts +23 -3
  88. package/src/__tests__/workgraph-context-scaffold-contracts.test.ts +232 -0
  89. package/src/__tests__/workgraph-operation-contracts.test.ts +151 -0
  90. package/src/data-accessor.ts +94 -1
  91. package/src/dispatch/__tests__/docs-update-registry.test.ts +35 -0
  92. package/src/dispatch/__tests__/operation-envelope-snapshots.test.ts +289 -0
  93. package/src/dispatch/operations-registry.ts +502 -7
  94. package/src/evidence-atom-schema.ts +10 -10
  95. package/src/index.ts +193 -2
  96. package/src/operation-envelope-validation.ts +319 -0
  97. package/src/operations/__tests__/agent-operation-contract.test.ts +161 -0
  98. package/src/operations/__tests__/tasks-input-contracts.test.ts +30 -0
  99. package/src/operations/agent-operation-contract.ts +253 -0
  100. package/src/operations/docs.ts +27 -7
  101. package/src/operations/focus.ts +1 -1
  102. package/src/operations/index.ts +1 -0
  103. package/src/operations/orchestrate.ts +71 -0
  104. package/src/operations/tasks.ts +612 -15
  105. package/src/task-record.ts +16 -0
  106. package/src/task.ts +16 -6
  107. package/src/tasks.ts +558 -0
  108. package/src/workgraph.ts +1153 -0
package/src/index.ts CHANGED
@@ -199,7 +199,7 @@ export {
199
199
  } from './branch-lock.js';
200
200
  export type { AdapterCapabilities } from './capabilities.js';
201
201
  // === Changesets (CLEO-native task-anchored DSL — T9738) ===
202
- export type { ChangesetEntry, ChangesetKind } from './changesets.js';
202
+ export type { ChangesetEntry, ChangesetKind, ChangesetReleaseNoteSection } from './changesets.js';
203
203
  export { CHANGESET_KINDS, ChangesetEntrySchema } from './changesets.js';
204
204
  // === CLI Category Types (help renderer grouping SSoT) ===
205
205
  export type { CliCategory } from './cli-category.js';
@@ -291,12 +291,18 @@ export { parseClaudeCodeCredentials } from './credentials.js';
291
291
  // === DataAccessor Interface ===
292
292
  export type {
293
293
  AcBindingRow,
294
+ AcProjectionAuditFinding,
295
+ AcProjectionAuditFindingCode,
296
+ AcProjectionAuditResult,
297
+ AcProjectionAuditStatus,
294
298
  AcRow,
295
299
  ArchiveFields,
296
300
  ArchiveFile,
297
301
  DataAccessor,
298
302
  DataAccessorAgentInstance,
299
303
  QueryTasksResult,
304
+ TaskAuditLogQuery,
305
+ TaskAuditLogRow,
300
306
  TaskFieldUpdates,
301
307
  TaskQueryFilters,
302
308
  TransactionAccessor,
@@ -841,6 +847,20 @@ export type {
841
847
  WikiStateFile,
842
848
  WikiSymbolRow,
843
849
  } from './nexus-wiki-ops.js';
850
+ // === Operation-aware LAFS envelope validation (T10610) ===
851
+ export type {
852
+ OperationEnvelopeName,
853
+ OperationEnvelopeValidationIssue,
854
+ OperationEnvelopeValidationOptions,
855
+ OperationEnvelopeValidationResult,
856
+ } from './operation-envelope-validation.js';
857
+ export {
858
+ E_LAFS_OPERATION_ERROR_SHAPE,
859
+ E_LAFS_OPERATION_RESULT_SCHEMA,
860
+ E_LAFS_OPERATION_UNREGISTERED,
861
+ OPERATION_RESULT_SCHEMAS,
862
+ validateOperationEnvelope,
863
+ } from './operation-envelope-validation.js';
844
864
  // Admin operation param/result types — re-exported at top level for typed-dispatch consumers
845
865
  // (T1426 Wave D · typed-narrowing migration for admin domain)
846
866
  export type {
@@ -926,6 +946,14 @@ export type {
926
946
  EvaluateDialecticParams,
927
947
  EvaluateDialecticResult,
928
948
  } from './operations/dialectic.js';
949
+ // === Docs Operation Types (T10618 — docs.update lifecycle status SSoT) ===
950
+ // Re-exported at top level so core and CLI layers can consume the same
951
+ // operation contract without relying on an unexported package subpath.
952
+ export {
953
+ DOCS_LIFECYCLE_STATUSES,
954
+ type DocsLifecycleStatus,
955
+ type DocsUpdateParams,
956
+ } from './operations/docs.js';
929
957
  // === Operations Types (API wire format, namespaced to avoid collision with domain types) ===
930
958
  export * as ops from './operations/index.js';
931
959
  // === Operation Input Contracts (T9914 / Saga T9855 / E7) ===
@@ -1203,7 +1231,12 @@ export type {
1203
1231
  UserProfileTrait,
1204
1232
  } from './operations/nexus-user-profile.js';
1205
1233
  // Commonly used ops types re-exported at top level for convenience
1206
- export type { BrainState } from './operations/orchestrate.js';
1234
+ export type {
1235
+ BrainState,
1236
+ OrchestrateReportEntry,
1237
+ OrchestrateReportGroup,
1238
+ OrchestrateReportParams,
1239
+ } from './operations/orchestrate.js';
1207
1240
  // ParamDef contract — re-exported at top level (SSoT for all operation param descriptors)
1208
1241
  export type {
1209
1242
  CittyArgDef,
@@ -1288,6 +1321,7 @@ export type {
1288
1321
  DepsTreeNode,
1289
1322
  TaskShowAcRowEntry,
1290
1323
  TaskShowAttachmentEntry,
1324
+ TaskShowRelationsEntry,
1291
1325
  TasksAddBatchEntry,
1292
1326
  TasksAddBatchParams,
1293
1327
  TasksAddBatchResult,
@@ -1307,6 +1341,15 @@ export type {
1307
1341
  TasksCompleteQueryResult,
1308
1342
  TasksComplexityEstimateParams,
1309
1343
  TasksComplexityEstimateResult,
1344
+ // T10629 — task context pack types
1345
+ TasksContextAcceptanceEntry,
1346
+ TasksContextActivityEvent,
1347
+ TasksContextBlockerEntry,
1348
+ TasksContextBudget,
1349
+ TasksContextDocEntry,
1350
+ TasksContextOmission,
1351
+ TasksContextParams,
1352
+ TasksContextResult,
1310
1353
  TasksCurrentParams,
1311
1354
  TasksCurrentResult,
1312
1355
  TasksDeleteQueryParams,
@@ -1333,6 +1376,9 @@ export type {
1333
1376
  TasksOps,
1334
1377
  TasksPlanParams,
1335
1378
  TasksPlanResult,
1379
+ TasksRelatesAddBatchEntry,
1380
+ TasksRelatesAddBatchParams,
1381
+ TasksRelatesAddBatchResult,
1336
1382
  TasksRelatesAddParams,
1337
1383
  TasksRelatesAddResult,
1338
1384
  TasksRelatesParams,
@@ -1357,8 +1403,14 @@ export type {
1357
1403
  TasksSagaMembersResult,
1358
1404
  TasksSagaRollupParams,
1359
1405
  TasksSagaRollupResult,
1406
+ TasksScopeMember,
1407
+ TasksScopeReadyEntry,
1408
+ TasksScopeRollup,
1360
1409
  TasksShowParams,
1361
1410
  TasksShowResult,
1411
+ TasksSliceNode,
1412
+ TasksSliceParams,
1413
+ TasksSliceResult,
1362
1414
  TasksStartQueryParams,
1363
1415
  TasksStartQueryResult,
1364
1416
  TasksStopQueryParams,
@@ -1893,6 +1945,7 @@ export type {
1893
1945
  MinimalTaskRecord,
1894
1946
  TaskRecord,
1895
1947
  TaskRecordRelation,
1948
+ TaskRecordRelationCounts,
1896
1949
  ValidationHistoryEntry,
1897
1950
  } from './task-record.js';
1898
1951
  // === Task Sync Types (provider-agnostic reconciliation) ===
@@ -1923,10 +1976,40 @@ export {
1923
1976
  } from './tasks/archive.js';
1924
1977
  // === Task Domain Result Types (T1703 — canonical shapes for operations/tasks.ts stubs) ===
1925
1978
  export type {
1979
+ CompletionBlockerReason,
1980
+ CompletionContextPack,
1981
+ CompletionContextPackOptions,
1982
+ CompletionContextPackSummary,
1983
+ CompletionCriterionEvaluation,
1984
+ CompletionCriterionKind,
1985
+ CompletionCriterionReplacement,
1986
+ CompletionCriterionStatus,
1987
+ CompletionCriterionWaiver,
1988
+ CompletionEvaluateParams,
1989
+ CompletionEvaluateResult,
1990
+ CompletionEvaluation,
1991
+ CompletionExplainParams,
1992
+ CompletionExplainResult,
1993
+ CompletionExplanation,
1994
+ CompletionHistoryEvent,
1995
+ CompletionHistoryEventAction,
1996
+ CompletionHistoryEventRelation,
1997
+ CompletionListParams,
1998
+ CompletionListResult,
1999
+ CompletionProjectionRepairError,
2000
+ CompletionProjectionRepairErrorCode,
2001
+ CompletionProjectionRepairParams,
2002
+ CompletionProjectionRepairResult,
2003
+ CompletionStaleReason,
1926
2004
  TaskComplexityFactor,
1927
2005
  TaskDependsRef,
1928
2006
  TaskDependsResult,
1929
2007
  TaskLabelInfo,
2008
+ TaskMutationBucket,
2009
+ TaskMutationDryRunSummary,
2010
+ TaskMutationEnvelope,
2011
+ TaskMutationWarning,
2012
+ TaskMutationWarningSeverity,
1930
2013
  TaskPlanBlockedTask,
1931
2014
  TaskPlanInProgressEpic,
1932
2015
  TaskPlanMetrics,
@@ -1941,6 +2024,19 @@ export type {
1941
2024
  TaskViewNextAction,
1942
2025
  TaskViewPipelineStage,
1943
2026
  } from './tasks.js';
2027
+ export {
2028
+ completionCriterionEvaluationSchema,
2029
+ completionEvaluateParamsSchema,
2030
+ completionEvaluationSchema,
2031
+ completionExplainParamsSchema,
2032
+ completionExplanationSchema,
2033
+ completionListParamsSchema,
2034
+ completionListResultSchema,
2035
+ completionProjectionRepairErrorSchema,
2036
+ completionProjectionRepairParamsSchema,
2037
+ completionProjectionRepairResultSchema,
2038
+ unsatisfiedCompletionCriterionSchema,
2039
+ } from './tasks.js';
1944
2040
  // === Template Manifest (T9875 / Saga T9855) ===
1945
2041
  export type {
1946
2042
  PlaceholderSource,
@@ -2046,4 +2142,99 @@ export type {
2046
2142
  WarpLink,
2047
2143
  WarpStage,
2048
2144
  } from './warp-chain.js';
2145
+ export type {
2146
+ TasksFrontierParamsInput,
2147
+ TasksRollupParamsInput,
2148
+ TasksTraverseParamsInput,
2149
+ TasksTreeParamsInput,
2150
+ TasksWorkGraphAuditParamsInput,
2151
+ WorkGraphAudienceMode,
2152
+ WorkGraphAuditOptions,
2153
+ WorkGraphAuditResult,
2154
+ WorkGraphContainmentAncestorsResult,
2155
+ WorkGraphContainmentChildrenResult,
2156
+ WorkGraphContainmentNode,
2157
+ WorkGraphContainmentQueryService,
2158
+ WorkGraphContextBudget,
2159
+ WorkGraphContextPack,
2160
+ WorkGraphContextPackParams,
2161
+ WorkGraphDependencyEdge,
2162
+ WorkGraphDirectEdge,
2163
+ WorkGraphEdge,
2164
+ WorkGraphEdgeDirection,
2165
+ WorkGraphEdgeSource,
2166
+ WorkGraphHierarchyInputNode,
2167
+ WorkGraphHierarchyValidationOptions,
2168
+ WorkGraphHierarchyValidationResult,
2169
+ WorkGraphHierarchyViolation,
2170
+ WorkGraphNode,
2171
+ WorkGraphNodeRef,
2172
+ WorkGraphOmission,
2173
+ WorkGraphOmissionReason,
2174
+ WorkGraphPageInfo,
2175
+ WorkGraphPaginationOptions,
2176
+ WorkGraphPercentDenominator,
2177
+ WorkGraphPlanningDoc,
2178
+ WorkGraphPlanningDocParams,
2179
+ WorkGraphProjectionMismatch,
2180
+ WorkGraphReader,
2181
+ WorkGraphReadinessParams,
2182
+ WorkGraphReadinessResult,
2183
+ WorkGraphReadyFrontierBlockedBy,
2184
+ WorkGraphReadyFrontierOptions,
2185
+ WorkGraphReadyFrontierResult,
2186
+ WorkGraphReadyFrontierTask,
2187
+ WorkGraphRelationEdge,
2188
+ WorkGraphRelationEdgesOptions,
2189
+ WorkGraphRelationEdgesResult,
2190
+ WorkGraphRelationKind,
2191
+ WorkGraphRelationQueryService,
2192
+ WorkGraphRollupCounts,
2193
+ WorkGraphScaffoldApplyParams,
2194
+ WorkGraphScaffoldApplyResult,
2195
+ WorkGraphScaffoldValidateParams,
2196
+ WorkGraphScaffoldValidateResult,
2197
+ WorkGraphScaffoldValidationIssue,
2198
+ WorkGraphSlice,
2199
+ WorkGraphSliceParams,
2200
+ WorkGraphSnapshot,
2201
+ WorkGraphSubtreePercentages,
2202
+ WorkGraphSubtreeSummaryOptions,
2203
+ WorkGraphSubtreeSummaryResult,
2204
+ WorkGraphTaskRelationType,
2205
+ WorkGraphTraversalDirection,
2206
+ WorkGraphTraversalOptions,
2207
+ WorkGraphTraversalResult,
2208
+ WorkGraphTreeNode,
2209
+ WorkGraphTreeOptions,
2210
+ WorkGraphTreeResult,
2211
+ } from './workgraph.js';
2212
+ // === PM-Core V2 WorkGraph public contracts ===
2213
+ export {
2214
+ E_WORKGRAPH_PARENT_TYPE_MATRIX,
2215
+ tasksFrontierParamsSchema,
2216
+ tasksFrontierResultSchema,
2217
+ tasksRollupParamsSchema,
2218
+ tasksRollupResultSchema,
2219
+ tasksTraverseParamsSchema,
2220
+ tasksTraverseResultSchema,
2221
+ tasksTreeParamsSchema,
2222
+ tasksTreeResultSchema,
2223
+ tasksWorkGraphAuditParamsSchema,
2224
+ tasksWorkGraphAuditResultSchema,
2225
+ validateWorkGraphHierarchy,
2226
+ WorkGraphHierarchyInvariantError,
2227
+ workGraphContextPackParamsSchema,
2228
+ workGraphContextPackSchema,
2229
+ workGraphPlanningDocParamsSchema,
2230
+ workGraphPlanningDocSchema,
2231
+ workGraphReadinessParamsSchema,
2232
+ workGraphReadinessResultSchema,
2233
+ workGraphScaffoldApplyParamsSchema,
2234
+ workGraphScaffoldApplyResultSchema,
2235
+ workGraphScaffoldValidateParamsSchema,
2236
+ workGraphScaffoldValidateResultSchema,
2237
+ workGraphSliceParamsSchema,
2238
+ workGraphSliceSchema,
2239
+ } from './workgraph.js';
2049
2240
  // === WASM SDK (Rust crate bindings) ===
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Operation-aware LAFS envelope validation.
3
+ *
4
+ * The base LAFS shape proves that an envelope is structurally valid. This
5
+ * module adds the dispatch-layer contract checks that require `_meta.operation`
6
+ * to resolve to a registered CLEO operation and, when a result schema is known,
7
+ * validates successful payloads against that operation's result contract.
8
+ *
9
+ * @task T10610
10
+ * @saga T10538
11
+ */
12
+
13
+ import type { OperationDef } from './dispatch/operation-def.js';
14
+ import { OPERATIONS } from './dispatch/operations-registry.js';
15
+ import type { LAFSEnvelope, LAFSErrorCategory, LAFSTransport, MVILevel } from './lafs.js';
16
+ import {
17
+ tasksFrontierResultSchema,
18
+ tasksRollupResultSchema,
19
+ tasksTraverseResultSchema,
20
+ tasksTreeResultSchema,
21
+ tasksWorkGraphAuditResultSchema,
22
+ } from './workgraph.js';
23
+
24
+ interface SafeParseIssue {
25
+ readonly path: readonly PropertyKey[];
26
+ readonly message: string;
27
+ readonly code?: string;
28
+ }
29
+
30
+ interface SafeParseError {
31
+ readonly issues: readonly SafeParseIssue[];
32
+ }
33
+
34
+ interface ResultSchema {
35
+ safeParse(
36
+ input: unknown,
37
+ ): { success: true; data: unknown } | { success: false; error: SafeParseError };
38
+ }
39
+
40
+ /** Stable issue code emitted when `_meta.operation` is absent or unknown. */
41
+ export const E_LAFS_OPERATION_UNREGISTERED = 'E_LAFS_OPERATION_UNREGISTERED';
42
+
43
+ /** Stable issue code emitted when an operation result fails its contract schema. */
44
+ export const E_LAFS_OPERATION_RESULT_SCHEMA = 'E_LAFS_OPERATION_RESULT_SCHEMA';
45
+
46
+ /** Stable issue code emitted when success/error envelope invariants are violated. */
47
+ export const E_LAFS_OPERATION_ERROR_SHAPE = 'E_LAFS_OPERATION_ERROR_SHAPE';
48
+
49
+ /** Dot-delimited operation name, e.g. `tasks.frontier`. */
50
+ export type OperationEnvelopeName = `${string}.${string}`;
51
+
52
+ /** One issue produced by operation-aware envelope validation. */
53
+ export interface OperationEnvelopeValidationIssue {
54
+ /** JSON Pointer path into the envelope. */
55
+ readonly path: string;
56
+ /** Stable machine-readable issue code. */
57
+ readonly code: string;
58
+ /** Human-readable validation failure. */
59
+ readonly message: string;
60
+ /** Optional underlying schema keyword or parser code. */
61
+ readonly keyword?: string;
62
+ }
63
+
64
+ /** Result of validating a LAFS envelope against the dispatch operation registry. */
65
+ export interface OperationEnvelopeValidationResult {
66
+ /** True only when base LAFS shape, registry lookup, and result schema checks pass. */
67
+ readonly valid: boolean;
68
+ /** Base LAFS structural verdict. */
69
+ readonly envelopeValid: boolean;
70
+ /** Resolved operation definition, if `_meta.operation` matched the registry. */
71
+ readonly operation?: OperationDef;
72
+ /** Result schema key that was enforced, when available. */
73
+ readonly resultSchemaOperation?: OperationEnvelopeName;
74
+ /** Structured issue list, including base LAFS and operation-aware issues. */
75
+ readonly issues: readonly OperationEnvelopeValidationIssue[];
76
+ /** Human-readable issue messages for CLI/reporting callers. */
77
+ readonly errors: readonly string[];
78
+ }
79
+
80
+ /** Options for operation-aware LAFS validation. */
81
+ export interface OperationEnvelopeValidationOptions {
82
+ /** Registry to resolve `_meta.operation` against. Defaults to canonical OPERATIONS. */
83
+ readonly operations?: readonly OperationDef[];
84
+ /** Per-operation result schemas. Defaults to the currently contracted WorkGraph reads. */
85
+ readonly resultSchemas?: ReadonlyMap<OperationEnvelopeName, ResultSchema>;
86
+ }
87
+
88
+ /** Canonical result schemas currently promoted to operation-aware LAFS validation. */
89
+ export const OPERATION_RESULT_SCHEMAS: ReadonlyMap<OperationEnvelopeName, ResultSchema> = new Map<
90
+ OperationEnvelopeName,
91
+ ResultSchema
92
+ >([
93
+ ['tasks.traverse', tasksTraverseResultSchema as ResultSchema],
94
+ ['tasks.tree', tasksTreeResultSchema as ResultSchema],
95
+ ['tasks.rollup', tasksRollupResultSchema as ResultSchema],
96
+ ['tasks.frontier', tasksFrontierResultSchema as ResultSchema],
97
+ ['tasks.workgraph.audit', tasksWorkGraphAuditResultSchema as ResultSchema],
98
+ ]);
99
+
100
+ const LAFS_SCHEMA_URL = 'https://lafs.dev/schemas/v1/envelope.schema.json';
101
+ const LAFS_TRANSPORTS: ReadonlySet<LAFSTransport> = new Set(['cli', 'http', 'grpc', 'sdk']);
102
+ const LAFS_MVI_LEVELS: ReadonlySet<MVILevel> = new Set(['minimal', 'standard', 'full', 'custom']);
103
+ const LAFS_ERROR_CATEGORIES: ReadonlySet<LAFSErrorCategory> = new Set([
104
+ 'VALIDATION',
105
+ 'AUTH',
106
+ 'PERMISSION',
107
+ 'NOT_FOUND',
108
+ 'CONFLICT',
109
+ 'RATE_LIMIT',
110
+ 'TRANSIENT',
111
+ 'INTERNAL',
112
+ 'CONTRACT',
113
+ 'MIGRATION',
114
+ ]);
115
+
116
+ function issue(path: string, message: string, keyword = 'type'): OperationEnvelopeValidationIssue {
117
+ return { path, code: `LAFS_SCHEMA_${keyword.toUpperCase()}`, keyword, message };
118
+ }
119
+
120
+ function isRecord(input: unknown): input is Record<string, unknown> {
121
+ return typeof input === 'object' && input !== null && !Array.isArray(input);
122
+ }
123
+
124
+ function validateBaseLafsEnvelope(input: unknown): OperationEnvelopeValidationIssue[] {
125
+ if (!isRecord(input)) return [issue('/', 'LAFS envelope must be an object')];
126
+
127
+ const issues: OperationEnvelopeValidationIssue[] = [];
128
+ if (input.$schema !== LAFS_SCHEMA_URL) {
129
+ issues.push(issue('/$schema', `must equal ${LAFS_SCHEMA_URL}`, 'const'));
130
+ }
131
+
132
+ if (!isRecord(input._meta)) {
133
+ issues.push(issue('/_meta', 'must be an object'));
134
+ } else {
135
+ const meta = input._meta;
136
+ for (const field of ['specVersion', 'schemaVersion', 'timestamp', 'operation', 'requestId']) {
137
+ if (typeof meta[field] !== 'string' || meta[field].length === 0) {
138
+ issues.push(issue(`/_meta/${field}`, 'must be a non-empty string'));
139
+ }
140
+ }
141
+ if (
142
+ typeof meta.transport !== 'string' ||
143
+ !LAFS_TRANSPORTS.has(meta.transport as LAFSTransport)
144
+ ) {
145
+ issues.push(issue('/_meta/transport', 'must be a valid LAFS transport', 'enum'));
146
+ }
147
+ if (typeof meta.strict !== 'boolean') {
148
+ issues.push(issue('/_meta/strict', 'must be a boolean'));
149
+ }
150
+ if (typeof meta.mvi !== 'string' || !LAFS_MVI_LEVELS.has(meta.mvi as MVILevel)) {
151
+ issues.push(issue('/_meta/mvi', 'must be a valid LAFS MVI level', 'enum'));
152
+ }
153
+ if (!Number.isInteger(meta.contextVersion) || Number(meta.contextVersion) < 0) {
154
+ issues.push(issue('/_meta/contextVersion', 'must be a non-negative integer'));
155
+ }
156
+ }
157
+
158
+ if (typeof input.success !== 'boolean') {
159
+ issues.push(issue('/success', 'must be a boolean'));
160
+ }
161
+ if (!('result' in input)) {
162
+ issues.push(issue('/result', 'is required', 'required'));
163
+ } else if (input.result !== null && !isRecord(input.result) && !Array.isArray(input.result)) {
164
+ issues.push(issue('/result', 'must be an object, array, or null'));
165
+ }
166
+
167
+ if (input.error !== null && input.error !== undefined) {
168
+ if (!isRecord(input.error)) {
169
+ issues.push(issue('/error', 'must be an object or null'));
170
+ } else {
171
+ const error = input.error;
172
+ if (typeof error.code !== 'string' || error.code.length === 0) {
173
+ issues.push(issue('/error/code', 'must be a non-empty string'));
174
+ }
175
+ if (typeof error.message !== 'string' || error.message.length === 0) {
176
+ issues.push(issue('/error/message', 'must be a non-empty string'));
177
+ }
178
+ if (
179
+ typeof error.category !== 'string' ||
180
+ !LAFS_ERROR_CATEGORIES.has(error.category as LAFSErrorCategory)
181
+ ) {
182
+ issues.push(issue('/error/category', 'must be a valid LAFS error category', 'enum'));
183
+ }
184
+ if (typeof error.retryable !== 'boolean') {
185
+ issues.push(issue('/error/retryable', 'must be a boolean'));
186
+ }
187
+ if (
188
+ error.retryAfterMs !== null &&
189
+ error.retryAfterMs !== undefined &&
190
+ (!Number.isInteger(error.retryAfterMs) || Number(error.retryAfterMs) < 0)
191
+ ) {
192
+ issues.push(issue('/error/retryAfterMs', 'must be a non-negative integer or null'));
193
+ }
194
+ if (!isRecord(error.details)) {
195
+ issues.push(issue('/error/details', 'must be an object'));
196
+ }
197
+ }
198
+ }
199
+
200
+ return issues;
201
+ }
202
+
203
+ function pointer(path: readonly PropertyKey[]): string {
204
+ if (path.length === 0) return '/result';
205
+ return `/result/${path
206
+ .map((segment) => String(segment).split('~').join('~0').split('/').join('~1'))
207
+ .join('/')}`;
208
+ }
209
+
210
+ function resolveOperation(
211
+ operationName: string,
212
+ operations: readonly OperationDef[],
213
+ ): OperationDef | undefined {
214
+ return operations.find(
215
+ (candidate) => `${candidate.domain}.${candidate.operation}` === operationName,
216
+ );
217
+ }
218
+
219
+ function hasCanonicalSuccessShape(envelope: LAFSEnvelope): OperationEnvelopeValidationIssue[] {
220
+ if (envelope.success) {
221
+ if (envelope.result === null) {
222
+ return [
223
+ {
224
+ path: '/result',
225
+ code: E_LAFS_OPERATION_ERROR_SHAPE,
226
+ message: 'successful LAFS envelopes must include a non-null result payload',
227
+ },
228
+ ];
229
+ }
230
+ if (envelope.error !== null && envelope.error !== undefined) {
231
+ return [
232
+ {
233
+ path: '/error',
234
+ code: E_LAFS_OPERATION_ERROR_SHAPE,
235
+ message: 'successful LAFS envelopes must not include an error payload',
236
+ },
237
+ ];
238
+ }
239
+ return [];
240
+ }
241
+
242
+ const issues: OperationEnvelopeValidationIssue[] = [];
243
+ if (envelope.error === null || envelope.error === undefined) {
244
+ issues.push({
245
+ path: '/error',
246
+ code: E_LAFS_OPERATION_ERROR_SHAPE,
247
+ message: 'failed LAFS envelopes must include the canonical error payload',
248
+ });
249
+ }
250
+ if (envelope.result !== null) {
251
+ issues.push({
252
+ path: '/result',
253
+ code: E_LAFS_OPERATION_ERROR_SHAPE,
254
+ message: 'failed LAFS envelopes must set result to null',
255
+ });
256
+ }
257
+ return issues;
258
+ }
259
+
260
+ /**
261
+ * Validate a LAFS envelope against the dispatch operation registry and known
262
+ * operation result contracts.
263
+ */
264
+ export function validateOperationEnvelope(
265
+ input: unknown,
266
+ options: OperationEnvelopeValidationOptions = {},
267
+ ): OperationEnvelopeValidationResult {
268
+ const issues: OperationEnvelopeValidationIssue[] = validateBaseLafsEnvelope(input);
269
+
270
+ if (issues.length > 0) {
271
+ return {
272
+ valid: false,
273
+ envelopeValid: false,
274
+ issues,
275
+ errors: issues.map((issue) => `${issue.path} ${issue.message}`.trim()),
276
+ };
277
+ }
278
+
279
+ const envelope = input as LAFSEnvelope;
280
+ const operationName = envelope._meta.operation;
281
+ const operations = options.operations ?? OPERATIONS;
282
+ const resultSchemas = options.resultSchemas ?? OPERATION_RESULT_SCHEMAS;
283
+ const operation = resolveOperation(operationName, operations);
284
+
285
+ if (operation === undefined) {
286
+ issues.push({
287
+ path: '/_meta/operation',
288
+ code: E_LAFS_OPERATION_UNREGISTERED,
289
+ message: `unknown LAFS operation '${operationName}'`,
290
+ });
291
+ }
292
+
293
+ issues.push(...hasCanonicalSuccessShape(envelope));
294
+
295
+ const schema = resultSchemas.get(operationName as OperationEnvelopeName);
296
+ if (envelope.success && schema !== undefined && envelope.result !== null) {
297
+ const parsed = schema.safeParse(envelope.result);
298
+ if (!parsed.success) {
299
+ for (const zodIssue of parsed.error.issues) {
300
+ issues.push({
301
+ path: pointer(zodIssue.path),
302
+ code: E_LAFS_OPERATION_RESULT_SCHEMA,
303
+ keyword: zodIssue.code,
304
+ message: zodIssue.message,
305
+ });
306
+ }
307
+ }
308
+ }
309
+
310
+ return {
311
+ valid: issues.length === 0,
312
+ envelopeValid: true,
313
+ operation,
314
+ resultSchemaOperation:
315
+ schema === undefined ? undefined : (operationName as OperationEnvelopeName),
316
+ issues,
317
+ errors: issues.map((issue) => `${issue.path} ${issue.message}`.trim()),
318
+ };
319
+ }