@adhd/dispatch-base-spec 0.1.0 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,35 @@
1
+ ## 0.1.2 (2026-08-08)
2
+
3
+
4
+ ### 🩹 Fixes
5
+
6
+ - **dispatch:** D-07 own-status eligibility guard (DEBT-DISPATCH-013) + formalize ICalibrationStore (DEBT-DISPATCH-018, BL-103)
7
+
8
+
9
+ ### ❤️ Thank You
10
+
11
+ - pseudosky
12
+
13
+ ## 0.1.1 (2026-08-07)
14
+
15
+
16
+ ### 🚀 Features
17
+
18
+ - **dispatch-base-spec:** split DispatchUnit.prompt into systemPrompt/prompt, add execution_mode
19
+
20
+
21
+ ### 🩹 Fixes
22
+
23
+ - **dispatch-base-spec:** enforce DispatchLogEntry.provider enum in validateDagJson
24
+
25
+ - **apigen-cli:** restore 2768 files mass-deleted by 0117eb22 (BUG-APIGEN-052)
26
+
27
+
28
+ ### ❤️ Thank You
29
+
30
+ - parity-harness-self-test
31
+ - pseudosky
32
+
1
33
  ## 0.0.6 (2026-07-25)
2
34
 
3
35
 
package/index.d.ts CHANGED
@@ -1,4 +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';
1
+ export type { BlastRadiusEntry, CodeKind, Confidence, ConfigKind, CrossPlanDep, DagJson, DagSnapshot, DispatchExecutionMode, DispatchKind, DispatchLogEntry, DispatchNote, DispatchResult, DispatchUnit, DispatchUnitStatus, EffortTier, GuardResult, ICalibrationStore, 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
2
  export { isValidOpForKind, VALID_OPS_BY_KIND, WRITE_CLASS_ACTIONS, } from './lib/types.js';
3
3
  export { assertValidDagJson, assertValidSnapshot, validateDagJson, validateSnapshot, } from './lib/validate.js';
4
4
  export { migrateDag } from './lib/migrate.js';
package/index.js CHANGED
@@ -1,5 +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;
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 r=x[t];return r?r.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"]),L=new Set(["anthropic","openai","deepseek","google","local","claudecli","teammate"]),N=new Set(["function","interface","type","class","enum","const","script","config","env","schema","manifest","doc","structured-output"]);function h(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function l(t){return typeof t=="string"}function n(t,e,r){return{path:t,message:e,value:r}}function R(t){var c,d;const e=Object.keys(t),r=new Map,a=new Map;for(const s of e)r.set(s,0),a.set(s,[]);for(const s of e)for(const i of((c=t[s])==null?void 0:c.depends_on)??[])t[i]&&((d=a.get(i))==null||d.push(s),r.set(s,(r.get(s)??0)+1));const p=e.filter(s=>(r.get(s)??0)===0),m=new Set;for(;p.length>0;){const s=p.shift();if(s!==void 0){m.add(s);for(const i of a.get(s)??[]){const u=(r.get(i)??1)-1;r.set(i,u),u===0&&p.push(i)}}}return m.size!==e.length?e.filter(s=>!m.has(s)).map(s=>({slug:s})):null}function V(t,e){for(const r of t){if(!h(r))continue;const a=l(r.id)?r.id:"?",p=r.provider;(!l(p)||!L.has(p))&&e.push(n(`dispatch_log[${a}].provider`,"invalid provider",p));const m=r.turns;if(!Array.isArray(m)){e.push(n(`dispatch_log[${a}].turns`,"must be an array",m));continue}for(let c=0;c<m.length;c++){const d=m[c],s=`dispatch_log[${a}].turns[${c}]`;if(!h(d)){e.push(n(s,"must be an object",d));continue}(typeof d.turn!="number"||!Number.isInteger(d.turn)||d.turn<1)&&e.push(n(`${s}.turn`,"must be a positive integer",d.turn)),typeof d.input_tokens!="number"&&e.push(n(`${s}.input_tokens`,"must be a number",d.input_tokens)),typeof d.output_tokens!="number"&&e.push(n(`${s}.output_tokens`,"must be a number",d.output_tokens)),l(d.t)||e.push(n(`${s}.t`,"must be a string",d.t));const i=d.model_calls;i!=null&&typeof i!="number"&&e.push(n(`${s}.model_calls`,"must be a number or null if present",i))}}}function O(t){const e=[];if(!h(t))return{valid:!1,errors:[n("","root must be an object")]};for(const m of["description","problem","approach","executor"]){const c=t[m];(!l(c)||c.trim()==="")&&e.push(n(m,"required string field missing/empty",c))}typeof t.schema_version!="number"&&e.push(n("schema_version","must be a number",t.schema_version));const r=t.plan_kind;r!=="brownfield"&&r!=="greenfield"&&e.push(n("plan_kind",'must be "brownfield" or "greenfield"',r)),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 a=t.terminal;!l(a)&&!Array.isArray(a)&&e.push(n("terminal","must be a string or array",a)),Array.isArray(t.dispatch_log)?V(t.dispatch_log,e):e.push(n("dispatch_log","must be an array",t.dispatch_log)),h(t.optimization)||e.push(n("optimization","must be an object",t.optimization)),h(t.providers)||e.push(n("providers","must be an object",t.providers)),h(t.effort_max_tokens)||e.push(n("effort_max_tokens","must be an object",t.effort_max_tokens));const p=t.milestones;if(!h(p))e.push(n("milestones","must be an object",p));else{const m=Object.keys(p);m.length===0&&e.push(n("milestones","must have at least one entry"));const c=new Set(m);for(const u of m){const o=p[u];if(!h(o)){e.push(n(`milestones.${u}`,"must be an object",o));continue}if((!l(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)l(_)?c.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;(!l(b)||b.trim()==="")&&e.push(n(`milestones.${u}.phase`,"required string",b)),(!l(o.authored_by)||o.authored_by.trim()==="")&&e.push(n(`milestones.${u}.authored_by`,"required string",o.authored_by));const f=o.model;f!=null&&!["Haiku","Sonnet","Opus"].includes(f)&&e.push(n(`milestones.${u}.model`,"invalid model tier",f));const y=o.effort;y!=null&&!["low","medium","high","xhigh","max"].includes(y)&&e.push(n(`milestones.${u}.effort`,"invalid effort tier",y)),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 d=R(p);if(d)for(const{slug:u}of d)e.push(n(`milestones.${u}`,"dependency cycle detected"));if(l(a)&&!c.has(a))e.push(n("terminal",`unknown milestone "${a}"`));else if(Array.isArray(a))for(const u of a)c.has(u)||e.push(n("terminal[]",`unknown milestone "${u}"`));const s=t.operations,i=new Set;if(!Array.isArray(s)&&!h(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(!h(o)){e.push(n("operations[]","must be an object",o));continue}const b=o.id;if(!l(b)){e.push(n("operations[?]","missing/invalid id",b));continue}const f=b;i.add(f);const y=o.milestone;l(y)?c.has(y)||e.push(n(`operations[${f}].milestone`,`unknown milestone "${y}"`)):e.push(n(`operations[${f}].milestone`,"required string",y));const _=o.type;l(_)&&!E.has(_)&&e.push(n(`operations[${f}].type`,"invalid type",_));const v=o.shape;if(h(v)){const g=v.kind;g!=null&&l(g)&&!N.has(g)&&e.push(n(`operations[${f}].shape.kind`,"invalid shape kind",g));const $=v.ops;if(Array.isArray($)&&l(g))for(let w=0;w<$.length;w++){const k=$[w];if(!h(k))continue;const A=k.op;l(A)&&!j(g,A)&&e.push(n(`operations[${f}].shape.ops[${w}]`,`op "${A}" not valid for kind "${g}"`))}}Array.isArray(o.depends_on)||e.push(n(`operations[${f}].depends_on`,"must be an array",o.depends_on))}for(const o of u){if(!h(o))continue;const b=o.id;if(!l(b))continue;const f=o.depends_on;if(Array.isArray(f))for(const y of f)l(y)&&!i.has(y)&&e.push(n(`operations[${b}].depends_on`,`unknown operation "${y}"`))}}}return{valid:e.length===0,errors:e}}function I(t){const e=[];if(!h(t))return{valid:!1,errors:[n("","root must be an object")]};for(const a of["snapshot_at","description"])l(t[a])||e.push(n(a,"required string field missing",t[a]));l(t.plan)||e.push(n("plan","required string",t.plan));const r=t.milestones;if(!h(r))e.push(n("milestones","must be an object",r));else{const a=Object.keys(r),p=new Set(a);for(const s of a){const i=r[s];if(!h(i)){e.push(n(`milestones.${s}`,"must be an object",i));continue}const u=i.eligible,o=i.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=i.status;(!l(b)||!T.has(b))&&e.push(n(`milestones.${s}.status`,"invalid status",b)),u===!0&&b==="complete"&&e.push(n(`milestones.${s}`,"D-07 violation: eligible=true but status is complete"));const f=i.wave;(typeof f!="number"||!Number.isInteger(f)||f<0)&&e.push(n(`milestones.${s}.wave`,"must be non-negative integer",f));const y=i.ki_estimate;y!=null&&typeof y!="number"&&e.push(n(`milestones.${s}.ki_estimate`,"must be number|null",y));for(const _ of["tokens_estimated","tokens_actual"]){const v=i[_];v!=null&&typeof v!="number"&&e.push(n(`milestones.${s}.${_}`,"must be number|null",v))}Array.isArray(i.artifacts)||e.push(n(`milestones.${s}.artifacts`,"must be an array",i.artifacts)),typeof i.si_bytes!="number"&&e.push(n(`milestones.${s}.si_bytes`,"must be a number",i.si_bytes))}const m=t.operations;if(!Array.isArray(m))e.push(n("operations","must be an array",m));else for(const s of m){if(!h(s))continue;const i=l(s.id)?s.id:"?",u=s.milestone;l(u)&&!p.has(u)&&e.push(n(`operations[${i}].milestone`,`unknown milestone "${u}"`));const o=s.status;(!l(o)||!q.has(o))&&e.push(n(`operations[${i}].status`,"invalid operation status",o)),Array.isArray(s.dispatch_ids)||e.push(n(`operations[${i}].dispatch_ids`,"must be an array",s.dispatch_ids)),typeof s.attempt_count!="number"&&e.push(n(`operations[${i}].attempt_count`,"must be a number",s.attempt_count)),Array.isArray(s.blast_radius)||e.push(n(`operations[${i}].blast_radius`,"must be an array",s.blast_radius))}const c=t.open_questions;if(!Array.isArray(c))e.push(n("open_questions","must be an array",c));else for(const s of c){if(!h(s))continue;const i=s.blocking;(!l(i)||!p.has(i))&&e.push(n("open_questions",`unknown blocking milestone "${i}"`)),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 d=t.dispatch_units;if(d!==void 0)if(!Array.isArray(d))e.push(n("dispatch_units","must be an array if present",d));else for(const s of d){if(!h(s))continue;const i=s.fits_context_window;typeof i!="boolean"&&e.push(n(`dispatch_units[${s.id}]`,"fits_context_window must be boolean",i));const u=s.milestones;if(Array.isArray(u))for(const o of u)l(o)&&!p.has(o)&&e.push(n(`dispatch_units[${s.id}].milestones`,`unknown milestone "${o}"`))}h(t.pairwise_overlap)||e.push(n("pairwise_overlap","must be an object",t.pairwise_overlap))}return{valid:e.length===0,errors:e}}function P(t){const e=O(t);if(!e.valid)throw new Error(`DagJson validation failed:
2
+ ${e.errors.map(r=>` ${r.path}: ${r.message}`).join(`
3
+ `)}`)}function K(t){const e=I(t);if(!e.valid)throw new Error(`Snapshot validation failed:
4
+ ${e.errors.map(r=>` ${r.path}: ${r.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 r of e)if(typeof r=="object"&&r!==null){const a=r;a.type===void 0&&(a.type="generative"),a.criteria===void 0&&(a.criteria=[]),a.tool===void 0&&(a.tool=null),a.args===void 0&&(a.args=null)}}}];function z(t,e,r){if(t===e)return;let a=t;for(const p of[...S].sort((m,c)=>m.from-c.from))if(p.from===a&&(p.apply(r),a=p.to,a===e)){r.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=P;exports.assertValidSnapshot=K;exports.isValidOpForKind=j;exports.migrateDag=z;exports.validateDagJson=O;exports.validateSnapshot=I;
package/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- const R = /* @__PURE__ */ new Set([
1
+ const z = /* @__PURE__ */ new Set([
2
2
  "create",
3
3
  "modify-signature",
4
4
  "modify-body",
@@ -127,8 +127,8 @@ const R = /* @__PURE__ */ new Set([
127
127
  ])
128
128
  };
129
129
  function j(t, e) {
130
- const i = x[t];
131
- return i ? i.has(e) : !1;
130
+ const r = x[t];
131
+ return r ? r.has(e) : !1;
132
132
  }
133
133
  const O = /* @__PURE__ */ new Set([
134
134
  "pending",
@@ -143,7 +143,15 @@ const O = /* @__PURE__ */ new Set([
143
143
  "complete",
144
144
  "failed",
145
145
  "skipped"
146
- ]), q = /* @__PURE__ */ new Set(["automated", "tool-call", "generative"]), D = /* @__PURE__ */ new Set([
146
+ ]), D = /* @__PURE__ */ new Set(["automated", "tool-call", "generative"]), q = /* @__PURE__ */ new Set([
147
+ "anthropic",
148
+ "openai",
149
+ "deepseek",
150
+ "google",
151
+ "local",
152
+ "claudecli",
153
+ "teammate"
154
+ ]), E = /* @__PURE__ */ new Set([
147
155
  "function",
148
156
  "interface",
149
157
  "type",
@@ -158,227 +166,229 @@ const O = /* @__PURE__ */ new Set([
158
166
  "doc",
159
167
  "structured-output"
160
168
  ]);
161
- function f(t) {
169
+ function h(t) {
162
170
  return typeof t == "object" && t !== null && !Array.isArray(t);
163
171
  }
164
- function c(t) {
172
+ function l(t) {
165
173
  return typeof t == "string";
166
174
  }
167
- function n(t, e, i) {
168
- return { path: t, message: e, value: i };
175
+ function n(t, e, r) {
176
+ return { path: t, message: e, value: r };
169
177
  }
170
178
  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
+ var d, c;
180
+ const e = Object.keys(t), r = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
181
+ for (const o of e)
182
+ r.set(o, 0), a.set(o, []);
183
+ for (const o of e)
184
+ for (const i of ((d = t[o]) == null ? void 0 : d.depends_on) ?? [])
185
+ t[i] && ((c = a.get(i)) == null || c.push(o), r.set(o, (r.get(o) ?? 0) + 1));
186
+ const p = e.filter((o) => (r.get(o) ?? 0) === 0), m = /* @__PURE__ */ new Set();
179
187
  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);
188
+ const o = p.shift();
189
+ if (o !== void 0) {
190
+ m.add(o);
191
+ for (const i of a.get(o) ?? []) {
192
+ const u = (r.get(i) ?? 1) - 1;
193
+ r.set(i, u), u === 0 && p.push(i);
186
194
  }
187
195
  }
188
196
  }
189
- return d.size !== e.length ? e.filter((s) => !d.has(s)).map((s) => ({ slug: s })) : null;
197
+ return m.size !== e.length ? e.filter((o) => !m.has(o)).map((o) => ({ slug: o })) : null;
190
198
  }
191
- function E(t, e) {
192
- for (const i of t) {
193
- if (!f(i))
199
+ function R(t, e) {
200
+ for (const r of t) {
201
+ if (!h(r))
194
202
  continue;
195
- const r = c(i.id) ? i.id : "?", p = i.turns;
196
- if (!Array.isArray(p)) {
203
+ const a = l(r.id) ? r.id : "?", p = r.provider;
204
+ (!l(p) || !q.has(p)) && e.push(n(`dispatch_log[${a}].provider`, "invalid provider", p));
205
+ const m = r.turns;
206
+ if (!Array.isArray(m)) {
197
207
  e.push(
198
- n(`dispatch_log[${r}].turns`, "must be an array", p)
208
+ n(`dispatch_log[${a}].turns`, "must be an array", m)
199
209
  );
200
210
  continue;
201
211
  }
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));
212
+ for (let d = 0; d < m.length; d++) {
213
+ const c = m[d], o = `dispatch_log[${a}].turns[${d}]`;
214
+ if (!h(c)) {
215
+ e.push(n(o, "must be an object", c));
206
216
  continue;
207
217
  }
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(
218
+ (typeof c.turn != "number" || !Number.isInteger(c.turn) || c.turn < 1) && e.push(n(`${o}.turn`, "must be a positive integer", c.turn)), typeof c.input_tokens != "number" && e.push(
219
+ n(`${o}.input_tokens`, "must be a number", c.input_tokens)
220
+ ), typeof c.output_tokens != "number" && e.push(
221
+ n(`${o}.output_tokens`, "must be a number", c.output_tokens)
222
+ ), l(c.t) || e.push(n(`${o}.t`, "must be a string", c.t));
223
+ const i = c.model_calls;
224
+ i != null && typeof i != "number" && e.push(
215
225
  n(
216
- `${y}.model_calls`,
226
+ `${o}.model_calls`,
217
227
  "must be a number or null if present",
218
- s
228
+ i
219
229
  )
220
230
  );
221
231
  }
222
232
  }
223
233
  }
224
- function N(t) {
234
+ function L(t) {
225
235
  const e = [];
226
- if (!f(t))
236
+ if (!h(t))
227
237
  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));
238
+ for (const m of ["description", "problem", "approach", "executor"]) {
239
+ const d = t[m];
240
+ (!l(d) || d.trim() === "") && e.push(n(m, "required string field missing/empty", d));
231
241
  }
232
242
  typeof t.schema_version != "number" && e.push(
233
243
  n("schema_version", "must be a number", t.schema_version)
234
244
  );
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(
245
+ const r = t.plan_kind;
246
+ r !== "brownfield" && r !== "greenfield" && e.push(n("plan_kind", 'must be "brownfield" or "greenfield"', r)), 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));
247
+ const a = t.terminal;
248
+ !l(a) && !Array.isArray(a) && e.push(n("terminal", "must be a string or array", a)), Array.isArray(t.dispatch_log) ? R(t.dispatch_log, e) : e.push(n("dispatch_log", "must be an array", t.dispatch_log)), h(t.optimization) || e.push(n("optimization", "must be an object", t.optimization)), h(t.providers) || e.push(n("providers", "must be an object", t.providers)), h(t.effort_max_tokens) || e.push(
239
249
  n("effort_max_tokens", "must be an object", t.effort_max_tokens)
240
250
  );
241
251
  const p = t.milestones;
242
- if (!f(p))
252
+ if (!h(p))
243
253
  e.push(n("milestones", "must be an object", p));
244
254
  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));
255
+ const m = Object.keys(p);
256
+ m.length === 0 && e.push(n("milestones", "must have at least one entry"));
257
+ const d = new Set(m);
258
+ for (const u of m) {
259
+ const s = p[u];
260
+ if (!h(s)) {
261
+ e.push(n(`milestones.${u}`, "must be an object", s));
252
262
  continue;
253
263
  }
254
- if ((!c(o.description) || o.description.trim() === "") && e.push(
264
+ if ((!l(s.description) || s.description.trim() === "") && e.push(
255
265
  n(
256
266
  `milestones.${u}.description`,
257
267
  "required string",
258
- o.description
268
+ s.description
259
269
  )
260
- ), !Array.isArray(o.depends_on))
270
+ ), !Array.isArray(s.depends_on))
261
271
  e.push(
262
272
  n(
263
273
  `milestones.${u}.depends_on`,
264
274
  "must be an array",
265
- o.depends_on
275
+ s.depends_on
266
276
  )
267
277
  );
268
278
  else
269
- for (const _ of o.depends_on)
270
- c(_) ? l.has(_) || e.push(
279
+ for (const _ of s.depends_on)
280
+ l(_) ? d.has(_) || e.push(
271
281
  n(`milestones.${u}.depends_on`, `unknown milestone "${_}"`)
272
282
  ) : e.push(
273
283
  n(`milestones.${u}.depends_on[]`, "must be a string", _)
274
284
  );
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(
285
+ const b = s.phase;
286
+ (!l(b) || b.trim() === "") && e.push(n(`milestones.${u}.phase`, "required string", b)), (!l(s.authored_by) || s.authored_by.trim() === "") && e.push(
277
287
  n(
278
288
  `milestones.${u}.authored_by`,
279
289
  "required string",
280
- o.authored_by
290
+ s.authored_by
281
291
  )
282
292
  );
283
- const m = o.model;
284
- m != null && !["Haiku", "Sonnet", "Opus"].includes(m) && e.push(
285
- n(`milestones.${u}.model`, "invalid model tier", m)
293
+ const f = s.model;
294
+ f != null && !["Haiku", "Sonnet", "Opus"].includes(f) && e.push(
295
+ n(`milestones.${u}.model`, "invalid model tier", f)
286
296
  );
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(
297
+ const y = s.effort;
298
+ y != null && !["low", "medium", "high", "xhigh", "max"].includes(y) && e.push(
299
+ n(`milestones.${u}.effort`, "invalid effort tier", y)
300
+ ), s.two_stage !== void 0 && typeof s.two_stage != "boolean" && e.push(
291
301
  n(
292
302
  `milestones.${u}.two_stage`,
293
303
  "must be a boolean",
294
- o.two_stage
304
+ s.two_stage
295
305
  )
296
- ), Array.isArray(o.read_only) || e.push(
306
+ ), Array.isArray(s.read_only) || e.push(
297
307
  n(
298
308
  `milestones.${u}.read_only`,
299
309
  "must be an array",
300
- o.read_only
310
+ s.read_only
301
311
  )
302
312
  );
303
313
  }
304
- const y = T(
314
+ const c = T(
305
315
  p
306
316
  );
307
- if (y)
308
- for (const { slug: u } of y)
317
+ if (c)
318
+ for (const { slug: u } of c)
309
319
  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));
320
+ if (l(a) && !d.has(a))
321
+ e.push(n("terminal", `unknown milestone "${a}"`));
322
+ else if (Array.isArray(a))
323
+ for (const u of a)
324
+ d.has(u) || e.push(n("terminal[]", `unknown milestone "${u}"`));
325
+ const o = t.operations, i = /* @__PURE__ */ new Set();
326
+ if (!Array.isArray(o) && !h(o))
327
+ e.push(n("operations", "must be an array or object", o));
318
328
  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));
329
+ const u = Array.isArray(o) ? o : Object.values(o);
330
+ for (const s of u) {
331
+ if (!h(s)) {
332
+ e.push(n("operations[]", "must be an object", s));
323
333
  continue;
324
334
  }
325
- const b = o.id;
326
- if (!c(b)) {
335
+ const b = s.id;
336
+ if (!l(b)) {
327
337
  e.push(n("operations[?]", "missing/invalid id", b));
328
338
  continue;
329
339
  }
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}"`)
340
+ const f = b;
341
+ i.add(f);
342
+ const y = s.milestone;
343
+ l(y) ? d.has(y) || e.push(
344
+ n(`operations[${f}].milestone`, `unknown milestone "${y}"`)
335
345
  ) : e.push(
336
- n(`operations[${m}].milestone`, "required string", h)
346
+ n(`operations[${f}].milestone`, "required string", y)
337
347
  );
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)) {
348
+ const _ = s.type;
349
+ l(_) && !D.has(_) && e.push(n(`operations[${f}].type`, "invalid type", _));
350
+ const v = s.shape;
351
+ if (h(v)) {
342
352
  const g = v.kind;
343
- g != null && c(g) && !D.has(g) && e.push(
344
- n(`operations[${m}].shape.kind`, "invalid shape kind", g)
353
+ g != null && l(g) && !E.has(g) && e.push(
354
+ n(`operations[${f}].shape.kind`, "invalid shape kind", g)
345
355
  );
346
356
  const $ = v.ops;
347
- if (Array.isArray($) && c(g))
357
+ if (Array.isArray($) && l(g))
348
358
  for (let w = 0; w < $.length; w++) {
349
359
  const A = $[w];
350
- if (!f(A))
360
+ if (!h(A))
351
361
  continue;
352
362
  const k = A.op;
353
- c(k) && !j(g, k) && e.push(
363
+ l(k) && !j(g, k) && e.push(
354
364
  n(
355
- `operations[${m}].shape.ops[${w}]`,
365
+ `operations[${f}].shape.ops[${w}]`,
356
366
  `op "${k}" not valid for kind "${g}"`
357
367
  )
358
368
  );
359
369
  }
360
370
  }
361
- Array.isArray(o.depends_on) || e.push(
371
+ Array.isArray(s.depends_on) || e.push(
362
372
  n(
363
- `operations[${m}].depends_on`,
373
+ `operations[${f}].depends_on`,
364
374
  "must be an array",
365
- o.depends_on
375
+ s.depends_on
366
376
  )
367
377
  );
368
378
  }
369
- for (const o of u) {
370
- if (!f(o))
379
+ for (const s of u) {
380
+ if (!h(s))
371
381
  continue;
372
- const b = o.id;
373
- if (!c(b))
382
+ const b = s.id;
383
+ if (!l(b))
374
384
  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(
385
+ const f = s.depends_on;
386
+ if (Array.isArray(f))
387
+ for (const y of f)
388
+ l(y) && !i.has(y) && e.push(
379
389
  n(
380
390
  `operations[${b}].depends_on`,
381
- `unknown operation "${h}"`
391
+ `unknown operation "${y}"`
382
392
  )
383
393
  );
384
394
  }
@@ -386,151 +396,156 @@ function N(t) {
386
396
  }
387
397
  return { valid: e.length === 0, errors: e };
388
398
  }
389
- function L(t) {
399
+ function N(t) {
390
400
  const e = [];
391
- if (!f(t))
401
+ if (!h(t))
392
402
  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));
403
+ for (const a of ["snapshot_at", "description"])
404
+ l(t[a]) || e.push(n(a, "required string field missing", t[a]));
405
+ l(t.plan) || e.push(n("plan", "required string", t.plan));
406
+ const r = t.milestones;
407
+ if (!h(r))
408
+ e.push(n("milestones", "must be an object", r));
399
409
  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));
410
+ const a = Object.keys(r), p = new Set(a);
411
+ for (const o of a) {
412
+ const i = r[o];
413
+ if (!h(i)) {
414
+ e.push(n(`milestones.${o}`, "must be an object", i));
405
415
  continue;
406
416
  }
407
- const u = a.eligible, o = a.pending;
408
- u === !0 && o !== null && o !== void 0 && e.push(
417
+ const u = i.eligible, s = i.pending;
418
+ u === !0 && s !== null && s !== void 0 && e.push(
409
419
  n(
410
- `milestones.${s}`,
420
+ `milestones.${o}`,
411
421
  "D-07 violation: eligible=true but pending not null"
412
422
  )
413
423
  ), typeof u != "boolean" && e.push(
414
- n(`milestones.${s}.eligible`, "must be boolean", u)
424
+ n(`milestones.${o}.eligible`, "must be boolean", u)
425
+ );
426
+ const b = i.status;
427
+ (!l(b) || !O.has(b)) && e.push(n(`milestones.${o}.status`, "invalid status", b)), u === !0 && b === "complete" && e.push(
428
+ n(
429
+ `milestones.${o}`,
430
+ "D-07 violation: eligible=true but status is complete"
431
+ )
415
432
  );
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)
433
+ const f = i.wave;
434
+ (typeof f != "number" || !Number.isInteger(f) || f < 0) && e.push(
435
+ n(`milestones.${o}.wave`, "must be non-negative integer", f)
421
436
  );
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)
437
+ const y = i.ki_estimate;
438
+ y != null && typeof y != "number" && e.push(
439
+ n(`milestones.${o}.ki_estimate`, "must be number|null", y)
425
440
  );
426
441
  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));
