@adhd/dispatch-base-spec 0.0.1

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.
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export type { BlastRadiusEntry, CodeKind, Confidence, ConfigKind, CrossPlanDep, DagJson, DagSnapshot, DispatchKind, DispatchLogEntry, DispatchNote, DispatchResult, DispatchUnit, DispatchUnitStatus, EffortTier, GuardResult, IOptimizerDeps, KindFamily, KiSource, MilestoneDag, MilestoneGuardResult, MilestoneSnapshot, MilestoneStatus, ModelTier, OpenQuestion, OperationAction, OperationConflict, OperationDag, OperationSnapshot, OperationStatus, OperationType, OptimizationConfig, PairwiseOverlapMap, PlanKind, Provenance, ProviderConfig, ProviderType, RetryConfig, SentinelFanoutConfig, SentinelRole, Shape, ShapeCode, ShapeCodeSnapshot, ShapeConfig, ShapeConfigSnapshot, ShapeDoc, ShapeKind, ShapeNull, ShapeOpDag, ShapeOpSnapshot, ShapeOpType, ShapeSnapshot, ShapeStructuredOutput, SnapshotOptimization, Turn, ValidationError, ValidationResult, } from './lib/types.js';
2
+ export { isValidOpForKind, VALID_OPS_BY_KIND, WRITE_CLASS_ACTIONS, } from './lib/types.js';
3
+ export { assertValidDagJson, assertValidSnapshot, validateDagJson, validateSnapshot, } from './lib/validate.js';
4
+ export { migrateDag } from './lib/migrate.js';
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const D=new Set(["create","modify-signature","modify-body","add-export","remove-export","rename"]),x={function:new Set(["add-param","remove-param","rename-param","retype-param","change-param-optional","reorder-params","change-return"]),interface:new Set(["add-field","remove-field","rename-field","retype-field","change-field-optional","add-generic","remove-generic","constrain-generic","add-extends","remove-extends"]),type:new Set(["add-field","remove-field","retype-field"]),class:new Set(["add-field","remove-field","rename-field","retype-field","change-field-optional","add-param","add-generic","remove-generic","constrain-generic","add-extends","remove-extends"]),enum:new Set(["add-field","remove-field","rename-field"]),const:new Set(["add-var","remove-var","rename-var","change-default"]),config:new Set(["set-key","remove-key","rename-key","add-entry","remove-entry","update-entry","bump-version","update-checksum"]),env:new Set(["set-key","remove-key","rename-key"]),schema:new Set(["add-table","remove-table","add-column","remove-column","rename-column","retype-column","change-nullable","add-index","remove-index"]),manifest:new Set(["add-entry","remove-entry","bump-version"]),doc:new Set(["add-section","remove-section","rename-section","update-section"]),"structured-output":new Set([]),script:new Set(["add-param","remove-param","rename-param","retype-param","change-param-optional","reorder-params","change-return","add-field","remove-field","rename-field","retype-field","change-field-optional","add-generic","remove-generic","constrain-generic","add-extends","remove-extends","set-key","remove-key","rename-key","add-array-item","remove-array-item","add-var","remove-var","rename-var","change-default","add-section","remove-section","rename-section","update-section","add-table","remove-table","add-column","remove-column","rename-column","retype-column","change-nullable","add-index","remove-index","add-entry","remove-entry","update-entry","bump-version","update-checksum","add-export"])};function j(t,e){const i=x[t];return i?i.has(e):!1}const T=new Set(["pending","pending-surfaced","in_progress","complete","failed","skipped"]),q=new Set(["pending","in_progress","complete","failed","skipped"]),E=new Set(["automated","tool-call","generative"]),N=new Set(["function","interface","type","class","enum","const","script","config","env","schema","manifest","doc","structured-output"]);function f(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function d(t){return typeof t=="string"}function n(t,e,i){return{path:t,message:e,value:i}}function L(t){var l,y;const e=Object.keys(t),i=new Map,r=new Map;for(const s of e)i.set(s,0),r.set(s,[]);for(const s of e)for(const a of((l=t[s])==null?void 0:l.depends_on)??[])t[a]&&((y=r.get(a))==null||y.push(s),i.set(s,(i.get(s)??0)+1));const p=e.filter(s=>(i.get(s)??0)===0),c=new Set;for(;p.length>0;){const s=p.shift();if(s!==void 0){c.add(s);for(const a of r.get(s)??[]){const u=(i.get(a)??1)-1;i.set(a,u),u===0&&p.push(a)}}}return c.size!==e.length?e.filter(s=>!c.has(s)).map(s=>({slug:s})):null}function R(t,e){for(const i of t){if(!f(i))continue;const r=d(i.id)?i.id:"?",p=i.turns;if(!Array.isArray(p)){e.push(n(`dispatch_log[${r}].turns`,"must be an array",p));continue}for(let c=0;c<p.length;c++){const l=p[c],y=`dispatch_log[${r}].turns[${c}]`;if(!f(l)){e.push(n(y,"must be an object",l));continue}(typeof l.turn!="number"||!Number.isInteger(l.turn)||l.turn<1)&&e.push(n(`${y}.turn`,"must be a positive integer",l.turn)),typeof l.input_tokens!="number"&&e.push(n(`${y}.input_tokens`,"must be a number",l.input_tokens)),typeof l.output_tokens!="number"&&e.push(n(`${y}.output_tokens`,"must be a number",l.output_tokens)),d(l.t)||e.push(n(`${y}.t`,"must be a string",l.t));const s=l.model_calls;s!=null&&typeof s!="number"&&e.push(n(`${y}.model_calls`,"must be a number or null if present",s))}}}function O(t){const e=[];if(!f(t))return{valid:!1,errors:[n("","root must be an object")]};for(const c of["description","problem","approach","executor"]){const l=t[c];(!d(l)||l.trim()==="")&&e.push(n(c,"required string field missing/empty",l))}typeof t.schema_version!="number"&&e.push(n("schema_version","must be a number",t.schema_version));const i=t.plan_kind;i!=="brownfield"&&i!=="greenfield"&&e.push(n("plan_kind",'must be "brownfield" or "greenfield"',i)),Array.isArray(t.phases)?t.phases.length===0&&e.push(n("phases","must have at least one entry")):e.push(n("phases","must be an array",t.phases));const r=t.terminal;!d(r)&&!Array.isArray(r)&&e.push(n("terminal","must be a string or array",r)),Array.isArray(t.dispatch_log)?R(t.dispatch_log,e):e.push(n("dispatch_log","must be an array",t.dispatch_log)),f(t.optimization)||e.push(n("optimization","must be an object",t.optimization)),f(t.providers)||e.push(n("providers","must be an object",t.providers)),f(t.effort_max_tokens)||e.push(n("effort_max_tokens","must be an object",t.effort_max_tokens));const p=t.milestones;if(!f(p))e.push(n("milestones","must be an object",p));else{const c=Object.keys(p);c.length===0&&e.push(n("milestones","must have at least one entry"));const l=new Set(c);for(const u of c){const o=p[u];if(!f(o)){e.push(n(`milestones.${u}`,"must be an object",o));continue}if((!d(o.description)||o.description.trim()==="")&&e.push(n(`milestones.${u}.description`,"required string",o.description)),!Array.isArray(o.depends_on))e.push(n(`milestones.${u}.depends_on`,"must be an array",o.depends_on));else for(const _ of o.depends_on)d(_)?l.has(_)||e.push(n(`milestones.${u}.depends_on`,`unknown milestone "${_}"`)):e.push(n(`milestones.${u}.depends_on[]`,"must be a string",_));const b=o.phase;(!d(b)||b.trim()==="")&&e.push(n(`milestones.${u}.phase`,"required string",b)),(!d(o.authored_by)||o.authored_by.trim()==="")&&e.push(n(`milestones.${u}.authored_by`,"required string",o.authored_by));const m=o.model;m!=null&&!["Haiku","Sonnet","Opus"].includes(m)&&e.push(n(`milestones.${u}.model`,"invalid model tier",m));const h=o.effort;h!=null&&!["low","medium","high","xhigh","max"].includes(h)&&e.push(n(`milestones.${u}.effort`,"invalid effort tier",h)),o.two_stage!==void 0&&typeof o.two_stage!="boolean"&&e.push(n(`milestones.${u}.two_stage`,"must be a boolean",o.two_stage)),Array.isArray(o.read_only)||e.push(n(`milestones.${u}.read_only`,"must be an array",o.read_only))}const y=L(p);if(y)for(const{slug:u}of y)e.push(n(`milestones.${u}`,"dependency cycle detected"));if(d(r)&&!l.has(r))e.push(n("terminal",`unknown milestone "${r}"`));else if(Array.isArray(r))for(const u of r)l.has(u)||e.push(n("terminal[]",`unknown milestone "${u}"`));const s=t.operations,a=new Set;if(!Array.isArray(s)&&!f(s))e.push(n("operations","must be an array or object",s));else{const u=Array.isArray(s)?s:Object.values(s);for(const o of u){if(!f(o)){e.push(n("operations[]","must be an object",o));continue}const b=o.id;if(!d(b)){e.push(n("operations[?]","missing/invalid id",b));continue}const m=b;a.add(m);const h=o.milestone;d(h)?l.has(h)||e.push(n(`operations[${m}].milestone`,`unknown milestone "${h}"`)):e.push(n(`operations[${m}].milestone`,"required string",h));const _=o.type;d(_)&&!E.has(_)&&e.push(n(`operations[${m}].type`,"invalid type",_));const v=o.shape;if(f(v)){const g=v.kind;g!=null&&d(g)&&!N.has(g)&&e.push(n(`operations[${m}].shape.kind`,"invalid shape kind",g));const $=v.ops;if(Array.isArray($)&&d(g))for(let w=0;w<$.length;w++){const k=$[w];if(!f(k))continue;const A=k.op;d(A)&&!j(g,A)&&e.push(n(`operations[${m}].shape.ops[${w}]`,`op "${A}" not valid for kind "${g}"`))}}Array.isArray(o.depends_on)||e.push(n(`operations[${m}].depends_on`,"must be an array",o.depends_on))}for(const o of u){if(!f(o))continue;const b=o.id;if(!d(b))continue;const m=o.depends_on;if(Array.isArray(m))for(const h of m)d(h)&&!a.has(h)&&e.push(n(`operations[${b}].depends_on`,`unknown operation "${h}"`))}}}return{valid:e.length===0,errors:e}}function I(t){const e=[];if(!f(t))return{valid:!1,errors:[n("","root must be an object")]};for(const r of["snapshot_at","description"])d(t[r])||e.push(n(r,"required string field missing",t[r]));d(t.plan)||e.push(n("plan","required string",t.plan));const i=t.milestones;if(!f(i))e.push(n("milestones","must be an object",i));else{const r=Object.keys(i),p=new Set(r);for(const s of r){const a=i[s];if(!f(a)){e.push(n(`milestones.${s}`,"must be an object",a));continue}const u=a.eligible,o=a.pending;u===!0&&o!==null&&o!==void 0&&e.push(n(`milestones.${s}`,"D-07 violation: eligible=true but pending not null")),typeof u!="boolean"&&e.push(n(`milestones.${s}.eligible`,"must be boolean",u));const b=a.status;(!d(b)||!T.has(b))&&e.push(n(`milestones.${s}.status`,"invalid status",b));const m=a.wave;(typeof m!="number"||!Number.isInteger(m)||m<0)&&e.push(n(`milestones.${s}.wave`,"must be non-negative integer",m));const h=a.ki_estimate;h!=null&&typeof h!="number"&&e.push(n(`milestones.${s}.ki_estimate`,"must be number|null",h));for(const _ of["tokens_estimated","tokens_actual"]){const v=a[_];v!=null&&typeof v!="number"&&e.push(n(`milestones.${s}.${_}`,"must be number|null",v))}Array.isArray(a.artifacts)||e.push(n(`milestones.${s}.artifacts`,"must be an array",a.artifacts)),typeof a.si_bytes!="number"&&e.push(n(`milestones.${s}.si_bytes`,"must be a number",a.si_bytes))}const c=t.operations;if(!Array.isArray(c))e.push(n("operations","must be an array",c));else for(const s of c){if(!f(s))continue;const a=d(s.id)?s.id:"?",u=s.milestone;d(u)&&!p.has(u)&&e.push(n(`operations[${a}].milestone`,`unknown milestone "${u}"`));const o=s.status;(!d(o)||!q.has(o))&&e.push(n(`operations[${a}].status`,"invalid operation status",o)),Array.isArray(s.dispatch_ids)||e.push(n(`operations[${a}].dispatch_ids`,"must be an array",s.dispatch_ids)),typeof s.attempt_count!="number"&&e.push(n(`operations[${a}].attempt_count`,"must be a number",s.attempt_count)),Array.isArray(s.blast_radius)||e.push(n(`operations[${a}].blast_radius`,"must be an array",s.blast_radius))}const l=t.open_questions;if(!Array.isArray(l))e.push(n("open_questions","must be an array",l));else for(const s of l){if(!f(s))continue;const a=s.blocking;(!d(a)||!p.has(a))&&e.push(n("open_questions",`unknown blocking milestone "${a}"`)),typeof s.surfaced!="boolean"&&e.push(n(`open_questions[${s.id}]`,"surfaced must be boolean",s.surfaced)),typeof s.answered!="boolean"&&e.push(n(`open_questions[${s.id}]`,"answered must be boolean",s.answered))}const y=t.dispatch_units;if(y!==void 0)if(!Array.isArray(y))e.push(n("dispatch_units","must be an array if present",y));else for(const s of y){if(!f(s))continue;const a=s.fits_context_window;typeof a!="boolean"&&e.push(n(`dispatch_units[${s.id}]`,"fits_context_window must be boolean",a));const u=s.milestones;if(Array.isArray(u))for(const o of u)d(o)&&!p.has(o)&&e.push(n(`dispatch_units[${s.id}].milestones`,`unknown milestone "${o}"`))}f(t.pairwise_overlap)||e.push(n("pairwise_overlap","must be an object",t.pairwise_overlap))}return{valid:e.length===0,errors:e}}function K(t){const e=O(t);if(!e.valid)throw new Error(`DagJson validation failed:
2
+ ${e.errors.map(i=>` ${i.path}: ${i.message}`).join(`
3
+ `)}`)}function P(t){const e=I(t);if(!e.valid)throw new Error(`Snapshot validation failed:
4
+ ${e.errors.map(i=>` ${i.path}: ${i.message}`).join(`
5
+ `)}`)}const S=[{from:2,to:3,apply:t=>{!t.milestones&&t.nodes&&(t.milestones=t.nodes,delete t.nodes);const e=t.operations;e&&typeof e=="object"&&!Array.isArray(e)&&(t.operations=Object.values(e))}},{from:3,to:4,apply:t=>{(!t.providers||Object.keys(t.providers).length===0)&&(t.providers={Haiku:{type:"claudecli",model_id:"claude-haiku-4-5",env_secret:null,base_url:null,timeout_ms:6e4,retry_config:{retries:3,min_timeout:1e3,max_timeout:3e4,factor:2}},Sonnet:{type:"claudecli",model_id:"claude-sonnet-4-5",env_secret:null,base_url:null,timeout_ms:12e4,retry_config:{retries:3,min_timeout:1e3,max_timeout:3e4,factor:2}},Opus:{type:"claudecli",model_id:"claude-opus-4-5",env_secret:null,base_url:null,timeout_ms:3e5,retry_config:{retries:3,min_timeout:1e3,max_timeout:3e4,factor:2}}}),(!t.effort_max_tokens||Object.keys(t.effort_max_tokens).length===0)&&(t.effort_max_tokens={low:1024,medium:4096,high:8192,xhigh:16384,max:32768}),t.optimization||(t.optimization={sentinel_fanout:{enabled:!0,write_multiplier:1.25,read_multiplier:.1,hit_probability:.9},b_per_tier:{Haiku:8e3,Sonnet:15e3,Opus:27e3},context_window_per_tier:{Haiku:16e3,Sonnet:16e3,Opus:32e3},context_window_override:null,b_override:null});let e=[];Array.isArray(t.operations)?e=t.operations:t.operations&&typeof t.operations=="object"&&(e=Object.values(t.operations));for(const i of e)if(typeof i=="object"&&i!==null){const r=i;r.type===void 0&&(r.type="generative"),r.criteria===void 0&&(r.criteria=[]),r.tool===void 0&&(r.tool=null),r.args===void 0&&(r.args=null)}}}];function V(t,e,i){if(t===e)return;let r=t;for(const p of[...S].sort((c,l)=>c.from-l.from))if(p.from===r&&(p.apply(i),r=p.to,r===e)){i.schema_version=e;return}throw new Error(`migrateDag: no path from v${t} to v${e}. Available: [${S.map(p=>`v${p.from}→v${p.to}`).join(", ")}]`)}exports.VALID_OPS_BY_KIND=x;exports.WRITE_CLASS_ACTIONS=D;exports.assertValidDagJson=K;exports.assertValidSnapshot=P;exports.isValidOpForKind=j;exports.migrateDag=V;exports.validateDagJson=O;exports.validateSnapshot=I;
package/index.mjs ADDED
@@ -0,0 +1,667 @@
1
+ const R = /* @__PURE__ */ new Set([
2
+ "create",
3
+ "modify-signature",
4
+ "modify-body",
5
+ "add-export",
6
+ "remove-export",
7
+ "rename"
8
+ ]), x = {
9
+ function: /* @__PURE__ */ new Set([
10
+ "add-param",
11
+ "remove-param",
12
+ "rename-param",
13
+ "retype-param",
14
+ "change-param-optional",
15
+ "reorder-params",
16
+ "change-return"
17
+ ]),
18
+ interface: /* @__PURE__ */ new Set([
19
+ "add-field",
20
+ "remove-field",
21
+ "rename-field",
22
+ "retype-field",
23
+ "change-field-optional",
24
+ "add-generic",
25
+ "remove-generic",
26
+ "constrain-generic",
27
+ "add-extends",
28
+ "remove-extends"
29
+ ]),
30
+ type: /* @__PURE__ */ new Set(["add-field", "remove-field", "retype-field"]),
31
+ class: /* @__PURE__ */ new Set([
32
+ "add-field",
33
+ "remove-field",
34
+ "rename-field",
35
+ "retype-field",
36
+ "change-field-optional",
37
+ "add-param",
38
+ "add-generic",
39
+ "remove-generic",
40
+ "constrain-generic",
41
+ "add-extends",
42
+ "remove-extends"
43
+ ]),
44
+ enum: /* @__PURE__ */ new Set(["add-field", "remove-field", "rename-field"]),
45
+ const: /* @__PURE__ */ new Set([
46
+ "add-var",
47
+ "remove-var",
48
+ "rename-var",
49
+ "change-default"
50
+ ]),
51
+ config: /* @__PURE__ */ new Set([
52
+ "set-key",
53
+ "remove-key",
54
+ "rename-key",
55
+ "add-entry",
56
+ "remove-entry",
57
+ "update-entry",
58
+ "bump-version",
59
+ "update-checksum"
60
+ ]),
61
+ env: /* @__PURE__ */ new Set(["set-key", "remove-key", "rename-key"]),
62
+ schema: /* @__PURE__ */ new Set([
63
+ "add-table",
64
+ "remove-table",
65
+ "add-column",
66
+ "remove-column",
67
+ "rename-column",
68
+ "retype-column",
69
+ "change-nullable",
70
+ "add-index",
71
+ "remove-index"
72
+ ]),
73
+ manifest: /* @__PURE__ */ new Set(["add-entry", "remove-entry", "bump-version"]),
74
+ doc: /* @__PURE__ */ new Set([
75
+ "add-section",
76
+ "remove-section",
77
+ "rename-section",
78
+ "update-section"
79
+ ]),
80
+ "structured-output": /* @__PURE__ */ new Set([]),
81
+ script: /* @__PURE__ */ new Set([
82
+ "add-param",
83
+ "remove-param",
84
+ "rename-param",
85
+ "retype-param",
86
+ "change-param-optional",
87
+ "reorder-params",
88
+ "change-return",
89
+ "add-field",
90
+ "remove-field",
91
+ "rename-field",
92
+ "retype-field",
93
+ "change-field-optional",
94
+ "add-generic",
95
+ "remove-generic",
96
+ "constrain-generic",
97
+ "add-extends",
98
+ "remove-extends",
99
+ "set-key",
100
+ "remove-key",
101
+ "rename-key",
102
+ "add-array-item",
103
+ "remove-array-item",
104
+ "add-var",
105
+ "remove-var",
106
+ "rename-var",
107
+ "change-default",
108
+ "add-section",
109
+ "remove-section",
110
+ "rename-section",
111
+ "update-section",
112
+ "add-table",
113
+ "remove-table",
114
+ "add-column",
115
+ "remove-column",
116
+ "rename-column",
117
+ "retype-column",
118
+ "change-nullable",
119
+ "add-index",
120
+ "remove-index",
121
+ "add-entry",
122
+ "remove-entry",
123
+ "update-entry",
124
+ "bump-version",
125
+ "update-checksum",
126
+ "add-export"
127
+ ])
128
+ };
129
+ function j(t, e) {
130
+ const i = x[t];
131
+ return i ? i.has(e) : !1;
132
+ }
133
+ const O = /* @__PURE__ */ new Set([
134
+ "pending",
135
+ "pending-surfaced",
136
+ "in_progress",
137
+ "complete",
138
+ "failed",
139
+ "skipped"
140
+ ]), I = /* @__PURE__ */ new Set([
141
+ "pending",
142
+ "in_progress",
143
+ "complete",
144
+ "failed",
145
+ "skipped"
146
+ ]), q = /* @__PURE__ */ new Set(["automated", "tool-call", "generative"]), D = /* @__PURE__ */ new Set([
147
+ "function",
148
+ "interface",
149
+ "type",
150
+ "class",
151
+ "enum",
152
+ "const",
153
+ "script",
154
+ "config",
155
+ "env",
156
+ "schema",
157
+ "manifest",
158
+ "doc",
159
+ "structured-output"
160
+ ]);
161
+ function f(t) {
162
+ return typeof t == "object" && t !== null && !Array.isArray(t);
163
+ }
164
+ function c(t) {
165
+ return typeof t == "string";
166
+ }
167
+ function n(t, e, i) {
168
+ return { path: t, message: e, value: i };
169
+ }
170
+ function T(t) {
171
+ var l, y;
172
+ const e = Object.keys(t), i = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map();
173
+ for (const s of e)
174
+ i.set(s, 0), r.set(s, []);
175
+ for (const s of e)
176
+ for (const a of ((l = t[s]) == null ? void 0 : l.depends_on) ?? [])
177
+ t[a] && ((y = r.get(a)) == null || y.push(s), i.set(s, (i.get(s) ?? 0) + 1));
178
+ const p = e.filter((s) => (i.get(s) ?? 0) === 0), d = /* @__PURE__ */ new Set();
179
+ for (; p.length > 0; ) {
180
+ const s = p.shift();
181
+ if (s !== void 0) {
182
+ d.add(s);
183
+ for (const a of r.get(s) ?? []) {
184
+ const u = (i.get(a) ?? 1) - 1;
185
+ i.set(a, u), u === 0 && p.push(a);
186
+ }
187
+ }
188
+ }
189
+ return d.size !== e.length ? e.filter((s) => !d.has(s)).map((s) => ({ slug: s })) : null;
190
+ }
191
+ function E(t, e) {
192
+ for (const i of t) {
193
+ if (!f(i))
194
+ continue;
195
+ const r = c(i.id) ? i.id : "?", p = i.turns;
196
+ if (!Array.isArray(p)) {
197
+ e.push(
198
+ n(`dispatch_log[${r}].turns`, "must be an array", p)
199
+ );
200
+ continue;
201
+ }
202
+ for (let d = 0; d < p.length; d++) {
203
+ const l = p[d], y = `dispatch_log[${r}].turns[${d}]`;
204
+ if (!f(l)) {
205
+ e.push(n(y, "must be an object", l));
206
+ continue;
207
+ }
208
+ (typeof l.turn != "number" || !Number.isInteger(l.turn) || l.turn < 1) && e.push(n(`${y}.turn`, "must be a positive integer", l.turn)), typeof l.input_tokens != "number" && e.push(
209
+ n(`${y}.input_tokens`, "must be a number", l.input_tokens)
210
+ ), typeof l.output_tokens != "number" && e.push(
211
+ n(`${y}.output_tokens`, "must be a number", l.output_tokens)
212
+ ), c(l.t) || e.push(n(`${y}.t`, "must be a string", l.t));
213
+ const s = l.model_calls;
214
+ s != null && typeof s != "number" && e.push(
215
+ n(
216
+ `${y}.model_calls`,
217
+ "must be a number or null if present",
218
+ s
219
+ )
220
+ );
221
+ }
222
+ }
223
+ }
224
+ function N(t) {
225
+ const e = [];
226
+ if (!f(t))
227
+ return { valid: !1, errors: [n("", "root must be an object")] };
228
+ for (const d of ["description", "problem", "approach", "executor"]) {
229
+ const l = t[d];
230
+ (!c(l) || l.trim() === "") && e.push(n(d, "required string field missing/empty", l));
231
+ }
232
+ typeof t.schema_version != "number" && e.push(
233
+ n("schema_version", "must be a number", t.schema_version)
234
+ );
235
+ const i = t.plan_kind;
236
+ i !== "brownfield" && i !== "greenfield" && e.push(n("plan_kind", 'must be "brownfield" or "greenfield"', i)), Array.isArray(t.phases) ? t.phases.length === 0 && e.push(n("phases", "must have at least one entry")) : e.push(n("phases", "must be an array", t.phases));
237
+ const r = t.terminal;
238
+ !c(r) && !Array.isArray(r) && e.push(n("terminal", "must be a string or array", r)), Array.isArray(t.dispatch_log) ? E(t.dispatch_log, e) : e.push(n("dispatch_log", "must be an array", t.dispatch_log)), f(t.optimization) || e.push(n("optimization", "must be an object", t.optimization)), f(t.providers) || e.push(n("providers", "must be an object", t.providers)), f(t.effort_max_tokens) || e.push(
239
+ n("effort_max_tokens", "must be an object", t.effort_max_tokens)
240
+ );
241
+ const p = t.milestones;
242
+ if (!f(p))
243
+ e.push(n("milestones", "must be an object", p));
244
+ else {
245
+ const d = Object.keys(p);
246
+ d.length === 0 && e.push(n("milestones", "must have at least one entry"));
247
+ const l = new Set(d);
248
+ for (const u of d) {
249
+ const o = p[u];
250
+ if (!f(o)) {
251
+ e.push(n(`milestones.${u}`, "must be an object", o));
252
+ continue;
253
+ }
254
+ if ((!c(o.description) || o.description.trim() === "") && e.push(
255
+ n(
256
+ `milestones.${u}.description`,
257
+ "required string",
258
+ o.description
259
+ )
260
+ ), !Array.isArray(o.depends_on))
261
+ e.push(
262
+ n(
263
+ `milestones.${u}.depends_on`,
264
+ "must be an array",
265
+ o.depends_on
266
+ )
267
+ );
268
+ else
269
+ for (const _ of o.depends_on)
270
+ c(_) ? l.has(_) || e.push(
271
+ n(`milestones.${u}.depends_on`, `unknown milestone "${_}"`)
272
+ ) : e.push(
273
+ n(`milestones.${u}.depends_on[]`, "must be a string", _)
274
+ );
275
+ const b = o.phase;
276
+ (!c(b) || b.trim() === "") && e.push(n(`milestones.${u}.phase`, "required string", b)), (!c(o.authored_by) || o.authored_by.trim() === "") && e.push(
277
+ n(
278
+ `milestones.${u}.authored_by`,
279
+ "required string",
280
+ o.authored_by
281
+ )
282
+ );
283
+ const m = o.model;
284
+ m != null && !["Haiku", "Sonnet", "Opus"].includes(m) && e.push(
285
+ n(`milestones.${u}.model`, "invalid model tier", m)
286
+ );
287
+ const h = o.effort;
288
+ h != null && !["low", "medium", "high", "xhigh", "max"].includes(h) && e.push(
289
+ n(`milestones.${u}.effort`, "invalid effort tier", h)
290
+ ), o.two_stage !== void 0 && typeof o.two_stage != "boolean" && e.push(
291
+ n(
292
+ `milestones.${u}.two_stage`,
293
+ "must be a boolean",
294
+ o.two_stage
295
+ )
296
+ ), Array.isArray(o.read_only) || e.push(
297
+ n(
298
+ `milestones.${u}.read_only`,
299
+ "must be an array",
300
+ o.read_only
301
+ )
302
+ );
303
+ }
304
+ const y = T(
305
+ p
306
+ );
307
+ if (y)
308
+ for (const { slug: u } of y)
309
+ e.push(n(`milestones.${u}`, "dependency cycle detected"));
310
+ if (c(r) && !l.has(r))
311
+ e.push(n("terminal", `unknown milestone "${r}"`));
312
+ else if (Array.isArray(r))
313
+ for (const u of r)
314
+ l.has(u) || e.push(n("terminal[]", `unknown milestone "${u}"`));
315
+ const s = t.operations, a = /* @__PURE__ */ new Set();
316
+ if (!Array.isArray(s) && !f(s))
317
+ e.push(n("operations", "must be an array or object", s));
318
+ else {
319
+ const u = Array.isArray(s) ? s : Object.values(s);
320
+ for (const o of u) {
321
+ if (!f(o)) {
322
+ e.push(n("operations[]", "must be an object", o));
323
+ continue;
324
+ }
325
+ const b = o.id;
326
+ if (!c(b)) {
327
+ e.push(n("operations[?]", "missing/invalid id", b));
328
+ continue;
329
+ }
330
+ const m = b;
331
+ a.add(m);
332
+ const h = o.milestone;
333
+ c(h) ? l.has(h) || e.push(
334
+ n(`operations[${m}].milestone`, `unknown milestone "${h}"`)
335
+ ) : e.push(
336
+ n(`operations[${m}].milestone`, "required string", h)
337
+ );
338
+ const _ = o.type;
339
+ c(_) && !q.has(_) && e.push(n(`operations[${m}].type`, "invalid type", _));
340
+ const v = o.shape;
341
+ if (f(v)) {
342
+ const g = v.kind;
343
+ g != null && c(g) && !D.has(g) && e.push(
344
+ n(`operations[${m}].shape.kind`, "invalid shape kind", g)
345
+ );
346
+ const $ = v.ops;
347
+ if (Array.isArray($) && c(g))
348
+ for (let w = 0; w < $.length; w++) {
349
+ const A = $[w];
350
+ if (!f(A))
351
+ continue;
352
+ const k = A.op;
353
+ c(k) && !j(g, k) && e.push(
354
+ n(
355
+ `operations[${m}].shape.ops[${w}]`,
356
+ `op "${k}" not valid for kind "${g}"`
357
+ )
358
+ );
359
+ }
360
+ }
361
+ Array.isArray(o.depends_on) || e.push(
362
+ n(
363
+ `operations[${m}].depends_on`,
364
+ "must be an array",
365
+ o.depends_on
366
+ )
367
+ );
368
+ }
369
+ for (const o of u) {
370
+ if (!f(o))
371
+ continue;
372
+ const b = o.id;
373
+ if (!c(b))
374
+ continue;
375
+ const m = o.depends_on;
376
+ if (Array.isArray(m))
377
+ for (const h of m)
378
+ c(h) && !a.has(h) && e.push(
379
+ n(
380
+ `operations[${b}].depends_on`,
381
+ `unknown operation "${h}"`
382
+ )
383
+ );
384
+ }
385
+ }
386
+ }
387
+ return { valid: e.length === 0, errors: e };
388
+ }
389
+ function L(t) {
390
+ const e = [];
391
+ if (!f(t))
392
+ return { valid: !1, errors: [n("", "root must be an object")] };
393
+ for (const r of ["snapshot_at", "description"])
394
+ c(t[r]) || e.push(n(r, "required string field missing", t[r]));
395
+ c(t.plan) || e.push(n("plan", "required string", t.plan));
396
+ const i = t.milestones;
397
+ if (!f(i))
398
+ e.push(n("milestones", "must be an object", i));
399
+ else {
400
+ const r = Object.keys(i), p = new Set(r);
401
+ for (const s of r) {
402
+ const a = i[s];
403
+ if (!f(a)) {
404
+ e.push(n(`milestones.${s}`, "must be an object", a));
405
+ continue;
406
+ }
407
+ const u = a.eligible, o = a.pending;
408
+ u === !0 && o !== null && o !== void 0 && e.push(
409
+ n(
410
+ `milestones.${s}`,
411
+ "D-07 violation: eligible=true but pending not null"
412
+ )
413
+ ), typeof u != "boolean" && e.push(
414
+ n(`milestones.${s}.eligible`, "must be boolean", u)
415
+ );
416
+ const b = a.status;
417
+ (!c(b) || !O.has(b)) && e.push(n(`milestones.${s}.status`, "invalid status", b));
418
+ const m = a.wave;
419
+ (typeof m != "number" || !Number.isInteger(m) || m < 0) && e.push(
420
+ n(`milestones.${s}.wave`, "must be non-negative integer", m)
421
+ );
422
+ const h = a.ki_estimate;
423
+ h != null && typeof h != "number" && e.push(
424
+ n(`milestones.${s}.ki_estimate`, "must be number|null", h)
425
+ );
426
+ for (const _ of ["tokens_estimated", "tokens_actual"]) {
427
+ const v = a[_];
428
+ v != null && typeof v != "number" && e.push(n(`milestones.${s}.${_}`, "must be number|null", v));
429
+ }
430
+ Array.isArray(a.artifacts) || e.push(
431
+ n(
432
+ `milestones.${s}.artifacts`,
433
+ "must be an array",
434
+ a.artifacts
435
+ )
436
+ ), typeof a.si_bytes != "number" && e.push(
437
+ n(`milestones.${s}.si_bytes`, "must be a number", a.si_bytes)
438
+ );
439
+ }
440
+ const d = t.operations;
441
+ if (!Array.isArray(d))
442
+ e.push(n("operations", "must be an array", d));
443
+ else
444
+ for (const s of d) {
445
+ if (!f(s))
446
+ continue;
447
+ const a = c(s.id) ? s.id : "?", u = s.milestone;
448
+ c(u) && !p.has(u) && e.push(
449
+ n(
450
+ `operations[${a}].milestone`,
451
+ `unknown milestone "${u}"`
452
+ )
453
+ );
454
+ const o = s.status;
455
+ (!c(o) || !I.has(o)) && e.push(
456
+ n(
457
+ `operations[${a}].status`,
458
+ "invalid operation status",
459
+ o
460
+ )
461
+ ), Array.isArray(s.dispatch_ids) || e.push(
462
+ n(
463
+ `operations[${a}].dispatch_ids`,
464
+ "must be an array",
465
+ s.dispatch_ids
466
+ )
467
+ ), typeof s.attempt_count != "number" && e.push(
468
+ n(
469
+ `operations[${a}].attempt_count`,
470
+ "must be a number",
471
+ s.attempt_count
472
+ )
473
+ ), Array.isArray(s.blast_radius) || e.push(
474
+ n(
475
+ `operations[${a}].blast_radius`,
476
+ "must be an array",
477
+ s.blast_radius
478
+ )
479
+ );
480
+ }
481
+ const l = t.open_questions;
482
+ if (!Array.isArray(l))
483
+ e.push(n("open_questions", "must be an array", l));
484
+ else
485
+ for (const s of l) {
486
+ if (!f(s))
487
+ continue;
488
+ const a = s.blocking;
489
+ (!c(a) || !p.has(a)) && e.push(
490
+ n("open_questions", `unknown blocking milestone "${a}"`)
491
+ ), typeof s.surfaced != "boolean" && e.push(
492
+ n(
493
+ `open_questions[${s.id}]`,
494
+ "surfaced must be boolean",
495
+ s.surfaced
496
+ )
497
+ ), typeof s.answered != "boolean" && e.push(
498
+ n(
499
+ `open_questions[${s.id}]`,
500
+ "answered must be boolean",
501
+ s.answered
502
+ )
503
+ );
504
+ }
505
+ const y = t.dispatch_units;
506
+ if (y !== void 0)
507
+ if (!Array.isArray(y))
508
+ e.push(
509
+ n("dispatch_units", "must be an array if present", y)
510
+ );
511
+ else
512
+ for (const s of y) {
513
+ if (!f(s))
514
+ continue;
515
+ const a = s.fits_context_window;
516
+ typeof a != "boolean" && e.push(
517
+ n(
518
+ `dispatch_units[${s.id}]`,
519
+ "fits_context_window must be boolean",
520
+ a
521
+ )
522
+ );
523
+ const u = s.milestones;
524
+ if (Array.isArray(u))
525
+ for (const o of u)
526
+ c(o) && !p.has(o) && e.push(
527
+ n(
528
+ `dispatch_units[${s.id}].milestones`,
529
+ `unknown milestone "${o}"`
530
+ )
531
+ );
532
+ }
533
+ f(t.pairwise_overlap) || e.push(
534
+ n(
535
+ "pairwise_overlap",
536
+ "must be an object",
537
+ t.pairwise_overlap
538
+ )
539
+ );
540
+ }
541
+ return { valid: e.length === 0, errors: e };
542
+ }
543
+ function z(t) {
544
+ const e = N(t);
545
+ if (!e.valid)
546
+ throw new Error(
547
+ `DagJson validation failed:
548
+ ${e.errors.map((i) => ` ${i.path}: ${i.message}`).join(`
549
+ `)}`
550
+ );
551
+ }
552
+ function H(t) {
553
+ const e = L(t);
554
+ if (!e.valid)
555
+ throw new Error(
556
+ `Snapshot validation failed:
557
+ ${e.errors.map((i) => ` ${i.path}: ${i.message}`).join(`
558
+ `)}`
559
+ );
560
+ }
561
+ const S = [
562
+ {
563
+ from: 2,
564
+ to: 3,
565
+ apply: (t) => {
566
+ !t.milestones && t.nodes && (t.milestones = t.nodes, delete t.nodes);
567
+ const e = t.operations;
568
+ e && typeof e == "object" && !Array.isArray(e) && (t.operations = Object.values(e));
569
+ }
570
+ },
571
+ {
572
+ from: 3,
573
+ to: 4,
574
+ apply: (t) => {
575
+ (!t.providers || Object.keys(t.providers).length === 0) && (t.providers = {
576
+ Haiku: {
577
+ type: "claudecli",
578
+ model_id: "claude-haiku-4-5",
579
+ env_secret: null,
580
+ base_url: null,
581
+ timeout_ms: 6e4,
582
+ retry_config: {
583
+ retries: 3,
584
+ min_timeout: 1e3,
585
+ max_timeout: 3e4,
586
+ factor: 2
587
+ }
588
+ },
589
+ Sonnet: {
590
+ type: "claudecli",
591
+ model_id: "claude-sonnet-4-5",
592
+ env_secret: null,
593
+ base_url: null,
594
+ timeout_ms: 12e4,
595
+ retry_config: {
596
+ retries: 3,
597
+ min_timeout: 1e3,
598
+ max_timeout: 3e4,
599
+ factor: 2
600
+ }
601
+ },
602
+ Opus: {
603
+ type: "claudecli",
604
+ model_id: "claude-opus-4-5",
605
+ env_secret: null,
606
+ base_url: null,
607
+ timeout_ms: 3e5,
608
+ retry_config: {
609
+ retries: 3,
610
+ min_timeout: 1e3,
611
+ max_timeout: 3e4,
612
+ factor: 2
613
+ }
614
+ }
615
+ }), (!t.effort_max_tokens || Object.keys(t.effort_max_tokens).length === 0) && (t.effort_max_tokens = {
616
+ low: 1024,
617
+ medium: 4096,
618
+ high: 8192,
619
+ xhigh: 16384,
620
+ max: 32768
621
+ }), t.optimization || (t.optimization = {
622
+ sentinel_fanout: {
623
+ enabled: !0,
624
+ write_multiplier: 1.25,
625
+ read_multiplier: 0.1,
626
+ hit_probability: 0.9
627
+ },
628
+ b_per_tier: { Haiku: 8e3, Sonnet: 15e3, Opus: 27e3 },
629
+ context_window_per_tier: { Haiku: 16e3, Sonnet: 16e3, Opus: 32e3 },
630
+ context_window_override: null,
631
+ b_override: null
632
+ });
633
+ let e = [];
634
+ Array.isArray(t.operations) ? e = t.operations : t.operations && typeof t.operations == "object" && (e = Object.values(t.operations));
635
+ for (const i of e)
636
+ if (typeof i == "object" && i !== null) {
637
+ const r = i;
638
+ r.type === void 0 && (r.type = "generative"), r.criteria === void 0 && (r.criteria = []), r.tool === void 0 && (r.tool = null), r.args === void 0 && (r.args = null);
639
+ }
640
+ }
641
+ }
642
+ ];
643
+ function K(t, e, i) {
644
+ if (t === e)
645
+ return;
646
+ let r = t;
647
+ for (const p of [...S].sort((d, l) => d.from - l.from))
648
+ if (p.from === r && (p.apply(i), r = p.to, r === e)) {
649
+ i.schema_version = e;
650
+ return;
651
+ }
652
+ throw new Error(
653
+ `migrateDag: no path from v${t} to v${e}. Available: [${S.map(
654
+ (p) => `v${p.from}→v${p.to}`
655
+ ).join(", ")}]`
656
+ );
657
+ }
658
+ export {
659
+ x as VALID_OPS_BY_KIND,
660
+ R as WRITE_CLASS_ACTIONS,
661
+ z as assertValidDagJson,
662
+ H as assertValidSnapshot,
663
+ j as isValidOpForKind,
664
+ K as migrateDag,
665
+ N as validateDagJson,
666
+ L as validateSnapshot
667
+ };
@@ -0,0 +1 @@
1
+ export declare function migrateDag(fromVersion: number, toVersion: number, dag: Record<string, unknown>): void;
package/lib/types.d.ts ADDED
@@ -0,0 +1,358 @@
1
+ /**
2
+ * types.ts — Complete TypeScript type system for the dispatch plan ecosystem.
3
+ *
4
+ * Source: docs/plan/dispatch-completion/superseded/dispatch-optimizer/PROPOSED_DAG_STRUCTURE.md
5
+ * Design decisions: D-01 through D-18 in DECISIONS.md
6
+ *
7
+ * Field provenance: dag | derived | scheduler | optimizer | clock | gitnexus
8
+ * Zero dependencies. Pure TypeScript. platform:shared.
9
+ */
10
+ /** D-03 + D-18 — which executor the orchestrator routes this operation to. */
11
+ export type OperationType = 'automated' | 'tool-call' | 'generative';
12
+ export type OperationAction = 'create' | 'delete' | 'move' | 'rename' | 'modify-signature' | 'modify-body' | 'add-export' | 'remove-export' | 'guard' | 'exec' | 'dag.inject' | 'dag.wait' | 'dag.add-milestone' | 'dag.set-field' | 'dag.clear-pending' | 'dag.append-dispatch-log' | 'fs.move' | 'fs.delete' | 'fs.scaffold';
13
+ export declare const WRITE_CLASS_ACTIONS: ReadonlySet<OperationAction>;
14
+ export type CodeKind = 'function' | 'interface' | 'type' | 'class' | 'enum' | 'const' | 'script';
15
+ export type ConfigKind = 'config' | 'env' | 'schema' | 'manifest';
16
+ export type ShapeKind = CodeKind | ConfigKind | 'doc' | 'structured-output';
17
+ export type KindFamily = 'code-config' | 'doc' | 'structured' | 'tool-call';
18
+ export type MilestoneStatus = 'pending' | 'pending-surfaced' | 'in_progress' | 'complete' | 'failed' | 'skipped';
19
+ export type DispatchUnitStatus = 'pending' | 'in_progress' | 'complete' | 'failed';
20
+ export type OperationStatus = 'pending' | 'in_progress' | 'complete' | 'failed' | 'skipped';
21
+ export type GuardResult = 'pass' | 'fail';
22
+ export type MilestoneGuardResult = 'pass' | 'fail' | 'pending';
23
+ export type Provenance = 'gitnexus' | 'manual' | 'assumed' | 'vendored';
24
+ export type Confidence = 'verified' | 'vendored' | 'documented' | 'assumed';
25
+ export type KiSource = 'estimate' | 'calibrated' | 'actual';
26
+ export type DispatchKind = 'planning' | 'execution';
27
+ export type ProviderType = 'anthropic' | 'openai' | 'claudecli';
28
+ export type SentinelRole = 'prewarm' | 'payload' | 'solo';
29
+ export type ModelTier = 'Haiku' | 'Sonnet' | 'Opus';
30
+ export type EffortTier = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
31
+ export type PlanKind = 'brownfield' | 'greenfield';
32
+ export type ShapeOpType = 'add-param' | 'remove-param' | 'rename-param' | 'retype-param' | 'change-param-optional' | 'reorder-params' | 'change-return' | 'add-field' | 'remove-field' | 'rename-field' | 'retype-field' | 'change-field-optional' | 'add-generic' | 'remove-generic' | 'constrain-generic' | 'add-extends' | 'remove-extends' | 'set-key' | 'remove-key' | 'rename-key' | 'add-array-item' | 'remove-array-item' | 'add-var' | 'remove-var' | 'rename-var' | 'change-default' | 'add-section' | 'remove-section' | 'rename-section' | 'update-section' | 'add-table' | 'remove-table' | 'add-column' | 'remove-column' | 'rename-column' | 'retype-column' | 'change-nullable' | 'add-index' | 'remove-index' | 'add-entry' | 'remove-entry' | 'update-entry' | 'bump-version' | 'update-checksum' | 'add-export' | 'remove-export';
33
+ export declare const VALID_OPS_BY_KIND: Record<string, ReadonlySet<ShapeOpType>>;
34
+ export declare function isValidOpForKind(kind: string, op: ShapeOpType): boolean;
35
+ export interface ShapeOpDag {
36
+ op: ShapeOpType;
37
+ target: string | null;
38
+ to: string | null;
39
+ position: number | null;
40
+ required: boolean | null;
41
+ }
42
+ export interface ShapeOpSnapshot extends ShapeOpDag {
43
+ from: string | null;
44
+ breaking: boolean | null;
45
+ severity: 'error' | 'warning' | 'info' | null;
46
+ }
47
+ export interface ShapeCode {
48
+ kind: CodeKind;
49
+ ops: ShapeOpDag[];
50
+ description?: null;
51
+ objective?: null;
52
+ required_sections?: null;
53
+ schema?: null;
54
+ }
55
+ export interface ShapeConfig {
56
+ kind: ConfigKind;
57
+ ops: ShapeOpDag[];
58
+ description?: null;
59
+ objective?: null;
60
+ required_sections?: null;
61
+ schema?: null;
62
+ }
63
+ export interface ShapeDoc {
64
+ kind: 'doc';
65
+ ops?: null;
66
+ description: string;
67
+ objective: string;
68
+ required_sections: string[];
69
+ schema?: null;
70
+ }
71
+ export interface ShapeStructuredOutput {
72
+ kind: 'structured-output';
73
+ ops?: null;
74
+ description?: null;
75
+ objective?: null;
76
+ required_sections?: null;
77
+ schema: Record<string, unknown>;
78
+ }
79
+ export interface ShapeNull {
80
+ kind: null;
81
+ ops?: null;
82
+ description?: null;
83
+ objective?: null;
84
+ required_sections?: null;
85
+ schema?: null;
86
+ }
87
+ export type Shape = ShapeCode | ShapeConfig | ShapeDoc | ShapeStructuredOutput | ShapeNull;
88
+ export interface ShapeCodeSnapshot extends Omit<ShapeCode, 'ops'> {
89
+ ops: ShapeOpSnapshot[];
90
+ }
91
+ export interface ShapeConfigSnapshot extends Omit<ShapeConfig, 'ops'> {
92
+ ops: ShapeOpSnapshot[];
93
+ }
94
+ export type ShapeSnapshot = ShapeCodeSnapshot | ShapeConfigSnapshot | ShapeDoc | ShapeStructuredOutput | ShapeNull;
95
+ export interface BlastRadiusEntry {
96
+ file: string;
97
+ symbol: string;
98
+ impact: 'implements' | 'calls' | 'imports' | 'extends' | 're-exports' | 'overrides';
99
+ consumer: 'current' | 'future';
100
+ }
101
+ export interface OperationConflict {
102
+ detected: boolean;
103
+ competing_op: string | null;
104
+ op_key: string | null;
105
+ resolution: 'safe-merge' | 'warning' | 'error' | null;
106
+ }
107
+ export interface RetryConfig {
108
+ retries: number;
109
+ min_timeout: number;
110
+ max_timeout: number;
111
+ factor: number;
112
+ }
113
+ export interface ProviderConfig {
114
+ type: ProviderType;
115
+ model_id: string;
116
+ env_secret: string | null;
117
+ base_url: string | null;
118
+ timeout_ms: number;
119
+ retry_config: RetryConfig;
120
+ }
121
+ export interface Turn {
122
+ turn: number;
123
+ input_tokens: number;
124
+ output_tokens: number;
125
+ t: string;
126
+ /**
127
+ * Number of real model calls this synthesized turn represents. Added for
128
+ * DEBT-DISPATCH-008: agent-mcp's MCP surface exposes only aggregate usage
129
+ * per task (no per-turn breakdown — see agent-runner.ts `SynthesizedTurn`/
130
+ * `DispatchUsageReport`), so a dispatcher collapses a task's entire usage
131
+ * into ONE `Turn` and needs somewhere to carry `modelCalls` through.
132
+ * Optional and nullable for backward compatibility: every dag.json written
133
+ * before this field existed — and any producer that doesn't track model
134
+ * call counts — remains valid without it.
135
+ */
136
+ model_calls?: number | null;
137
+ }
138
+ export interface DispatchResult {
139
+ op_id: string;
140
+ status: 'complete' | 'failed' | 'skipped';
141
+ guard_result: GuardResult | null;
142
+ guard_output: string | null;
143
+ guard_ran_at: string | null;
144
+ /**
145
+ * Real outcome payload for `type: "tool-call"` operations (BUG-DISPATCH-EXEC-001
146
+ * / D-13, D-18). `null`/absent for every other op kind (generative,
147
+ * automated-guard). On success, the tool handler's own return value (e.g.
148
+ * `{ path, to }` for `fs.move`, `{ slug }` for `dag.add-milestone`). On
149
+ * failure, `{ error: string }` — never a silent 'skipped' with no payload.
150
+ * Optional for backward compatibility: every `dispatch_log` entry written
151
+ * before this field existed remains valid without it.
152
+ */
153
+ tool_result?: unknown | null;
154
+ }
155
+ export interface DispatchNote {
156
+ level: 'info' | 'warn' | 'error';
157
+ text: string;
158
+ }
159
+ export interface DispatchLogEntry {
160
+ id: string;
161
+ kind: DispatchKind;
162
+ provider: 'anthropic' | 'openai' | 'deepseek' | 'google' | 'local' | 'claudecli' | 'teammate';
163
+ model: string | null;
164
+ agent: string;
165
+ effort: EffortTier | null;
166
+ started_at: string;
167
+ completed_at: string | null;
168
+ operations: string[];
169
+ turns: Turn[];
170
+ results: DispatchResult[];
171
+ notes: DispatchNote[];
172
+ }
173
+ export interface OperationDag {
174
+ id: string;
175
+ milestone: string;
176
+ depends_on: string[];
177
+ type: OperationType;
178
+ action: OperationAction;
179
+ file: string | null;
180
+ symbol: string | null;
181
+ provenance: Provenance | null;
182
+ confidence: Confidence | null;
183
+ audit_check: string | null;
184
+ criteria: string[];
185
+ tool: string | null;
186
+ args: Record<string, unknown> | null;
187
+ guard: string | null;
188
+ to_file: string | null;
189
+ to_symbol: string | null;
190
+ ki_estimate: number | null;
191
+ ki_source: KiSource | null;
192
+ authored_by: string;
193
+ status: OperationStatus;
194
+ shape: Shape | null;
195
+ }
196
+ export interface OperationSnapshot extends Omit<OperationDag, 'shape'> {
197
+ shape: ShapeSnapshot | null;
198
+ dispatch_ids: string[];
199
+ attempt_count: number;
200
+ guard_result: GuardResult | null;
201
+ guard_output: string | null;
202
+ guard_ran_at: string | null;
203
+ blast_radius: BlastRadiusEntry[];
204
+ conflict: OperationConflict;
205
+ tokens_actual: number | null;
206
+ }
207
+ export interface MilestoneDag {
208
+ description: string;
209
+ rationale?: string;
210
+ authored_by: string;
211
+ pending: string | null;
212
+ triggered_by: string | null;
213
+ phase: string;
214
+ depends_on: string[];
215
+ agent: string | null;
216
+ model: ModelTier | null;
217
+ effort: EffortTier | null;
218
+ two_stage: boolean;
219
+ read_only: string[];
220
+ guard: string | null;
221
+ }
222
+ export interface CrossPlanDep {
223
+ plan: string;
224
+ milestone: string;
225
+ }
226
+ export type PairwiseOverlapMap = Record<string, number>;
227
+ export interface OpenQuestion {
228
+ id: string;
229
+ text: string;
230
+ blocking: string;
231
+ surfaced: boolean;
232
+ raised_at_dispatch: string | null;
233
+ raised_at_turn: number | null;
234
+ answered: boolean;
235
+ answer: string | null;
236
+ }
237
+ export interface MilestoneSnapshot extends MilestoneDag {
238
+ context: string;
239
+ wave: number;
240
+ eligible: boolean;
241
+ status: MilestoneStatus;
242
+ started_at: string | null;
243
+ completed_at: string | null;
244
+ guard_result: MilestoneGuardResult | null;
245
+ guard_output: string | null;
246
+ artifacts: string[];
247
+ si_bytes: number;
248
+ ki_estimate: number | null;
249
+ tokens_estimated: number | null;
250
+ tokens_actual: number | null;
251
+ }
252
+ export interface SentinelFanoutConfig {
253
+ enabled: boolean;
254
+ write_multiplier: number;
255
+ read_multiplier: number;
256
+ hit_probability: number;
257
+ }
258
+ export interface OptimizationConfig {
259
+ sentinel_fanout: SentinelFanoutConfig;
260
+ b_per_tier: Record<string, number | null>;
261
+ context_window_per_tier: Record<string, number>;
262
+ context_window_override: Record<string, number> | null;
263
+ b_override: Record<string, number> | null;
264
+ }
265
+ export interface DagJson {
266
+ schema_version: number;
267
+ plan_kind: PlanKind;
268
+ description: string;
269
+ problem: string;
270
+ approach: string;
271
+ executor: string;
272
+ executor_model?: ModelTier;
273
+ executor_effort?: EffortTier;
274
+ phases: string[];
275
+ terminal: string | string[];
276
+ assumed_baseline?: string[] | Record<string, unknown>;
277
+ cross_plan_deps?: CrossPlanDep[];
278
+ optimization: OptimizationConfig;
279
+ providers: Record<string, ProviderConfig>;
280
+ effort_max_tokens: Record<string, number>;
281
+ milestones: Record<string, MilestoneDag>;
282
+ operations: OperationDag[] | Record<string, OperationDag>;
283
+ dispatch_log: DispatchLogEntry[];
284
+ }
285
+ export interface SnapshotOptimization {
286
+ tokens_naive: number | null;
287
+ sentinel_fanout: SentinelFanoutConfig;
288
+ context_window_override: Record<string, number> | null;
289
+ b_override: Record<string, number> | null;
290
+ b_per_tier: Record<string, number | null>;
291
+ b_eff_per_tier: Record<string, number | null>;
292
+ context_window_per_tier: Record<string, number>;
293
+ }
294
+ export interface DagSnapshot {
295
+ snapshot_at: string;
296
+ snapshot_version: number;
297
+ plan: string;
298
+ schema_version: number;
299
+ plan_kind: PlanKind;
300
+ description: string;
301
+ problem: string;
302
+ approach: string;
303
+ executor: string;
304
+ executor_model?: ModelTier;
305
+ executor_effort?: EffortTier;
306
+ phases: string[];
307
+ terminal: string | string[];
308
+ assumed_baseline?: string[] | Record<string, unknown>;
309
+ cross_plan_deps?: CrossPlanDep[];
310
+ optimization: SnapshotOptimization;
311
+ milestones: Record<string, MilestoneSnapshot>;
312
+ operations: OperationSnapshot[];
313
+ pairwise_overlap: PairwiseOverlapMap;
314
+ dispatch_units?: DispatchUnit[];
315
+ open_questions: OpenQuestion[];
316
+ }
317
+ export interface DispatchUnit {
318
+ id: string;
319
+ milestones: string[];
320
+ operations: string[];
321
+ model: ModelTier | null;
322
+ effort: EffortTier | null;
323
+ two_stage: boolean;
324
+ provider: ProviderConfig | null;
325
+ agent_name: string;
326
+ mcp_servers: Record<string, unknown> | null;
327
+ resolved_max_tokens: number | null;
328
+ background: true;
329
+ prompt: string | null;
330
+ context_files: string[];
331
+ si_bytes: number;
332
+ tokens_estimated: number | null;
333
+ fits_context_window: boolean;
334
+ sentinel_role: SentinelRole | null;
335
+ dispatch_log_id: string | null;
336
+ remote_task_id: string | null;
337
+ result: string | null;
338
+ status: DispatchUnitStatus;
339
+ started_at: string | null;
340
+ completed_at: string | null;
341
+ tokens_actual: number | null;
342
+ }
343
+ export interface ValidationError {
344
+ path: string;
345
+ message: string;
346
+ value?: unknown;
347
+ }
348
+ export interface ValidationResult {
349
+ valid: boolean;
350
+ errors: ValidationError[];
351
+ }
352
+ export interface IOptimizerDeps {
353
+ bPerTier: Record<string, number>;
354
+ contextWindowPerTier: Record<string, number>;
355
+ fileSizes?: (paths: string[]) => Map<string, number>;
356
+ readFiles?: (paths: string[]) => Map<string, string>;
357
+ dispatchLog?: DispatchLogEntry[];
358
+ }
@@ -0,0 +1,6 @@
1
+ import { DagJson, DagSnapshot, ValidationResult } from './types.js';
2
+
3
+ export declare function validateDagJson(dag: unknown): ValidationResult;
4
+ export declare function validateSnapshot(snapshot: unknown): ValidationResult;
5
+ export declare function assertValidDagJson(dag: unknown): asserts dag is DagJson;
6
+ export declare function assertValidSnapshot(s: unknown): asserts s is DagSnapshot;
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@adhd/dispatch-base-spec",
3
+ "version": "0.0.1",
4
+ "dependencies": {},
5
+ "main": "./index.js",
6
+ "module": "./index.mjs",
7
+ "typings": "./index.d.ts",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ }
11
+ }