yaaw-se 0.3.11 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/installer/migrations/index.d.ts +2 -2
  2. package/dist/installer/migrations/index.js +2 -2
  3. package/dist/installer/migrations/index.js.map +1 -1
  4. package/dist/installer/migrations/project/index.js +2 -1
  5. package/dist/installer/migrations/project/index.js.map +1 -1
  6. package/dist/installer/migrations/project/v2-to-v3.d.ts +2 -0
  7. package/dist/installer/migrations/project/v2-to-v3.js +56 -0
  8. package/dist/installer/migrations/project/v2-to-v3.js.map +1 -0
  9. package/dist/installer/plan.js +98 -268
  10. package/dist/installer/plan.js.map +1 -1
  11. package/dist/installer/project-state.js +3 -13
  12. package/dist/installer/project-state.js.map +1 -1
  13. package/dist/installer/status.js +67 -231
  14. package/dist/installer/status.js.map +1 -1
  15. package/dist/installer/transaction.js +58 -109
  16. package/dist/installer/transaction.js.map +1 -1
  17. package/dist/installer/types.d.ts +5 -2
  18. package/dist/installer/verify.js +27 -27
  19. package/dist/installer/verify.js.map +1 -1
  20. package/dist/payload/bootstrap/claude-code.md +6 -6
  21. package/dist/payload/bootstrap/cline.md +8 -8
  22. package/dist/payload/bootstrap/codex.md +7 -7
  23. package/dist/payload/bootstrap/gemini-cli.md +6 -6
  24. package/dist/payload/payload-files.json +97 -417
  25. package/dist/payload/payload.json +1 -1
  26. package/dist/payload/skills/yaaw-create-spec/SKILL.md +2 -6
  27. package/dist/payload/skills/yaaw-create-ticket/SKILL.md +2 -6
  28. package/dist/payload/skills/yaaw-create-tickets/SKILL.md +2 -6
  29. package/dist/payload/skills/yaaw-implement/SKILL.md +2 -6
  30. package/dist/payload/skills/yaaw-orchestrator/SKILL.md +2 -3
  31. package/dist/payload/skills/yaaw-planner/SKILL.md +2 -6
  32. package/dist/payload/skills/yaaw-planning-review/SKILL.md +2 -6
  33. package/dist/payload/skills/yaaw-prd/SKILL.md +2 -6
  34. package/dist/payload/skills/yaaw-refine-prd/SKILL.md +2 -6
  35. package/dist/payload/skills/yaaw-repair/SKILL.md +2 -6
  36. package/dist/payload/skills/yaaw-review/SKILL.md +2 -6
  37. package/dist/payload/skills/yaaw-revise-prd/SKILL.md +2 -6
  38. package/dist/payload/yaaw-core/registries/skills.json +38 -0
  39. package/dist/payload/yaaw-core/system/SYSTEM.md +121 -0
  40. package/dist/payload/yaaw-core/system/engine/contracts.mjs +147 -0
  41. package/dist/payload/yaaw-core/system/engine/repository-identity.mjs +24 -0
  42. package/dist/payload/yaaw-core/system/engine/routing.mjs +153 -0
  43. package/dist/payload/yaaw-core/system/engine/runtime.mjs +66 -0
  44. package/dist/payload/yaaw-core/system/kernel.yaml +145 -0
  45. package/dist/payload/yaaw-core/system/modules/changeability.md +27 -0
  46. package/dist/payload/yaaw-core/system/roles/implementer.md +16 -12
  47. package/dist/payload/yaaw-core/system/roles/orchestrator.md +24 -14
  48. package/dist/payload/yaaw-core/system/roles/planner.md +37 -11
  49. package/dist/payload/yaaw-core/system/roles/prd.md +26 -18
  50. package/dist/payload/yaaw-core/system/roles/reviewer.md +16 -15
  51. package/dist/payload/yaaw-core/system/schemas/handoff.schema.json +24 -161
  52. package/dist/payload/yaaw-core/system/schemas/intent.schema.json +11 -64
  53. package/dist/payload/yaaw-core/system/schemas/observed-state.schema.json +12 -162
  54. package/dist/payload/yaaw-core/system/schemas/repository-identity.schema.json +12 -39
  55. package/dist/payload/yaaw-core/system/templates/evidence.json +1 -18
  56. package/dist/payload/yaaw-core/system/templates/handoff.json +11 -27
  57. package/dist/payload/yaaw-core/system/templates/intent.json +2 -2
  58. package/dist/payload/yaaw-core/system/templates/observed-state.json +5 -28
  59. package/dist/payload/yaaw-core/system/templates/review.md +2 -2
  60. package/package.json +5 -2
  61. package/dist/payload/yaaw-core/system/core/artifact-model.md +0 -43
  62. package/dist/payload/yaaw-core/system/core/authority.md +0 -21
  63. package/dist/payload/yaaw-core/system/core/context-loading.md +0 -56
  64. package/dist/payload/yaaw-core/system/core/dispatch-execution.md +0 -28
  65. package/dist/payload/yaaw-core/system/core/execution-context.md +0 -61
  66. package/dist/payload/yaaw-core/system/core/framework-integrity.md +0 -60
  67. package/dist/payload/yaaw-core/system/core/invalidation.md +0 -13
  68. package/dist/payload/yaaw-core/system/core/io-contract.md +0 -23
  69. package/dist/payload/yaaw-core/system/core/lifecycle.md +0 -23
  70. package/dist/payload/yaaw-core/system/core/recovery.md +0 -17
  71. package/dist/payload/yaaw-core/system/core/routing.md +0 -21
  72. package/dist/payload/yaaw-core/system/core/state-model.md +0 -14
  73. package/dist/payload/yaaw-core/system/core/transitions.md +0 -36
  74. package/dist/payload/yaaw-core/system/expertise/changeability/MODULE.md +0 -58
  75. package/dist/payload/yaaw-core/system/registries/artifacts.json +0 -99
  76. package/dist/payload/yaaw-core/system/registries/execution-policy.json +0 -169
  77. package/dist/payload/yaaw-core/system/registries/expertise.json +0 -47
  78. package/dist/payload/yaaw-core/system/registries/handoff-policy.json +0 -67
  79. package/dist/payload/yaaw-core/system/registries/paths.json +0 -18
  80. package/dist/payload/yaaw-core/system/registries/reconciliation-policy.json +0 -18
  81. package/dist/payload/yaaw-core/system/registries/role-io.json +0 -30
  82. package/dist/payload/yaaw-core/system/registries/routing-policy.json +0 -74
  83. package/dist/payload/yaaw-core/system/registries/skills.json +0 -110
  84. package/dist/payload/yaaw-core/system/registries/transitions.json +0 -178
  85. package/dist/payload/yaaw-core/system/registries/workflows.json +0 -38
  86. package/dist/payload/yaaw-core/system/rules/artifact-writing.md +0 -10
  87. package/dist/payload/yaaw-core/system/rules/assumption-challenge.md +0 -109
  88. package/dist/payload/yaaw-core/system/rules/changeability.md +0 -38
  89. package/dist/payload/yaaw-core/system/rules/durable-memory.md +0 -9
  90. package/dist/payload/yaaw-core/system/rules/question-format.md +0 -20
  91. package/dist/payload/yaaw-core/system/rules/recovery-evidence.md +0 -9
  92. package/dist/payload/yaaw-core/system/rules/repository-identity.md +0 -42
  93. package/dist/payload/yaaw-core/system/rules/research-admission.md +0 -30
  94. package/dist/payload/yaaw-core/system/rules/review-independence.md +0 -11
  95. package/dist/payload/yaaw-core/system/rules/ticket-sizing.md +0 -9
  96. package/dist/payload/yaaw-core/system/schemas/project-state.schema.json +0 -12
  97. package/dist/payload/yaaw-core/system/templates/project-state.json +0 -13
  98. package/dist/payload/yaaw-core/system/tools/orchestration-engine.mjs +0 -210
  99. package/dist/payload/yaaw-core/system/tools/orchestration-runtime.mjs +0 -46
  100. package/dist/payload/yaaw-core/system/tools/repository-identity.mjs +0 -92
  101. package/dist/payload/yaaw-core/system/workflows/implementation/implement-ticket.md +0 -18
  102. package/dist/payload/yaaw-core/system/workflows/implementation/repair-ticket.md +0 -17
  103. package/dist/payload/yaaw-core/system/workflows/implementation/select-ticket.md +0 -17
  104. package/dist/payload/yaaw-core/system/workflows/implementation/verify-ticket.md +0 -19
  105. package/dist/payload/yaaw-core/system/workflows/orchestration/determine-next-action.md +0 -24
  106. package/dist/payload/yaaw-core/system/workflows/orchestration/dispatch.md +0 -35
  107. package/dist/payload/yaaw-core/system/workflows/orchestration/inspect-state.md +0 -19
  108. package/dist/payload/yaaw-core/system/workflows/orchestration/reconcile-state.md +0 -19
  109. package/dist/payload/yaaw-core/system/workflows/orchestration/recover-interruption.md +0 -32
  110. package/dist/payload/yaaw-core/system/workflows/orchestration/route.md +0 -27
  111. package/dist/payload/yaaw-core/system/workflows/planning/create-spec.md +0 -21
  112. package/dist/payload/yaaw-core/system/workflows/planning/create-tickets.md +0 -50
  113. package/dist/payload/yaaw-core/system/workflows/planning/decision-frontier.md +0 -33
  114. package/dist/payload/yaaw-core/system/workflows/planning/discover.md +0 -21
  115. package/dist/payload/yaaw-core/system/workflows/planning/question-round.md +0 -22
  116. package/dist/payload/yaaw-core/system/workflows/planning/readiness-review.md +0 -33
  117. package/dist/payload/yaaw-core/system/workflows/planning/record-decisions.md +0 -19
  118. package/dist/payload/yaaw-core/system/workflows/planning/replan.md +0 -24
  119. package/dist/payload/yaaw-core/system/workflows/planning/research.md +0 -20
  120. package/dist/payload/yaaw-core/system/workflows/planning/route.md +0 -28
  121. package/dist/payload/yaaw-core/system/workflows/planning/write-understanding.md +0 -26
  122. package/dist/payload/yaaw-core/system/workflows/prd/create.md +0 -22
  123. package/dist/payload/yaaw-core/system/workflows/prd/question-round.md +0 -20
  124. package/dist/payload/yaaw-core/system/workflows/prd/readiness.md +0 -22
  125. package/dist/payload/yaaw-core/system/workflows/prd/record-decisions.md +0 -20
  126. package/dist/payload/yaaw-core/system/workflows/prd/refine.md +0 -15
  127. package/dist/payload/yaaw-core/system/workflows/prd/revise.md +0 -18
  128. package/dist/payload/yaaw-core/system/workflows/prd/route.md +0 -23
  129. package/dist/payload/yaaw-core/system/workflows/review/classify-findings.md +0 -17
  130. package/dist/payload/yaaw-core/system/workflows/review/inspect-change.md +0 -20
  131. package/dist/payload/yaaw-core/system/workflows/review/record-review.md +0 -19
  132. package/dist/payload/yaaw-core/system/workflows/review/review-ticket.md +0 -24
  133. /package/dist/payload/yaaw-core/system/{tools → engine}/frontmatter.mjs +0 -0
  134. /package/dist/payload/yaaw-core/system/{tools/framework-integrity.mjs → engine/integrity.mjs} +0 -0
  135. /package/dist/payload/yaaw-core/system/{expertise/frontend-design/MODULE.md → modules/frontend-design.md} +0 -0
  136. /package/dist/payload/yaaw-core/system/{expertise/python/MODULE.md → modules/python.md} +0 -0
  137. /package/dist/payload/yaaw-core/system/{expertise/security/MODULE.md → modules/security.md} +0 -0
  138. /package/dist/payload/yaaw-core/system/{expertise/testing/MODULE.md → modules/testing.md} +0 -0
