@adhd/dispatch-core-optimizer 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 @@
1
+ export * from './lib';
package/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const de=new Set(["create","modify-signature","modify-body","add-export","remove-export","rename"]),me=new Set(["pending","pending-surfaced","in_progress","complete","failed","skipped"]),_e=new Set(["pending","in_progress","complete","failed","skipped"]);function S(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function O(e){return typeof e=="string"}function h(e,t,n){return{path:e,message:t,value:n}}function ge(e){const t=[];if(!S(e))return{valid:!1,errors:[h("","root must be an object")]};for(const o of["snapshot_at","description"])O(e[o])||t.push(h(o,"required string field missing",e[o]));O(e.plan)||t.push(h("plan","required string",e.plan));const n=e.milestones;if(!S(n))t.push(h("milestones","must be an object",n));else{const o=Object.keys(n),i=new Set(o);for(const s of o){const r=n[s];if(!S(r)){t.push(h(`milestones.${s}`,"must be an object",r));continue}const c=r.eligible,u=r.pending;c===!0&&u!==null&&u!==void 0&&t.push(h(`milestones.${s}`,"D-07 violation: eligible=true but pending not null")),typeof c!="boolean"&&t.push(h(`milestones.${s}.eligible`,"must be boolean",c));const p=r.status;(!O(p)||!me.has(p))&&t.push(h(`milestones.${s}.status`,"invalid status",p));const _=r.wave;(typeof _!="number"||!Number.isInteger(_)||_<0)&&t.push(h(`milestones.${s}.wave`,"must be non-negative integer",_));const b=r.ki_estimate;b!=null&&typeof b!="number"&&t.push(h(`milestones.${s}.ki_estimate`,"must be number|null",b));for(const v of["tokens_estimated","tokens_actual"]){const y=r[v];y!=null&&typeof y!="number"&&t.push(h(`milestones.${s}.${v}`,"must be number|null",y))}Array.isArray(r.artifacts)||t.push(h(`milestones.${s}.artifacts`,"must be an array",r.artifacts)),typeof r.si_bytes!="number"&&t.push(h(`milestones.${s}.si_bytes`,"must be a number",r.si_bytes))}const l=e.operations;if(!Array.isArray(l))t.push(h("operations","must be an array",l));else for(const s of l){if(!S(s))continue;const r=O(s.id)?s.id:"?",c=s.milestone;O(c)&&!i.has(c)&&t.push(h(`operations[${r}].milestone`,`unknown milestone "${c}"`));const u=s.status;(!O(u)||!_e.has(u))&&t.push(h(`operations[${r}].status`,"invalid operation status",u)),Array.isArray(s.dispatch_ids)||t.push(h(`operations[${r}].dispatch_ids`,"must be an array",s.dispatch_ids)),typeof s.attempt_count!="number"&&t.push(h(`operations[${r}].attempt_count`,"must be a number",s.attempt_count)),Array.isArray(s.blast_radius)||t.push(h(`operations[${r}].blast_radius`,"must be an array",s.blast_radius))}const f=e.open_questions;if(!Array.isArray(f))t.push(h("open_questions","must be an array",f));else for(const s of f){if(!S(s))continue;const r=s.blocking;(!O(r)||!i.has(r))&&t.push(h("open_questions",`unknown blocking milestone "${r}"`)),typeof s.surfaced!="boolean"&&t.push(h(`open_questions[${s.id}]`,"surfaced must be boolean",s.surfaced)),typeof s.answered!="boolean"&&t.push(h(`open_questions[${s.id}]`,"answered must be boolean",s.answered))}const a=e.dispatch_units;if(a!==void 0)if(!Array.isArray(a))t.push(h("dispatch_units","must be an array if present",a));else for(const s of a){if(!S(s))continue;const r=s.fits_context_window;typeof r!="boolean"&&t.push(h(`dispatch_units[${s.id}]`,"fits_context_window must be boolean",r));const c=s.milestones;if(Array.isArray(c))for(const u of c)O(u)&&!i.has(u)&&t.push(h(`dispatch_units[${s.id}].milestones`,`unknown milestone "${u}"`))}S(e.pairwise_overlap)||t.push(h("pairwise_overlap","must be an object",e.pairwise_overlap))}return{valid:t.length===0,errors:t}}function he(e){const t=ge(e);if(!t.valid)throw new Error(`Snapshot validation failed:
2
+ ${t.errors.map(n=>` ${n.path}: ${n.message}`).join(`
3
+ `)}`)}const U={prose:5.5,md:5.5,ts:6.3,tsx:6.3,py:6.3,default:4};function N(e,t){if(e<=0)return 0;const o=U[""]??U.default??4;return Math.ceil(e/o)}function Z(e,t){if(e.length===0||t.fileSizes===void 0)return new Map;try{return t.fileSizes(e)}catch{return new Map}}const be={low:600,medium:1e3,high:2e3},ye=de;function ve(e){const t=e.properties??{};return Object.keys(t).length||Object.keys(e).length}function we(e,t){if(e.type==="tool-call")return 0;const n=e.shape;if(!n||n.kind===null)return 0;const o=n.kind;if(o==="function"||o==="interface"||o==="type"||o==="class"||o==="enum"||o==="const"||o==="script")return("ops"in n&&Array.isArray(n.ops)?n.ops:[]).length*200;if(o==="config"||o==="env"||o==="schema"||o==="manifest")return("ops"in n&&Array.isArray(n.ops)?n.ops:[]).length*100;if(o==="doc")return be[t??"medium"]??1e3;if(o==="structured-output"){const i="schema"in n&&n.schema?n.schema:{};return ve(i)*50}return 0}function ke(e,t){const n=new Set([...Object.keys(e),...Object.keys(t)]),o={};for(const i of n){const l=e[i];o[i]=l??t[i]??null}return o}function $e(e,t){const n=new Set([...Object.keys(e),...Object.keys(t)]),o={};for(const i of n){const l=e[i]??t[i];if(l===void 0)throw new TypeError(`Model tier "${i}" not found in dag or deps context_window_per_tier`);o[i]=l}return o}function ee(e){var a;const t=Object.keys(e),n=new Map,o=new Map;for(const s of t)n.has(s)||n.set(s,0),o.has(s)||o.set(s,[]);for(const s of t){const r=((a=e[s])==null?void 0:a.depends_on)??[];for(const c of r){const u=o.get(c);u!==void 0&&u.push(s),n.set(s,(n.get(s)??0)+1)}}const i=new Map,l=[];let f=t.filter(s=>(n.get(s)??0)===0).sort();for(const s of f)i.set(s,0);for(;f.length>0;){const s=[];for(const r of f){l.push(r);const c=(o.get(r)??[]).slice().sort();for(const u of c){const p=(n.get(u)??0)-1;n.set(u,p);const _=i.get(r)??0,b=i.get(u)??0;i.set(u,Math.max(b,_+1)),p===0&&s.push(u)}}f=s.sort()}if(l.length!==t.length){const s=new Set(l),r=t.filter(c=>!s.has(c));throw new Error(`snapshot(): cycle detected in milestone depends_on graph. Nodes involved: [${r.join(", ")}]`)}return{order:l,waves:i}}function te(e,t){if(t.length===0)return[];const n=new Set(t);return e.filter(o=>o.operations.some(i=>n.has(i)))}function ne(e,t){return e.filter(n=>n.operations.includes(t))}function Ae(e,t,n,o,i){const l=`${e}.guard`,f=[...n,l],a=te(o,f);for(const r of a)for(const c of r.results)if(c.op_id===l&&c.guard_result==="pass")return"complete";for(const r of a)for(const c of r.results)if(f.includes(c.op_id)&&c.status==="failed")return"failed";for(const r of a)for(const c of r.results)if(f.includes(c.op_id)&&c.status==="in_progress")return"in_progress";const s=i.length===0||i.every(r=>r==="complete");return t.pending!==null&&s?"pending-surfaced":"pending"}function Oe(e,t){const n=`${e}.guard`;let o=null,i=null,l=null;for(const f of t)if(f.operations.includes(n))for(const a of f.results)a.op_id===n&&a.guard_result!==null&&(o=a.guard_result,i=a.guard_output,a.guard_result==="pass"&&(l=a.guard_ran_at));return o!==null?{guard_result:o,guard_output:i,completed_at:l}:{guard_result:"pending",guard_output:null,completed_at:null}}function Se(e){const t=new Set;for(const n of e)n.action==="move"&&n.to_file?t.add(n.to_file):ye.has(n.action)&&n.file&&t.add(n.file);return Array.from(t)}function Me(e,t,n){const o=ne(n,e.id),i=o.map(c=>c.id);let l=null,f=null,a=null;for(const c of o)for(const u of c.results)u.op_id===e.id&&u.guard_result!==null&&(l=u.guard_result,f=u.guard_output,a=u.guard_ran_at);const s=e.ki_estimate!==null&&e.ki_estimate!==void 0?e.ki_estimate:we(e,t),r=je(e.shape);return{...e,ki_estimate:s,shape:r,dispatch_ids:i,attempt_count:0,guard_result:l,guard_output:f,guard_ran_at:a,blast_radius:[],conflict:{detected:!1,competing_op:null,op_key:null,resolution:null},tokens_actual:null}}function je(e){if(!e)return null;if(e.kind===null)return e;const t=e.kind;if(t==="function"||t==="interface"||t==="type"||t==="class"||t==="enum"||t==="const"||t==="script"||t==="config"||t==="env"||t==="schema"||t==="manifest"){const n=("ops"in e&&Array.isArray(e.ops)?e.ops:[]).map(o=>({...o,from:null,breaking:null,severity:null}));return{...e,ops:n}}return e}function Te(e,t,n,o){const i=`${e}.guard`,l=ne(o,i),f=l.map(c=>c.id);let a=null,s=null,r=null;for(const c of l)for(const u of c.results)u.op_id===i&&u.guard_result!==null&&(a=u.guard_result,s=u.guard_output,r=u.guard_ran_at);return{id:i,milestone:e,depends_on:n,type:"tool-call",action:"guard",file:null,symbol:null,provenance:null,confidence:null,audit_check:null,criteria:[],tool:null,args:null,guard:t.guard??null,to_file:null,to_symbol:null,ki_estimate:0,ki_source:null,authored_by:"synthesized",status:"pending",shape:null,dispatch_ids:f,attempt_count:0,guard_result:a,guard_output:s,guard_ran_at:r,blast_radius:[],conflict:{detected:!1,competing_op:null,op_key:null,resolution:null},tokens_actual:null}}function Ie(e,t){const n={},{write_multiplier:o,read_multiplier:i,hit_probability:l}=t;for(const[f,a]of Object.entries(e))a===null?n[f]=null:n[f]=Math.round(a*((1-l)*o+l*i));return n}function xe(e,t,n){const o=Array.from(e.keys()),i={};for(let l=0;l<o.length;l++)for(let f=l+1;f<o.length;f++){const a=o[l],s=o[f];if(a===void 0||s===void 0)continue;const[r,c]=a<s?[a,s]:[s,a],u=`${r}:${c}`,p=e.get(a)??[],_=e.get(s)??[],b=new Set(p.map(w=>w.file).filter(w=>w!==null)),v=new Set(_.map(w=>w.file).filter(w=>w!==null)),y=Array.from(b).filter(w=>v.has(w));if(y.length===0)continue;const k=t.get(a)??"pending",$=t.get(s)??"pending",M=k==="complete"&&$==="complete";let P=0;for(const w of y)P+=M?n(w):0;i[u]=P}return i}function ze(e){return(Array.isArray(e)?e:Object.values(e)).map(n=>n.type===void 0?{...n,type:"generative"}:n)}function Pe(e,t){const n=new Date().toISOString(),o=t.dispatchLog??e.dispatch_log,i=e.optimization.sentinel_fanout,l=ke(e.optimization.b_per_tier,t.bPerTier),f=$e(e.optimization.context_window_per_tier,t.contextWindowPerTier),a=Ie(l,i),{waves:s}=ee(e.milestones),r=ze(e.operations),c=new Map;for(const m of r){const d=c.get(m.milestone)??[];d.push(m),c.set(m.milestone,d)}const u=new Set;for(const m of r)m.file&&u.add(m.file),m.action==="move"&&m.to_file&&u.add(m.to_file);const p=Z(Array.from(u),t),_=m=>p.get(m)??0,b=new Map,v=Array.from(s.entries()).sort((m,d)=>m[1]-d[1]||m[0].localeCompare(d[0])).map(([m])=>m);for(const m of v){const d=e.milestones[m];if(d===void 0)continue;const j=(c.get(m)??[]).map(I=>I.id),T=d.depends_on.map(I=>b.get(I)??"pending"),x=Ae(m,d,j,o,T);b.set(m,x)}const y={},k=[];for(const m of v){const d=e.milestones[m];if(d===void 0)continue;const j=s.get(m)??0,T=c.get(m)??[],x=T.map(g=>g.id),I=b.get(m)??"pending",D=d.depends_on.map(g=>b.get(g)??"pending"),le=d.depends_on.length===0||D.every(g=>g==="complete"),ue=D.every(g=>g!=="failed"),ae=d.pending===null&&le&&ue,E=te(o,[...x,`${m}.guard`]),ce=E.length>0?E.map(g=>g.started_at).sort()[0]??null:null;let F,q,B;if(d.guard){const g=Oe(m,o);F=g.guard_result,q=g.guard_output,B=g.completed_at}else F=null,q=null,B=null;const R=Se(T),K=R.reduce((g,A)=>g+_(A),0),W=T.map(g=>Me(g,d.effort,o));let z=null,G=0,L=!1;for(const g of W){if(g.ki_estimate===null){L=!0;break}G+=g.ki_estimate}L||(z=G);let V=null;const Y=d.model;if(Y!==null&&z!==null){const g=a[Y]??null;if(g!==null){const A=N(K);V=g+A+z}}let H=null;const J=E.filter(g=>g.completed_at!==null&&g.results.length>0&&g.results.every(A=>A.status==="complete"||A.status==="skipped"));J.length>0&&(H=J.reduce((g,A)=>g+A.turns.reduce((pe,Q)=>pe+Q.input_tokens+Q.output_tokens,0),0));const fe=Te(m,d,x,o);k.push(...W,fe),y[m]={description:d.description,...d.rationale!==void 0?{rationale:d.rationale}:{},authored_by:d.authored_by,pending:d.pending,triggered_by:d.triggered_by,phase:d.phase,depends_on:d.depends_on,agent:d.agent,model:d.model,effort:d.effort,two_stage:d.two_stage,read_only:d.read_only,guard:d.guard,context:`contexts/${m}.md`,wave:j,eligible:ae,status:I,started_at:ce,completed_at:B,guard_result:F,guard_output:q,artifacts:R,si_bytes:K,ki_estimate:z,tokens_estimated:V,tokens_actual:H}}const $=xe(c,b,_),M=Ee(e,y),w={tokens_naive:(()=>{let m=0;for(const d of Object.values(y))if(d.eligible&&d.status==="pending"&&d.model!==null){const j=l[d.model]??t.bPerTier[d.model]??0;m+=j+(d.ki_estimate??0)}return m})(),sentinel_fanout:i,context_window_override:e.optimization.context_window_override,b_override:e.optimization.b_override,b_per_tier:l,b_eff_per_tier:a,context_window_per_tier:f},C={snapshot_at:n,snapshot_version:1,plan:"",schema_version:e.schema_version,plan_kind:e.plan_kind,description:e.description,problem:e.problem,approach:e.approach,executor:e.executor,...e.executor_model!==void 0?{executor_model:e.executor_model}:{},...e.executor_effort!==void 0?{executor_effort:e.executor_effort}:{},phases:[...e.phases],terminal:e.terminal,...e.assumed_baseline!==void 0?{assumed_baseline:e.assumed_baseline}:{},optimization:w,milestones:y,operations:k,pairwise_overlap:$,open_questions:M};return he(C),C}function Ee(e,t){const n=[];for(const[o,i]of Object.entries(t)){const l=e.milestones[o];if(l===void 0||l.pending===null)continue;const f=i.status==="pending-surfaced";n.push({id:`q:${o}`,text:l.pending,blocking:o,surfaced:f,raised_at_dispatch:null,raised_at_turn:null,answered:!1,answer:null})}return n}function oe(e){return Object.keys(e.milestones).filter(t=>{const n=e.milestones[t];return n!==void 0&&n.eligible===!0&&n.status==="pending"})}function Fe(e){if(e.length===0||e.every(t=>t.type==="tool-call"))return"tool-call";for(const t of e){if(t.type!=="generative"||!t.shape||t.shape.kind===null)continue;const n=t.shape.kind;if(n==="function"||n==="interface"||n==="type"||n==="class"||n==="enum"||n==="const"||n==="script"||n==="config"||n==="env"||n==="schema"||n==="manifest")return"code-config";if(n==="doc")return"doc";if(n==="structured-output")return"structured"}return"code-config"}function qe(e){if(!e)return"";const t=e.indexOf(":");return t>=0?e.slice(t+1):e}function Be(e,t,n){let o=!0;const i=[];for(const l of e){const f=t[l];if(f===void 0)continue;const a=n.filter(r=>r.milestone===l&&r.action!=="guard");a.some(r=>r.type==="generative")&&(o=!1),i.push(`## Milestone: ${l}`),i.push(f.description),f.rationale&&i.push(`
4
+ *Rationale:* ${f.rationale}`),i.push(`
5
+ ### Operations:`);for(const r of a){const c=r.file?` ${r.file}`:"",u=r.symbol?` (${r.symbol})`:"";if(i.push(`- [${r.id}] ${r.action}${c}${u}`),r.shape&&r.shape.kind!==null){const p=r.shape;if(p.kind==="doc")i.push(` Description: ${p.description}`),i.push(` Objective: ${p.objective}`),p.required_sections&&p.required_sections.length>0&&i.push(` Required sections: ${p.required_sections.join(", ")}`);else if(p.kind==="structured-output")i.push(` Schema:
6
+ `+JSON.stringify(p.schema,null,2).split(`
7
+ `).map(_=>` ${_}`).join(`
8
+ `));else if("ops"in p&&Array.isArray(p.ops))for(const _ of p.ops){const b=_.to!==null?` → ${_.to}`:"",v=_.target!==null?` "${_.target}"`:"";i.push(` - ${_.op}${v}${b}`)}}}const s=f.guard;i.push(`
9
+ ### Guard: ${s??"none"}`),i.push("")}return o?null:i.join(`
10
+ `)}function se(e,t,n){if(e===null)return null;const o=t.optimization.b_eff_per_tier[e];if(o!=null)return o;const i=t.optimization.b_per_tier[e];if(i!=null)return i;const l=n.bPerTier[e];return l!==void 0?l:null}function ie(e,t,n){if(e===null)return Number.POSITIVE_INFINITY;const o=t.optimization.context_window_per_tier[e];if(o!==void 0)return o;const i=n.contextWindowPerTier[e];return i!==void 0?i:Number.POSITIVE_INFINITY}function re(e,t){const n=new Set;for(const o of e){const i=t.snapshot.milestones[o];if(i!==void 0){n.add(i.context);for(const l of i.read_only)n.add(l);for(const l of t.opsByMilestone.get(o)??[])l.file&&n.add(l.file)}}return Array.from(n)}function X(e,t,n){const o=se(t,n.snapshot,n.deps);if(o===null)return null;const l=re(e,n).reduce((s,r)=>s+(n.sizeMap.get(r)??0),0),f=N(l),a=e.reduce((s,r)=>{var c;return s+(((c=n.snapshot.milestones[r])==null?void 0:c.ki_estimate)??0)},0);return o+f+a}function Ne(e,t,n){const o=ie(t,n.snapshot,n.deps),i=[...e].sort((a,s)=>{var u,p;const r=((u=n.snapshot.milestones[a])==null?void 0:u.ki_estimate)??0,c=((p=n.snapshot.milestones[s])==null?void 0:p.ki_estimate)??0;return r-c||a.localeCompare(s)}),l=[];let f=[];for(const a of i){const s=[...f,a],r=X(s,t,n);r!==null&&r>o&&f.length>0?(l.push(f),f=[a]):f=s}return f.length>0&&l.push(f),l.filter(a=>{const s=X(a,t,n);return s===null||s<=o})}function Ce(e,t,n,o){const i=e[0]??"",l=o.snapshot.milestones[i],f=(l==null?void 0:l.effort)??null,a=[];for(const k of e)for(const $ of o.opsByMilestone.get(k)??[])a.push($.id);const s=re(e,o),r=s.reduce((k,$)=>k+(o.sizeMap.get($)??0),0),c=se(n,o.snapshot,o.deps),u=e.reduce((k,$)=>{var M;return k+(((M=o.snapshot.milestones[$])==null?void 0:M.ki_estimate)??0)},0),p=c!==null?c+N(r)+u:null,_=ie(n,o.snapshot,o.deps),b=p!==null?p<=_:!0,v=qe((l==null?void 0:l.agent)??null),y=Be(e,o.snapshot.milestones,o.snapshot.operations);return{id:`${i}.dispatch.${t}`,milestones:e,operations:a,model:n,effort:f,two_stage:(l==null?void 0:l.two_stage)??!1,provider:null,agent_name:v,mcp_servers:null,resolved_max_tokens:null,background:!0,prompt:y,context_files:s,si_bytes:r,tokens_estimated:p,fits_context_window:b,sentinel_role:null,dispatch_log_id:null,remote_task_id:null,result:null,status:"pending",started_at:null,completed_at:null,tokens_actual:null}}function De(e,t){const n=oe(e);if(n.length===0)return[];const o=new Map;for(const u of e.operations){if(u.action==="guard")continue;const p=o.get(u.milestone)??[];p.push(u),o.set(u.milestone,p)}const i=new Map;for(const u of n){const p=e.milestones[u];if(p===void 0)continue;const _=Fe(o.get(u)??[]),b=p.model??"_null",v=`${_}:${b}`,y=i.get(v)??[];y.push(u),i.set(v,y)}const l=new Set;for(const u of n){const p=e.milestones[u];if(p!==void 0){l.add(p.context);for(const _ of p.read_only)l.add(_);for(const _ of o.get(u)??[])_.file&&l.add(_.file)}}const f=Z(Array.from(l),t),a={snapshot:e,deps:t,sizeMap:f,opsByMilestone:o},s=[];let r=0;const c=Array.from(i.keys()).sort();for(const u of c){const p=i.get(u);if(p===void 0)continue;const _=u.slice(u.indexOf(":")+1),b=_!=="_null"?_:null,v=Ne(p,b,a);for(const y of v)s.push(Ce(y,r++,b,a))}return s}function Re(e,t){let n=0;for(const o of oe(e)){const i=e.milestones[o];if(i===void 0)continue;const l=Ke(i.model,e,t)??0;n+=l+(i.ki_estimate??0)}return n}function Ke(e,t,n){if(e===null)return null;const o=t.optimization.b_per_tier[e];if(o!=null)return o;const i=n.bPerTier[e];return i!==void 0?i:null}exports.computeTokensNaive=Re;exports.optimize=De;exports.snapshot=Pe;exports.topoSortMilestones=ee;
package/index.mjs ADDED
@@ -0,0 +1,901 @@
1
+ const pe = /* @__PURE__ */ new Set([
2
+ "create",
3
+ "modify-signature",
4
+ "modify-body",
5
+ "add-export",
6
+ "remove-export",
7
+ "rename"
8
+ ]), de = /* @__PURE__ */ new Set([
9
+ "pending",
10
+ "pending-surfaced",
11
+ "in_progress",
12
+ "complete",
13
+ "failed",
14
+ "skipped"
15
+ ]), _e = /* @__PURE__ */ new Set([
16
+ "pending",
17
+ "in_progress",
18
+ "complete",
19
+ "failed",
20
+ "skipped"
21
+ ]);
22
+ function S(e) {
23
+ return typeof e == "object" && e !== null && !Array.isArray(e);
24
+ }
25
+ function O(e) {
26
+ return typeof e == "string";
27
+ }
28
+ function g(e, t, n) {
29
+ return { path: e, message: t, value: n };
30
+ }
31
+ function me(e) {
32
+ const t = [];
33
+ if (!S(e))
34
+ return { valid: !1, errors: [g("", "root must be an object")] };
35
+ for (const o of ["snapshot_at", "description"])
36
+ O(e[o]) || t.push(g(o, "required string field missing", e[o]));
37
+ O(e.plan) || t.push(g("plan", "required string", e.plan));
38
+ const n = e.milestones;
39
+ if (!S(n))
40
+ t.push(g("milestones", "must be an object", n));
41
+ else {
42
+ const o = Object.keys(n), i = new Set(o);
43
+ for (const s of o) {
44
+ const r = n[s];
45
+ if (!S(r)) {
46
+ t.push(g(`milestones.${s}`, "must be an object", r));
47
+ continue;
48
+ }
49
+ const c = r.eligible, u = r.pending;
50
+ c === !0 && u !== null && u !== void 0 && t.push(
51
+ g(
52
+ `milestones.${s}`,
53
+ "D-07 violation: eligible=true but pending not null"
54
+ )
55
+ ), typeof c != "boolean" && t.push(
56
+ g(`milestones.${s}.eligible`, "must be boolean", c)
57
+ );
58
+ const p = r.status;
59
+ (!O(p) || !de.has(p)) && t.push(g(`milestones.${s}.status`, "invalid status", p));
60
+ const m = r.wave;
61
+ (typeof m != "number" || !Number.isInteger(m) || m < 0) && t.push(
62
+ g(`milestones.${s}.wave`, "must be non-negative integer", m)
63
+ );
64
+ const b = r.ki_estimate;
65
+ b != null && typeof b != "number" && t.push(
66
+ g(`milestones.${s}.ki_estimate`, "must be number|null", b)
67
+ );
68
+ for (const v of ["tokens_estimated", "tokens_actual"]) {
69
+ const y = r[v];
70
+ y != null && typeof y != "number" && t.push(g(`milestones.${s}.${v}`, "must be number|null", y));
71
+ }
72
+ Array.isArray(r.artifacts) || t.push(
73
+ g(
74
+ `milestones.${s}.artifacts`,
75
+ "must be an array",
76
+ r.artifacts
77
+ )
78
+ ), typeof r.si_bytes != "number" && t.push(
79
+ g(`milestones.${s}.si_bytes`, "must be a number", r.si_bytes)
80
+ );
81
+ }
82
+ const l = e.operations;
83
+ if (!Array.isArray(l))
84
+ t.push(g("operations", "must be an array", l));
85
+ else
86
+ for (const s of l) {
87
+ if (!S(s))
88
+ continue;
89
+ const r = O(s.id) ? s.id : "?", c = s.milestone;
90
+ O(c) && !i.has(c) && t.push(
91
+ g(
92
+ `operations[${r}].milestone`,
93
+ `unknown milestone "${c}"`
94
+ )
95
+ );
96
+ const u = s.status;
97
+ (!O(u) || !_e.has(u)) && t.push(
98
+ g(
99
+ `operations[${r}].status`,
100
+ "invalid operation status",
101
+ u
102
+ )
103
+ ), Array.isArray(s.dispatch_ids) || t.push(
104
+ g(
105
+ `operations[${r}].dispatch_ids`,
106
+ "must be an array",
107
+ s.dispatch_ids
108
+ )
109
+ ), typeof s.attempt_count != "number" && t.push(
110
+ g(
111
+ `operations[${r}].attempt_count`,
112
+ "must be a number",
113
+ s.attempt_count
114
+ )
115
+ ), Array.isArray(s.blast_radius) || t.push(
116
+ g(
117
+ `operations[${r}].blast_radius`,
118
+ "must be an array",
119
+ s.blast_radius
120
+ )
121
+ );
122
+ }
123
+ const f = e.open_questions;
124
+ if (!Array.isArray(f))
125
+ t.push(g("open_questions", "must be an array", f));
126
+ else
127
+ for (const s of f) {
128
+ if (!S(s))
129
+ continue;
130
+ const r = s.blocking;
131
+ (!O(r) || !i.has(r)) && t.push(
132
+ g("open_questions", `unknown blocking milestone "${r}"`)
133
+ ), typeof s.surfaced != "boolean" && t.push(
134
+ g(
135
+ `open_questions[${s.id}]`,
136
+ "surfaced must be boolean",
137
+ s.surfaced
138
+ )
139
+ ), typeof s.answered != "boolean" && t.push(
140
+ g(
141
+ `open_questions[${s.id}]`,
142
+ "answered must be boolean",
143
+ s.answered
144
+ )
145
+ );
146
+ }
147
+ const a = e.dispatch_units;
148
+ if (a !== void 0)
149
+ if (!Array.isArray(a))
150
+ t.push(
151
+ g("dispatch_units", "must be an array if present", a)
152
+ );
153
+ else
154
+ for (const s of a) {
155
+ if (!S(s))
156
+ continue;
157
+ const r = s.fits_context_window;
158
+ typeof r != "boolean" && t.push(
159
+ g(
160
+ `dispatch_units[${s.id}]`,
161
+ "fits_context_window must be boolean",
162
+ r
163
+ )
164
+ );
165
+ const c = s.milestones;
166
+ if (Array.isArray(c))
167
+ for (const u of c)
168
+ O(u) && !i.has(u) && t.push(
169
+ g(
170
+ `dispatch_units[${s.id}].milestones`,
171
+ `unknown milestone "${u}"`
172
+ )
173
+ );
174
+ }
175
+ S(e.pairwise_overlap) || t.push(
176
+ g(
177
+ "pairwise_overlap",
178
+ "must be an object",
179
+ e.pairwise_overlap
180
+ )
181
+ );
182
+ }
183
+ return { valid: t.length === 0, errors: t };
184
+ }
185
+ function he(e) {
186
+ const t = me(e);
187
+ if (!t.valid)
188
+ throw new Error(
189
+ `Snapshot validation failed:
190
+ ${t.errors.map((n) => ` ${n.path}: ${n.message}`).join(`
191
+ `)}`
192
+ );
193
+ }
194
+ const U = {
195
+ prose: 5.5,
196
+ md: 5.5,
197
+ ts: 6.3,
198
+ tsx: 6.3,
199
+ py: 6.3,
200
+ default: 4
201
+ };
202
+ function C(e, t) {
203
+ if (e <= 0)
204
+ return 0;
205
+ const o = U[""] ?? U.default ?? 4;
206
+ return Math.ceil(e / o);
207
+ }
208
+ function Z(e, t) {
209
+ if (e.length === 0 || t.fileSizes === void 0)
210
+ return /* @__PURE__ */ new Map();
211
+ try {
212
+ return t.fileSizes(e);
213
+ } catch {
214
+ return /* @__PURE__ */ new Map();
215
+ }
216
+ }
217
+ const ge = {
218
+ low: 600,
219
+ medium: 1e3,
220
+ high: 2e3
221
+ }, be = pe;
222
+ function ye(e) {
223
+ const t = e.properties ?? {};
224
+ return Object.keys(t).length || Object.keys(e).length;
225
+ }
226
+ function ve(e, t) {
227
+ if (e.type === "tool-call")
228
+ return 0;
229
+ const n = e.shape;
230
+ if (!n || n.kind === null)
231
+ return 0;
232
+ const o = n.kind;
233
+ if (o === "function" || o === "interface" || o === "type" || o === "class" || o === "enum" || o === "const" || o === "script")
234
+ return ("ops" in n && Array.isArray(n.ops) ? n.ops : []).length * 200;
235
+ if (o === "config" || o === "env" || o === "schema" || o === "manifest")
236
+ return ("ops" in n && Array.isArray(n.ops) ? n.ops : []).length * 100;
237
+ if (o === "doc")
238
+ return ge[t ?? "medium"] ?? 1e3;
239
+ if (o === "structured-output") {
240
+ const i = "schema" in n && n.schema ? n.schema : {};
241
+ return ye(i) * 50;
242
+ }
243
+ return 0;
244
+ }
245
+ function we(e, t) {
246
+ const n = /* @__PURE__ */ new Set([...Object.keys(e), ...Object.keys(t)]), o = {};
247
+ for (const i of n) {
248
+ const l = e[i];
249
+ o[i] = l ?? t[i] ?? null;
250
+ }
251
+ return o;
252
+ }
253
+ function ke(e, t) {
254
+ const n = /* @__PURE__ */ new Set([...Object.keys(e), ...Object.keys(t)]), o = {};
255
+ for (const i of n) {
256
+ const l = e[i] ?? t[i];
257
+ if (l === void 0)
258
+ throw new TypeError(
259
+ `Model tier "${i}" not found in dag or deps context_window_per_tier`
260
+ );
261
+ o[i] = l;
262
+ }
263
+ return o;
264
+ }
265
+ function $e(e) {
266
+ var a;
267
+ const t = Object.keys(e), n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
268
+ for (const s of t)
269
+ n.has(s) || n.set(s, 0), o.has(s) || o.set(s, []);
270
+ for (const s of t) {
271
+ const r = ((a = e[s]) == null ? void 0 : a.depends_on) ?? [];
272
+ for (const c of r) {
273
+ const u = o.get(c);
274
+ u !== void 0 && u.push(s), n.set(s, (n.get(s) ?? 0) + 1);
275
+ }
276
+ }
277
+ const i = /* @__PURE__ */ new Map(), l = [];
278
+ let f = t.filter((s) => (n.get(s) ?? 0) === 0).sort();
279
+ for (const s of f)
280
+ i.set(s, 0);
281
+ for (; f.length > 0; ) {
282
+ const s = [];
283
+ for (const r of f) {
284
+ l.push(r);
285
+ const c = (o.get(r) ?? []).slice().sort();
286
+ for (const u of c) {
287
+ const p = (n.get(u) ?? 0) - 1;
288
+ n.set(u, p);
289
+ const m = i.get(r) ?? 0, b = i.get(u) ?? 0;
290
+ i.set(u, Math.max(b, m + 1)), p === 0 && s.push(u);
291
+ }
292
+ }
293
+ f = s.sort();
294
+ }
295
+ if (l.length !== t.length) {
296
+ const s = new Set(l), r = t.filter((c) => !s.has(c));
297
+ throw new Error(
298
+ `snapshot(): cycle detected in milestone depends_on graph. Nodes involved: [${r.join(", ")}]`
299
+ );
300
+ }
301
+ return { order: l, waves: i };
302
+ }
303
+ function ee(e, t) {
304
+ if (t.length === 0)
305
+ return [];
306
+ const n = new Set(t);
307
+ return e.filter(
308
+ (o) => o.operations.some((i) => n.has(i))
309
+ );
310
+ }
311
+ function te(e, t) {
312
+ return e.filter((n) => n.operations.includes(t));
313
+ }
314
+ function Ae(e, t, n, o, i) {
315
+ const l = `${e}.guard`, f = [...n, l], a = ee(o, f);
316
+ for (const r of a)
317
+ for (const c of r.results)
318
+ if (c.op_id === l && c.guard_result === "pass")
319
+ return "complete";
320
+ for (const r of a)
321
+ for (const c of r.results)
322
+ if (f.includes(c.op_id) && c.status === "failed")
323
+ return "failed";
324
+ for (const r of a)
325
+ for (const c of r.results)
326
+ if (f.includes(c.op_id) && // "in_progress" is not a dispatch_log result status (only complete/failed/skipped)
327
+ // but orchestrators may write it; guard defensively
328
+ c.status === "in_progress")
329
+ return "in_progress";
330
+ const s = i.length === 0 || i.every((r) => r === "complete");
331
+ return t.pending !== null && s ? "pending-surfaced" : "pending";
332
+ }
333
+ function Oe(e, t) {
334
+ const n = `${e}.guard`;
335
+ let o = null, i = null, l = null;
336
+ for (const f of t)
337
+ if (f.operations.includes(n))
338
+ for (const a of f.results)
339
+ a.op_id === n && a.guard_result !== null && (o = a.guard_result, i = a.guard_output, a.guard_result === "pass" && (l = a.guard_ran_at));
340
+ return o !== null ? {
341
+ guard_result: o,
342
+ guard_output: i,
343
+ completed_at: l
344
+ } : { guard_result: "pending", guard_output: null, completed_at: null };
345
+ }
346
+ function Se(e) {
347
+ const t = /* @__PURE__ */ new Set();
348
+ for (const n of e)
349
+ n.action === "move" && n.to_file ? t.add(n.to_file) : be.has(n.action) && n.file && t.add(n.file);
350
+ return Array.from(t);
351
+ }
352
+ function je(e, t, n) {
353
+ const o = te(n, e.id), i = o.map((c) => c.id);
354
+ let l = null, f = null, a = null;
355
+ for (const c of o)
356
+ for (const u of c.results)
357
+ u.op_id === e.id && u.guard_result !== null && (l = u.guard_result, f = u.guard_output, a = u.guard_ran_at);
358
+ const s = e.ki_estimate !== null && e.ki_estimate !== void 0 ? e.ki_estimate : ve(e, t), r = Me(e.shape);
359
+ return {
360
+ ...e,
361
+ ki_estimate: s,
362
+ shape: r,
363
+ dispatch_ids: i,
364
+ // TODO: stubbed as 0 — op-level attempt_count requires per-op dispatch log scan
365
+ attempt_count: 0,
366
+ guard_result: l,
367
+ guard_output: f,
368
+ guard_ran_at: a,
369
+ // TODO: stubbed — requires gitnexus_impact MCP call (future work)
370
+ blast_radius: [],
371
+ // TODO: stubbed — requires same-wave op-key collision scan (future work)
372
+ conflict: {
373
+ detected: !1,
374
+ competing_op: null,
375
+ op_key: null,
376
+ resolution: null
377
+ },
378
+ // TODO: stubbed — requires ki_estimate share prorating across dispatch totals
379
+ tokens_actual: null
380
+ };
381
+ }
382
+ function Me(e) {
383
+ if (!e)
384
+ return null;
385
+ if (e.kind === null)
386
+ return e;
387
+ const t = e.kind;
388
+ if (t === "function" || t === "interface" || t === "type" || t === "class" || t === "enum" || t === "const" || t === "script" || t === "config" || t === "env" || t === "schema" || t === "manifest") {
389
+ const n = ("ops" in e && Array.isArray(e.ops) ? e.ops : []).map((o) => ({
390
+ ...o,
391
+ // TODO: from/breaking/severity require AST read + gitnexus (future work)
392
+ from: null,
393
+ breaking: null,
394
+ severity: null
395
+ }));
396
+ return { ...e, ops: n };
397
+ }
398
+ return e;
399
+ }
400
+ function Te(e, t, n, o) {
401
+ const i = `${e}.guard`, l = te(o, i), f = l.map((c) => c.id);
402
+ let a = null, s = null, r = null;
403
+ for (const c of l)
404
+ for (const u of c.results)
405
+ u.op_id === i && u.guard_result !== null && (a = u.guard_result, s = u.guard_output, r = u.guard_ran_at);
406
+ return {
407
+ id: i,
408
+ milestone: e,
409
+ depends_on: n,
410
+ type: "tool-call",
411
+ action: "guard",
412
+ file: null,
413
+ symbol: null,
414
+ provenance: null,
415
+ confidence: null,
416
+ audit_check: null,
417
+ criteria: [],
418
+ tool: null,
419
+ args: null,
420
+ guard: t.guard ?? null,
421
+ to_file: null,
422
+ to_symbol: null,
423
+ ki_estimate: 0,
424
+ ki_source: null,
425
+ authored_by: "synthesized",
426
+ status: "pending",
427
+ shape: null,
428
+ dispatch_ids: f,
429
+ attempt_count: 0,
430
+ guard_result: a,
431
+ guard_output: s,
432
+ guard_ran_at: r,
433
+ blast_radius: [],
434
+ conflict: {
435
+ detected: !1,
436
+ competing_op: null,
437
+ op_key: null,
438
+ resolution: null
439
+ },
440
+ tokens_actual: null
441
+ };
442
+ }
443
+ function xe(e, t) {
444
+ const n = {}, {
445
+ write_multiplier: o,
446
+ read_multiplier: i,
447
+ hit_probability: l
448
+ } = t;
449
+ for (const [f, a] of Object.entries(e))
450
+ a === null ? n[f] = null : n[f] = Math.round(a * ((1 - l) * o + l * i));
451
+ return n;
452
+ }
453
+ function Ie(e, t, n) {
454
+ const o = Array.from(e.keys()), i = {};
455
+ for (let l = 0; l < o.length; l++)
456
+ for (let f = l + 1; f < o.length; f++) {
457
+ const a = o[l], s = o[f];
458
+ if (a === void 0 || s === void 0)
459
+ continue;
460
+ const [r, c] = a < s ? [a, s] : [s, a], u = `${r}:${c}`, p = e.get(a) ?? [], m = e.get(s) ?? [], b = new Set(
461
+ p.map((w) => w.file).filter((w) => w !== null)
462
+ ), v = new Set(
463
+ m.map((w) => w.file).filter((w) => w !== null)
464
+ ), y = Array.from(b).filter((w) => v.has(w));
465
+ if (y.length === 0)
466
+ continue;
467
+ const k = t.get(a) ?? "pending", $ = t.get(s) ?? "pending", j = k === "complete" && $ === "complete";
468
+ let P = 0;
469
+ for (const w of y)
470
+ P += j ? n(w) : 0;
471
+ i[u] = P;
472
+ }
473
+ return i;
474
+ }
475
+ function ze(e) {
476
+ return (Array.isArray(e) ? e : Object.values(e)).map(
477
+ (n) => n.type === void 0 ? { ...n, type: "generative" } : n
478
+ );
479
+ }
480
+ function Ne(e, t) {
481
+ const n = (/* @__PURE__ */ new Date()).toISOString(), o = t.dispatchLog ?? e.dispatch_log, i = e.optimization.sentinel_fanout, l = we(e.optimization.b_per_tier, t.bPerTier), f = ke(
482
+ e.optimization.context_window_per_tier,
483
+ t.contextWindowPerTier
484
+ ), a = xe(l, i), { waves: s } = $e(e.milestones), r = ze(e.operations), c = /* @__PURE__ */ new Map();
485
+ for (const _ of r) {
486
+ const d = c.get(_.milestone) ?? [];
487
+ d.push(_), c.set(_.milestone, d);
488
+ }
489
+ const u = /* @__PURE__ */ new Set();
490
+ for (const _ of r)
491
+ _.file && u.add(_.file), _.action === "move" && _.to_file && u.add(_.to_file);
492
+ const p = Z(Array.from(u), t), m = (_) => p.get(_) ?? 0, b = /* @__PURE__ */ new Map(), v = Array.from(s.entries()).sort((_, d) => _[1] - d[1] || _[0].localeCompare(d[0])).map(([_]) => _);
493
+ for (const _ of v) {
494
+ const d = e.milestones[_];
495
+ if (d === void 0)
496
+ continue;
497
+ const M = (c.get(_) ?? []).map((x) => x.id), T = d.depends_on.map(
498
+ (x) => b.get(x) ?? "pending"
499
+ ), I = Ae(
500
+ _,
501
+ d,
502
+ M,
503
+ o,
504
+ T
505
+ );
506
+ b.set(_, I);
507
+ }
508
+ const y = {}, k = [];
509
+ for (const _ of v) {
510
+ const d = e.milestones[_];
511
+ if (d === void 0)
512
+ continue;
513
+ const M = s.get(_) ?? 0, T = c.get(_) ?? [], I = T.map((h) => h.id), x = b.get(_) ?? "pending", N = d.depends_on.map(
514
+ (h) => b.get(h) ?? "pending"
515
+ ), re = d.depends_on.length === 0 || N.every((h) => h === "complete"), le = N.every((h) => h !== "failed"), ue = d.pending === null && re && le, E = ee(o, [
516
+ ...I,
517
+ `${_}.guard`
518
+ ]), ae = E.length > 0 ? E.map((h) => h.started_at).sort()[0] ?? null : null;
519
+ let F, q, B;
520
+ if (d.guard) {
521
+ const h = Oe(_, o);
522
+ F = h.guard_result, q = h.guard_output, B = h.completed_at;
523
+ } else
524
+ F = null, q = null, B = null;
525
+ const R = Se(T), K = R.reduce((h, A) => h + m(A), 0), W = T.map(
526
+ (h) => je(h, d.effort, o)
527
+ );
528
+ let z = null, G = 0, L = !1;
529
+ for (const h of W) {
530
+ if (h.ki_estimate === null) {
531
+ L = !0;
532
+ break;
533
+ }
534
+ G += h.ki_estimate;
535
+ }
536
+ L || (z = G);
537
+ let V = null;
538
+ const Y = d.model;
539
+ if (Y !== null && z !== null) {
540
+ const h = a[Y] ?? null;
541
+ if (h !== null) {
542
+ const A = C(K);
543
+ V = h + A + z;
544
+ }
545
+ }
546
+ let H = null;
547
+ const J = E.filter(
548
+ (h) => h.completed_at !== null && h.results.length > 0 && h.results.every(
549
+ (A) => A.status === "complete" || A.status === "skipped"
550
+ )
551
+ );
552
+ J.length > 0 && (H = J.reduce((h, A) => h + A.turns.reduce((fe, Q) => fe + Q.input_tokens + Q.output_tokens, 0), 0));
553
+ const ce = Te(
554
+ _,
555
+ d,
556
+ I,
557
+ o
558
+ );
559
+ k.push(...W, ce), y[_] = {
560
+ description: d.description,
561
+ ...d.rationale !== void 0 ? { rationale: d.rationale } : {},
562
+ authored_by: d.authored_by,
563
+ pending: d.pending,
564
+ triggered_by: d.triggered_by,
565
+ phase: d.phase,
566
+ depends_on: d.depends_on,
567
+ agent: d.agent,
568
+ model: d.model,
569
+ effort: d.effort,
570
+ two_stage: d.two_stage,
571
+ read_only: d.read_only,
572
+ guard: d.guard,
573
+ context: `contexts/${_}.md`,
574
+ wave: M,
575
+ eligible: ue,
576
+ status: x,
577
+ started_at: ae,
578
+ completed_at: B,
579
+ guard_result: F,
580
+ guard_output: q,
581
+ artifacts: R,
582
+ si_bytes: K,
583
+ ki_estimate: z,
584
+ tokens_estimated: V,
585
+ tokens_actual: H
586
+ };
587
+ }
588
+ const $ = Ie(
589
+ c,
590
+ b,
591
+ m
592
+ ), j = Pe(e, y), w = {
593
+ tokens_naive: (() => {
594
+ let _ = 0;
595
+ for (const d of Object.values(y))
596
+ if (d.eligible && d.status === "pending" && d.model !== null) {
597
+ const M = l[d.model] ?? t.bPerTier[d.model] ?? 0;
598
+ _ += M + (d.ki_estimate ?? 0);
599
+ }
600
+ return _;
601
+ })(),
602
+ sentinel_fanout: i,
603
+ context_window_override: e.optimization.context_window_override,
604
+ b_override: e.optimization.b_override,
605
+ b_per_tier: l,
606
+ b_eff_per_tier: a,
607
+ context_window_per_tier: f
608
+ }, D = {
609
+ snapshot_at: n,
610
+ snapshot_version: 1,
611
+ // callers may increment if persisting
612
+ plan: "",
613
+ // caller sets this from directory name
614
+ schema_version: e.schema_version,
615
+ plan_kind: e.plan_kind,
616
+ description: e.description,
617
+ problem: e.problem,
618
+ approach: e.approach,
619
+ executor: e.executor,
620
+ ...e.executor_model !== void 0 ? { executor_model: e.executor_model } : {},
621
+ ...e.executor_effort !== void 0 ? { executor_effort: e.executor_effort } : {},
622
+ phases: [...e.phases],
623
+ terminal: e.terminal,
624
+ ...e.assumed_baseline !== void 0 ? { assumed_baseline: e.assumed_baseline } : {},
625
+ optimization: w,
626
+ milestones: y,
627
+ operations: k,
628
+ pairwise_overlap: $,
629
+ open_questions: j
630
+ };
631
+ return he(D), D;
632
+ }
633
+ function Pe(e, t) {
634
+ const n = [];
635
+ for (const [o, i] of Object.entries(t)) {
636
+ const l = e.milestones[o];
637
+ if (l === void 0 || l.pending === null)
638
+ continue;
639
+ const f = i.status === "pending-surfaced";
640
+ n.push({
641
+ id: `q:${o}`,
642
+ text: l.pending,
643
+ blocking: o,
644
+ surfaced: f,
645
+ // TODO: scan dispatch_log notes for the turn/dispatch where question appeared
646
+ raised_at_dispatch: null,
647
+ raised_at_turn: null,
648
+ answered: !1,
649
+ answer: null
650
+ });
651
+ }
652
+ return n;
653
+ }
654
+ function ne(e) {
655
+ return Object.keys(e.milestones).filter((t) => {
656
+ const n = e.milestones[t];
657
+ return n !== void 0 && n.eligible === !0 && n.status === "pending";
658
+ });
659
+ }
660
+ function Ee(e) {
661
+ if (e.length === 0 || e.every((t) => t.type === "tool-call"))
662
+ return "tool-call";
663
+ for (const t of e) {
664
+ if (t.type !== "generative" || !t.shape || t.shape.kind === null)
665
+ continue;
666
+ const n = t.shape.kind;
667
+ if (n === "function" || n === "interface" || n === "type" || n === "class" || n === "enum" || n === "const" || n === "script" || n === "config" || n === "env" || n === "schema" || n === "manifest")
668
+ return "code-config";
669
+ if (n === "doc")
670
+ return "doc";
671
+ if (n === "structured-output")
672
+ return "structured";
673
+ }
674
+ return "code-config";
675
+ }
676
+ function Fe(e) {
677
+ if (!e)
678
+ return "";
679
+ const t = e.indexOf(":");
680
+ return t >= 0 ? e.slice(t + 1) : e;
681
+ }
682
+ function qe(e, t, n) {
683
+ let o = !0;
684
+ const i = [];
685
+ for (const l of e) {
686
+ const f = t[l];
687
+ if (f === void 0)
688
+ continue;
689
+ const a = n.filter(
690
+ (r) => r.milestone === l && r.action !== "guard"
691
+ );
692
+ a.some((r) => r.type === "generative") && (o = !1), i.push(`## Milestone: ${l}`), i.push(f.description), f.rationale && i.push(`
693
+ *Rationale:* ${f.rationale}`), i.push(`
694
+ ### Operations:`);
695
+ for (const r of a) {
696
+ const c = r.file ? ` ${r.file}` : "", u = r.symbol ? ` (${r.symbol})` : "";
697
+ if (i.push(`- [${r.id}] ${r.action}${c}${u}`), r.shape && r.shape.kind !== null) {
698
+ const p = r.shape;
699
+ if (p.kind === "doc")
700
+ i.push(` Description: ${p.description}`), i.push(` Objective: ${p.objective}`), p.required_sections && p.required_sections.length > 0 && i.push(
701
+ ` Required sections: ${p.required_sections.join(", ")}`
702
+ );
703
+ else if (p.kind === "structured-output")
704
+ i.push(
705
+ ` Schema:
706
+ ` + JSON.stringify(p.schema, null, 2).split(`
707
+ `).map((m) => ` ${m}`).join(`
708
+ `)
709
+ );
710
+ else if ("ops" in p && Array.isArray(p.ops))
711
+ for (const m of p.ops) {
712
+ const b = m.to !== null ? ` → ${m.to}` : "", v = m.target !== null ? ` "${m.target}"` : "";
713
+ i.push(` - ${m.op}${v}${b}`);
714
+ }
715
+ }
716
+ }
717
+ const s = f.guard;
718
+ i.push(`
719
+ ### Guard: ${s ?? "none"}`), i.push("");
720
+ }
721
+ return o ? null : i.join(`
722
+ `);
723
+ }
724
+ function oe(e, t, n) {
725
+ if (e === null)
726
+ return null;
727
+ const o = t.optimization.b_eff_per_tier[e];
728
+ if (o != null)
729
+ return o;
730
+ const i = t.optimization.b_per_tier[e];
731
+ if (i != null)
732
+ return i;
733
+ const l = n.bPerTier[e];
734
+ return l !== void 0 ? l : null;
735
+ }
736
+ function se(e, t, n) {
737
+ if (e === null)
738
+ return Number.POSITIVE_INFINITY;
739
+ const o = t.optimization.context_window_per_tier[e];
740
+ if (o !== void 0)
741
+ return o;
742
+ const i = n.contextWindowPerTier[e];
743
+ return i !== void 0 ? i : Number.POSITIVE_INFINITY;
744
+ }
745
+ function ie(e, t) {
746
+ const n = /* @__PURE__ */ new Set();
747
+ for (const o of e) {
748
+ const i = t.snapshot.milestones[o];
749
+ if (i !== void 0) {
750
+ n.add(i.context);
751
+ for (const l of i.read_only)
752
+ n.add(l);
753
+ for (const l of t.opsByMilestone.get(o) ?? [])
754
+ l.file && n.add(l.file);
755
+ }
756
+ }
757
+ return Array.from(n);
758
+ }
759
+ function X(e, t, n) {
760
+ const o = oe(t, n.snapshot, n.deps);
761
+ if (o === null)
762
+ return null;
763
+ const l = ie(e, n).reduce(
764
+ (s, r) => s + (n.sizeMap.get(r) ?? 0),
765
+ 0
766
+ ), f = C(l), a = e.reduce(
767
+ (s, r) => {
768
+ var c;
769
+ return s + (((c = n.snapshot.milestones[r]) == null ? void 0 : c.ki_estimate) ?? 0);
770
+ },
771
+ 0
772
+ );
773
+ return o + f + a;
774
+ }
775
+ function Be(e, t, n) {
776
+ const o = se(t, n.snapshot, n.deps), i = [...e].sort((a, s) => {
777
+ var u, p;
778
+ const r = ((u = n.snapshot.milestones[a]) == null ? void 0 : u.ki_estimate) ?? 0, c = ((p = n.snapshot.milestones[s]) == null ? void 0 : p.ki_estimate) ?? 0;
779
+ return r - c || a.localeCompare(s);
780
+ }), l = [];
781
+ let f = [];
782
+ for (const a of i) {
783
+ const s = [...f, a], r = X(s, t, n);
784
+ r !== null && r > o && f.length > 0 ? (l.push(f), f = [a]) : f = s;
785
+ }
786
+ return f.length > 0 && l.push(f), l.filter((a) => {
787
+ const s = X(a, t, n);
788
+ return s === null || s <= o;
789
+ });
790
+ }
791
+ function Ce(e, t, n, o) {
792
+ const i = e[0] ?? "", l = o.snapshot.milestones[i], f = (l == null ? void 0 : l.effort) ?? null, a = [];
793
+ for (const k of e)
794
+ for (const $ of o.opsByMilestone.get(k) ?? [])
795
+ a.push($.id);
796
+ const s = ie(e, o), r = s.reduce(
797
+ (k, $) => k + (o.sizeMap.get($) ?? 0),
798
+ 0
799
+ ), c = oe(n, o.snapshot, o.deps), u = e.reduce(
800
+ (k, $) => {
801
+ var j;
802
+ return k + (((j = o.snapshot.milestones[$]) == null ? void 0 : j.ki_estimate) ?? 0);
803
+ },
804
+ 0
805
+ ), p = c !== null ? c + C(r) + u : null, m = se(n, o.snapshot, o.deps), b = p !== null ? p <= m : !0, v = Fe((l == null ? void 0 : l.agent) ?? null), y = qe(e, o.snapshot.milestones, o.snapshot.operations);
806
+ return {
807
+ id: `${i}.dispatch.${t}`,
808
+ milestones: e,
809
+ operations: a,
810
+ model: n,
811
+ effort: f,
812
+ two_stage: (l == null ? void 0 : l.two_stage) ?? !1,
813
+ provider: null,
814
+ agent_name: v,
815
+ mcp_servers: null,
816
+ resolved_max_tokens: null,
817
+ background: !0,
818
+ prompt: y,
819
+ context_files: s,
820
+ si_bytes: r,
821
+ tokens_estimated: p,
822
+ fits_context_window: b,
823
+ sentinel_role: null,
824
+ dispatch_log_id: null,
825
+ remote_task_id: null,
826
+ result: null,
827
+ status: "pending",
828
+ started_at: null,
829
+ completed_at: null,
830
+ tokens_actual: null
831
+ };
832
+ }
833
+ function Re(e, t) {
834
+ const n = ne(e);
835
+ if (n.length === 0)
836
+ return [];
837
+ const o = /* @__PURE__ */ new Map();
838
+ for (const u of e.operations) {
839
+ if (u.action === "guard")
840
+ continue;
841
+ const p = o.get(u.milestone) ?? [];
842
+ p.push(u), o.set(u.milestone, p);
843
+ }
844
+ const i = /* @__PURE__ */ new Map();
845
+ for (const u of n) {
846
+ const p = e.milestones[u];
847
+ if (p === void 0)
848
+ continue;
849
+ const m = Ee(o.get(u) ?? []), b = p.model ?? "_null", v = `${m}:${b}`, y = i.get(v) ?? [];
850
+ y.push(u), i.set(v, y);
851
+ }
852
+ const l = /* @__PURE__ */ new Set();
853
+ for (const u of n) {
854
+ const p = e.milestones[u];
855
+ if (p !== void 0) {
856
+ l.add(p.context);
857
+ for (const m of p.read_only)
858
+ l.add(m);
859
+ for (const m of o.get(u) ?? [])
860
+ m.file && l.add(m.file);
861
+ }
862
+ }
863
+ const f = Z(Array.from(l), t), a = { snapshot: e, deps: t, sizeMap: f, opsByMilestone: o }, s = [];
864
+ let r = 0;
865
+ const c = Array.from(i.keys()).sort();
866
+ for (const u of c) {
867
+ const p = i.get(u);
868
+ if (p === void 0)
869
+ continue;
870
+ const m = u.slice(u.indexOf(":") + 1), b = m !== "_null" ? m : null, v = Be(p, b, a);
871
+ for (const y of v)
872
+ s.push(Ce(y, r++, b, a));
873
+ }
874
+ return s;
875
+ }
876
+ function Ke(e, t) {
877
+ let n = 0;
878
+ for (const o of ne(e)) {
879
+ const i = e.milestones[o];
880
+ if (i === void 0)
881
+ continue;
882
+ const l = De(i.model, e, t) ?? 0;
883
+ n += l + (i.ki_estimate ?? 0);
884
+ }
885
+ return n;
886
+ }
887
+ function De(e, t, n) {
888
+ if (e === null)
889
+ return null;
890
+ const o = t.optimization.b_per_tier[e];
891
+ if (o != null)
892
+ return o;
893
+ const i = n.bPerTier[e];
894
+ return i !== void 0 ? i : null;
895
+ }
896
+ export {
897
+ Ke as computeTokensNaive,
898
+ Re as optimize,
899
+ Ne as snapshot,
900
+ $e as topoSortMilestones
901
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { snapshot, topoSortMilestones } from './snapshot.js';
2
+ export { optimize, computeTokensNaive } from './optimize.js';
3
+ export type { IOptimizerDeps } from '@adhd/dispatch-base-spec';
@@ -0,0 +1 @@
1
+ export { optimize, computeTokensNaive } from '../optimize.js';
@@ -0,0 +1,36 @@
1
+ import { DagSnapshot, DispatchUnit, IOptimizerDeps } from '@adhd/dispatch-base-spec';
2
+
3
+ /**
4
+ * Compute the greedy dispatch plan for the current scheduling cycle.
5
+ *
6
+ * Input: DagSnapshot — fully computed from snapshot().
7
+ * Output: DispatchUnit[] — one unit per batch assignment. Never mixes
8
+ * shape.kind families or model tiers within one unit. Never contains a
9
+ * unit whose tokens_estimated exceeds the tier's context window.
10
+ *
11
+ * Does not mutate the input snapshot.
12
+ */
13
+ export declare function optimize(snapshot: DagSnapshot, deps: IOptimizerDeps): DispatchUnit[];
14
+ /**
15
+ * Naive baseline (SCOPE.md §F4): one milestone = one agent call, no packing,
16
+ * no Sentinel-Fanout caching.
17
+ *
18
+ * tokens_naive = Σ (b_per_tier[model] + ki_estimate) for each packable milestone
19
+ *
20
+ * Deliberately uses the RAW b_per_tier (not b_eff_per_tier) — the point of
21
+ * this baseline is "what would it cost with zero optimization applied",
22
+ * which specifically means no caching benefit either.
23
+ *
24
+ * The optimizer-core.2 shape.ops entry says to "emit tokens_naive... into
25
+ * the snapshot's optimization block" — but SnapshotOptimization (types.ts)
26
+ * has no tokens_naive field, and optimize()'s return type is fixed at
27
+ * DispatchUnit[], so there's no type-safe way to attach it to the snapshot
28
+ * from inside this module without either mutating the input (breaking the
29
+ * "does not mutate its input" contract) or editing @adhd/dispatch-base-spec
30
+ * (outside this milestone's file scope). This named export is the type-safe
31
+ * alternative: the value is computed, testable, and available to any caller
32
+ * that wants "packed vs naive". Adding a `tokens_naive` field to
33
+ * SnapshotOptimization belongs to a future @adhd/dispatch-base-spec change, not
34
+ * this file.
35
+ */
36
+ export declare function computeTokensNaive(snapshot: DagSnapshot, deps: IOptimizerDeps): number;
@@ -0,0 +1,15 @@
1
+ import { IOptimizerDeps } from '@adhd/dispatch-base-spec';
2
+
3
+ /**
4
+ * Convert file bytes to an approximate token count.
5
+ * Source: SCOPE.md §C3 — byte-count is the right proxy (r>0.98, arxiv 2511.08066).
6
+ */
7
+ export declare function siBytesAsTokens(bytes: number, filePath?: string): number;
8
+ /**
9
+ * Batch-resolve file sizes via the injected deps.fileSizes callback.
10
+ *
11
+ * Graceful degradation: returns an empty map (every lookup then resolves to 0
12
+ * via `map.get(path) ?? 0` at the call site) when deps.fileSizes is undefined,
13
+ * the path list is empty, or the callback itself throws.
14
+ */
15
+ export declare function lookupFileSizes(paths: string[], deps: IOptimizerDeps): Map<string, number>;
@@ -0,0 +1 @@
1
+ export { snapshot, topoSortMilestones } from '../snapshot.js';
@@ -0,0 +1,35 @@
1
+ import { DagJson, DagSnapshot, IOptimizerDeps, MilestoneDag } from '@adhd/dispatch-base-spec';
2
+
3
+ /**
4
+ * Topologically sort milestone slugs and assign wave numbers.
5
+ * Uses Kahn's algorithm (BFS) for O(V+E).
6
+ *
7
+ * Wave 0: nodes with empty depends_on.
8
+ * Wave N: 1 + max(wave of deps).
9
+ * Ties within a wave are broken by slug (lexicographic) for determinism.
10
+ *
11
+ * @throws {Error} if a cycle is detected, including the cycle path.
12
+ */
13
+ export declare function topoSortMilestones(milestones: Record<string, MilestoneDag>): {
14
+ order: string[];
15
+ waves: Map<string, number>;
16
+ };
17
+ /**
18
+ * Compute the fully-derived DagSnapshot from a dag.json document.
19
+ *
20
+ * Computation order (per SCOPE.md §N1):
21
+ * 1. Copy dag-level fields verbatim; resolve b_per_tier / context_window_per_tier
22
+ * cold-start defaults from deps.
23
+ * 2. Compute b_eff_per_tier.
24
+ * 3. Topological sort → assign wave numbers.
25
+ * 4. Per-milestone derived fields.
26
+ * 5. Per-operation derived fields.
27
+ * 6. pairwise_overlap.
28
+ * 7. open_questions.
29
+ *
30
+ * Does not mutate the input dag.
31
+ *
32
+ * @throws {Error} if a dependency cycle is detected, or if the computed snapshot
33
+ * fails structural validation (assertValidSnapshot).
34
+ */
35
+ export declare function snapshot(dag: DagJson, deps: IOptimizerDeps): DagSnapshot;
package/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@adhd/dispatch-core-optimizer",
3
+ "version": "0.0.1",
4
+ "dependencies": {
5
+ "@adhd/dispatch-base-spec": "^0.0.1"
6
+ },
7
+ "main": "./index.js",
8
+ "module": "./index.mjs",
9
+ "typings": "./index.d.ts"
10
+ }
@@ -0,0 +1,21 @@
1
+ import { DagJson, DispatchLogEntry, IOptimizerDeps, MilestoneDag, OperationDag } from '@adhd/dispatch-base-spec';
2
+
3
+ /** Recommended cold-start defaults per SCOPE.md Open Decision #2. */
4
+ export declare function defaultDeps(overrides?: Partial<IOptimizerDeps>): IOptimizerDeps;
5
+ export declare function miniMilestone(overrides?: Partial<MilestoneDag>): MilestoneDag;
6
+ export declare function miniOp(overrides?: Partial<OperationDag>): OperationDag;
7
+ export declare function miniDag(overrides?: Partial<DagJson>): DagJson;
8
+ /**
9
+ * Recursively Object.freeze a value and everything reachable from it (own
10
+ * enumerable keys only, arrays included). Functions are left unfrozen-and-
11
+ * unrecursed — freezing them adds nothing and isn't needed to prove data
12
+ * non-mutation.
13
+ *
14
+ * Used to prove snapshot()/optimize()/computeTokensNaive() honor their "does
15
+ * not mutate its input" contract: ES module code runs in strict mode, so any
16
+ * in-place write to a frozen object throws a TypeError instead of silently
17
+ * no-oping — a mutation bug turns into a failing test, not a passing one.
18
+ */
19
+ export declare function deepFreeze<T>(value: T, seen?: WeakSet<object>): T;
20
+ /** A dispatch_log entry recording a passed guard for `${slug}.guard`. */
21
+ export declare function passingGuardEntry(slug: string, overrides?: Partial<DispatchLogEntry>): DispatchLogEntry;