442
+ const v = i[_];
443
+ v != null && typeof v != "number" && e.push(n(`milestones.${o}.${_}`, "must be number|null", v));
429
444
  }
430
- Array.isArray(a.artifacts) || e.push(
445
+ Array.isArray(i.artifacts) || e.push(
431
446
  n(
432
- `milestones.${s}.artifacts`,
447
+ `milestones.${o}.artifacts`,
433
448
  "must be an array",
434
- a.artifacts
449
+ i.artifacts
435
450
  )
436
- ), typeof a.si_bytes != "number" && e.push(
437
- n(`milestones.${s}.si_bytes`, "must be a number", a.si_bytes)
451
+ ), typeof i.si_bytes != "number" && e.push(
452
+ n(`milestones.${o}.si_bytes`, "must be a number", i.si_bytes)
438
453
  );
439
454
  }
440
- const d = t.operations;
441
- if (!Array.isArray(d))
442
- e.push(n("operations", "must be an array", d));
455
+ const m = t.operations;
456
+ if (!Array.isArray(m))
457
+ e.push(n("operations", "must be an array", m));
443
458
  else
444
- for (const s of d) {
445
- if (!f(s))
459
+ for (const o of m) {
460
+ if (!h(o))
446
461
  continue;
447
- const a = c(s.id) ? s.id : "?", u = s.milestone;
448
- c(u) && !p.has(u) && e.push(
462
+ const i = l(o.id) ? o.id : "?", u = o.milestone;
463
+ l(u) && !p.has(u) && e.push(
449
464
  n(
450
- `operations[${a}].milestone`,
465
+ `operations[${i}].milestone`,
451
466
  `unknown milestone "${u}"`
452
467
  )
453
468
  );
454
- const o = s.status;
455
- (!c(o) || !I.has(o)) && e.push(
469
+ const s = o.status;
470
+ (!l(s) || !I.has(s)) && e.push(
456
471
  n(
457
- `operations[${a}].status`,
472
+ `operations[${i}].status`,
458
473
  "invalid operation status",
459
- o
474
+ s
460
475
  )
461
- ), Array.isArray(s.dispatch_ids) || e.push(
476
+ ), Array.isArray(o.dispatch_ids) || e.push(
462
477
  n(
463
- `operations[${a}].dispatch_ids`,
478
+ `operations[${i}].dispatch_ids`,
464
479
  "must be an array",
465
- s.dispatch_ids
480
+ o.dispatch_ids
466
481
  )
467
- ), typeof s.attempt_count != "number" && e.push(
482
+ ), typeof o.attempt_count != "number" && e.push(
468
483
  n(
469
- `operations[${a}].attempt_count`,
484
+ `operations[${i}].attempt_count`,
470
485
  "must be a number",
471
- s.attempt_count
486
+ o.attempt_count
472
487
  )
473
- ), Array.isArray(s.blast_radius) || e.push(
488
+ ), Array.isArray(o.blast_radius) || e.push(
474
489
  n(
475
- `operations[${a}].blast_radius`,
490
+ `operations[${i}].blast_radius`,
476
491
  "must be an array",
477
- s.blast_radius
492
+ o.blast_radius
478
493
  )
479
494
  );
480
495
  }
481
- const l = t.open_questions;
482
- if (!Array.isArray(l))
483
- e.push(n("open_questions", "must be an array", l));
496
+ const d = t.open_questions;
497
+ if (!Array.isArray(d))
498
+ e.push(n("open_questions", "must be an array", d));
484
499
  else
485
- for (const s of l) {
486
- if (!f(s))
500
+ for (const o of d) {
501
+ if (!h(o))
487
502
  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(
503
+ const i = o.blocking;
504
+ (!l(i) || !p.has(i)) && e.push(
505
+ n("open_questions", `unknown blocking milestone "${i}"`)
506
+ ), typeof o.surfaced != "boolean" && e.push(
492
507
  n(
493
- `open_questions[${s.id}]`,
508
+ `open_questions[${o.id}]`,
494
509
  "surfaced must be boolean",
495
- s.surfaced
510
+ o.surfaced
496
511
  )
497
- ), typeof s.answered != "boolean" && e.push(
512
+ ), typeof o.answered != "boolean" && e.push(
498
513
  n(
499
- `open_questions[${s.id}]`,
514
+ `open_questions[${o.id}]`,
500
515
  "answered must be boolean",
501
- s.answered
516
+ o.answered
502
517
  )
503
518
  );
504
519
  }
505
- const y = t.dispatch_units;
506
- if (y !== void 0)
507
- if (!Array.isArray(y))
520
+ const c = t.dispatch_units;
521
+ if (c !== void 0)
522
+ if (!Array.isArray(c))
508
523
  e.push(
509
- n("dispatch_units", "must be an array if present", y)
524
+ n("dispatch_units", "must be an array if present", c)
510
525
  );
511
526
  else
512
- for (const s of y) {
513
- if (!f(s))
527
+ for (const o of c) {
528
+ if (!h(o))
514
529
  continue;
515
- const a = s.fits_context_window;
516
- typeof a != "boolean" && e.push(
530
+ const i = o.fits_context_window;
531
+ typeof i != "boolean" && e.push(
517
532
  n(
518
- `dispatch_units[${s.id}]`,
533
+ `dispatch_units[${o.id}]`,
519
534
  "fits_context_window must be boolean",
520
- a
535
+ i
521
536
  )
522
537
  );
523
- const u = s.milestones;
538
+ const u = o.milestones;
524
539
  if (Array.isArray(u))
525
- for (const o of u)
526
- c(o) && !p.has(o) && e.push(
540
+ for (const s of u)
541
+ l(s) && !p.has(s) && e.push(
527
542
  n(
528
- `dispatch_units[${s.id}].milestones`,
529
- `unknown milestone "${o}"`
543
+ `dispatch_units[${o.id}].milestones`,
544
+ `unknown milestone "${s}"`
530
545
  )
531
546
  );
532
547
  }
533
- f(t.pairwise_overlap) || e.push(
548
+ h(t.pairwise_overlap) || e.push(
534
549
  n(
535
550
  "pairwise_overlap",
536
551
  "must be an object",
@@ -540,21 +555,21 @@ function L(t) {
540
555
  }
541
556
  return { valid: e.length === 0, errors: e };
542
557
  }
543
- function z(t) {
544
- const e = N(t);
558
+ function P(t) {
559
+ const e = L(t);
545
560
  if (!e.valid)
546
561
  throw new Error(
547
562
  `DagJson validation failed:
548
- ${e.errors.map((i) => ` ${i.path}: ${i.message}`).join(`
563
+ ${e.errors.map((r) => ` ${r.path}: ${r.message}`).join(`
549
564
  `)}`
550
565
  );
551
566
  }
552
567
  function H(t) {
553
- const e = L(t);
568
+ const e = N(t);
554
569
  if (!e.valid)
555
570
  throw new Error(
556
571
  `Snapshot validation failed:
557
- ${e.errors.map((i) => ` ${i.path}: ${i.message}`).join(`
572
+ ${e.errors.map((r) => ` ${r.path}: ${r.message}`).join(`
558
573
  `)}`
559
574
  );
560
575
  }
@@ -632,21 +647,21 @@ const S = [
632
647
  });
633
648
  let e = [];
634
649
  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);
650
+ for (const r of e)
651
+ if (typeof r == "object" && r !== null) {
652
+ const a = r;
653
+ a.type === void 0 && (a.type = "generative"), a.criteria === void 0 && (a.criteria = []), a.tool === void 0 && (a.tool = null), a.args === void 0 && (a.args = null);
639
654
  }
640
655
  }
641
656
  }
642
657
  ];
643
- function K(t, e, i) {
658
+ function K(t, e, r) {
644
659
  if (t === e)
645
660
  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;
661
+ let a = t;
662
+ for (const p of [...S].sort((m, d) => m.from - d.from))
663
+ if (p.from === a && (p.apply(r), a = p.to, a === e)) {
664
+ r.schema_version = e;
650
665
  return;
651
666
  }
652
667
  throw new Error(
@@ -657,11 +672,11 @@ function K(t, e, i) {
657
672
  }
658
673
  export {
659
674
  x as VALID_OPS_BY_KIND,
660
- R as WRITE_CLASS_ACTIONS,
661
- z as assertValidDagJson,
675
+ z as WRITE_CLASS_ACTIONS,
676
+ P as assertValidDagJson,
662
677
  H as assertValidSnapshot,
663
678
  j as isValidOpForKind,
664
679
  K as migrateDag,
665
- N as validateDagJson,
666
- L as validateSnapshot
680
+ L as validateDagJson,
681
+ N as validateSnapshot
667
682
  };
package/lib/types.d.ts CHANGED
@@ -17,6 +17,7 @@ export type ShapeKind = CodeKind | ConfigKind | 'doc' | 'structured-output';
17
17
  export type KindFamily = 'code-config' | 'doc' | 'structured' | 'tool-call';
18
18
  export type MilestoneStatus = 'pending' | 'pending-surfaced' | 'in_progress' | 'complete' | 'failed' | 'skipped';
19
19
  export type DispatchUnitStatus = 'pending' | 'in_progress' | 'complete' | 'failed';
20
+ export type DispatchExecutionMode = 'model-dispatch' | 'guard-only';
20
21
  export type OperationStatus = 'pending' | 'in_progress' | 'complete' | 'failed' | 'skipped';
21
22
  export type GuardResult = 'pass' | 'fail';
22
23
  export type MilestoneGuardResult = 'pass' | 'fail' | 'pending';
@@ -38,6 +39,7 @@ export interface ShapeOpDag {
38
39
  to: string | null;
39
40
  position: number | null;
40
41
  required: boolean | null;
42
+ type_spec?: Record<string, string> | null;
41
43
  }
42
44
  export interface ShapeOpSnapshot extends ShapeOpDag {
43
45
  from: string | null;
@@ -323,9 +325,11 @@ export interface DispatchUnit {
323
325
  two_stage: boolean;
324
326
  provider: ProviderConfig | null;
325
327
  agent_name: string;
328
+ execution_mode: DispatchExecutionMode;
326
329
  mcp_servers: Record<string, unknown> | null;
327
330
  resolved_max_tokens: number | null;
328
331
  background: true;
332
+ systemPrompt: string | null;
329
333
  prompt: string | null;
330
334
  context_files: string[];
331
335
  si_bytes: number;
@@ -356,3 +360,15 @@ export interface IOptimizerDeps {
356
360
  readFiles?: (paths: string[]) => Map<string, string>;
357
361
  dispatchLog?: DispatchLogEntry[];
358
362
  }
363
+ /**
364
+ * B-calibration store contract (SCOPE.md §C4 / §Open Decisions #2 —
365
+ * `~/.adhd/dispatch-calibration.json`). Formalizes what was previously a
366
+ * dispatch-orchestrator-local `ICalibrationPlaceholder` (DEBT-DISPATCH-018):
367
+ * read-only access to whatever calibrated per-tier B values a calibration
368
+ * utility has persisted. Deliberately minimal — a single `read()` accessor —
369
+ * so any storage backend (JSON file, DB, in-memory fixture) can implement it
370
+ * without dispatch-orchestrator depending on the storage mechanism.
371
+ */
372
+ export interface ICalibrationStore {
373
+ read(): Promise<Record<string, number>> | Record<string, number>;
374
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhd/dispatch-base-spec",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "dependencies": {},
5
5
  "main": "./index.js",
6
6
  "module": "./index.mjs",