elanous 0.2.16 → 0.2.18

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 (354) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{1307-b46d45b705e8c9ae.js → 1307-30dff22c9e1f3073.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d92f7ee3994e84c7.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3cb52e5ea8fc2fa5.js → page-4733c967b56a4277.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/board/page-d22fd849cc22f104.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c1e20d7d338713f1.js → page-bb742f995c6769e7.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/bots/{page-482c5dbde699947c.js → page-02b073a2ac793b83.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/control/{page-a0e2a6d1bfb52fc5.js → page-308111abadf35670.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-c53598cfce6156f2.js → page-5d97b2901e0d1ea6.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/editor/{page-13a63953f53c6a23.js → page-c39554c321576982.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/exec/{page-5d3c8429d04f3831.js → page-8c3d8e63ae2e1c98.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/app/field/{page-3193bdc0bbab047a.js → page-9b72a07d424701c6.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/intake/{page-6107ca8ddd5e3bdd.js → page-a572f58cd3ca5864.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/market/page-fa244bd146a00ae8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-44d56d6391965a6a.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/app/morning/{page-4167348049166852.js → page-83a700d4c754d802.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/{not-found-de52e8fdf9095f7a.js → not-found-7b3170f374f77b9d.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-ca3952cd78a83ca2.js → page-a3c58f5c72a5f3f9.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1ebfe049c640e171.js → page-1467047104141297.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-abe8e3b74bea1ffc.js → page-44d5f767491692bf.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/share/{page-d28573d6792b4275.js → page-3cc47dca0922ab9a.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-d751de5f8062ff5e.js → page-b17cd46cf0e9b8cf.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/app/term/{page-fe1759f94003b32b.js → page-61553851aa606146.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/app/today/{page-d64c984e855c8c68.js → page-a1f585e154e61c59.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/app/trace/page-7bf54c9bb9274af8.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/vault/{page-9afdee66b820c76e.js → page-5150ef17b5383a57.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-3544f154c87b43ee.js → page-134da468538baf48.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
  67. package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +1 -0
  68. package/apps/pwa/out/approvals/index.html +1 -1
  69. package/apps/pwa/out/approvals/index.txt +14 -14
  70. package/apps/pwa/out/autopilot/index.html +1 -1
  71. package/apps/pwa/out/autopilot/index.txt +14 -14
  72. package/apps/pwa/out/board/index.html +1 -1
  73. package/apps/pwa/out/board/index.txt +14 -14
  74. package/apps/pwa/out/botlab/index.html +1 -1
  75. package/apps/pwa/out/botlab/index.txt +14 -14
  76. package/apps/pwa/out/bots/index.html +1 -1
  77. package/apps/pwa/out/bots/index.txt +14 -14
  78. package/apps/pwa/out/ceo/index.html +1 -1
  79. package/apps/pwa/out/ceo/index.txt +14 -14
  80. package/apps/pwa/out/chat/index.html +1 -1
  81. package/apps/pwa/out/chat/index.txt +14 -14
  82. package/apps/pwa/out/consult/index.html +1 -1
  83. package/apps/pwa/out/consult/index.txt +14 -14
  84. package/apps/pwa/out/control/index.html +1 -1
  85. package/apps/pwa/out/control/index.txt +14 -14
  86. package/apps/pwa/out/decisions/index.html +1 -0
  87. package/apps/pwa/out/decisions/index.txt +30 -0
  88. package/apps/pwa/out/design-check/index.html +1 -1
  89. package/apps/pwa/out/design-check/index.txt +14 -14
  90. package/apps/pwa/out/editor/index.html +1 -1
  91. package/apps/pwa/out/editor/index.txt +14 -14
  92. package/apps/pwa/out/exec/index.html +1 -1
  93. package/apps/pwa/out/exec/index.txt +14 -14
  94. package/apps/pwa/out/field/index.html +1 -1
  95. package/apps/pwa/out/field/index.txt +14 -14
  96. package/apps/pwa/out/healthz +1 -1
  97. package/apps/pwa/out/index.html +1 -1
  98. package/apps/pwa/out/index.txt +14 -14
  99. package/apps/pwa/out/inside/index.html +1 -1
  100. package/apps/pwa/out/inside/index.txt +14 -14
  101. package/apps/pwa/out/intake/index.html +1 -1
  102. package/apps/pwa/out/intake/index.txt +14 -14
  103. package/apps/pwa/out/live/index.html +1 -1
  104. package/apps/pwa/out/live/index.txt +14 -14
  105. package/apps/pwa/out/loops/index.html +1 -1
  106. package/apps/pwa/out/loops/index.txt +14 -14
  107. package/apps/pwa/out/market/index.html +1 -1
  108. package/apps/pwa/out/market/index.txt +14 -14
  109. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  110. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  111. package/apps/pwa/out/morning/index.html +1 -1
  112. package/apps/pwa/out/morning/index.txt +14 -14
  113. package/apps/pwa/out/observatory/index.html +1 -1
  114. package/apps/pwa/out/observatory/index.txt +14 -14
  115. package/apps/pwa/out/ops/checklist/index.html +1 -1
  116. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  117. package/apps/pwa/out/ops/release/index.html +1 -1
  118. package/apps/pwa/out/ops/release/index.txt +14 -14
  119. package/apps/pwa/out/ops/seats/index.html +1 -1
  120. package/apps/pwa/out/ops/seats/index.txt +14 -14
  121. package/apps/pwa/out/outputs/index.html +1 -1
  122. package/apps/pwa/out/outputs/index.txt +14 -14
  123. package/apps/pwa/out/reflection/index.html +1 -1
  124. package/apps/pwa/out/reflection/index.txt +15 -15
  125. package/apps/pwa/out/scheduler/index.html +1 -1
  126. package/apps/pwa/out/scheduler/index.txt +14 -14
  127. package/apps/pwa/out/sessions/index.html +1 -1
  128. package/apps/pwa/out/sessions/index.txt +14 -14
  129. package/apps/pwa/out/settings/devices/index.html +1 -1
  130. package/apps/pwa/out/settings/devices/index.txt +14 -14
  131. package/apps/pwa/out/settings/index.html +1 -1
  132. package/apps/pwa/out/settings/index.txt +14 -14
  133. package/apps/pwa/out/setup/done/index.html +1 -1
  134. package/apps/pwa/out/setup/done/index.txt +14 -14
  135. package/apps/pwa/out/setup/index.html +1 -1
  136. package/apps/pwa/out/setup/index.txt +14 -14
  137. package/apps/pwa/out/share/index.html +1 -1
  138. package/apps/pwa/out/share/index.txt +14 -14
  139. package/apps/pwa/out/showroom/index.html +1 -1
  140. package/apps/pwa/out/showroom/index.txt +14 -14
  141. package/apps/pwa/out/tasks/index.html +1 -1
  142. package/apps/pwa/out/tasks/index.txt +14 -14
  143. package/apps/pwa/out/term/index.html +1 -1
  144. package/apps/pwa/out/term/index.txt +14 -14
  145. package/apps/pwa/out/today/index.html +1 -1
  146. package/apps/pwa/out/today/index.txt +14 -14
  147. package/apps/pwa/out/trace/index.html +1 -1
  148. package/apps/pwa/out/trace/index.txt +14 -14
  149. package/apps/pwa/out/vault/index.html +1 -1
  150. package/apps/pwa/out/vault/index.txt +14 -14
  151. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  152. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  153. package/apps/pwa/out/workflows/index.html +1 -1
  154. package/apps/pwa/out/workflows/index.txt +14 -14
  155. package/apps/pwa/out/workspace/index.html +1 -1
  156. package/apps/pwa/out/workspace/index.txt +14 -14
  157. package/apps/pwa/out/worktrees/index.html +1 -1
  158. package/apps/pwa/out/worktrees/index.txt +14 -14
  159. package/graphs/intake/intake-daily.yaml +9 -2
  160. package/graphs/intake/news-sources.yaml +4 -0
  161. package/graphs/intake/recipes.yaml +6 -1
  162. package/package.json +2 -1
  163. package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
  164. package/samples/workflows/templates/chat-driven-research.yaml +35 -0
  165. package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
  166. package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
  167. package/samples/workflows/templates/manual-quickstart.yaml +21 -0
  168. package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
  169. package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
  170. package/scripts/__fixtures__/intake-news.xml +7 -0
  171. package/scripts/ci-public-export-import-gate.ts +7 -2
  172. package/scripts/ci-typecheck-changed.ts +21 -6
  173. package/scripts/discord-seat-probe.ts +3 -2
  174. package/scripts/docs/generate-cli-anatomy.ts +162 -0
  175. package/scripts/goals/authoring-metrics.ts +73 -0
  176. package/scripts/intake-news.ts +169 -0
  177. package/scripts/landing-heal/must-fix-match.ts +7 -7
  178. package/scripts/landing-heal/run-mission.ts +3 -2
  179. package/scripts/public-export-test-run.ts +37 -2
  180. package/scripts/release-loop/checklist-gate-node.ts +13 -11
  181. package/scripts/release-loop/cut-branch.ts +2 -1
  182. package/scripts/release-loop/cutoff.ts +4 -2
  183. package/scripts/release-loop/docs-land-node.ts +77 -11
  184. package/scripts/release-loop/docs-node.ts +1 -1
  185. package/scripts/release-loop/gate-node.ts +99 -14
  186. package/scripts/release-loop/node-verdict.ts +2 -1
  187. package/scripts/release-loop/npm-publish-node.ts +11 -7
  188. package/scripts/release-loop/ops-upgrade-node.ts +8 -1
  189. package/scripts/release-loop/preflight.ts +246 -0
  190. package/scripts/release-loop/publish-node.ts +10 -3
  191. package/scripts/release-loop/release-docs.ts +134 -0
  192. package/scripts/release-loop/release-readiness.ts +11 -6
  193. package/scripts/release-loop/release-version.ts +48 -0
  194. package/scripts/release-loop/resume-release.ts +115 -0
  195. package/scripts/release-loop/unattended-release.ts +43 -7
  196. package/scripts/release-loop/version-node.ts +149 -41
  197. package/scripts/release-story/draft.ts +42 -14
  198. package/scripts/rhythm/daily.ts +25 -4
  199. package/scripts/skills/public-check.ts +62 -12
  200. package/scripts/test-deterministic.ts +51 -33
  201. package/scripts/test-diet/effectiveness.ts +62 -1
  202. package/scripts/test-diet/lib.ts +1 -1
  203. package/scripts/test-diet/node.ts +8 -1
  204. package/src/agent-mission/driver.ts +53 -10
  205. package/src/agent-mission/review-loop.ts +4 -3
  206. package/src/agent-substrate/pr-comment-meta.ts +20 -0
  207. package/src/autopilot/build/llm-conflict-merge.ts +28 -8
  208. package/src/autopilot/track-agent.ts +5 -3
  209. package/src/briefing/brief-items.ts +86 -17
  210. package/src/cli/autopilot-cli.ts +86 -1
  211. package/src/cli/brief-cli.ts +36 -2
  212. package/src/cli/decisions-cli.ts +4 -1
  213. package/src/cli/freeze-cli.ts +3 -2
  214. package/src/cli/intake-cli.ts +74 -2
  215. package/src/cli/log-event-names.ts +1 -0
  216. package/src/cli/notify-cli.ts +75 -0
  217. package/src/cli/pr-cli.ts +12 -1
  218. package/src/cli/provider-cli.ts +24 -0
  219. package/src/cli/release-cli.ts +101 -17
  220. package/src/cli/self-update-canary.ts +46 -0
  221. package/src/cli/self-update.ts +200 -1
  222. package/src/cli/tasks-cli.ts +309 -3
  223. package/src/context-bus/condense.ts +19 -3
  224. package/src/context-bus/context-day.ts +77 -24
  225. package/src/context-bus/context-first.ts +33 -0
  226. package/src/context-card/index.ts +45 -0
  227. package/src/context-card/task.ts +76 -0
  228. package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
  229. package/src/decisions/decision-ledger.ts +10 -1
  230. package/src/discord-self-message.ts +20 -2
  231. package/src/discord-slash-wire.ts +1 -0
  232. package/src/domains/outbound-alert.ts +200 -27
  233. package/src/domains/outbound-log-probe.ts +16 -1
  234. package/src/domains/release-tool.ts +2 -2
  235. package/src/execution-loop/gates.ts +11 -2
  236. package/src/flow-loop/split.ts +8 -4
  237. package/src/git-fs/worktree.ts +23 -3
  238. package/src/harness/harness-cli-command.ts +151 -11
  239. package/src/harness/harness-pod-dispatch.ts +11 -0
  240. package/src/harness/harness-queue.ts +466 -32
  241. package/src/harness/helper-scan.ts +10 -3
  242. package/src/harness/nested-elanous-policy.ts +23 -4
  243. package/src/hq/fence-wrapper.ts +123 -0
  244. package/src/hq/hq.ts +8 -0
  245. package/src/hq/seats.ts +369 -0
  246. package/src/index.ts +190 -23
  247. package/src/intake-plane/adopt-gate.ts +49 -0
  248. package/src/intake-plane/check.ts +3 -0
  249. package/src/intake-plane/collect-github.ts +57 -25
  250. package/src/intake-plane/digest.ts +39 -4
  251. package/src/intake-plane/discord-seat-work.ts +8 -2
  252. package/src/intake-plane/requirement-funnel.ts +316 -0
  253. package/src/knowledge/know-find.ts +10 -1
  254. package/src/loops/budget.ts +36 -0
  255. package/src/loops/orchestrator/finish-rate.ts +73 -1
  256. package/src/loops/orchestrator/grid-place.ts +11 -0
  257. package/src/loops/orchestrator/tick.ts +295 -20
  258. package/src/maturity/feature-maturity.ts +3 -2
  259. package/src/nexus/api/decisions-route.ts +11 -1
  260. package/src/nexus/api/grid.ts +61 -0
  261. package/src/nexus/api/http-server.ts +19 -1
  262. package/src/nexus/api/loop-edges.ts +69 -13
  263. package/src/nexus/api/ops-seats-sources.ts +2 -2
  264. package/src/nexus/api/task-cards-api.ts +19 -0
  265. package/src/nexus/api/workflow-templates.ts +3 -0
  266. package/src/oauth/codex.ts +120 -2
  267. package/src/onboarding/first-chat-gate.ts +29 -0
  268. package/src/release-loop/checklist.ts +104 -6
  269. package/src/release-loop/feature-store.ts +10 -9
  270. package/src/release-loop/landing-freeze.ts +24 -14
  271. package/src/release-loop/placement.ts +13 -3
  272. package/src/release-loop/rubric.ts +80 -0
  273. package/src/seat-dispatch/seat-ask.ts +79 -16
  274. package/src/seat-loop/seat-loop.ts +107 -37
  275. package/src/self-dev/ask-launch-flow.ts +50 -10
  276. package/src/self-dev/ask-path-candidates.ts +68 -0
  277. package/src/self-dev/dev-pipeline.ts +4 -0
  278. package/src/self-dev/draft-triage-rules.ts +1 -1
  279. package/src/self-dev/orchestrate-cli.ts +22 -0
  280. package/src/self-dev/round-pr-supersede.ts +244 -0
  281. package/src/self-dev/run-store.ts +5 -1
  282. package/src/self-dev/run-supervisor.ts +21 -1
  283. package/src/self-dev/supervisor-verdict-edges.ts +46 -0
  284. package/src/self-implement/follow-up-goals.ts +2 -2
  285. package/src/self-implement/frozen-merges.ts +25 -20
  286. package/src/self-implement/gate-remote.ts +427 -0
  287. package/src/self-implement/goal-author-clarification.ts +81 -0
  288. package/src/self-implement/goal-author-cli.ts +33 -5
  289. package/src/self-implement/goal-author.ts +93 -12
  290. package/src/self-implement/goal-clarification-escalation.ts +37 -1
  291. package/src/self-implement/goal-document-write.ts +28 -0
  292. package/src/self-implement/heal-intake.ts +4 -0
  293. package/src/self-implement/host-regate.ts +3 -1
  294. package/src/self-implement/l8-merge-queue.ts +29 -5
  295. package/src/self-implement/orchestrator.ts +78 -13
  296. package/src/self-implement/prior-draft-findings.ts +4 -3
  297. package/src/skills/skill-feedback.ts +76 -0
  298. package/src/task-agent/actions.ts +151 -0
  299. package/src/task-agent/judge.ts +122 -0
  300. package/src/task-agent/mission.ts +423 -0
  301. package/src/task-agent/shadow.ts +90 -0
  302. package/src/task-agent/task-hand.ts +235 -0
  303. package/src/task-cards/card-store.ts +4 -1
  304. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -4
  305. package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
  306. package/src/task-orchestrator/surfaces/pod-pool.ts +81 -13
  307. package/src/task-orchestrator/surfaces/self-implement-pod.ts +225 -35
  308. package/src/telegram-commands.ts +1 -1
  309. package/src/telegram-loops-command.ts +22 -15
  310. package/src/telegram.ts +5 -20
  311. package/src/user-config.ts +86 -4
  312. package/src/version/packed-revision.json +1 -1
  313. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
  314. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/chat/page-8c2d9526164f5d09.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/layout-dce686e582c23937.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/loops/page-1a61c7ca1ceb3ae7.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/market/page-51f811fa119308b6.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-5b042736aa113756.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-57924393e3e4e539.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/app/settings/page-0f5cd57ca928f42b.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/app/showroom/page-59f03fda76c03d22.js +0 -1
  348. package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +0 -1
  349. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +0 -1
  350. package/apps/pwa/out/_next/static/chunks/app/workflows/page-1ea64acadd704ace.js +0 -1
  351. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +0 -1
  352. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
  353. package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +0 -1
  354. /package/apps/pwa/out/_next/static/{rv9cierV0jSOvDBm1g0XB → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
@@ -0,0 +1,244 @@
1
+ // DRAFT-TRIAGE (TC 10-07) — a supervisor rework round of the same run and goal opens a new PR
2
+ // (new task id → new goal hash → new branch). Continuing the branch would mean pinning the shard
3
+ // identity and the Pod child's run suffix, so this closes the previous round's draft instead:
4
+ // label `elanous:superseded`, comment with the newer PR link, close. Same run = one tracker per
5
+ // supervised invocation; same goal = `resumeKey(feature)`, the key the resume path already uses.
6
+
7
+ import { spawnSync } from 'node:child_process';
8
+ import { debug } from '../debug/log.js';
9
+ import { PR_LABELS } from '../github/pr-labels.js';
10
+ import { PROTECTED_LABELS } from './draft-triage-rules.js';
11
+ import { resumeKey, type SelfDevJobResult } from './orchestrate.js';
12
+
13
+ const SUPERSEDED_LABEL = PR_LABELS.find((label) => label.name.endsWith(':superseded'))!.name;
14
+
15
+ export interface RoundPrRef {
16
+ readonly repository: string;
17
+ readonly number: number;
18
+ readonly url: string;
19
+ }
20
+
21
+ export interface RoundPrView {
22
+ readonly state: string;
23
+ readonly isDraft: boolean;
24
+ readonly labels: readonly string[];
25
+ readonly mergedAt?: string | null;
26
+ /** Comment bodies — a link already posted (even by a write that reported failure) is not posted again.
27
+ * An adapter that cannot read them returns a null view, so nothing is written. */
28
+ readonly comments: readonly string[];
29
+ }
30
+
31
+ /** gh seams. A null view or a false write means «not observed / not applied» — never a reason to close. */
32
+ export interface RoundPrAdapters {
33
+ view(pr: RoundPrRef): RoundPrView | null;
34
+ addLabel(pr: RoundPrRef, label: string): boolean;
35
+ comment(pr: RoundPrRef, body: string): boolean;
36
+ close(pr: RoundPrRef): boolean;
37
+ }
38
+
39
+ type SupersedeOutcome =
40
+ | { kind: 'superseded'; round: number; prevPr: number; pr: number }
41
+ | { kind: 'kept'; round: number; prevPr: number; pr: number; reason: string };
42
+
43
+ type RoundPrOutcome = SupersedeOutcome | { kind: 'continued'; round: number; prevPr: number; pr: number };
44
+
45
+ export function roundPrRef(prUrl: string | undefined): RoundPrRef | null {
46
+ if (!prUrl) return null;
47
+ try {
48
+ const url = new URL(prUrl.trim());
49
+ const match = /^\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/.exec(url.pathname);
50
+ return url.protocol === 'https:' && url.hostname === 'github.com' && match
51
+ // GitHub owner/repo names are case-insensitive: one PR has one identity.
52
+ ? { repository: `${match[1]}/${match[2]}`.toLowerCase(), number: Number(match[3]), url: `https://github.com/${match[1]}/${match[2]}/pull/${match[3]}` }
53
+ : null;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ const prId = (pr: RoundPrRef): string => `${pr.repository}#${pr.number}`;
60
+
61
+ const commentMarker = (prev: RoundPrRef, next: RoundPrRef): string => `<!-- elanous:round-pr-superseded ${prId(prev)} -> ${prId(next)} -->`;
62
+
63
+ function roundPrSupersedeComment(input: { runId: string | null; round: number; prev?: RoundPrRef; next: RoundPrRef }): string {
64
+ return `${input.prev ? `${commentMarker(input.prev, input.next)}\n` : ''}Supervisor round ${input.round}${input.runId ? ` of ${input.runId}` : ''}: superseded by #${input.next.number} (${input.next.url}) — same run and goal, the rework round opened a new PR. Branch preserved.`;
65
+ }
66
+
67
+ /**
68
+ * One tracker per supervised run. `record(results, round)` after each round: for every goal whose newest PR
69
+ * differs from a PR an earlier round of this run opened, the earlier one is superseded exactly once —
70
+ * only while it is an open draft, unmerged, without a protected label, in the same repository.
71
+ */
72
+ export function createRoundPrTracker(input: {
73
+ runId: string | null;
74
+ adapters: RoundPrAdapters;
75
+ observe?: (event: string, data: Record<string, unknown>) => void;
76
+ }) {
77
+ const byGoal = new Map<string, Array<{ ref: RoundPrRef; merged: boolean; round: number }>>();
78
+ const handled = new Set<string>();
79
+ // The link comment is written once per PR even when the close after it fails and is retried next round.
80
+ const commented = new Set<string>();
81
+ const emit = (event: string, data: Record<string, unknown>): void => {
82
+ try { (input.observe ?? ((e, d) => debug.log('self-dev.supervisor', e, d)))(event, data); } catch { /* observation must not interrupt supervision */ }
83
+ };
84
+
85
+ const supersede = (prev: RoundPrRef, next: RoundPrRef, round: number): SupersedeOutcome => {
86
+ const base = { round, prevPr: prev.number, pr: next.number };
87
+ if (prev.repository !== next.repository) return { kind: 'kept', ...base, reason: 'other-repository' };
88
+ if (prId(prev) === prId(next)) return { kind: 'kept', ...base, reason: 'same-pr' };
89
+ let view: RoundPrView | null = null;
90
+ try { view = input.adapters.view(prev); } catch { view = null; }
91
+ if (!view) return { kind: 'kept', ...base, reason: 'view-unobserved' };
92
+ if (view.state !== 'OPEN' || view.mergedAt) return { kind: 'kept', ...base, reason: `state:${view.mergedAt ? 'MERGED' : view.state}` };
93
+ if (!view.isDraft) return { kind: 'kept', ...base, reason: 'not-draft' };
94
+ const held = view.labels.find((label) => PROTECTED_LABELS.has(label));
95
+ if (held) return { kind: 'kept', ...base, reason: `label:${held}` };
96
+ // Label first: a closed but unlabelled PR would fall outside every later sweep (draft-sweep rule).
97
+ let labelled = view.labels.includes(SUPERSEDED_LABEL);
98
+ if (!labelled) {
99
+ try { labelled = input.adapters.addLabel(prev, SUPERSEDED_LABEL); } catch { labelled = false; }
100
+ }
101
+ if (!labelled) return { kind: 'kept', ...base, reason: 'label-failed' };
102
+ const id = prId(prev);
103
+ if (view.comments.some((body) => body.includes(commentMarker(prev, next)))) commented.add(id);
104
+ if (!commented.has(id)) {
105
+ let ok = false;
106
+ try { ok = input.adapters.comment(prev, roundPrSupersedeComment({ runId: input.runId, round, prev, next })); } catch { ok = false; }
107
+ if (!ok) return { kind: 'kept', ...base, reason: 'comment-failed' };
108
+ commented.add(id);
109
+ }
110
+ let closed = false;
111
+ try { closed = input.adapters.close(prev); } catch { closed = false; }
112
+ return closed ? { kind: 'superseded', ...base } : { kind: 'kept', ...base, reason: 'close-failed' };
113
+ };
114
+
115
+ /** Unfinished supersedes, keyed by the earlier PR; `next` stays the first successor so retries link the same PR. */
116
+ const pending = new Map<string, { prev: { ref: RoundPrRef; merged: boolean }; next: RoundPrRef }>();
117
+
118
+ return {
119
+ /** Record one round's results, then (re)try every unfinished supersede — even when this round has no PR. */
120
+ record(results: readonly SelfDevJobResult[], round: number): RoundPrOutcome[] {
121
+ const outcomes: RoundPrOutcome[] = [];
122
+ for (const result of results) {
123
+ const ref = roundPrRef(result.prUrl);
124
+ if (!ref) continue;
125
+ const key = resumeKey(result.feature);
126
+ const seen = byGoal.get(key) ?? [];
127
+ // A PR already seen in this run is never a new successor — only a new PR is.
128
+ const known = seen.find((entry) => prId(entry.ref) === prId(ref));
129
+ if (known) {
130
+ known.merged ||= result.merged === true;
131
+ // A carried (skipped) shard is not a new round's work.
132
+ if (result.resumeDisposition !== 'skip' && known.round < round) {
133
+ outcomes.push({ kind: 'continued', round, prevPr: known.ref.number, pr: ref.number });
134
+ emit('round-pr-continued', { runId: input.runId, round, prevPr: known.ref.number, pr: ref.number });
135
+ }
136
+ continue;
137
+ }
138
+ // A carried result this run has not seen before was opened by an earlier process — not this run's PR,
139
+ // so it is neither recorded nor allowed to supersede anything.
140
+ if (result.resumeDisposition === 'skip') continue;
141
+ // Only PRs opened in an earlier round are superseded — never a sibling of the same round.
142
+ for (const prev of seen) {
143
+ const id = prId(prev.ref);
144
+ if (prev.round >= round || handled.has(id) || pending.has(id)) continue;
145
+ if (prev.merged) {
146
+ handled.add(id);
147
+ outcomes.push({ kind: 'kept', round, prevPr: prev.ref.number, pr: ref.number, reason: 'merged' });
148
+ continue;
149
+ }
150
+ pending.set(id, { prev, next: ref });
151
+ }
152
+ seen.push({ ref, merged: result.merged === true, round });
153
+ byGoal.set(key, seen);
154
+ }
155
+ for (const [id, { prev: entry, next }] of [...pending]) {
156
+ const prev = entry.ref;
157
+ // A result may have reported the earlier PR merged after it was queued — re-checked right before any write.
158
+ const outcome: SupersedeOutcome = entry.merged
159
+ ? { kind: 'kept', round, prevPr: prev.number, pr: next.number, reason: 'merged' }
160
+ : supersede(prev, next, round);
161
+ outcomes.push(outcome);
162
+ // Retry only what was not observed or not applied; a decided keep/supersede is final for this run.
163
+ if (outcome.kind === 'superseded' || !/^(?:view-unobserved|label-failed|comment-failed|close-failed)$/.test(outcome.reason)) {
164
+ handled.add(id);
165
+ pending.delete(id);
166
+ }
167
+ if (outcome.kind === 'superseded') {
168
+ emit('round-pr-superseded', { runId: input.runId, round, prevPr: prev.number, pr: next.number });
169
+ } else {
170
+ emit('round-pr-supersede-kept', { runId: input.runId, round, prevPr: prev.number, pr: next.number, reason: outcome.reason });
171
+ }
172
+ }
173
+ return outcomes;
174
+ },
175
+ };
176
+ }
177
+
178
+ function ghEnv(): Record<string, string> {
179
+ const env: Record<string, string> = {};
180
+ for (const [k, v] of Object.entries(process.env)) if (v !== undefined && !/^(https?|all|no)_proxy$/i.test(k)) env[k] = v;
181
+ return env;
182
+ }
183
+
184
+ /** Real gh adapters: proxy variables removed, App identity when available (never the personal login by choice). */
185
+ export async function defaultRoundPrAdapters(deps: {
186
+ appToken?: () => string | null;
187
+ run?: (args: string[], env: Record<string, string>) => { status: number | null; stdout: string };
188
+ inTest?: () => boolean;
189
+ } = {}): Promise<RoundPrAdapters> {
190
+ const env = ghEnv();
191
+ let tokenSource: () => string | null = () => null;
192
+ try {
193
+ tokenSource = deps.appToken ?? (await import('../auth/github-app-token.js')).githubAutomationToken;
194
+ } catch { /* no App credential module — every write refuses */ }
195
+ const freshToken = (): string | null => { try { return tokenSource(); } catch { return null; } };
196
+ const gh = (args: string[], token?: string | null) => {
197
+ const callEnv = { ...env };
198
+ if (token) callEnv.GH_TOKEN = token;
199
+ return deps.run
200
+ ? deps.run(args, callEnv)
201
+ : spawnSync('gh', args, { encoding: 'utf8', env: callEnv as NodeJS.ProcessEnv, timeout: 30_000 });
202
+ };
203
+ // Writes (label · comment · close) run only as the automation App — the token is fetched per write, so a
204
+ // token that was missing or expired earlier in the run recovers; without it the write refuses and is retried.
205
+ // Reads may use the machine login.
206
+ const write = (op: string, pr: RoundPrRef, args: string[]): boolean => {
207
+ const token = freshToken();
208
+ if (!token) {
209
+ try { debug.log('self-dev.supervisor', 'round-pr-write-refused', { op, pr: pr.number, reason: 'no-app-token' }); } catch { /* fail-open */ }
210
+ return false;
211
+ }
212
+ return gh(args, token).status === 0;
213
+ };
214
+ const inTest = deps.inTest ?? ((): boolean => process.env.NODE_ENV === 'test');
215
+ return {
216
+ view(pr) {
217
+ if (inTest()) return null;
218
+ const out = gh(['pr', 'view', String(pr.number), '--repo', pr.repository, '--json', 'state,isDraft,labels,mergedAt,comments'], freshToken());
219
+ if (out.status !== 0) return null;
220
+ try {
221
+ const parsed = JSON.parse(out.stdout) as { state?: unknown; isDraft?: unknown; labels?: unknown; mergedAt?: unknown; comments?: unknown };
222
+ if (typeof parsed.state !== 'string' || typeof parsed.isDraft !== 'boolean' || !Array.isArray(parsed.labels)) return null;
223
+ const labels = parsed.labels.map((label) => (label as { name?: unknown })?.name);
224
+ if (labels.some((name) => typeof name !== 'string')) return null;
225
+ // Unverifiable comments make the whole view unobserved — no write may follow it.
226
+ if (!Array.isArray(parsed.comments)) return null;
227
+ const comments = parsed.comments.map((comment) => (comment as { body?: unknown })?.body);
228
+ if (comments.some((body) => typeof body !== 'string')) return null;
229
+ return { state: parsed.state, isDraft: parsed.isDraft, labels: labels as string[], mergedAt: typeof parsed.mergedAt === 'string' && parsed.mergedAt ? parsed.mergedAt : null, comments: comments as string[] };
230
+ } catch {
231
+ return null;
232
+ }
233
+ },
234
+ addLabel(pr, label) {
235
+ return !inTest() && write('label', pr, ['pr', 'edit', String(pr.number), '--repo', pr.repository, '--add-label', label]);
236
+ },
237
+ comment(pr, body) {
238
+ return !inTest() && write('comment', pr, ['pr', 'comment', String(pr.number), '--repo', pr.repository, '--body', body]);
239
+ },
240
+ close(pr) {
241
+ return !inTest() && write('close', pr, ['pr', 'close', String(pr.number), '--repo', pr.repository]);
242
+ },
243
+ };
244
+ }
@@ -28,6 +28,7 @@ import type { RunIdSource } from '../harness/harness-space.js';
28
28
  import { withFileLockSync } from '../storage/file-lock.js';
29
29
  import type { SelfDevJobResult, SelfDevGoal } from './orchestrate.js';
30
30
  import type { SupervisorStopReason } from './run-supervisor.js';
31
+ import type { SupervisorNext } from './supervisor-verdict-edges.js';
31
32
  import { loadMirroredRunRecord, mirrorRunRecord } from './run-record-mirror.js';
32
33
 
33
34
  export interface SelfDevRunParticipant {
@@ -92,6 +93,8 @@ export interface SelfDevRunState {
92
93
  parkedResolution?: SelfDevRunResolution;
93
94
  /** The closed supervisor termination reason, persisted independently from per-goal results. */
94
95
  supervisorStopReason?: SupervisorStopReason;
96
+ /** Named next step at supervisor termination; legacy checkpoints omit it. */
97
+ next?: SupervisorNext;
95
98
  /** ★ C(2026-07-21·HITL 정황 품질) — 이 run 을 구동하는 오케스트레이터 프로세스 pid. 살아있으면
96
99
  * non-terminal goal 은 **실행중(running)** 이지 막힌(interrupted) 게 아니다. 죽었으면(오케스트레이터
97
100
  * killed) 진짜 interrupted. isPidAlive 로 구분해 라이브 잡을 parked/repair-signals 에서 제외. */
@@ -311,13 +314,14 @@ export function recordSelfDevRunSupervisorStop(
311
314
  runId: string,
312
315
  supervisorStopReason: SupervisorStopReason,
313
316
  dir = selfDevRunsDir(),
317
+ next?: SupervisorNext,
314
318
  ): SupervisorStopPersistenceOutcome {
315
319
  try {
316
320
  if (!existsSync(dir)) return { outcome: 'missing-directory' };
317
321
  return withFileLockSync(join(dir, `${runId}.lock`), () => {
318
322
  const state = loadSelfDevRunFromPath(runPath(runId, dir));
319
323
  if (!state) return { outcome: 'missing-record' };
320
- writeSelfDevRun({ ...state, updatedAt: Date.now(), supervisorStopReason }, dir);
324
+ writeSelfDevRun({ ...state, updatedAt: Date.now(), supervisorStopReason, ...(next === undefined ? {} : { next }) }, dir);
321
325
  return { outcome: 'persisted' };
322
326
  });
323
327
  } catch {
@@ -6,6 +6,8 @@ import type { SelfDevJobResult } from './orchestrate.js';
6
6
  import { recordSelfDevRunSupervisorStop, selfDevRunsDir } from './run-store.js';
7
7
  import { debug } from '../debug/log.js';
8
8
  import { sweepFrozenMerges } from '../self-implement/frozen-merges.js';
9
+ import { routeSupervisorVerdict, type SupervisorNext } from './supervisor-verdict-edges.js';
10
+ import { recordTaskAgentShadowMove, type TaskAgentShadowInput } from '../task-agent/shadow.js';
9
11
 
10
12
  export interface SupervisorRound {
11
13
  round: number;
@@ -186,6 +188,7 @@ function formatGoalPlanRevisionObservation(results: readonly SelfDevJobResult[])
186
188
  export type SupervisorJobResult = SelfDevJobResult & {
187
189
  reviewReason?: string;
188
190
  salvage?: 'launched' | 'parked';
191
+ next?: SupervisorNext;
189
192
  };
190
193
 
191
194
  export function decideNextRun(input: {
@@ -459,6 +462,11 @@ export interface SuperviseRunOptions {
459
462
  runStore?: { runId?: string; operatorDir?: string; dir?: string; isolatedDir?: string };
460
463
  observe?: (event: string, data: Record<string, unknown>) => void;
461
464
  sweepPendingMerges?: typeof sweepFrozenMerges;
465
+ /**
466
+ * TASK-AGENT-SHADOW — 멈춤이 확정되면 판단부를 그림자로 부른다(기본 `recordTaskAgentShadowMove`).
467
+ * `false` 면 끈다. 던져도 런의 결말은 바뀌지 않는다(fail-soft).
468
+ */
469
+ taskAgentShadow?: ((input: TaskAgentShadowInput) => unknown) | false;
462
470
  observeDeliverables?: () => Promise<{
463
471
  readonly deployFindings: ReadonlyMap<string, { target: string; findings?: readonly DeployVerifyFinding[] | readonly unknown[] }>;
464
472
  readonly unmeasured: readonly unknown[];
@@ -592,9 +600,13 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
592
600
  } catch { /* fail-open */ }
593
601
 
594
602
  if (decision.action === 'stop') {
603
+ const reason = decision.stopReason ?? 'unregistered-stop';
604
+ const next = routeSupervisorVerdict(reason, results).next;
605
+ try { debug.log('self-dev.supervisor', 'verdict-edge', { runId, reason, next }); } catch { /* observation must not interrupt supervision */ }
606
+ results = results.map((result) => ({ ...result, next }));
595
607
  if (runId && decision.stopReason) {
596
608
  const storage = resolveSupervisorStopStorage(opts.runStore);
597
- const persistence = recordSelfDevRunSupervisorStop(runId, decision.stopReason, storage.path);
609
+ const persistence = recordSelfDevRunSupervisorStop(runId, decision.stopReason, storage.path, next);
598
610
  const storageObservation = {
599
611
  storagePath: storage.path,
600
612
  storageSource: storage.source,
@@ -608,6 +620,14 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
608
620
  });
609
621
  } catch { /* fail-open */ }
610
622
  }
623
+ if (decision.stopReason && opts.taskAgentShadow !== false) {
624
+ // TASK-AGENT-SHADOW — 판단부를 그림자로 부른다(실행 0). 어떤 오류도 런의 결말을 바꾸지 않는다.
625
+ try {
626
+ await (opts.taskAgentShadow ?? recordTaskAgentShadowMove)({ runId, stopReason: decision.stopReason, results });
627
+ } catch (error) {
628
+ try { debug.log('task-agent', 'shadow-move-failed', { runId, stopReason: decision.stopReason, error: String(error) }); } catch { /* fail-open */ }
629
+ }
630
+ }
611
631
  return results;
612
632
  }
613
633
 
@@ -0,0 +1,46 @@
1
+ import type { SelfDevJobResult } from './orchestrate.js';
2
+ import type { SupervisorStopReason } from './run-supervisor.js';
3
+
4
+ export type SupervisorNext = 'harvest' | 'scope-decompose' | 'self-review' | 'human-gate' | 'proposal';
5
+ export type SupervisorVerdictEdge = { readonly meaning: string; readonly next: SupervisorNext };
6
+
7
+ // 목표 규칙: 사람 관문은 결제·공개·자격·비가역 결정에만. 지금 max-rounds·provider-exhausted·step-timeout 이 human-gate 인 것은
8
+ // 그 규칙의 예외로 «그대로» 둔 값이다(시험에 고정 — 바꾸면 보인다).
9
+ export const SUPERVISOR_VERDICT_EDGES = {
10
+ converged: { meaning: 'run converged', next: 'harvest' },
11
+ 'human-stopped': { meaning: 'human stopped the run', next: 'human-gate' },
12
+ 'parent-signals-red': { meaning: 'parent decision signals are red', next: 'human-gate' },
13
+ 'needs-human': { meaning: 'unresolved human or harvest decision', next: 'human-gate' },
14
+ 'no-actionable-work': { meaning: 'no actionable work remains', next: 'self-review' },
15
+ 'harvestable-awaiting-human': { meaning: 'implementation awaits harvest', next: 'harvest' },
16
+ 'handed-off-to-salvage': { meaning: 'salvage already owns the work', next: 'harvest' },
17
+ 'max-rounds': { meaning: 'round budget exhausted', next: 'human-gate' },
18
+ 'no-progress': { meaning: 'run stalled', next: 'scope-decompose' },
19
+ 'provider-exhausted': { meaning: 'provider quota exhausted', next: 'human-gate' },
20
+ 'step-timeout': { meaning: 'step budget exhausted', next: 'human-gate' },
21
+ 'decomposable-no-progress': { meaning: 'stalled with a decomposition proposal', next: 'scope-decompose' },
22
+ 'review-unobserved': { meaning: 'review was not observed', next: 'self-review' },
23
+ 'deliverable-unobserved': { meaning: 'deliverable was not observed', next: 'self-review' },
24
+ 'deliverable-merged': { meaning: 'deliverable is merged', next: 'harvest' },
25
+ } as const satisfies Record<SupervisorStopReason, SupervisorVerdictEdge>;
26
+
27
+ const HUMAN_GATE_MERGE_REASONS: ReadonlySet<string> = new Set(['decision-signal-red', 'signal-incomplete', 'parent-unlanded', 'human-gate']);
28
+
29
+ /** Unknown stop vocabularies are proposals, never silently treated as approved edges. */
30
+ export function routeSupervisorVerdict(
31
+ reason: string,
32
+ results: readonly Partial<Pick<SelfDevJobResult, 'status' | 'stage' | 'merged' | 'prNumber' | 'prUrl' | 'blockReason' | 'mergeReason'>>[] = [],
33
+ ): SupervisorVerdictEdge {
34
+ const edge = Object.hasOwn(SUPERVISOR_VERDICT_EDGES, reason)
35
+ ? SUPERVISOR_VERDICT_EDGES[reason as SupervisorStopReason]
36
+ : undefined;
37
+ if (!edge) return { meaning: 'unregistered supervisor stop reason', next: 'proposal' };
38
+ if (reason !== 'needs-human') return edge;
39
+ if (results.some((result) => result.blockReason === 'parent-unlanded')) return edge;
40
+ // A human-gate reason on ANY result in the run keeps the whole run at the human gate.
41
+ if (results.some((result) => HUMAN_GATE_MERGE_REASONS.has(String(result.mergeReason)))) return edge;
42
+ const openPrs = results.filter((result) => result.status === 'done' && result.stage === 'pr-opened'
43
+ && result.merged !== true && (result.prNumber !== undefined || Boolean(result.prUrl)));
44
+ if (openPrs.length === 0) return edge;
45
+ return { meaning: 'open PR ready for harvest', next: 'harvest' };
46
+ }
@@ -12,7 +12,7 @@ import { addHarnessQueue, type QueueSeat } from '../harness/harness-queue.js';
12
12
  import { debug } from '../debug/log.js';
13
13
  import { withFileLockSync } from '../storage/file-lock.js';
14
14
  import { elanousStateRoot } from '../autopilot/state-paths.js';
15
- import { devVersion, listChecklist, parseOwner } from '../release-loop/checklist.js';
15
+ import { checklistDevVersion, listChecklist, parseOwner } from '../release-loop/checklist.js';
16
16
  import { getUserConfig } from '../user-config.js';
17
17
 
18
18
  export const FOLLOW_UP_DEPTH_LIMIT = 3;
@@ -193,7 +193,7 @@ function defaultAppendDraft(stateRoot: string, record: FollowUpDraftRecord): voi
193
193
 
194
194
  function defaultCellOwner(cellId: string): string | undefined {
195
195
  try {
196
- return listChecklist(devVersion()).items.find((item) => item.id === cellId)?.owner;
196
+ return listChecklist(checklistDevVersion()).items.find((item) => item.id === cellId)?.owner;
197
197
  } catch {
198
198
  return undefined;
199
199
  }
@@ -2,9 +2,9 @@ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync,
2
2
  import { dlopen, FFIType } from 'bun:ffi';
3
3
  import { createHash } from 'node:crypto';
4
4
  import { dirname, join, resolve } from 'node:path';
5
- import { effectiveInstanceRoot } from '../instance/resolve.js';
5
+ import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
6
6
  import { debug } from '../debug/log.js';
7
- import { beginLandingMerge, readLandingFreeze, type LandingFreeze } from '../release-loop/landing-freeze.js';
7
+ import { beginLandingMerge, type LandingFreeze } from '../release-loop/landing-freeze.js';
8
8
 
9
9
  /** `manual`: the owning repository could not be determined — a sweep never resumes it; a person does. */
10
10
  export interface FrozenMerge { prNumber: number; headCommit: string; repoRoot: string; manual?: true; /** Host goal document, so a resumed merge can still apply its «이 칸 완료» declaration (CL-AUTO). */ goalFile?: string }
@@ -182,8 +182,10 @@ export function admitLandingMerge(
182
182
  root = effectiveInstanceRoot(),
183
183
  hooks: { afterQueue?: () => void } = {},
184
184
  claimKey?: { prNumber: number; repoRoot: string; headCommit?: string },
185
+ opts: { prodFreezeRoot?: string; forceReason?: string } = {},
185
186
  ): LandingAdmission {
186
187
  let entry: FrozenMerge | null | undefined;
188
+ const prodFreezeRoot = opts.prodFreezeRoot ?? prodInstanceRoot();
187
189
  // A held PR is merged by exactly one runner: the claim (repository + PR) is held from admission until the lander's
188
190
  // merge has ended. Its queue entry stays until that merge is confirmed — `end(true)` removes this lander's own head;
189
191
  // a failed or interrupted merge leaves it for the next resume sweep.
@@ -201,7 +203,7 @@ export function admitLandingMerge(
201
203
  } finally { landing.end(); held?.release(); }
202
204
  };
203
205
  for (;;) {
204
- const landing = beginLandingMerge(root);
206
+ const landing = beginLandingMerge(root, new Date(), prodFreezeRoot, opts.forceReason);
205
207
  if (!landing.frozen) {
206
208
  if (!claim && claimKey) {
207
209
  const key = { prNumber: claimKey.prNumber, repoRoot: repoIdentity(claimKey.repoRoot), headCommit: claimKey.headCommit };
@@ -222,17 +224,19 @@ export function admitLandingMerge(
222
224
  if (!entry) return { kind: 'held', freeze: landing.frozen };
223
225
  queueFrozenMerge(entry, root);
224
226
  hooks.afterQueue?.();
225
- if (readLandingFreeze(root)) return { kind: 'held', freeze: landing.frozen };
226
- const mine = { ...entry, repoRoot: repoIdentity(entry.repoRoot) };
227
- claim = claimFrozenMerge(mine, root);
228
- if (!claim) return { kind: 'taken' };
229
- // A resume sweep already merged and dequeued it between our queueing and our claim: nothing left to merge.
230
- let stillHeld = false;
231
- try { stillHeld = readQueue(root).some((item) => sameEntry(item, mine)); }
232
- catch (error) { claim.release(); throw error; }
233
- if (!stillHeld) { claim.release(); return { kind: 'taken' }; }
234
- ownHead = mine;
235
- debug.log('harness.merge', 'freeze-lifted-while-queueing', { pr: entry.prNumber });
227
+ const afterQueue = beginLandingMerge(root, new Date(), prodFreezeRoot, opts.forceReason);
228
+ if (afterQueue.frozen) return { kind: 'held', freeze: afterQueue.frozen };
229
+ try {
230
+ const mine = { ...entry, repoRoot: repoIdentity(entry.repoRoot) };
231
+ claim = claimFrozenMerge(mine, root);
232
+ if (!claim) return { kind: 'taken' };
233
+ // A resume sweep already merged and dequeued it between our queueing and our claim: nothing left to merge.
234
+ const stillHeld = readQueue(root).some((item) => sameEntry(item, mine));
235
+ if (!stillHeld) { claim.release(); return { kind: 'taken' }; }
236
+ ownHead = mine;
237
+ debug.log('harness.merge', 'freeze-lifted-while-queueing', { pr: entry.prNumber });
238
+ } catch (error) { claim?.release(); throw error; }
239
+ finally { afterQueue.end(); }
236
240
  }
237
241
  }
238
242
 
@@ -259,16 +263,17 @@ export async function sweepFrozenMerges(
259
263
  },
260
264
  root = effectiveInstanceRoot(),
261
265
  alreadyMerged: (entry: FrozenMerge) => boolean | Promise<boolean> = githubMergedAt,
266
+ prodFreezeRoot = prodInstanceRoot(),
262
267
  ): Promise<{ pending: number; merged: number }> {
263
- const frozen = readLandingFreeze(root);
264
- if (frozen) return { pending: readQueue(root).length, merged: 0 };
265
268
  const pending = readQueue(root);
266
269
  let merged = 0;
267
270
  for (const entry of pending) {
268
- if (readLandingFreeze(root)) break;
269
- const claim = claimFrozenMerge(entry, root);
270
- if (!claim) continue; // another sweep or the original lander is on it
271
+ const guard = beginLandingMerge(root, new Date(), prodFreezeRoot);
272
+ if (guard.frozen) break;
273
+ let claim: { release: () => void } | null = null;
271
274
  try {
275
+ claim = claimFrozenMerge(entry, root);
276
+ if (!claim) continue; // another sweep or the original lander is on it
272
277
  if (!readQueue(root).some((item) => sameEntry(item, entry))) continue; // taken back by its lander
273
278
  if (entry.manual) { debug.log('harness.merge', 'resume-manual', { pr: entry.prNumber, repoRoot: entry.repoRoot }); continue; }
274
279
  if (!(await alreadyMerged(entry))) {
@@ -289,7 +294,7 @@ export async function sweepFrozenMerges(
289
294
  });
290
295
  } catch (error) {
291
296
  debug.log('harness.merge', 'resume-failed', { pr: entry.prNumber, error: String(error) });
292
- } finally { claim.release(); }
297
+ } finally { guard.end(); claim?.release(); }
293
298
  }
294
299
  return { pending: readQueue(root).length, merged };
295
300
  }