@@ -1,13 +0,0 @@
1
- {
2
- "schema": "yaaw.project-state/v2",
3
- "phase": "product",
4
- "product": {"artifact": ".yaaw-core/project/product.md", "status": "missing", "revision": 0},
5
- "planning": {"artifact": ".yaaw-core/project/engineering.md", "status": "missing", "revision": 0, "current_frontier": null, "readiness": "pending", "active_spec": null, "scope_status": "UNKNOWN"},
6
- "active_ticket": null,
7
- "tickets": {},
8
- "transition_sequence": 0,
9
- "last_transition": null,
10
- "blocker": null,
11
- "last_observed_commit": null,
12
- "last_workflow": null
13
- }
@@ -1,210 +0,0 @@
1
- const EXECUTABLE = new Set(["READY","IN_PROGRESS","REVIEW_REQUIRED","REPAIR_REQUIRED","PASS"]);
2
- export const normalizeStatus=v=>String(v??"").trim().toLowerCase();
3
- const num=(a,b)=>Number(a)===Number(b);
4
- const specId=v=>String(v??"").match(/SPEC-[0-9]+/)?.[0]??null;
5
- const legal=(t,a,b)=>(t?.legal??[]).some(x=>x.from===a&&x.to===b);
6
- const currentRepo=(r,repo)=>Boolean(r?.value?.repository?.worktree_digest&&repo?.status==="READY"&&r.value.repository.worktree_digest===repo.worktree_digest);
7
- const latest=(rows,pred)=>(rows??[]).filter(pred).sort((a,b)=>String(a.path).localeCompare(String(b.path))).at(-1)??null;
8
- const evidenceRefs=r=>new Set((r?.value?.evidence??[]).map(String));
9
-
10
- export function ticketSourceStatus(ticket,state,activeSpec){
11
- if(!ticket||!activeSpec)return "SPEC_SOURCE_STALE";
12
- if(!num(ticket.product_revision,state?.product?.revision))return "PRODUCT_SOURCE_STALE";
13
- if(!num(ticket.engineering_revision,state?.planning?.revision))return "ENGINEERING_SOURCE_STALE";
14
- if(String(ticket.spec)!==String(activeSpec.id))return "SPEC_SOURCE_STALE";
15
- if(!num(ticket.spec_revision,activeSpec.revision))return "TICKET_SOURCE_STALE";
16
- return "CURRENT";
17
- }
18
- export function currentAcceptedSpecs(state,a){
19
- return Object.entries(a?.specs??{}).filter(([,x])=>normalizeStatus(x?.meta?.status)==="accepted")
20
- .filter(([,x])=>num(x.meta.product_revision,state?.product?.revision))
21
- .filter(([,x])=>num(x.meta.engineering_revision,state?.planning?.revision))
22
- .filter(([,x])=>String(x.meta.frontier_id??"")===String(state?.planning?.current_frontier??""))
23
- .sort(([x],[y])=>x.localeCompare(y));
24
- }
25
- function activeSpec(state,a){const id=specId(state?.planning?.active_spec);const x=id&&a?.specs?.[id];return x?{id,...x.meta,path:x.path}:null;}
26
- function ev(rows,id,tr,sr,kind){return latest(rows,x=>x?.value?.ticket===id&&x.value.kind===kind&&num(x.value.ticket_revision,tr)&&num(x.value.spec_revision,sr));}
27
- function start(rows,id,tr,sr){const x=ev(rows,id,tr,sr,"implementation_start");return x?.value?.schema==="yaaw.evidence/v3"&&x.value.result==="STARTED"?x:null;}
28
- function verify(rows,id,tr,sr){return ev(rows,id,tr,sr,"implementation_verification");}
29
- function review(rows,id,tr,sr){return (rows??[]).filter(x=>x?.value?.ticket===id&&num(x.value.ticket_revision,tr)&&num(x.value.spec_revision,sr)).sort((a,b)=>Number(a.value.round)-Number(b.value.round)).at(-1)??null;}
30
- function transition(id,subject,from,to,reason,evidence=[]){return{id,kind:"STATE",subject,from,to,reason,workflow:"orchestration.reconcile-state",evidence};}
31
- function currentTicketIds(state,a){const s=activeSpec(state,a);return s?Object.keys(state?.tickets??{}).filter(id=>ticketSourceStatus(a?.tickets?.[id]?.meta,state,s)==="CURRENT").sort():[];}
32
-
33
- export function selectReconciliation({state,artifacts:a,evidence=[],reviews=[],repository,transitions,reconciliationPolicy,routingPolicy}){
34
- const order=["PROJECT_STATE_SCHEMA_MIGRATION_REQUIRED","PRODUCT_LEDGER_SYNC","PLANNING_LEDGER_SYNC","ACTIVE_SPEC_UNADOPTED","TICKET_UNREGISTERED","TICKET_ADMISSION","CONTRACT_SOURCE_STALE","IMPLEMENTATION_STARTED","IMPLEMENTATION_VERIFIED","REVIEW_RESULT_UNAPPLIED","ACCEPTANCE_STALE","PROJECT_COMPLETION_SYNC"];
35
- const declared=(reconciliationPolicy?.priority??[]).map(x=>typeof x==="string"?x:x.id);
36
- if(declared.length&&JSON.stringify(declared)!==JSON.stringify(order))return{id:"RECONCILIATION_POLICY_DRIFT",kind:"BLOCKED",reason:"reconciliation priority differs from engine contract"};
37
- if(!state)return{id:"PROJECT_STATE_MISSING",kind:"BLOCKED",reason:"project state missing"};
38
- if(state.schema!=="yaaw.project-state/v2")return{id:"PROJECT_STATE_SCHEMA_MIGRATION_REQUIRED",kind:"BLOCKED",reason:"project schema requires installer migration"};
39
-
40
- const p=a?.product?.meta;
41
- if(p){
42
- if(Number(state.product.revision)>Number(p.revision))return{id:"PRODUCT_LEDGER_AHEAD",kind:"BLOCKED",reason:"state product revision is ahead of durable product"};
43
- if(Number(p.revision)>Number(state.product.revision)||normalizeStatus(p.status)!==normalizeStatus(state.product.status))
44
- return{id:"PRODUCT_LEDGER_SYNC",kind:"STATE",subject:"product",from:String(state.product.revision),to:String(p.revision),reason:"durable product metadata is ahead",workflow:"orchestration.reconcile-state",evidence:[a.product.path],patch:{revision:Number(p.revision),status:p.status}};
45
- }
46
- const e=a?.engineering?.meta;
47
- if(e){
48
- if(Number(state.planning.revision)>Number(e.revision))return{id:"PLANNING_LEDGER_AHEAD",kind:"BLOCKED",reason:"state planning revision is ahead of durable engineering"};
49
- const scope=e.scope_status??"UNKNOWN";
50
- if(Number(e.revision)>Number(state.planning.revision)||normalizeStatus(e.status)!==normalizeStatus(state.planning.status)||normalizeStatus(e.readiness)!==normalizeStatus(state.planning.readiness)||String(e.current_frontier??"")!==String(state.planning.current_frontier??"")||scope!==(state.planning.scope_status??"UNKNOWN"))
51
- return{id:"PLANNING_LEDGER_SYNC",kind:"STATE",subject:"planning",from:String(state.planning.revision),to:String(e.revision),reason:"durable engineering metadata is ahead",workflow:"orchestration.reconcile-state",evidence:[a.engineering.path],patch:{revision:Number(e.revision),status:e.status,readiness:e.readiness,current_frontier:e.current_frontier??null,scope_status:scope}};
52
- }
53
-
54
- const specs=currentAcceptedSpecs(state,a);
55
- if(specs.length>1)return{id:"MULTIPLE_CURRENT_ACCEPTED_SPECS",kind:"BLOCKED",reason:"multiple current accepted specs"};
56
- if(specs.length===1&&specId(state.planning.active_spec)!==specs[0][0]){
57
- const [id,x]=specs[0];return{id:"ACTIVE_SPEC_UNADOPTED",kind:"STATE",subject:id,from:state.planning.active_spec??null,to:x.path,reason:"current accepted spec not adopted",workflow:"orchestration.reconcile-state",evidence:[x.path],patch:{active_spec:x.path}};
58
- }
59
-
60
- const s=activeSpec(state,a);
61
- if(s){
62
- for(const [id,x] of Object.entries(a?.tickets??{}).sort(([x],[y])=>x.localeCompare(y))){
63
- if(Object.hasOwn(state.tickets??{},id))continue;
64
- if(!["DRAFT","READY"].includes(String(x?.meta?.status)))continue;
65
- if(ticketSourceStatus(x.meta,state,s)!=="CURRENT")continue;
66
- return{id:"TICKET_UNREGISTERED",kind:"STATE",subject:id,from:null,to:x.meta.status,reason:"current ticket artifact not registered",workflow:"orchestration.reconcile-state",evidence:[x.path],patch:{ticket:id,status:x.meta.status}};
67
- }
68
- for(const id of Object.keys(state.tickets??{}).sort()){
69
- const st=state.tickets[id],m=a?.tickets?.[id]?.meta;
70
- if(st!=="DRAFT"||String(m?.status??"")!=="READY")continue;
71
- if(ticketSourceStatus(m,state,s)!=="CURRENT")continue;
72
- if(normalizeStatus(state?.planning?.status)!=="ready"||state?.planning?.readiness!=="PASS")continue;
73
- if(!deps(id,a,state)||repository?.status!=="READY")continue;
74
- const contract=(transitions?.legal??[]).find(x=>x.from==="DRAFT"&&x.to==="READY");
75
- if(contract?.owner!=="planner"||contract?.workflow!=="planning.create-tickets")
76
- return{id:"ILLEGAL_TICKET_ADMISSION_CONTRACT",kind:"BLOCKED",reason:"DRAFT -> READY admission must be owned by Planner planning.create-tickets"};
77
- return transition("TICKET_ADMISSION",id,"DRAFT","READY","PLANNER_ADMISSION_CURRENT",[a.tickets[id].path,s.path]);
78
- }
79
- for(const id of Object.keys(state.tickets??{}).sort()){
80
- const st=state.tickets[id];if(!EXECUTABLE.has(st))continue;
81
- const cause=ticketSourceStatus(a?.tickets?.[id]?.meta,state,s);
82
- if(cause!=="CURRENT"){
83
- const target=routingPolicy?.invalidation_routes?.[cause]?.state;
84
- if(target!=="REPLAN_REQUIRED"||!legal(transitions,st,target))return{id:"ILLEGAL_INVALIDATION_TRANSITION",kind:"BLOCKED",reason:st+" cannot transition for "+cause};
85
- return transition("CONTRACT_SOURCE_STALE",id,st,target,cause,[a?.tickets?.[id]?.path,s.path].filter(Boolean));
86
- }
87
- }
88
- for(const id of Object.keys(state.tickets??{}).sort()){
89
- const st=state.tickets[id],m=a?.tickets?.[id]?.meta;if(!m)continue;
90
- const tr=Number(m.revision),sr=Number(s.revision);
91
- if(st==="READY"){
92
- const x=start(evidence,id,tr,sr);
93
- if(x)return transition("IMPLEMENTATION_STARTED",id,"READY","IN_PROGRESS","IMPLEMENTATION_STARTED",[x.path]);
94
- }
95
- if(st==="IN_PROGRESS"){
96
- const x=verify(evidence,id,tr,sr);
97
- if(x?.value?.schema==="yaaw.evidence/v3"&&x.value.result==="PASS"&&currentRepo(x,repository))
98
- return transition("IMPLEMENTATION_VERIFIED",id,"IN_PROGRESS","REVIEW_REQUIRED","IMPLEMENTATION_VERIFIED",[x.path]);
99
- }
100
- if(st==="REPAIR_REQUIRED"){
101
- const v=verify(evidence,id,tr,sr),r=review(reviews,id,tr,sr),used=evidenceRefs(r);
102
- if(v?.value?.schema==="yaaw.evidence/v3"&&v.value.result==="PASS"&&currentRepo(v,repository)&&!used.has(String(v.value.id)))
103
- return transition("IMPLEMENTATION_VERIFIED",id,"REPAIR_REQUIRED","REVIEW_REQUIRED","REPAIR_VERIFIED",[v.path,r?.path].filter(Boolean));
104
- }
105
- if(st==="REVIEW_REQUIRED"){
106
- const v=verify(evidence,id,tr,sr),r=review(reviews,id,tr,sr),to={PASS:"PASS",REPAIR:"REPAIR_REQUIRED",REPLAN:"REPLAN_REQUIRED",BLOCKED:"BLOCKED"}[r?.value?.result];
107
- if(r&&to&&currentRepo(r,repository)){
108
- const verificationCurrent=Boolean(v?.value?.schema==="yaaw.evidence/v3"&&v.value.result==="PASS"&&currentRepo(v,repository));
109
- if(!verificationCurrent)return null;
110
- if(!evidenceRefs(r).has(String(v.value.id)))return null;
111
- if(!legal(transitions,"REVIEW_REQUIRED",to))
112
- return{id:"ILLEGAL_REVIEW_TRANSITION",kind:"BLOCKED",reason:"review result is not a legal lifecycle transition"};
113
- return transition("REVIEW_RESULT_UNAPPLIED",id,"REVIEW_REQUIRED",to,"REVIEW_"+r.value.result,[r.path,v.path]);
114
- }
115
- }
116
- if(st==="PASS"){
117
- const v=verify(evidence,id,tr,sr),r=review(reviews,id,tr,sr);let reason=null;
118
- if(!r)reason="REVIEW_MISSING";
119
- else if(!r.value?.repository?.worktree_digest)reason="LEGACY_IDENTITY_UNVERIFIABLE";
120
- else if(!currentRepo(r,repository))reason="REVIEW_REPOSITORY_STALE";
121
- else if(!v||v.value?.schema!=="yaaw.evidence/v3"||v.value.result!=="PASS")reason="VERIFICATION_MISSING";
122
- else if(!currentRepo(v,repository))reason="VERIFICATION_REPOSITORY_STALE";
123
- else if(!evidenceRefs(r).has(String(v.value.id)))reason="VERIFICATION_MISSING";
124
- if(reason){
125
- const target=routingPolicy?.invalidation_routes?.[reason]?.state;
126
- if(target!=="REVIEW_REQUIRED"||!legal(transitions,"PASS",target))return{id:"ILLEGAL_ACCEPTANCE_INVALIDATION",kind:"BLOCKED",reason};
127
- return transition("ACCEPTANCE_STALE",id,"PASS",target,reason,[r?.path,v?.path].filter(Boolean));
128
- }
129
- }
130
- }
131
- }
132
- const ids=currentTicketIds(state,a),vals=ids.map(id=>state.tickets[id]);
133
- if(ids.length&&vals.every(x=>x==="PASS"||x==="CANCELLED")&&state.planning.scope_status==="COMPLETE"&&state.phase!=="complete")
134
- return{id:"PROJECT_COMPLETION_SYNC",kind:"STATE",subject:"project",from:state.phase,to:"complete",reason:"all current admitted tickets terminal and scope COMPLETE",workflow:"orchestration.reconcile-state",evidence:ids};
135
- return null;
136
- }
137
- function deps(id,a,state){return (a?.tickets?.[id]?.meta?.dependencies??[]).every(d=>state.tickets?.[d]==="PASS");}
138
- function baseRoute(state,a,evidence,reviews,repository,p){
139
- if(state?.blocker)return{kind:"BLOCKED",terminal:"BLOCKED",reason:state.blocker.kind??"PROJECT_BLOCKED"};
140
- if(normalizeStatus(state?.product?.status)!=="ready")return{kind:"DISPATCH_READY",workflow:p.product_unready_workflow};
141
- const tickets=state?.tickets??{},replan=Object.keys(tickets).sort().find(id=>tickets[id]==="REPLAN_REQUIRED");if(replan)return{kind:"DISPATCH_READY",workflow:"planning.replan",ticket:replan};
142
- if(normalizeStatus(state?.planning?.status)!=="ready"||state.planning.readiness!=="PASS")return{kind:"DISPATCH_READY",workflow:p.planning_unready_workflow};
143
- const s=activeSpec(state,a);if(!s||normalizeStatus(s.status)!=="accepted")return{kind:"DISPATCH_READY",workflow:p.missing_spec_workflow};
144
- const ids=currentTicketIds(state,a);if(!ids.length)return{kind:"DISPATCH_READY",workflow:p.missing_tickets_workflow};
145
- const repair=ids.find(x=>tickets[x]==="REPAIR_REQUIRED");if(repair)return{kind:"DISPATCH_READY",workflow:"implementation.repair-ticket",ticket:repair};
146
- const reviewRequired=ids.find(x=>tickets[x]==="REVIEW_REQUIRED");
147
- if(reviewRequired){
148
- const m=a.tickets[reviewRequired].meta,tr=Number(m.revision),sr=Number(s.revision),v=verify(evidence,reviewRequired,tr,sr);
149
- const verificationCurrent=Boolean(v?.value?.schema==="yaaw.evidence/v3"&&v.value.result==="PASS"&&currentRepo(v,repository));
150
- if(!verificationCurrent)return{kind:"DISPATCH_READY",workflow:"implementation.verify-ticket",ticket:reviewRequired,reason:"REVIEW_VERIFICATION_RECOVERY_REQUIRED"};
151
- const r=review(reviews,reviewRequired,tr,sr),reviewCurrent=Boolean(r&&currentRepo(r,repository)&&evidenceRefs(r).has(String(v.value.id)));
152
- if(!reviewCurrent)return{kind:"DISPATCH_READY",workflow:"review.review-ticket",ticket:reviewRequired,reason:"REVIEW_REFRESH_REQUIRED"};
153
- return{kind:"DISPATCH_READY",workflow:"review.review-ticket",ticket:reviewRequired,reason:"REVIEW_RESULT_REFRESH_REQUIRED"};
154
- }
155
- const ip=ids.find(x=>tickets[x]==="IN_PROGRESS");if(ip){const m=a.tickets[ip].meta;return start(evidence,ip,Number(m.revision),Number(s.revision))?{kind:"DISPATCH_READY",workflow:"implementation.verify-ticket",ticket:ip}:{kind:"ROOT_ACTION",workflow:"orchestration.recover-interruption",ticket:ip,reason:"IMPLEMENTATION_START_EVIDENCE_MISSING"};}
156
- const ready=ids.find(x=>tickets[x]==="READY"&&deps(x,a,state));if(ready)return{kind:"DISPATCH_READY",workflow:"implementation.implement-ticket",ticket:ready};
157
- const draft=ids.find(x=>tickets[x]==="DRAFT"&&deps(x,a,state));if(draft)return{kind:"DISPATCH_READY",workflow:p.draft_admission_workflow??"planning.create-tickets",ticket:draft,reason:"TICKET_ADMISSION_REQUIRED"};
158
- const vals=ids.map(x=>tickets[x]);if(vals.some(x=>x==="BLOCKED"))return{kind:"BLOCKED",terminal:"BLOCKED",reason:"TICKET_BLOCKED"};
159
- if(vals.length&&vals.every(x=>x==="PASS"||x==="CANCELLED"))return state.planning.scope_status==="COMPLETE"?{kind:"TERMINAL",terminal:p.complete_terminal}:{kind:"DISPATCH_READY",workflow:p.next_frontier_workflow};
160
- if(vals.some(x=>x==="DRAFT"))return{kind:"DISPATCH_READY",workflow:p.next_frontier_workflow,reason:"DRAFT_DEPENDENCIES_UNSATISFIED"};
161
- const unsupported=vals.find(x=>!["READY","IN_PROGRESS","REVIEW_REQUIRED","REPAIR_REQUIRED","REPLAN_REQUIRED","BLOCKED","PASS","CANCELLED","DRAFT"].includes(x));
162
- if(unsupported)return{kind:"BLOCKED",terminal:"BLOCKED",reason:"UNSUPPORTED_TICKET_STATE:"+unsupported};
163
- return{kind:"DISPATCH_READY",workflow:p.next_frontier_workflow,reason:"PLANNING_ROUTE_REQUIRED"};
164
- }
165
- function legalNow(w,state,a){
166
- const s=activeSpec(state,a),t=state?.tickets??{},productReady=normalizeStatus(state?.product?.status)==="ready";
167
- if(w==="prd.route")return true;
168
- if(["prd.revise","prd.refine"].includes(w))return Boolean(a?.product?.meta);
169
- if(["planning.route","planning.readiness-review"].includes(w))return productReady;
170
- if(w==="planning.create-spec")return productReady&&normalizeStatus(state.planning.status)==="ready"&&state.planning.readiness==="PASS"&&!s;
171
- if(w==="planning.create-tickets"){const ids=currentTicketIds(state,a);return Boolean(s)&&(ids.length===0||ids.some(id=>t[id]==="DRAFT"&&deps(id,a,state)));}
172
- if(w==="implementation.implement-ticket")return Object.values(t).includes("READY");
173
- if(w==="implementation.repair-ticket")return Object.values(t).includes("REPAIR_REQUIRED");
174
- if(w==="review.review-ticket")return Object.values(t).includes("REVIEW_REQUIRED");
175
- return false;
176
- }
177
- export function intentComplete(i,state,a){
178
- if(!i)return false;const target=String(i.target_artifact??"").match(/TASK-[0-9]+/)?.[0]??null,o=i.desired_outcome,advanced=Number(state?.last_transition?.sequence??0)>Number(i.created_from_transition_sequence??0);
179
- if(o==="CREATE_SPEC")return currentAcceptedSpecs(state,a).length===1;
180
- if(o==="CREATE_TICKETS"){const ids=currentTicketIds(state,a);return ids.length>0&&!ids.some(id=>state.tickets?.[id]==="DRAFT"&&deps(id,a,state));}
181
- if(o==="IMPLEMENT"||o==="REPAIR")return target?state.tickets?.[target]==="REVIEW_REQUIRED":advanced&&state.last_transition?.to==="REVIEW_REQUIRED";
182
- if(o==="REVIEW")return target?Boolean(state.tickets?.[target]&&state.tickets[target]!=="REVIEW_REQUIRED"):advanced&&state.last_transition?.workflow==="review.record-review";
183
- if(o==="CONTINUE_PRODUCT")return normalizeStatus(state.product?.status)==="ready";
184
- if(o==="REVISE_PRODUCT"||o==="REFINE_PRODUCT")return advanced&&state.last_transition?.subject==="product";
185
- if(o==="CONTINUE_PLANNING")return normalizeStatus(state.planning?.status)==="ready"&&state.planning?.readiness==="PASS";
186
- if(o==="PLANNING_REVIEW")return ["PASS","MISSING_DECISIONS","PRODUCT_GAP","REPLAN","BLOCKED"].includes(state.planning?.readiness);
187
- return false;
188
- }
189
- export function selectRoute({state,artifacts:a,evidence=[],reviews=[],repository,intent,routingPolicy}){
190
- const normal=baseRoute(state,a,evidence,reviews,repository,routingPolicy);if(!intent||intent.desired_outcome==="CONTINUE")return normal;
191
- if(intentComplete(intent,state,a))return{kind:"INTENT_COMPLETE",reason:intent.completion_kind};
192
- if(intent.requested_workflow&&legalNow(intent.requested_workflow,state,a)){const map={"implementation.implement-ticket":"READY","implementation.repair-ticket":"REPAIR_REQUIRED","review.review-ticket":"REVIEW_REQUIRED"},st=map[intent.requested_workflow];let ticket=st&&Object.keys(state.tickets??{}).sort().find(id=>state.tickets[id]===st);if(intent.requested_workflow==="planning.create-tickets")ticket=currentTicketIds(state,a).find(id=>state.tickets?.[id]==="DRAFT"&&deps(id,a,state))??null;return{kind:"DISPATCH_READY",workflow:intent.requested_workflow,...(ticket?{ticket}:{})};}
193
- if(["REVIEW","REPAIR"].includes(intent.desired_outcome))return{kind:"BLOCKED",terminal:"BLOCKED",reason:intent.desired_outcome+"_PRECONDITION_UNSATISFIED"};
194
- return normal;
195
- }
196
- export function applyReconciliation(state,r,repo){
197
- if(!r||r.kind!=="STATE")throw new Error("reconciliation is not applicable");const n=structuredClone(state),seq=Number(n.transition_sequence??0)+1;
198
- if(r.id==="PRODUCT_LEDGER_SYNC")Object.assign(n.product,r.patch);
199
- else if(r.id==="PLANNING_LEDGER_SYNC")Object.assign(n.planning,r.patch);
200
- else if(r.id==="ACTIVE_SPEC_UNADOPTED")n.planning.active_spec=r.patch.active_spec;
201
- else if(r.id==="TICKET_UNREGISTERED"){n.tickets[r.patch.ticket]=r.patch.status;if(!n.active_ticket&&r.patch.status==="READY")n.active_ticket=r.patch.ticket;}
202
- else if(r.id==="PROJECT_COMPLETION_SYNC"){n.phase="complete";n.active_ticket=null;}
203
- else if(r.subject?.startsWith?.("TASK-")){n.tickets[r.subject]=r.to;if(["IN_PROGRESS","REVIEW_REQUIRED","REPAIR_REQUIRED","REPLAN_REQUIRED"].includes(r.to))n.active_ticket=r.subject;if(["PASS","CANCELLED"].includes(r.to)&&n.active_ticket===r.subject)n.active_ticket=null;}
204
- n.transition_sequence=seq;n.last_transition={sequence:seq,subject:r.subject,from:r.from??null,to:r.to,workflow:r.workflow??"orchestration.reconcile-state",reason:r.reason,evidence:r.evidence??[],observed_commit:repo?.head_commit??null};n.last_observed_commit=repo?.head_commit??null;n.last_workflow=r.workflow??"orchestration.reconcile-state";return n;
205
- }
206
- export function planNext(input){
207
- if(input?.framework?.integrity_status!=="HEALTHY"){const s=input?.framework?.integrity_status??"UNKNOWN";return{kind:"FRAMEWORK_STOP",reason:s==="CONTRACT_INCONSISTENT"?"FRAMEWORK_CONTRACT_INCONSISTENCY":["UNKNOWN","MANIFEST_INVALID"].includes(s)?"FRAMEWORK_INTEGRITY_UNKNOWN":"FRAMEWORK_INTEGRITY_VIOLATION"};}
208
- const r=selectReconciliation(input);if(r?.kind==="BLOCKED")return{kind:"BLOCKED",terminal:"BLOCKED",reason:r.id,reconciliation:r};if(r)return{kind:"RECONCILE_REQUIRED",workflow:"orchestration.reconcile-state",reconciliation:r};return selectRoute(input);
209
- }
210
- export const handoffBasis=({selected,state,intent})=>({workflow:selected.workflow,ticket:selected.ticket??null,desired_intent:intent?.desired_outcome??null,transition_sequence:Number(state?.transition_sequence??0)});
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
- import {spawnSync} from "node:child_process";
3
- import {mkdir,readFile,readdir,rm,writeFile} from "node:fs/promises";
4
- import {basename,join,relative,resolve} from "node:path";
5
- import {applyReconciliation,handoffBasis,planNext} from "./orchestration-engine.mjs";
6
- import {parseFrontmatter} from "./frontmatter.mjs";
7
-
8
- const argv=process.argv.slice(2);let workspaceArg=".",mode="prepare",invokeSkill=null;
9
- for(let i=0;i<argv.length;i++){if(argv[i]==="--workspace"&&argv[i+1])workspaceArg=argv[++i];else if(argv[i]==="--inspect-only")mode="inspect";else if(argv[i]==="--check-handoff")mode="check";else if(argv[i]==="--reconcile-one")mode="reconcile";else if(argv[i]==="--invoke-skill"&&argv[i+1])invokeSkill=argv[++i];else{process.stderr.write("usage: orchestration-runtime.mjs [--workspace <path>] [--invoke-skill <id>] [--inspect-only|--check-handoff|--reconcile-one]\n");process.exit(2);}}
10
- const workspace=resolve(workspaceArg),systemRoot=join(workspace,".yaaw-core","system"),projectRoot=join(workspace,".yaaw-core","project"),runtimeRoot=join(workspace,".yaaw-core","runtime");
11
- const observedPath=join(runtimeRoot,"observed-state.json"),handoffPath=join(runtimeRoot,"handoff.json"),intentPath=join(runtimeRoot,"intent.json"),statePath=join(projectRoot,"state.json");
12
- const json=async p=>JSON.parse(await readFile(p,"utf8"));const maybe=async p=>{try{return await json(p)}catch{return null}};const uniq=a=>[...new Set(a.filter(Boolean))];
13
- async function fm(path){try{return parseFrontmatter(await readFile(path,"utf8"))}catch{return null}}
14
- async function list(dir,re){try{return(await readdir(dir,{withFileTypes:true})).filter(x=>x.isFile()&&re.test(x.name)).map(x=>join(dir,x.name)).sort()}catch{return[]}}
15
- function run(tool,args){const r=spawnSync(process.execPath,[tool,...args],{cwd:workspace,encoding:"utf8",windowsHide:true,maxBuffer:64*1024*1024});try{return JSON.parse(r.stdout||"{}")}catch{throw new Error((r.stderr||"").trim()||"invalid JSON from "+basename(tool))}}
16
- function framework(raw,status=raw?.status??"UNKNOWN"){return{yaaw_version:String(raw?.package_version??"unknown"),system_schema:Number(raw?.system_schema??1),installation_schema:Number(raw?.installation_schema??1),project_schema:Number(raw?.project_schema??1),manifest_digest:String(raw?.manifest_digest??"sha256:unknown"),integrity_status:status,modified:Array.isArray(raw?.modified)?raw.modified:[],missing:Array.isArray(raw?.missing)?raw.missing:[],local_overrides:Array.isArray(raw?.local_overrides)?raw.local_overrides:[],repair_required:status!=="HEALTHY"}}
17
- function repo(raw){return{schema:"yaaw.repository-identity/v2",algorithm:raw?.algorithm??null,status:raw?.status??"UNAVAILABLE",workspace_scope:String(raw?.workspace_scope??"."),git_root_relation:raw?.git_root_relation??"unknown",head_commit:raw?.head_commit??null,dirty:typeof raw?.dirty==="boolean"?raw.dirty:null,worktree_digest:raw?.worktree_digest??null,components:raw?.components??null,changed_paths:Array.isArray(raw?.changed_paths)?raw.changed_paths:[],error:raw?.error??null}}
18
- async function contracts(){const names=["workflows","execution-policy","role-io","routing-policy","handoff-policy","artifacts","skills","transitions","reconciliation-policy"];const vals=await Promise.all(names.map(n=>json(join(systemRoot,"registries",n+".json"))));const c=Object.fromEntries(names.map((n,i)=>[n.replaceAll("-","_"),vals[i]])),errors=[];for(const[w,p]of Object.entries(c.handoff_policy.workflows??{})){const reg=c.workflows[w],ex=c.execution_policy.workflows?.[w],io=c.role_io.roles?.[reg?.role];if(!reg)errors.push("unknown workflow "+w);if(!ex)errors.push("no execution policy "+w);if(!io)errors.push("unknown role "+reg?.role);if(io){const br=(p.reads??[]).filter(x=>!io.reads.includes(x)),bw=(p.writes??[]).filter(x=>!io.writes.includes(x));if(br.length)errors.push(w+" bad reads "+br);if(bw.length)errors.push(w+" bad writes "+bw)}}return{...c,errors}}
19
- const rel=p=>relative(workspace,p).replaceAll("\\","/"),specId=v=>String(v??"").match(/SPEC-[0-9]+/)?.[0]??null;
20
- async function inspect(state){const pp=join(projectRoot,"product.md"),ep=join(projectRoot,"engineering.md"),specs={},tickets={},reviews=[],evidence=[];for(const p of await list(join(projectRoot,"specs"),/^SPEC-[0-9]+\.md$/)){const m=await fm(p);if(m?.id)specs[m.id]={path:rel(p),meta:m}}for(const p of await list(join(projectRoot,"tickets"),/^TASK-[0-9]+\.md$/)){const m=await fm(p);if(m?.id)tickets[m.id]={path:rel(p),meta:m}}for(const p of await list(join(projectRoot,"reviews"),/^TASK-[0-9]+-R[0-9]+\.md$/)){const v=await fm(p);if(v)reviews.push({path:rel(p),value:v})}for(const p of await list(join(projectRoot,"evidence"),/\.json$/)){try{evidence.push({path:rel(p),value:await json(p)})}catch{}}return{product:{path:rel(pp),meta:await fm(pp)},engineering:{path:rel(ep),meta:await fm(ep)},specs,tickets,reviews,evidence,research:(await list(join(projectRoot,"research"),/^RSH-.*\.md$/)).map(rel),rules:(await list(join(projectRoot,"rules"),/\.md$/)).map(rel),activeSpecId:specId(state?.planning?.active_spec)}}
21
- function summary(a){return{product:a.product.meta?a.product:null,engineering:a.engineering.meta?a.engineering:null,specs:a.specs,tickets:a.tickets,reviews:a.reviews.map(x=>x.path),evidence:a.evidence.map(x=>x.path)}}
22
- function requirement(r,repository){return r==="NONE"||r==="INSPECT"&&["READY","UNVERSIONED"].includes(repository.status)||r==="IDENTITY"&&repository.status==="READY"}
23
- function refs(policy,a,ticket,state){const out=[];for(const x of policy.reads??[]){if(x==="product"&&a.product.meta)out.push(a.product.path);else if(x==="engineering"&&a.engineering.meta)out.push(a.engineering.path);else if(x==="spec"){const id=specId(state?.planning?.active_spec);if(id&&a.specs[id])out.push(a.specs[id].path)}else if(x==="ticket"&&ticket&&a.tickets[ticket])out.push(a.tickets[ticket].path);else if(x==="evidence")out.push(...a.evidence.map(v=>v.path));else if(x==="review")out.push(...a.reviews.map(v=>v.path));else if(x==="engineering_research")out.push(...a.research);else if(x==="project_rule")out.push(...a.rules);else if(x==="state")out.push(".yaaw-core/project/state.json")}return uniq(out)}
24
- function revisions(state,a,ticket){const id=specId(state?.planning?.active_spec);return{product:Number(state?.product?.revision??0),engineering:Number(state?.planning?.revision??0),spec:Number(id?a.specs?.[id]?.meta?.revision??0:0),ticket:Number(ticket?a.tickets?.[ticket]?.meta?.revision??0:0)}}
25
- async function expertise(policy,role,a,ticket){const reg=await json(join(systemRoot,"registries","expertise.json")),ids=[...(policy.required_expertise??[])];if(policy.ticket_expertise&&ticket&&Array.isArray(a.tickets?.[ticket]?.meta?.expertise))ids.push(...a.tickets[ticket].meta.expertise);return uniq(ids).filter(id=>reg[id]&&(reg[id].usable_by??[]).includes(role)).sort()}
26
- function active(policy,a,ticket,state){if(policy.active_artifact==="product")return a.product.meta?a.product.path:null;if(policy.active_artifact==="engineering")return a.engineering.meta?a.engineering.path:null;if(policy.active_artifact==="spec"){const id=specId(state?.planning?.active_spec);return id?a.specs[id]?.path??null:null}if(policy.active_artifact==="ticket")return ticket?a.tickets[ticket]?.path??null:null;return null}
27
- async function handoff(sel,c,state,a,fw,repository,intent){const reg=c.workflows[sel.workflow],p=c.handoff_policy.workflows[sel.workflow],ex=c.execution_policy.workflows[sel.workflow];if(!reg||!p||!ex)throw new Error("missing deterministic handoff contract for "+sel.workflow);const role=reg.role;return{schema:"yaaw.handoff/v3",framework:{yaaw_version:fw.yaaw_version,system_schema:fw.system_schema,installation_schema:fw.installation_schema,project_schema:fw.project_schema,manifest_digest:fw.manifest_digest,integrity_status:"HEALTHY"},role,workflow:sel.workflow,desired_intent:intent?.desired_outcome??p.desired_intent??null,active_artifact:active(p,a,sel.ticket,state),references:refs(p,a,sel.ticket,state),reads:uniq(p.reads??[]),writes:uniq(p.writes??[]),forbidden_writes:uniq(c.role_io.roles[role].forbidden_writes??[]),revisions:revisions(state,a,sel.ticket),expertise:await expertise(p,role,a,sel.ticket),host_expertise:[],expected_output:p.expected_output,result_vocabulary:uniq(p.result_vocabulary??[]),repository_requirement:ex.repository_requirement,repository,transition_sequence:Number(state.transition_sequence??0)}}
28
- async function seed(skill,c,state){const s=c.skills[skill];if(!s)throw new Error("unknown public skill "+skill);const v={schema:"yaaw.intent/v2",source_skill:skill,desired_outcome:s.desired_outcome,requested_workflow:s.requested_workflow??s.workflow_id,target_artifact:null,completion_kind:s.completion_kind,created_from_transition_sequence:Number(state?.transition_sequence??0)};await mkdir(runtimeRoot,{recursive:true});await writeFile(intentPath,JSON.stringify(v,null,2)+"\n");return v}
29
- const clear=async p=>rm(p,{force:true}),same=(a,b)=>JSON.stringify(a)===JSON.stringify(b);
30
- async function main(){
31
- let raw;try{raw=run(join(systemRoot,"tools","framework-integrity.mjs"),["--workspace",workspace])}catch(e){raw={status:"UNKNOWN",error:String(e)}}let fw=framework(raw),c;try{c=await contracts()}catch(e){return{schema:"yaaw.orchestration-runtime/v2",status:"FRAMEWORK_STOP",reason:"FRAMEWORK_CONTRACT_INCONSISTENCY",error:String(e)}}if(c.errors.length)fw=framework(raw,"CONTRACT_INCONSISTENT");
32
- let rr;try{rr=run(join(systemRoot,"tools","repository-identity.mjs"),["--workspace",workspace])}catch(e){rr={status:"UNAVAILABLE",workspace_scope:".",git_root_relation:"unknown",error:String(e)}}const repository=repo(rr);let state=await maybe(statePath);if(invokeSkill)await seed(invokeSkill,c,state);const intent=await maybe(intentPath),a=await inspect(state);
33
- const p=planNext({framework:fw,repository,state,artifacts:a,evidence:a.evidence,reviews:a.reviews,intent,transitions:c.transitions,routingPolicy:c.routing_policy,reconciliationPolicy:c.reconciliation_policy});
34
- const observed={schema:"yaaw.observed-state/v3",framework:fw,repository,transition_sequence:Number(state?.transition_sequence??0),claims:state??{},artifacts:summary(a),inconsistencies:c.errors.map(x=>"FRAMEWORK_CONTRACT_INCONSISTENCY:"+x),candidate_actions:[],reconciliation:p.reconciliation??null};
35
- const persist=async()=>{await mkdir(runtimeRoot,{recursive:true});await writeFile(observedPath,JSON.stringify(observed,null,2)+"\n")};
36
- if(p.kind==="FRAMEWORK_STOP"){observed.candidate_actions=["INSTALLER_REPAIR"];if(mode!=="check"){await persist();await clear(handoffPath)}return{schema:"yaaw.orchestration-runtime/v2",status:"FRAMEWORK_STOP",reason:p.reason,observed}}
37
- if(p.kind==="RECONCILE_REQUIRED"){observed.candidate_actions=["orchestration.reconcile-state"];if(mode==="reconcile"){state=applyReconciliation(state,p.reconciliation,repository);await writeFile(statePath,JSON.stringify(state,null,2)+"\n");observed.transition_sequence=state.transition_sequence;observed.claims=state;await persist();await clear(handoffPath);return{schema:"yaaw.orchestration-runtime/v2",status:"RECONCILED",reconciliation:p.reconciliation,state}}if(mode!=="check"){await persist();await clear(handoffPath)}return{schema:"yaaw.orchestration-runtime/v2",status:"RECONCILE_REQUIRED",workflow:"orchestration.reconcile-state",reconciliation:p.reconciliation,observed}}
38
- if(p.kind==="INTENT_COMPLETE"){observed.candidate_actions=["INTENT_COMPLETE"];if(mode!=="check"){await persist();await clear(handoffPath);await clear(intentPath)}return{schema:"yaaw.orchestration-runtime/v2",status:"INTENT_COMPLETE",completion_kind:intent?.completion_kind??null,observed}}
39
- if(mode==="inspect"){observed.candidate_actions=[p.workflow??p.terminal??p.reason??p.kind];await persist();return{schema:"yaaw.orchestration-runtime/v2",status:"INSPECTED",observed}}
40
- if(["ROOT_ACTION","TERMINAL","BLOCKED"].includes(p.kind)){observed.candidate_actions=[p.workflow??p.terminal??p.reason??p.kind];if(mode!=="check"){await persist();await clear(handoffPath)}return{schema:"yaaw.orchestration-runtime/v2",status:p.kind,workflow:p.workflow??null,terminal:p.terminal??null,reason:p.reason??null,observed}}
41
- const ex=c.execution_policy.workflows[p.workflow];if(!requirement(ex.repository_requirement,repository)){if(mode!=="check"){await persist();await clear(handoffPath)}return{schema:"yaaw.orchestration-runtime/v2",status:"BLOCKED",terminal:"BLOCKED",reason:ex.repository_requirement==="IDENTITY"?"REPOSITORY_IDENTITY_UNAVAILABLE":"REPOSITORY_INSPECTION_UNAVAILABLE",workflow:p.workflow,observed}}
42
- const h=await handoff(p,c,state,a,fw,repository,intent);observed.candidate_actions=[p.workflow];
43
- if(mode==="check"){const old=await maybe(handoffPath);return old&&same(old,h)?{schema:"yaaw.orchestration-runtime/v2",status:"HANDOFF_FRESH",...handoffBasis({selected:p,state,intent})}:{schema:"yaaw.orchestration-runtime/v2",status:"HANDOFF_STALE",workflow:p.workflow,reason:"HANDOFF_BASIS_CHANGED"}}
44
- await persist();await writeFile(handoffPath,JSON.stringify(h,null,2)+"\n");return{schema:"yaaw.orchestration-runtime/v2",status:"DISPATCH_READY",workflow:h.workflow,role:h.role,handoff:h}
45
- }
46
- try{const r=await main();process.stdout.write(JSON.stringify(r,null,2)+"\n");process.exitCode=["DISPATCH_READY","INSPECTED","HANDOFF_FRESH","ROOT_ACTION","TERMINAL","RECONCILED","INTENT_COMPLETE"].includes(r.status)?0:2}catch(e){process.stdout.write(JSON.stringify({schema:"yaaw.orchestration-runtime/v2",status:"FRAMEWORK_STOP",reason:"FRAMEWORK_CONTRACT_INCONSISTENCY",error:String(e?.message??e)},null,2)+"\n");process.exitCode=2}
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createHash } from "node:crypto";
3
- import { lstatSync, readFileSync, readlinkSync } from "node:fs";
4
- import { resolve } from "node:path";
5
- import { spawnSync } from "node:child_process";
6
-
7
- const ALGORITHM = "yaaw-worktree-v2";
8
- const CONTROL_OUTPUT_EXCLUDES = [
9
- ".yaaw-core/runtime/**",
10
- ".yaaw-core/project/state.json",
11
- ".yaaw-core/project/evidence/**",
12
- ".yaaw-core/project/reviews/**"
13
- ];
14
- const EXCLUDE_PATHSPECS = CONTROL_OUTPUT_EXCLUDES.map(path => `:(exclude)${path}`);
15
- const hash = value => createHash("sha256").update(value).digest("hex");
16
- function stable(value) {
17
- if (Array.isArray(value)) return value.map(stable);
18
- if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map(k => [k, stable(value[k])]));
19
- return value;
20
- }
21
- const canonicalJson = value => JSON.stringify(stable(value));
22
- function fail(status, message) {
23
- process.stdout.write(JSON.stringify({
24
- schema:"yaaw.repository-identity/v2", algorithm:null, status, workspace_scope:".",
25
- git_root_relation:status==="UNVERSIONED"?"none":"unknown", head_commit:null, dirty:null,
26
- worktree_digest:null, components:null, changed_paths:[], error:message
27
- })+"\n");
28
- process.exitCode=2;
29
- }
30
- function git(workspace,args) {
31
- const r=spawnSync("git",["-C",workspace,...args],{encoding:null,windowsHide:true,maxBuffer:64*1024*1024});
32
- if(r.error) throw r.error;
33
- if(r.status!==0) throw new Error((r.stderr??Buffer.alloc(0)).toString("utf8").trim()||`git ${args.join(" ")} exited ${r.status}`);
34
- return r.stdout??Buffer.alloc(0);
35
- }
36
- const nulPaths=buf=>buf.toString("utf8").split("\0").filter(Boolean).map(p=>p.replaceAll("\\","/")).sort();
37
- function untrackedRecord(workspace,path) {
38
- const absolute=resolve(workspace,path);
39
- const stat=lstatSync(absolute);
40
- if(stat.isSymbolicLink()) return {path,kind:"symlink",sha256:hash(Buffer.from(readlinkSync(absolute),"utf8"))};
41
- if(stat.isFile()) return {path,kind:"file",sha256:hash(readFileSync(absolute))};
42
- throw new Error(`unsupported untracked filesystem object: ${path}`);
43
- }
44
-
45
- const args=process.argv.slice(2);
46
- let workspaceArg=".";
47
- for(let i=0;i<args.length;i+=1){
48
- if(args[i]==="--workspace" && args[i+1]) {workspaceArg=args[i+1]; i+=1;}
49
- else {process.stderr.write("usage: repository-identity.mjs [--workspace <path>]\n"); process.exit(2);}
50
- }
51
- const workspace=resolve(workspaceArg);
52
- let workspacePrefix;
53
- try {
54
- const inside=git(workspace,["rev-parse","--is-inside-work-tree"]).toString("utf8").trim();
55
- if(inside!=="true") throw new Error("workspace is not inside a Git work tree");
56
- workspacePrefix=git(workspace,["rev-parse","--show-prefix"]).toString("utf8").trim().replaceAll("\\","/");
57
- }
58
- catch(error){ fail("UNVERSIONED",error instanceof Error?error.message:String(error)); }
59
-
60
- if(workspacePrefix !== undefined){
61
- try {
62
- const head=git(workspace,["rev-parse","HEAD"]).toString("utf8").trim();
63
- const scoped=[".",...EXCLUDE_PATHSPECS];
64
- const status=git(workspace,["status","--porcelain=v1","-z","--untracked-files=all","--",...scoped]);
65
- const unstaged=git(workspace,["diff","--binary","--no-ext-diff","--no-textconv","HEAD","--",...scoped]);
66
- const staged=git(workspace,["diff","--cached","--binary","--no-ext-diff","--no-textconv","HEAD","--",...scoped]);
67
- const untracked=nulPaths(git(workspace,["ls-files","--others","--exclude-standard","-z","--",...scoped])).map(p=>untrackedRecord(workspace,p));
68
- const tracked=new Set([
69
- ...nulPaths(git(workspace,["diff","--name-only","-z","HEAD","--",...scoped])),
70
- ...nulPaths(git(workspace,["diff","--cached","--name-only","-z","HEAD","--",...scoped]))
71
- ]);
72
- const components={
73
- status_sha256:hash(status),
74
- unstaged_diff_sha256:hash(unstaged),
75
- staged_diff_sha256:hash(staged),
76
- untracked_manifest_sha256:hash(Buffer.from(canonicalJson(untracked),"utf8"))
77
- };
78
- const payload={algorithm:ALGORITHM,workspace_scope:".",head_commit:head,excluded:CONTROL_OUTPUT_EXCLUDES,...components,untracked};
79
- const changed_paths=[
80
- ...[...tracked].sort().map(path=>({path,kind:"tracked"})),
81
- ...untracked.map(x=>({path:x.path,kind:"untracked",sha256:x.sha256}))
82
- ].sort((a,b)=>a.path.localeCompare(b.path));
83
- process.stdout.write(JSON.stringify({
84
- schema:"yaaw.repository-identity/v2",algorithm:ALGORITHM,status:"READY",workspace_scope:".",
85
- git_root_relation:workspacePrefix?"ancestor":"same",head_commit:head,dirty:status.length>0,
86
- worktree_digest:"sha256:"+hash(Buffer.from(canonicalJson(payload),"utf8")),
87
- components,changed_paths,error:null
88
- })+"\n");
89
- } catch(error) {
90
- if(process.exitCode!==2) fail("IDENTITY_FAILED",error instanceof Error?error.message:String(error));
91
- }
92
- }
@@ -1,18 +0,0 @@
1
- # Implement ticket
2
-
3
- ## Purpose
4
- Implement one admitted bounded ticket while making interruption recoverable from durable facts.
5
-
6
- ## Preconditions
7
- Reconciled state says the ticket is `READY`; source revisions are current; dependencies pass; repository identity is `READY`.
8
-
9
- ## Procedure
10
- 1. Read the exact handoff, state, ticket, source contracts, `.yaaw-core/system/rules/changeability.md`, applicable expertise, and minimal code.
11
- 2. Before editing any application file, write immutable v3 `implementation_start` evidence with result `STARTED`, current ticket/spec revisions, workflow ID, and the exact pre-edit repository identity.
12
- 3. Only after that durable start fact exists, implement strictly inside allowed scope.
13
- 4. If a material contract gap appears, stop with `REPLAN_REQUIRED` or `BLOCKED`; do not invent it.
14
- 5. Execute `implementation.verify-ticket`.
15
- 6. Persist verification evidence. Do not mutate lifecycle state directly.
16
-
17
- ## Output
18
- Bounded implementation plus durable start/verification evidence or a typed stop. Orchestrator adopts `READY -> IN_PROGRESS` from start evidence and `IN_PROGRESS -> REVIEW_REQUIRED` only from current PASS verification.
@@ -1,17 +0,0 @@
1
- # Repair ticket
2
-
3
- ## Purpose
4
- Correct implementation defects while keeping the accepted ticket/spec contract unchanged.
5
-
6
- ## Preconditions
7
- Reconciled state is `REPAIR_REQUIRED` and the latest review result is `REPAIR` against the current contract.
8
-
9
- ## Procedure
10
- 1. Load state, the unchanged ticket contract, `.yaaw-core/system/rules/changeability.md`, latest review findings, prior evidence, and relevant code.
11
- 2. Repair only the bounded findings; do not broaden scope.
12
- 3. If repair requires product/architecture contract changes, return `REPLAN_REQUIRED`.
13
- 4. Rerun `implementation.verify-ticket` and write a new v3 verification record distinct from evidence referenced by the REPAIR review.
14
- 5. Never edit state. Orchestrator moves `REPAIR_REQUIRED -> REVIEW_REQUIRED` only after fresh PASS verification.
15
-
16
- ## Output
17
- Repaired reviewable implementation with fresh verification evidence, or a typed replan/blocker result.
@@ -1,17 +0,0 @@
1
- # Select ticket
2
-
3
- ## Purpose
4
- Choose exactly one safe implementation unit.
5
-
6
- ## Inputs
7
- Ticket states, dependencies, current source revisions, repository status/identity, and optional specifically requested ticket.
8
-
9
- ## Procedure
10
- 1. Require repository status `READY`; otherwise return `PRECONDITION_UNSATISFIED:REPOSITORY_IDENTITY_UNAVAILABLE` to Orchestrator.
11
- 2. If state/repository disagree, return control to orchestration recovery first.
12
- 3. Reject `DRAFT`, `BLOCKED`, `REPLAN_REQUIRED`, `REPAIR_REQUIRED`, `REVIEW_REQUIRED`, `PASS`, or stale-source tickets for normal implementation.
13
- 4. Choose the specifically requested eligible ticket, otherwise the next dependency-satisfied `READY` ticket.
14
- 5. Revalidate source spec/product/engineering revisions immediately before admission.
15
-
16
- ## Output
17
- One admitted `READY` ticket, or `PRECONDITION_UNSATISFIED:NO_READY_TICKET` / another exact blocker result for Orchestrator.
@@ -1,19 +0,0 @@
1
- # Verify ticket
2
-
3
- ## Purpose
4
- Produce reproducible implementation evidence without self-accepting the ticket.
5
-
6
- ## Inputs
7
- Exact handoff, reconciled state, ticket requirements/tests, changed surface, project rules/tooling, and current repository identity.
8
-
9
- ## Procedure
10
- 1. Run every ticket-required test/check and justified targeted regressions.
11
- 2. Verify materially relevant changeability properties as concrete behavior.
12
- 3. After verification commands finish, capture the canonical repository identity.
13
- 4. Write a new immutable `.yaaw-core/project/evidence/EVIDENCE-TASK-NNN-VK.json` using `yaaw.evidence/v3`.
14
- 5. Set `kind: implementation_verification` and exactly one result: `PASS`, `FAIL`, or `BLOCKED`.
15
- 6. Preserve failed records; fixes produce a later evidence record.
16
- 7. If verification proves implementation materially incomplete, return `IMPLEMENTATION_INCOMPLETE` to Orchestrator rather than silently reimplementing in this verification workflow.
17
-
18
- ## Output
19
- One explicit verification result. Only a current PASS record can authorize review admission; evidence-file existence alone never does.
@@ -1,24 +0,0 @@
1
- # Determine next action
2
-
3
- ## Purpose
4
- Expose the deterministic route/handoff preparation contract without duplicating routing logic in the Orchestrator model.
5
-
6
- ## Procedure
7
- Normal orchestration satisfies this workflow by executing:
8
-
9
- ```text
10
- node .yaaw-core/system/tools/orchestration-runtime.mjs --workspace <WORKSPACE_ROOT>
11
- ```
12
-
13
- The runtime reads the machine routing/execution/role-I/O/handoff policies, current durable metadata, framework basis, and canonical repository identity; it then returns exactly one typed result and, only for a dispatchable non-Orchestrator semantic workflow, persists one `yaaw.handoff/v2`.
14
-
15
- - `DISPATCH_READY`: consume the persisted handoff; do not rebuild it.
16
- - `RECONCILE_REQUIRED`: execute only `orchestration.reconcile-state` before preparing again.
17
- - `ROOT_ACTION`: execute only the selected Orchestrator recovery workflow. This result is valid only for a proven interruption boundary; ordinary `DRAFT` admission routes to Planner.
18
- - `TERMINAL`/`BLOCKED`/`FRAMEWORK_STOP`: write no executable handoff.
19
-
20
- ## Boundary
21
- Do not perform target-role semantic work here. Do not hand-serialize a handoff or select expertise outside the deterministic handoff policy.
22
-
23
- ## Framework write guard
24
- The runtime verifies selected reads/writes against `role-io.json`; an unauthorized artifact class or missing machine contract is `FRAMEWORK_CONTRACT_INCONSISTENCY`, never a semantic reroute.
@@ -1,35 +0,0 @@
1
- # Dispatch one handoff
2
-
3
- ## Purpose
4
- Execute exactly one already-selected canonical workflow. This file is not the orchestration loop.
5
-
6
- ## Inputs
7
- `.yaaw-core/runtime/handoff.json` produced by the deterministic orchestration runtime.
8
-
9
- ## Procedure
10
- 1. Immediately before loading target semantics, execute:
11
-
12
- ```text
13
- node .yaaw-core/system/tools/orchestration-runtime.mjs --workspace <WORKSPACE_ROOT> --check-handoff
14
- ```
15
-
16
- 2. Require `HANDOFF_FRESH`. `FRAMEWORK_STOP`, `RECONCILE_REQUIRED`, `ROOT_ACTION`, `TERMINAL`, `BLOCKED`, or `HANDOFF_STALE` returns control to `orchestration.route`; do not execute the stale target. This single freshness check reuses the same framework, repository-identity, artifact, routing, and handoff serialization implementation that created the handoff.
17
- 3. Resolve the handoff's execution-context policy from `execution-policy.json` and `.yaaw-core/system/core/dispatch-execution.md`.
18
- 4. If the target is an Orchestrator workflow or policy is `ROOT_ONLY`, execute in the current root context.
19
- 5. Otherwise ask the active host adapter to resolve the selected authority's required effective execution profile. This is host execution metadata; do not write provider model/reasoning identifiers into the canonical handoff.
20
- 6. Prefer the adapter's exact named authority worker when available. If named selection is unavailable before child creation, the adapter may choose a generic isolated mechanism only after proving profile equivalence or authoritatively correcting every mismatched profile dimension.
21
- 7. If an isolated mechanism exists but cannot guarantee the required profile, return `BLOCKED:HOST_EXECUTION_PROFILE_UNAVAILABLE`. If strict isolation is required and no isolated mechanism exists at all, return `BLOCKED:HOST_ISOLATION_UNAVAILABLE`.
22
- 8. In an adapter `auto` mode, inline fallback is legal only when the adapter proves the inline/root execution profile equivalent to the required authority profile. A deliberately selected adapter `inline` mode is an explicit opt-out from per-role model isolation.
23
- 9. If a child was already created and then failed, do **not** switch mechanisms or retry blindly; return to deterministic reality preparation first.
24
- 10. For Implementer or Reviewer, consult `.yaaw-core/runtime/dispatch-failures.json` only after inspection. If the unchanged handoff basis has reached the host-configured execution-failure threshold, request the configured host capability-fallback profile for this attempt. Resolve and preserve that fallback profile using the same fidelity rules. If the fallback itself already failed with no progress on this basis, return `BLOCKED:AUTHORITY_EXECUTION_FAILED`.
25
- 11. A pre-execution `HOST_EXECUTION_PROFILE_UNAVAILABLE` or `HOST_ISOLATION_UNAVAILABLE` stop does not increment the execution-failure ledger because no authority child executed.
26
- 12. The executing context loads only the target role contract, selected workflow contract, exact handoff reads, selected expertise, and minimal relevant repository context.
27
- 13. Execute the target workflow once. A worker may not route or spawn another YAAW authority role.
28
- 14. Require the expected durable output/state/evidence or an explicit legal typed stop/prerequisite result.
29
- 15. Treat any child response only as an execution signal; do not accept it as project truth without re-inspection.
30
- 16. Mark/remove the consumed runtime handoff and return control to `orchestration.route`.
31
-
32
- Never recursively dispatch `orchestration.dispatch` as its own target. A target role never dispatches a peer.
33
-
34
- ## Package boundary
35
- Dispatch rejects any handoff whose writes include installer-managed artifacts or `.yaaw-core/system/**`. An admitted application-file write never authorizes package framework mutation.
@@ -1,19 +0,0 @@
1
- # Inspect state
2
-
3
- ## Purpose
4
- Create a non-mutating observed-reality snapshot using the same deterministic runtime implementation used by normal orchestration.
5
-
6
- ## Procedure
7
- 1. Resolve `WORKSPACE_ROOT` using `.yaaw-core/system/core/execution-context.md`.
8
- 2. Execute:
9
-
10
- ```text
11
- node .yaaw-core/system/tools/orchestration-runtime.mjs --workspace <WORKSPACE_ROOT> --inspect-only
12
- ```
13
-
14
- 3. Consume `.yaaw-core/runtime/observed-state.json` and the typed `INSPECTED`/framework-stop result.
15
- 4. The runtime compares current product/engineering/spec/ticket revisions and, for accepted work, current review/verification presence and repository identity so stale acceptance becomes a reconciliation input rather than a direct terminal route.
16
- 5. Do not independently invoke Git, calculate `worktree_digest`, discover artifact locations, or serialize a second observation.
17
-
18
- ## Output
19
- Observed-state snapshot only; no semantic/ticket-state mutation and no executable handoff construction in `--inspect-only` mode.
@@ -1,19 +0,0 @@
1
- # Reconcile state
2
-
3
- ## Purpose
4
- Apply exactly one highest-priority evidence-backed state adoption or legal lifecycle transition.
5
-
6
- ## Inputs
7
- Current observed-state snapshot and its `reconciliation` object from the production orchestration engine.
8
-
9
- ## Procedure
10
- 1. Require framework integrity `HEALTHY`.
11
- 2. Run `node .yaaw-core/system/tools/orchestration-runtime.mjs --workspace <WORKSPACE_ROOT> --reconcile-one`.
12
- 3. Apply only the one reconciliation returned by the engine; do not batch multiple discovered facts.
13
- 4. Increment `transition_sequence` exactly once and persist provenance.
14
- 5. Return immediately to `orchestration.route` so reality is observed again.
15
-
16
- Examples include ledger sync, accepted-spec adoption, ticket registration, implementation-start adoption, PASS-verification adoption, review-result adoption, acceptance invalidation, and project completion sync.
17
-
18
- ## Output
19
- One reconciled mutation or a typed blocker. No semantic product/engineering/implementation/acceptance judgment is invented here.
@@ -1,32 +0,0 @@
1
- # Recover interruption
2
-
3
- ## Purpose
4
- Resume safely after an actual context/session failure without duplicating already-completed work.
5
-
6
- This workflow is interruption-specific. It is not the generic fallback for an unresolved lifecycle state, a `DRAFT` ticket, missing planning admission, or a shortcut precondition failure.
7
-
8
- ## Inputs
9
- Reconciled state, durable artifacts, implementation-start/verification evidence, reviews, and repository identity.
10
-
11
- ## Selection guard
12
- Select `orchestration.recover-interruption` only when durable evidence indicates an interrupted execution boundary that cannot yet be classified by the ordinary semantic routes. The normal example is `IN_PROGRESS` with missing implementation-start provenance after a worker/session loss.
13
-
14
- Do not select recovery merely because no Implementer handoff is currently available.
15
-
16
- ## Procedure
17
- 1. Discard stale runtime caches/handoffs.
18
- 2. Identify the last trustworthy durable boundary.
19
- 3. For `IN_PROGRESS`: current PASS verification is reconciled to review; valid start evidence without PASS verification routes to `implementation.verify-ticket`; missing start evidence requires explicit recovery/blocking proof.
20
- 4. Never rerun full implementation merely because a worker response disappeared.
21
- 5. A source-current review artifact is adopted before another Reviewer dispatch.
22
- 6. Use only legal transitions with provenance; if the interruption boundary cannot be proven, return an exact typed blocker rather than repeatedly selecting recovery.
23
- 7. If inspection proves there is no interrupted execution boundary, return immediately to `orchestration.route`. Do not remain parked in recovery.
24
-
25
- ## Explicit non-cases
26
- - registered `DRAFT` ticket with current planning/spec sources -> Planner admission;
27
- - `DRAFT` whose prerequisite ticket is runnable -> continue the prerequisite;
28
- - all accepted work PASS/CANCELLED with scope OPEN/UNKNOWN -> Planner next frontier;
29
- - direct Review/Repair shortcut with unsatisfied lifecycle preconditions -> typed shortcut precondition failure.
30
-
31
- ## Output
32
- A safe next boundary or exact interruption blocker, then control returns to `orchestration.route`.