elanous 0.2.25 → 0.2.28

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 (337) 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/chunks/{1454-37fd16f479f5554a.js → 1454-29d1d773d5996a49.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{599-a914cc0468f56143.js → 599-56f2c0a5cc4a7295.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-b12149c51f7d04b1.js → page-fa46d4588e8442c2.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-305cd294e754906e.js → page-256ffd365d32e100.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-dea4b795741126fd.js → page-e0de78ae7b99f01c.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/exec/{page-45889e7070d11126.js → page-4c741eb760f2f77c.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/field/{page-16a2e893f83ae4d0.js → page-e9ba98f5f1440a21.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/app/intake/{page-56888bc744e08dac.js → page-2f6f303fa114ec24.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/live/{page-952c6ceec6d9de38.js → page-ced0bcfc6542b3fc.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-6d0202af568f4c63.js → page-bb1f05cd51c38944.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-4fb6fc34e20c07b7.js → page-f736324afa2117c1.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-e9502921f4f01b1f.js → page-6f131b51193a36f9.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/{not-found-b6169ea2f09cf287.js → not-found-435f5e2faf02bc01.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-6a14333d1b1b3967.js → page-497e0eedf20e7890.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-8c91a9a66c9ec90a.js → page-a8d28fdf995e16ea.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/{page-3d3fc73dd187ed52.js → page-66a67eb19ebd976b.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-9c04dac0831c2bbb.js → page-c71267e7681da982.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-04de9ca6d52a3893.js → page-cb249e81ed339318.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/app/settings/{page-62527677bbc2fdc4.js → page-781cfc5114e097da.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-9a2e09c4a311764c.js → page-478157214db6a023.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/setup/{page-bfeb35d69e8b24bb.js → page-86141f12cf090b7f.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/share/{page-89cb33bced824e4a.js → page-7256c73bb1c36751.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/today/{page-6445ac4cbb8dd869.js → page-979d318a534e92da.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-20162174b77f96cf.js → page-601fb77340e63c9a.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-05a4b2c9b91034aa.js → page-11bf9e5725a72c22.js} +1 -1
  53. package/apps/pwa/out/approvals/index.html +1 -1
  54. package/apps/pwa/out/approvals/index.txt +13 -13
  55. package/apps/pwa/out/autopilot/index.html +1 -1
  56. package/apps/pwa/out/autopilot/index.txt +13 -13
  57. package/apps/pwa/out/board/index.html +1 -1
  58. package/apps/pwa/out/board/index.txt +13 -13
  59. package/apps/pwa/out/botlab/index.html +1 -1
  60. package/apps/pwa/out/botlab/index.txt +13 -13
  61. package/apps/pwa/out/bots/index.html +1 -1
  62. package/apps/pwa/out/bots/index.txt +13 -13
  63. package/apps/pwa/out/ceo/index.html +1 -1
  64. package/apps/pwa/out/ceo/index.txt +13 -13
  65. package/apps/pwa/out/chat/index.html +1 -1
  66. package/apps/pwa/out/chat/index.txt +13 -13
  67. package/apps/pwa/out/consult/index.html +1 -1
  68. package/apps/pwa/out/consult/index.txt +13 -13
  69. package/apps/pwa/out/control/index.html +1 -1
  70. package/apps/pwa/out/control/index.txt +13 -13
  71. package/apps/pwa/out/decisions/index.html +1 -1
  72. package/apps/pwa/out/decisions/index.txt +13 -13
  73. package/apps/pwa/out/design-check/index.html +1 -1
  74. package/apps/pwa/out/design-check/index.txt +13 -13
  75. package/apps/pwa/out/editor/index.html +1 -1
  76. package/apps/pwa/out/editor/index.txt +13 -13
  77. package/apps/pwa/out/exec/index.html +1 -1
  78. package/apps/pwa/out/exec/index.txt +13 -13
  79. package/apps/pwa/out/field/index.html +1 -1
  80. package/apps/pwa/out/field/index.txt +13 -13
  81. package/apps/pwa/out/healthz +1 -1
  82. package/apps/pwa/out/index.html +1 -1
  83. package/apps/pwa/out/index.txt +13 -13
  84. package/apps/pwa/out/inside/index.html +1 -1
  85. package/apps/pwa/out/inside/index.txt +13 -13
  86. package/apps/pwa/out/intake/index.html +1 -1
  87. package/apps/pwa/out/intake/index.txt +13 -13
  88. package/apps/pwa/out/live/index.html +1 -1
  89. package/apps/pwa/out/live/index.txt +13 -13
  90. package/apps/pwa/out/live-run/index.html +1 -1
  91. package/apps/pwa/out/live-run/index.txt +13 -13
  92. package/apps/pwa/out/loops/index.html +1 -1
  93. package/apps/pwa/out/loops/index.txt +13 -13
  94. package/apps/pwa/out/market/index.html +1 -1
  95. package/apps/pwa/out/market/index.txt +13 -13
  96. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  97. package/apps/pwa/out/missions/placeholder/index.txt +13 -13
  98. package/apps/pwa/out/morning/index.html +1 -1
  99. package/apps/pwa/out/morning/index.txt +13 -13
  100. package/apps/pwa/out/observatory/index.html +1 -1
  101. package/apps/pwa/out/observatory/index.txt +13 -13
  102. package/apps/pwa/out/ops/checklist/index.html +1 -1
  103. package/apps/pwa/out/ops/checklist/index.txt +13 -13
  104. package/apps/pwa/out/ops/release/index.html +1 -1
  105. package/apps/pwa/out/ops/release/index.txt +13 -13
  106. package/apps/pwa/out/ops/seats/index.html +1 -1
  107. package/apps/pwa/out/ops/seats/index.txt +13 -13
  108. package/apps/pwa/out/outputs/index.html +1 -1
  109. package/apps/pwa/out/outputs/index.txt +13 -13
  110. package/apps/pwa/out/reflection/index.html +1 -1
  111. package/apps/pwa/out/reflection/index.txt +14 -14
  112. package/apps/pwa/out/scheduler/index.html +1 -1
  113. package/apps/pwa/out/scheduler/index.txt +13 -13
  114. package/apps/pwa/out/sessions/index.html +1 -1
  115. package/apps/pwa/out/sessions/index.txt +13 -13
  116. package/apps/pwa/out/settings/devices/index.html +1 -1
  117. package/apps/pwa/out/settings/devices/index.txt +13 -13
  118. package/apps/pwa/out/settings/index.html +1 -1
  119. package/apps/pwa/out/settings/index.txt +13 -13
  120. package/apps/pwa/out/setup/done/index.html +1 -1
  121. package/apps/pwa/out/setup/done/index.txt +13 -13
  122. package/apps/pwa/out/setup/index.html +1 -1
  123. package/apps/pwa/out/setup/index.txt +13 -13
  124. package/apps/pwa/out/share/index.html +1 -1
  125. package/apps/pwa/out/share/index.txt +13 -13
  126. package/apps/pwa/out/showroom/index.html +1 -1
  127. package/apps/pwa/out/showroom/index.txt +13 -13
  128. package/apps/pwa/out/tasks/index.html +1 -1
  129. package/apps/pwa/out/tasks/index.txt +13 -13
  130. package/apps/pwa/out/term/index.html +1 -1
  131. package/apps/pwa/out/term/index.txt +13 -13
  132. package/apps/pwa/out/today/index.html +1 -1
  133. package/apps/pwa/out/today/index.txt +13 -13
  134. package/apps/pwa/out/trace/index.html +1 -1
  135. package/apps/pwa/out/trace/index.txt +13 -13
  136. package/apps/pwa/out/vault/index.html +1 -1
  137. package/apps/pwa/out/vault/index.txt +13 -13
  138. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  139. package/apps/pwa/out/workflows/chat-ui/index.txt +13 -13
  140. package/apps/pwa/out/workflows/index.html +1 -1
  141. package/apps/pwa/out/workflows/index.txt +13 -13
  142. package/apps/pwa/out/workspace/index.html +1 -1
  143. package/apps/pwa/out/workspace/index.txt +13 -13
  144. package/apps/pwa/out/worktrees/index.html +1 -1
  145. package/apps/pwa/out/worktrees/index.txt +13 -13
  146. package/graphs/implement-loop.yaml +38 -1
  147. package/graphs/steward/recipes.yaml +6 -0
  148. package/graphs/steward/release-day.yaml +23 -0
  149. package/graphs/steward/steward.yaml +9 -1
  150. package/graphs/work/code-pr.yaml +23 -0
  151. package/package.json +1 -1
  152. package/scripts/browser-observe-cycle.ts +76 -2
  153. package/scripts/ci-typecheck-changed.ts +119 -7
  154. package/scripts/coord-msg-bridge.ts +209 -0
  155. package/scripts/coord-post.sh +40 -7
  156. package/scripts/coord-requests-bridge.ts +10 -5
  157. package/scripts/detect-test-interference.ts +31 -1
  158. package/scripts/expose-rubric-suggest.ts +1 -1
  159. package/scripts/measure-tsc-affected-roots.ts +10 -1
  160. package/scripts/release-loop/docs-node.ts +51 -22
  161. package/scripts/release-loop/export-check-node.ts +8 -5
  162. package/scripts/release-loop/gate-node.ts +37 -4
  163. package/scripts/release-loop/node-verdict.ts +10 -3
  164. package/scripts/release-loop/publish-node.ts +3 -0
  165. package/scripts/release-loop/tui-node-checks.ts +5 -2
  166. package/scripts/skills/public-check.ts +5 -2
  167. package/scripts/test-deterministic.ts +520 -37
  168. package/scripts/ux-sim.ts +7 -1
  169. package/scripts/verify-kpack6-live.ts +230 -0
  170. package/src/acp/codex-plugins.ts +17 -0
  171. package/src/agent/task-notification.ts +32 -2
  172. package/src/agent-mission/in-agent-plugin.ts +8 -3
  173. package/src/agent-substrate/review-observation.ts +9 -2
  174. package/src/agent-substrate/self-review-cli.ts +62 -16
  175. package/src/boot/daemon-tools/self-implement.ts +72 -4
  176. package/src/briefing/brief-items.ts +151 -35
  177. package/src/budget/llm-cost.ts +30 -3
  178. package/src/budget/usage-outcomes.ts +32 -0
  179. package/src/claims/claims-ledger.ts +23 -0
  180. package/src/claims/claims-render.ts +22 -2
  181. package/src/cli/brief-cli.ts +131 -10
  182. package/src/cli/claims-cli.ts +24 -4
  183. package/src/cli/control-cli.ts +29 -0
  184. package/src/cli/doctor-cli.ts +90 -8
  185. package/src/cli/msg-cli.ts +177 -11
  186. package/src/cli/plugin-cli.ts +11 -3
  187. package/src/cli/pr-cli.ts +133 -18
  188. package/src/cli/pr-land-overlap-evidence.ts +18 -10
  189. package/src/cli/pr-land-refusal.ts +20 -0
  190. package/src/cli/release-cli.ts +34 -5
  191. package/src/cli/rfc-cli.ts +24 -0
  192. package/src/cli/seat-cli.ts +121 -0
  193. package/src/cli/self-utterances-cli.ts +140 -0
  194. package/src/cli/tailscale-serve.ts +5 -8
  195. package/src/cli/tasks-cli.ts +528 -31
  196. package/src/cli/usage-cli.ts +9 -4
  197. package/src/context-card/cell.ts +40 -0
  198. package/src/dashboard/boot-timing.ts +35 -0
  199. package/src/dashboard/first-screen-band.ts +63 -13
  200. package/src/dashboard/index.ts +94 -27
  201. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +26 -0
  202. package/src/dashboard/skill-detection-runtime.ts +24 -4
  203. package/src/dashboard/slash-runtime/dashboard-handlers.ts +6 -0
  204. package/src/debug/log.ts +1 -1
  205. package/src/domains/morning-report.ts +3 -1
  206. package/src/domains/release-tool.ts +29 -12
  207. package/src/domains/surface-events.ts +16 -0
  208. package/src/elanous-config-dir.ts +2 -2
  209. package/src/fleet/host-doctor.ts +73 -2
  210. package/src/github/pr-labels.ts +10 -4
  211. package/src/graph-runner/runner.ts +104 -15
  212. package/src/harness/harness-clean.ts +21 -3
  213. package/src/harness/harness-cli-command.ts +68 -10
  214. package/src/harness/harness-pod-dispatch.ts +11 -4
  215. package/src/harness/harness-stop.ts +11 -7
  216. package/src/hq/seats.ts +17 -3
  217. package/src/index.ts +69 -9
  218. package/src/instance/current.ts +2 -6
  219. package/src/instance/resolve.ts +5 -6
  220. package/src/intelligence-map/model-bench-scores.ts +102 -0
  221. package/src/llm/openai-sampling-fields.ts +89 -0
  222. package/src/llm/runtime-model-family.ts +61 -0
  223. package/src/llm/zai-coding-plan.ts +27 -1
  224. package/src/llm.ts +13 -89
  225. package/src/loops/orchestrator/inbox.ts +73 -0
  226. package/src/loops/orchestrator/resync-shadow.ts +100 -12
  227. package/src/loops/orchestrator/tick.ts +73 -2
  228. package/src/machines/machine-ledger.ts +31 -3
  229. package/src/maturity/cli-maturity.ts +13 -3
  230. package/src/maturity/feature-maturity.ts +1 -1
  231. package/src/msg/msg-sse-server.ts +141 -0
  232. package/src/msg/msg-store.ts +149 -17
  233. package/src/ops/overview-collect.ts +3 -2
  234. package/src/ops/overview-owner.ts +29 -0
  235. package/src/patents/ledger.ts +128 -0
  236. package/src/plugin-hooks/shell-hook.ts +17 -2
  237. package/src/plugin-missions/evaluator-runner.ts +18 -2
  238. package/src/plugins/install/market-fetch.ts +32 -1
  239. package/src/plugins/install/plugin-install.ts +11 -1
  240. package/src/pod-lease/admission.ts +116 -41
  241. package/src/prompt-enhance/enhance.ts +11 -0
  242. package/src/release-loop/checklist.ts +52 -4
  243. package/src/release-loop/feature-store.ts +10 -9
  244. package/src/release-loop/merged-pr-checklist.ts +24 -5
  245. package/src/release-loop/rubric.ts +70 -0
  246. package/src/rfc-registry/registry.ts +88 -0
  247. package/src/seat-dispatch/seat-request-ledger.ts +4 -0
  248. package/src/seat-loop/knowhow-lint.ts +233 -0
  249. package/src/seat-loop/seat-loop.ts +5 -1
  250. package/src/seat-loop/seat-pack.ts +16 -4
  251. package/src/seat-loop/traffic-signal.ts +7 -0
  252. package/src/self-dev/ask-launch-flow.ts +48 -8
  253. package/src/self-dev/ask-path-candidates.ts +69 -14
  254. package/src/self-dev/decompose-proposal.ts +43 -1
  255. package/src/self-dev/draft-sweep.ts +23 -14
  256. package/src/self-dev/graph-journey-nodes.ts +2 -0
  257. package/src/self-dev/launch-preflight-text.ts +5 -1
  258. package/src/self-dev/launch-preflight.ts +143 -10
  259. package/src/self-dev/orchestrate.ts +6 -2
  260. package/src/self-dev/run-supervisor.ts +35 -1
  261. package/src/self-implement/gate-cli.ts +6 -2
  262. package/src/self-implement/goal-author.ts +5 -28
  263. package/src/self-implement/goal-command-execution.ts +26 -0
  264. package/src/self-implement/goal-new-paths.ts +4 -5
  265. package/src/self-implement/graph-authority.ts +17 -3
  266. package/src/self-implement/graph-yaml.ts +15 -7
  267. package/src/self-implement/headless-elanous-driver.ts +16 -1
  268. package/src/self-implement/host-regate.ts +44 -9
  269. package/src/self-implement/orchestrator.ts +5 -11
  270. package/src/self-implement/pwa-import-graph.ts +194 -7
  271. package/src/self-implement/rework-policy.ts +80 -2
  272. package/src/self-implement/self-implement-runtime.ts +126 -21
  273. package/src/self-implement/speed-claims.ts +145 -6
  274. package/src/session/index.ts +14 -4
  275. package/src/session-runtime/index.ts +55 -11
  276. package/src/skills/tools/persistent-grounding.ts +59 -4
  277. package/src/skills/tools/shell.ts +79 -1
  278. package/src/steward/rejudge-tick.ts +12 -1
  279. package/src/steward/release-day.ts +158 -0
  280. package/src/steward/resync-tick.ts +123 -0
  281. package/src/task-agent/actions.ts +1 -0
  282. package/src/task-agent/adopt-pr.ts +140 -0
  283. package/src/task-agent/board.ts +11 -1
  284. package/src/task-agent/card-evidence.ts +37 -5
  285. package/src/task-agent/cover.ts +105 -19
  286. package/src/task-agent/failure-notify.ts +61 -0
  287. package/src/task-agent/green-sweep.ts +170 -0
  288. package/src/task-agent/land-worktree.ts +52 -5
  289. package/src/task-agent/live-move-history.ts +1 -0
  290. package/src/task-agent/live-moves.ts +56 -17
  291. package/src/task-agent/parent-host.ts +1 -1
  292. package/src/task-agent/postland-card.ts +139 -0
  293. package/src/task-agent/rejudge-on-head.ts +321 -25
  294. package/src/task-agent/remote-rejudge-bind.ts +62 -0
  295. package/src/task-agent/repair-branch-cas.ts +182 -0
  296. package/src/task-agent/repair-cycle.ts +275 -0
  297. package/src/task-agent/repair-next-move.ts +31 -0
  298. package/src/task-agent/repair-review.ts +24 -0
  299. package/src/task-agent/repair-worker.ts +87 -0
  300. package/src/task-agent/review-result-capture.ts +66 -2
  301. package/src/task-agent/shadow.ts +150 -29
  302. package/src/task-agent/task-hand.ts +133 -8
  303. package/src/task-orchestrator/surfaces/pod-repair-commit.ts +96 -0
  304. package/src/task-orchestrator/surfaces/self-implement-pod.ts +71 -12
  305. package/src/telegram.ts +30 -1
  306. package/src/tool-runtime/tool-search-runtime.ts +25 -0
  307. package/src/tui.ts +20 -1
  308. package/src/user-config.ts +11 -60
  309. package/src/ux-sim/fault-sqlite-busy.ts +50 -0
  310. package/src/version/packed-revision.json +1 -1
  311. package/apps/pwa/out/_next/static/chunks/app/approvals/page-9360b1285b2a346e.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f08f6529d09ffae1.js +0 -1
  313. package/apps/pwa/out/_next/static/chunks/app/board/page-f000d54d258f9ef6.js +0 -1
  314. package/apps/pwa/out/_next/static/chunks/app/bots/page-22f674e7e4c2c9d5.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8888733e40cea6d1.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/app/chat/page-9717ec6964b92276.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/app/consult/page-44eaad486bc98a45.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/app/control/page-97fa6cf3d34c7eb9.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/app/editor/page-35e05d7450461a67.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/app/inside/page-b2638013c0bf7dfd.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/app/layout-c44f93a41167536f.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/app/loops/page-1b810474507f3702.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/app/market/page-74366ae2693a7d95.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4e634c5b98fd4e93.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ed3070d637850548.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b9cc8a10bc6078b4.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/reflection/page-128458c8d86fbb5f.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/sessions/page-cf0c2b0183991d09.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/showroom/page-b008aff96be56654.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/tasks/page-814394dcbae789b1.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/term/page-6a87325c87f09dd2.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/trace/page-852170303c6cd755.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/vault/page-087ceeb975ef272b.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-97c7361cc2a19319.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/workspace/page-8c6901833f897496.js +0 -1
  336. /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_buildManifest.js +0 -0
  337. /package/apps/pwa/out/_next/static/{dRS6lEhSRRBr1abbzp7fJ → y1ltt7oCZxVpC6zVPhZ2O}/_ssgManifest.js +0 -0
@@ -348,7 +348,31 @@ export interface DecomposeApplication {
348
348
  * ⓐ 같은 goal 을 «두 번» 쪼개지 않는다(alreadyDecomposed)
349
349
  * ⓑ 조각이 둘 미만인 제안은 애초에 안 온다(pickDecomposeProposals 가 거른다)
350
350
  * ⓒ 상한을 넘기면 «쪼개지 않는다» — 조각이 무한히 불어나는 것을 막는다
351
+ *
352
+ * ⭐ **조각은 원 골의 «대상 경로:» 머리줄을 물려받는다** (DECOMPOSE-PIECE-TARGET-LINE · 10-11 부검 run-76941fca):
353
+ * 조각 feature 가 골을 «통째로» 갈아 끼우면 첫 줄 선언이 사라져 Pod 예비 검사가 «대상 경로 0» →
354
+ * 재저작 NOT-GROUNDED 로 조각 전부가 exit 1 했다. 좁히는 규칙 = 원 선언 경로 중 조각 문장에
355
+ * 나오는 것만(없으면 원 선언 전부) · «신설:» 줄도 같은 규칙 · 첫 줄이 라벨이 아니면 지금 그대로.
351
356
  */
357
+
358
+ // ── 머리줄 선언을 읽고 좁히는 순수 조각 (위 규칙의 실물) ──────────────────────────
359
+ const TARGET_PATHS_LINE = /^대상 경로:[ \t]*(.*)$/;
360
+ const NEW_PATHS_LINE = /^신설:[ \t]*(.*)$/;
361
+
362
+ /** 선언 줄의 경로를 나눈다 — « · »·쉼표(`goal-new-paths` 와 같은 분리 규칙). 유효성은 걸러내지 않는다: 선언 그대로다. */
363
+ function splitDeclaredPaths(payload: string): string[] {
364
+ return payload.split(/[·,]/).map((token) => token.trim()).filter((token) => token.length > 0);
365
+ }
366
+
367
+ /** 조각 문장에 «나오는» 선언 경로만 남긴다. 하나도 안 나오면 원 선언 전부 — 선언 밖 경로는 지어내지 않는다. */
368
+ function narrowDeclaredPaths(declared: readonly string[], pieceFeature: string): string[] {
369
+ const mentioned = declared.filter((path) => {
370
+ const escaped = path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
371
+ return new RegExp(`(^|[^\\w./-])${escaped}(?=$|[^\\w./-]|\\.(?:\\s|$))`).test(pieceFeature);
372
+ });
373
+ return mentioned.length ? mentioned : [...declared];
374
+ }
375
+
352
376
  export function applyDecomposeProposals(
353
377
  goals: readonly SelfDevGoal[],
354
378
  results: readonly SelfDevJobResult[],
@@ -374,11 +398,29 @@ export function applyDecomposeProposals(
374
398
  // ⛔ 상한을 넘기면 «쪼개지 않는다» — 조용히 자르지 않고 원본을 그대로 둔다.
375
399
  if (out.length + pieces.length + (goals.length - out.length - 1) > maxGoals) { out.push(goal); continue; }
376
400
  decomposed.push(goal.feature);
401
+ // 원 골의 머리줄 선언 — 첫 줄이 «대상 경로:» 가 아니면 조각은 지금 그대로(바이트 불변)다.
402
+ const goalLines = goal.feature.split('\n');
403
+ const targetLine = TARGET_PATHS_LINE.exec(goalLines[0] ?? '');
404
+ // «신설:» 상속은 머리줄 선언이 «있을 때만» — 첫 줄이 라벨이 아니면 조각은 바이트 불변이어야 한다.
405
+ const newLine = targetLine ? goalLines.map((line) => NEW_PATHS_LINE.exec(line)).find((m) => m !== null) : null;
377
406
  for (const piece of pieces) {
407
+ const inherited: string[] = [];
408
+ let paths: string[] = [];
409
+ if (targetLine) {
410
+ const declared = splitDeclaredPaths(targetLine[1]!);
411
+ paths = declared.length ? narrowDeclaredPaths(declared, piece.feature) : declared;
412
+ // 선언이 비어 있으면 원 줄을 그대로 실는다 — 라벨만이라도 예비 검사가 읽게 한다.
413
+ inherited.push(declared.length ? `대상 경로: ${paths.join(' · ')}` : goalLines[0]!);
414
+ }
415
+ if (newLine) {
416
+ const declaredNew = splitDeclaredPaths(newLine[1]!);
417
+ const narrowedNew = declaredNew.length ? narrowDeclaredPaths(declaredNew, piece.feature) : declaredNew;
418
+ inherited.push(declaredNew.length ? `신설: ${narrowedNew.join(' · ')}` : newLine[0]!);
419
+ }
378
420
  out.push({
379
421
  ...goal, // 승격 플래그(openPr·autoReview·autoMerge·base)를 물려받는다
380
422
  id: piece.id,
381
- feature: piece.feature,
423
+ feature: inherited.length ? [...inherited, piece.feature].join('\n') : piece.feature,
382
424
  ...(piece.dependsOn.length ? { dependsOn: [...piece.dependsOn] } : {}),
383
425
  // ⛔⭐ `...goal` 이 «부모의» hotPaths 를 그대로 흘린다 — 그러면 조각 전부가 서로 겹쳐
384
426
  // `orchestrate.ts` 가 전부를 직렬화한다(위상이 아니라 «한 줄»이 된다).
@@ -33,7 +33,7 @@ export interface DraftSweepAdapters {
33
33
  /** Undefined means liveness could not be established. */
34
34
  listLiveBranches(repository: string): Promise<ReadonlySet<string> | undefined>;
35
35
  setLabels(repository: string, number: number, change: { add: string; remove: readonly string[] }): Promise<void>;
36
- closeDraft(repository: string, number: number, comment: string): Promise<void>;
36
+ closeDraft(repository: string, number: number): Promise<void>;
37
37
  /** Resolve the latest claim comment owner; a failed lookup must not authorize a close. */
38
38
  getClaimOwner?(repository: string, number: number): Promise<string | undefined>;
39
39
  /** Current owner, accounting for a later claim release; only draft metrics need this distinction. */
@@ -478,6 +478,23 @@ const label = (action: 'mark-stalled' | 'close', name?: string): string => {
478
478
  const runningLabel = label('mark-stalled');
479
479
  const stalledLabel = label('close', 'stalled');
480
480
  const supersededLabel = label('close', 'superseded');
481
+
482
+ /** Keep repository labels bounded; the complete reason and PR number live in the closed event. */
483
+ export function sweptReasonLabel(reason: string): string {
484
+ const kinds = ['superseded-by', 'duplicate-of-open', 'claim-expired', 'stale-unobserved',
485
+ 'harvest-expired', 'stale-ended-run'] as const;
486
+ const kind = kinds.find((name) => reason === name || reason.startsWith(`${name} `)) ?? 'other';
487
+ return `elanous:swept:${kind}`;
488
+ }
489
+
490
+ async function closeSweptDraft(adapters: Pick<DraftSweepAdapters, 'setLabels' | 'closeDraft'>,
491
+ repository: string, number: number, reason: string, onLabelApplied?: () => void): Promise<void> {
492
+ await adapters.setLabels(repository, number, { add: sweptReasonLabel(reason), remove: [] });
493
+ onLabelApplied?.();
494
+ await adapters.closeDraft(repository, number);
495
+ try { debug.log('self-dev.draft-sweep', 'closed', { pr: number, reason }); } catch { /* fail-soft */ }
496
+ }
497
+
481
498
  const approvalLabel = PR_LABELS.find((entry) => entry.axis === 'state' && entry.sweep.action === 'none')?.name;
482
499
  const keepLabel = PR_LABELS.find((entry) => entry.sweep.action === 'exclude')?.name;
483
500
  const benchLabel = PR_LABELS.find((entry) => entry.name === 'elanous:bench')!.name;
@@ -572,8 +589,7 @@ export async function supersedeDraftsOnMerge(input: {
572
589
  try {
573
590
  // Label first: a failed label write must never strand a closed, unlabelled PR outside open-draft sweeps.
574
591
  if (!draft.labels.includes(supersededLabel)) await adapters.setLabels(repository, draft.number, { add: supersededLabel, remove });
575
- await adapters.closeDraft(repository, draft.number,
576
- `Draft sweep: superseded-by #${merged.number} (https://github.com/${repository}/pull/${merged.number}). Branch preserved.`);
592
+ await closeSweptDraft(adapters, repository, draft.number, decision.reason);
577
593
  closed.push(draft.number);
578
594
  } catch (failure) {
579
595
  kept.push(draft.number);
@@ -975,8 +991,7 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
975
991
  });
976
992
  labelsChanged = true;
977
993
  }
978
- await adapters.closeDraft(repository, draft.number,
979
- `Draft sweep: ${reason} (https://github.com/${repository}/pull/${lineageNewer.number}). Branch preserved.`);
994
+ await closeSweptDraft(adapters, repository, draft.number, reason, () => { labelsChanged = true; });
980
995
  entry.applied = true;
981
996
  } catch (error) {
982
997
  if (labelsChanged) entry.partialApplied = true;
@@ -1035,11 +1050,10 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
1035
1050
  if (!apply || action !== 'close') continue;
1036
1051
  let labelsChanged = false;
1037
1052
  try {
1038
- const owner = await adapters.getClaimOwner?.(repository, draft.number) ?? '미상';
1053
+ await adapters.getClaimOwner?.(repository, draft.number);
1039
1054
  await adapters.setLabels(repository, draft.number, { add: stalledLabel, remove: [runningLabel] });
1040
1055
  labelsChanged = true;
1041
- await adapters.closeDraft(repository, draft.number,
1042
- `Draft sweep: 처리 중 표식이 ${CLAIM_IDLE_HOURS}시간 갱신 없음 — ${runStatus === 'self-implement.result final' ? 'self-implement.result final · 남은 워크트리는 live 아님' : '런·워크트리 없음'} · 주인 ${owner}. Closed as stalled. Branch preserved; reopen to restore.`);
1056
+ await closeSweptDraft(adapters, repository, draft.number, entry.reason, () => { labelsChanged = true; });
1043
1057
  entry.applied = true;
1044
1058
  } catch (error) {
1045
1059
  if (labelsChanged) entry.partialApplied = true;
@@ -1085,12 +1099,7 @@ export async function runDraftSweep({ repository, adapters, apply = false, now =
1085
1099
  labelsChanged = true;
1086
1100
  }
1087
1101
  if (action === 'close') {
1088
- await adapters.closeDraft(repository, draft.number, decision.reason === 'stale-unobserved'
1089
- ? `Draft sweep: run unobserved — no run record reachable here, no host worktree for this branch, idle ≥${STALLED_DRAFT_HOURS}h. Closed as stalled. Branch preserved; reopen to restore.`
1090
- : decision.reason === 'harvest-expired'
1091
- ? `Draft sweep: harvest-expired — 종단이 수확 대상으로 표시한 뒤 ${STALLED_DRAFT_HOURS}h 동안 수확 없음. Branch preserved; reopen to restore.`
1092
- : `Draft sweep: ${decision.reason}${decision.reason.startsWith('superseded-by #')
1093
- ? ` (https://github.com/${repository}/pull/${decision.reason.slice('superseded-by #'.length).match(/^\d+/)![0]})` : ''}. Branch preserved.`);
1102
+ await closeSweptDraft(adapters, repository, draft.number, reason, () => { labelsChanged = true; });
1094
1103
  }
1095
1104
  entry.applied = labelsChanged || action === 'close';
1096
1105
  } catch (error) {
@@ -37,6 +37,8 @@ export const JOURNEY_NODE_IDS = [
37
37
  'salvage',
38
38
  'heal',
39
39
  'next-move',
40
+ 'ready',
41
+ 'pr-head-checkout',
40
42
  'stopped',
41
43
  ] as const;
42
44
  export type JourneyNodeId = typeof JOURNEY_NODE_IDS[number];
@@ -17,7 +17,7 @@ export interface LiveRunOverlapPath {
17
17
 
18
18
  /** 검사 대상 한 건이 막힌 이유. `name` 은 사람이 바로 열어 볼 수 있는 식별자다. */
19
19
  export interface LaunchPreflightBlocker {
20
- readonly kind: 'open-pr' | 'sibling-pr' | 'live-run' | 'no-target-paths' | 'recent-change' | 'ask-outside-path' | 'ask-marker';
20
+ readonly kind: 'open-pr' | 'sibling-pr' | 'live-run' | 'no-target-paths' | 'missing-traced-path' | 'goal-unreadable' | 'recent-change' | 'ask-outside-path' | 'ask-marker';
21
21
  /** `#8100` · `run-6ce5765b…` 처럼 «바로 조회 가능한» 이름. */
22
22
  readonly name: string;
23
23
  /** 왜 막혔는지 한 줄 — 경로와 수를 포함한다. */
@@ -490,6 +490,10 @@ export function renderLaunchPreflight(
490
490
  }
491
491
  lines.push(`[preflight] ⛔ 막는 것 ${result.blockers.length}건${forced ? '' : ' — 발사하지 않는다'}`);
492
492
  for (const blocker of result.blockers) lines.push(`[preflight] · ${blocker.name} — ${blocker.detail}`);
493
+ if (result.blockers.some((blocker) => blocker.kind === 'missing-traced-path' || blocker.kind === 'goal-unreadable')) {
494
+ lines.push('[preflight] 골 판독 실패 또는 traced path 오류는 --force-preflight 로 우회할 수 없다 — 골 문서와 경로를 바로잡아야 한다');
495
+ return lines.join('\n');
496
+ }
493
497
  lines.push(forced
494
498
  ? '[preflight] ⚠️ --force-preflight — 위 막힘을 «뚫고» 발사한다 (이 우회는 관측에 남는다)'
495
499
  : '[preflight] 그래도 가려면 --force-preflight (그 우회는 관측에 남는다) — elanous dev');
@@ -17,8 +17,10 @@
17
17
  // ⛔ 그래도 관측·산출에는 «그대로» 남긴다 — 「지나갔다」와 「없었다」는 다른 값이다.
18
18
 
19
19
  import { execFileSync } from 'node:child_process';
20
- import { existsSync } from 'node:fs';
21
- import { isAbsolute, resolve } from 'node:path';
20
+ import { closeSync, existsSync, lstatSync, openSync, readSync, statSync } from 'node:fs';
21
+ import { dirname, isAbsolute, resolve, sep } from 'node:path';
22
+ import { linesOutsideFencedCode } from '../self-implement/goal-author.js';
23
+ import { declaredNewFilePaths } from '../self-implement/goal-new-paths.js';
22
24
  import { resolveLogTargets } from '../cli/logs-cli.js';
23
25
  import { debug } from '../debug/log.js';
24
26
  import { siblingTestPath, normalizePreexistingFailureObservedAt, normalizePreexistingFailureRecords, emitAskMarkerObservation, emittedAskMarkerObservations, setAskMarkerObserver } from './launch-preflight-text.js';
@@ -734,8 +736,9 @@ export interface AskPreflightOptions {
734
736
  readonly openPrsLimit?: number;
735
737
  /** 중단 런 원장 조회의 최대 반환 행 수. 상한 도달은 결과의 `truncated` 상태로 남는다. */
736
738
  readonly interruptedRunsLimit?: number;
737
- /** ⛔ 저작 «전» 예비 검사용 — 주면 골 문서를 읽지 않고 이 경로로 판정한다(ask 힌트).
738
- * ⚠️ 이것은 «추정»이다. 정본은 저작된 골의 TRACED PATHS 이고 발사 직전에 다시 판정한다. */
739
+ /** ⛔ 저작 «전» 예비 검사용 — 충돌 판정에는 이 ask 힌트 경로를 쓴다.
740
+ * 골 파일이 이미 있으면 문서의 TRACED PATHS 는 별도로 읽어 누락을 검사한다.
741
+ * ⚠️ 힌트는 «추정»이다. 정본은 저작된 골의 TRACED PATHS 이고 발사 직전에 다시 판정한다. */
739
742
  readonly pathsOverride?: readonly string[];
740
743
  /** 원 ask 경로와 최종 대상의 차이를 ask-outside-path 경고에 보인다. */
741
744
  readonly askOutsidePathHints?: readonly string[];
@@ -770,6 +773,78 @@ export interface AskPreflightDecision {
770
773
  readonly plannedBranchResolution?: PlannedBranchResolution;
771
774
  }
772
775
 
776
+ /** OP #980: fragments kept by cleanup remain inspectable traced targets, including bare names. */
777
+ function isFileShapedTracedPath(path: string): boolean {
778
+ return path.trim().length > 0 && (!/\s/.test(path) || path.includes('/'));
779
+ }
780
+
781
+ // Keep lexical `..`/`.` segments: resolving them first hides ENOTDIR behind a file parent.
782
+ function tracedFsPath(path: string, root: string): string {
783
+ return isAbsolute(path) ? path : `${root}${root.endsWith(sep) ? '' : sep}${path}`;
784
+ }
785
+
786
+ function tracedPathExists(path: string, root: string): boolean {
787
+ try {
788
+ const target = tracedFsPath(path, root);
789
+ if (!statSync(target).isFile()) return false;
790
+ const fd = openSync(target, 'r');
791
+ try { readSync(fd, Buffer.alloc(1), 0, 1, 0); }
792
+ finally { closeSync(fd); }
793
+ return true;
794
+ }
795
+ catch { return false; }
796
+ }
797
+
798
+ function tracedPathIsFile(path: string, root: string): boolean {
799
+ try { return statSync(tracedFsPath(path, root)).isFile(); }
800
+ catch { return false; }
801
+ }
802
+
803
+ function tracedPathOccupation(path: string, root: string): 'occupied' | 'missing' | 'inaccessible' {
804
+ const target = tracedFsPath(path, root);
805
+ try { lstatSync(target); return 'occupied'; }
806
+ catch (error) {
807
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return 'inaccessible';
808
+ for (let parent = dirname(target); parent !== dirname(parent); parent = dirname(parent)) {
809
+ try {
810
+ if (lstatSync(parent).isSymbolicLink()) {
811
+ try { statSync(parent); } catch { return 'inaccessible'; }
812
+ }
813
+ } catch (parentError) {
814
+ if ((parentError as NodeJS.ErrnoException).code !== 'ENOENT') return 'inaccessible';
815
+ }
816
+ }
817
+ return 'missing';
818
+ }
819
+ }
820
+
821
+ /** Remove only malformed TRACED PATHS fragments before the child reads the goal file again. */
822
+ export function discardNonFileTracedPathItems(document: string, _root: string): { document: string; dropped: string[] } {
823
+ const dropped: string[] = [];
824
+ let inTracedPaths = false;
825
+ const outsideStarts = new Set(linesOutsideFencedCode(document).map((line) => line.start));
826
+ let offset = 0;
827
+ const lines = document.split('\n').flatMap((line) => {
828
+ const lineStart = offset;
829
+ offset += line.length + 1;
830
+ if (!outsideStarts.has(lineStart)) return [line];
831
+ const body = line.replace(/\r$/, '');
832
+ if (/^\s*##\s+TRACED PATHS\s*$/.test(body)) { inTracedPaths = true; return [line]; }
833
+ if (inTracedPaths && /^\s*#{1,6}\s/.test(body)) inTracedPaths = false;
834
+ if (!inTracedPaths) return [line];
835
+ const item = /^(\s*(?:[-*+]|\d+[.)])\s+)(.*)$/.exec(body);
836
+ if (!item) return [line];
837
+ // OP #980: a fragment with whitespace and no slash is prose; all other forms stay verbatim.
838
+ const fragments = item[2]!.split(' · ');
839
+ const kept = fragments.filter((fragment) => !(/\s/.test(fragment.trim()) && !fragment.includes('/')));
840
+ if (kept.length === fragments.length) return [line];
841
+ if (kept.length === 0) { dropped.push(line); return []; }
842
+ dropped.push(line);
843
+ return [`${item[1]}${kept.join(' · ')}${line.endsWith('\r') ? '\r' : ''}`];
844
+ });
845
+ return { document: lines.join('\n'), dropped };
846
+ }
847
+
773
848
  /** 인자 검증 — ⛔ 잘못된 값을 «조용히» 기본값으로 바꾸지 않는다(리뷰 should-fix). */
774
849
  /** ⛔ 기본값은 «내부 상수»다 — 아무도 안 넘기는 인자를 계약 표면으로 두지 않는다(리뷰 must-fix). */
775
850
  export const DEFAULT_LIVE_RUN_WINDOW_MINUTES = 30;
@@ -1001,20 +1076,48 @@ export function decideAskPreflight(
1001
1076
  ): AskPreflightDecision {
1002
1077
  const limit = opts.openPrsLimit ?? 200;
1003
1078
  const interruptedRunsLimit = resolveInterruptedRunsLimit(opts.interruptedRunsLimit);
1004
- // ⛔⭐ 「저작 전 예비 검사」 경로 — 골 파일이 «아직 없다». 그때는 ask 힌트를 대상 경로로 쓴다.
1005
- // ⚠️ 골 문서를 읽지 «않는다» — 없는 파일을 읽으려 하면 여기서 던져 저작 자체가 막힌다.
1079
+ // pathsOverride is an independent pre-authoring inspection, even if a stale goal file exists.
1006
1080
  let goalDocument: string | null = null;
1007
1081
  let goalRulersUnknownReason: string | undefined;
1082
+ let goalReadFailed = false;
1083
+ const resolvedGoalFile = opts.declaredPathsRoot?.trim() && !isAbsolute(opts.goalFile)
1084
+ ? resolve(opts.declaredPathsRoot, opts.goalFile)
1085
+ : opts.goalFile;
1008
1086
  if (!opts.pathsOverride) {
1009
1087
  try {
1010
- goalDocument = deps.readGoalDocument(opts.goalFile);
1088
+ goalDocument = deps.readGoalDocument(resolvedGoalFile);
1011
1089
  } catch (error) {
1090
+ goalReadFailed = true;
1012
1091
  goalRulersUnknownReason = `골 문서 판독 불가: ${error instanceof Error ? error.message.split('\n')[0] : String(error)}`;
1013
1092
  }
1014
1093
  } else {
1015
1094
  goalRulersUnknownReason = '저작 전 예비 검사에는 골 문서가 없다';
1016
1095
  }
1017
- const tracedPaths = opts.pathsOverride ?? (goalDocument === null ? [] : deps.tracedPaths(goalDocument));
1096
+ // Resolve against the caller's root, but keep injected virtual goal documents out of
1097
+ // filesystem checks when no authored file exists there.
1098
+ const hasAuthoredGoal = !opts.pathsOverride && opts.goalFile !== '' && goalDocument !== null
1099
+ && existsSync(resolvedGoalFile);
1100
+ const authoredTracedPaths = goalDocument === null ? [] : deps.tracedPaths(goalDocument);
1101
+ const tracedPathsRaw = opts.pathsOverride ?? authoredTracedPaths;
1102
+ const inspectionRoot = goalDocument === null || !hasAuthoredGoal
1103
+ ? undefined
1104
+ : opts.declaredPathsRoot?.trim() || process.cwd();
1105
+ const tracedPaths = inspectionRoot && !opts.pathsOverride
1106
+ ? tracedPathsRaw.filter((path) => tracedPathExists(path, inspectionRoot) || isFileShapedTracedPath(path))
1107
+ : tracedPathsRaw;
1108
+ const declaredNewPaths = goalDocument === null ? new Set<string>() : new Set(declaredNewFilePaths(goalDocument));
1109
+ const invalidTracedPaths: Array<{ path: string; reason: 'occupied-new' | 'not-file' | 'missing' | 'inaccessible' }> = inspectionRoot
1110
+ ? authoredTracedPaths.flatMap<{ path: string; reason: 'occupied-new' | 'not-file' | 'missing' | 'inaccessible' }>((path) => {
1111
+ const occupation = tracedPathOccupation(path, inspectionRoot);
1112
+ if (!isFileShapedTracedPath(path) && occupation !== 'occupied') return [];
1113
+ const isDeclaredNew = declaredNewPaths.has(path.replace(/^\.\//, ''));
1114
+ if (isDeclaredNew) return occupation === 'missing' ? [] : [{ path, reason: occupation === 'occupied' ? 'occupied-new' as const : 'inaccessible' as const }];
1115
+ if (tracedPathExists(path, inspectionRoot)) return [];
1116
+ return [{ path, reason: occupation === 'occupied'
1117
+ ? (tracedPathIsFile(path, inspectionRoot) ? 'inaccessible' as const : 'not-file' as const)
1118
+ : occupation }];
1119
+ })
1120
+ : [];
1018
1121
  let premiseMockedBySignal: number | null = null;
1019
1122
  let invariantFunctionExits: number | null = null;
1020
1123
  if (goalDocument !== null) {
@@ -1197,11 +1300,41 @@ export function decideAskPreflight(
1197
1300
  ...(interruptedRunsUnknownReason ? { interruptedRunsUnknownReason } : {}),
1198
1301
  ...(recentChangesUnknownReason ? { recentChangesUnknownReason } : {}),
1199
1302
  });
1200
- const decisionResult = { ...result, warnings: [...result.warnings, ...askMarkerWarnings], runningRunsConfidenceAppendix: runningRunsConfidence.appendix };
1303
+ const missingTracedPathBlockers: LaunchPreflightBlocker[] = [
1304
+ ...(hasAuthoredGoal && authoredTracedPaths.length === 0 ? [{
1305
+ kind: 'missing-traced-path' as const,
1306
+ name: '최종 골의 TRACED PATHS 경로 0',
1307
+ detail: '정리 후 보존된 traced 경로가 없다 — 실제 파일 또는 유효한 신설: 파일 경로를 골에 선언해야 발사할 수 있다',
1308
+ }] : []),
1309
+ ...invalidTracedPaths.map(({ path, reason }) => ({
1310
+ kind: 'missing-traced-path' as const,
1311
+ name: reason === 'occupied-new' ? `신설 traced path 가 이미 점유됨: ${path}`
1312
+ : reason === 'not-file' ? `traced path 가 파일이 아님: ${path}`
1313
+ : reason === 'inaccessible' ? `traced path 경로 접근 불가: ${path}` : `실재하지 않는 traced path: ${path}`,
1314
+ detail: reason === 'occupied-new'
1315
+ ? `골의 신설: 경로 ${path} 가 이미 파일·디렉터리·링크로 점유돼 있다 — 새 파일 경로를 바로잡아야 한다`
1316
+ : reason === 'not-file'
1317
+ ? `골의 TRACED PATHS 경로 ${path} 가 존재하지만 읽을 수 있는 파일이 아니다 — 파일 경로를 바로잡아야 한다`
1318
+ : reason === 'inaccessible'
1319
+ ? `골의 TRACED PATHS 경로 ${path} 를 검사할 수 없다 — 부모가 파일이거나 접근 권한이 없다. 경로를 바로잡아야 한다`
1320
+ : `골의 TRACED PATHS 에 선언됐지만 ${inspectionRoot} 에 없고 유효한 신설: 목록에도 없다 — 발사 전에 골 경로를 바로잡아야 한다. 신설은 파일 경로로(확장자 포함) 선언한다`,
1321
+ })),
1322
+ ];
1323
+ const goalReadBlockers: LaunchPreflightBlocker[] = goalReadFailed ? [{
1324
+ kind: 'goal-unreadable',
1325
+ name: `골 문서 판독 불가: ${opts.goalFile}`,
1326
+ detail: `${goalRulersUnknownReason} — 최종 골의 traced path 를 확인할 수 없어 발사를 멈춘다`,
1327
+ }] : [];
1328
+ const decisionResult = {
1329
+ ...result,
1330
+ blockers: [...result.blockers, ...missingTracedPathBlockers, ...goalReadBlockers],
1331
+ warnings: [...result.warnings, ...askMarkerWarnings],
1332
+ runningRunsConfidenceAppendix: runningRunsConfidence.appendix,
1333
+ };
1201
1334
  emittedAskMarkerObservations.add(decisionResult);
1202
1335
  return {
1203
1336
  result: decisionResult,
1204
- shouldLaunch: result.blockers.length === 0 || force,
1337
+ shouldLaunch: missingTracedPathBlockers.length === 0 && goalReadBlockers.length === 0 && (result.blockers.length === 0 || force),
1205
1338
  plannedBranchResolution,
1206
1339
  };
1207
1340
  }
@@ -23,6 +23,7 @@ import { parentUnlandedProgressLine } from './run-supervisor.js';
23
23
  import { mkdirSync, openSync, writeFileSync, closeSync, renameSync, unlinkSync } from 'node:fs';
24
24
  import { join, resolve } from 'node:path';
25
25
  import { goalPathFromArgv, launchRegistryDir, removeLaunchBinding, writeLaunchBinding } from '../harness/launch-registry.js';
26
+ import { psProcessStartMs } from '../harness/harness-stop.js';
26
27
  import { adoptRunCard } from '../task-agent/run-adopt.js';
27
28
  import { availableParallelism, cpus } from 'node:os';
28
29
  import { selfDevRunsDir } from './run-store.js';
@@ -912,12 +913,15 @@ async function deleteLabelledJobs(runId: string, selectedTargets: readonly { con
912
913
  * take minutes before that, and a stop in that window found «no process» (10-01 · three runs). */
913
914
  export function writeRunPidRecord(runId: string, runsDir: string = selfDevRunsDir(), registryDir: string | null = launchRegistryDir()): string {
914
915
  const pidPath = join(runsDir, runId, 'pid.json');
916
+ // The observer reads `ps -o lstart=`, whose precision is one second. Record that same
917
+ // process birth value when available so a launch near a second boundary still matches.
918
+ const startedAt = psProcessStartMs(process.pid) ?? Date.now() - Math.floor(process.uptime() * 1000);
915
919
  mkdirSync(join(runsDir, runId), { recursive: true });
916
920
  const temporary = `${pidPath}.${process.pid}.${randomUUID()}.tmp`;
917
921
  const fd = openSync(temporary, 'wx', 0o600);
918
922
  try {
919
923
  try {
920
- writeFileSync(fd, JSON.stringify({ pid: process.pid, startedAt: Date.now() - Math.floor(process.uptime() * 1000), argv0: process.argv0 }));
924
+ writeFileSync(fd, JSON.stringify({ pid: process.pid, startedAt, argv0: process.argv0 }));
921
925
  } finally { closeSync(fd); }
922
926
  renameSync(temporary, pidPath);
923
927
  } catch (error) { unlinkSync(temporary); throw error; }
@@ -925,7 +929,7 @@ export function writeRunPidRecord(runId: string, runsDir: string = selfDevRunsDi
925
929
  if (registryDir !== null) {
926
930
  const cwd = process.cwd();
927
931
  const goalPath = goalPathFromArgv(process.argv, cwd);
928
- writeLaunchBinding(runId, { pid: process.pid, startedAt: Date.now() - Math.floor(process.uptime() * 1000), argv0: process.argv0,
932
+ writeLaunchBinding(runId, { pid: process.pid, startedAt, argv0: process.argv0,
929
933
  cwd, ...(goalPath ? { goalPath } : {}), runsDir: resolve(runsDir),
930
934
  ...(process.env.ELANOUS_POD_NAME ? { podName: process.env.ELANOUS_POD_NAME } : {}),
931
935
  ...(process.env.ELANOUS_TASK_CARD_ID ? { taskCardId: process.env.ELANOUS_TASK_CARD_ID } : {}) }, registryDir);
@@ -9,7 +9,8 @@ import { recordSelfDevRunSupervisorStop, selfDevRunsDir } from './run-store.js';
9
9
  import { debug } from '../debug/log.js';
10
10
  import { sweepFrozenMerges } from '../self-implement/frozen-merges.js';
11
11
  import { routeSupervisorVerdict } from './supervisor-verdict-edges.js';
12
- import { recordTaskAgentShadowMove, type TaskAgentShadowInput } from '../task-agent/shadow.js';
12
+ import { recordTaskAgentShadowMove, runCardFor, type TaskAgentShadowInput } from '../task-agent/shadow.js';
13
+ import { cardPrNumber, taskAgentStatePath } from '../task-agent/task-hand.js';
13
14
 
14
15
  export interface SupervisorRound {
15
16
  round: number;
@@ -641,6 +642,12 @@ export interface SuperviseRunOptions {
641
642
  * `false` 면 끈다. 던져도 런의 결말은 바뀌지 않는다(fail-soft).
642
643
  */
643
644
  taskAgentShadow?: ((input: TaskAgentShadowInput) => unknown) | false;
645
+ /** TA-LAND-AFTER-REVIEW — false disables the hook; function replaces only the final rejudge for isolated tests. */
646
+ taskAgentRejudge?: ((cardId: string, statePath: string) => Promise<unknown>) | false;
647
+ /** Test-only external I/O for the real completion rejudge. The decision and shadow gate remain production code. */
648
+ taskAgentRejudgeExternal?: Parameters<typeof import('../task-agent/rejudge-on-head.js').rejudgeAsyncResultCard>[2];
649
+ /** Isolated state ledger for the terminal rejudge test; production uses the active instance. */
650
+ taskAgentStatePath?: string;
644
651
  observeDeliverables?: () => Promise<{
645
652
  readonly deployFindings: ReadonlyMap<string, { target: string; findings?: readonly DeployVerifyFinding[] | readonly unknown[] }>;
646
653
  readonly unmeasured: readonly unknown[];
@@ -658,6 +665,23 @@ function resolveSupervisorStopStorage(runStore: SuperviseRunOptions['runStore'])
658
665
  return { path: selfDevRunsDir(), source: 'process-default' };
659
666
  }
660
667
 
668
+ /** The supervisor's terminal hook, limited to a uniquely run-bound TA land card. */
669
+ export async function rejudgeTerminalTaskCard(runId: string | null, deps: {
670
+ statePath?: string;
671
+ env?: NodeJS.ProcessEnv;
672
+ rejudgeCard?: (cardId: string, statePath: string) => Promise<unknown>;
673
+ external?: Parameters<typeof import('../task-agent/rejudge-on-head.js').rejudgeAsyncResultCard>[2];
674
+ } = {}): Promise<void> {
675
+ const statePath = deps.statePath ?? taskAgentStatePath();
676
+ const card = runCardFor(runId, { statePath, ...(deps.env ? { env: deps.env } : {}) });
677
+ if (!card || card.status !== 'launched' || card.taLand !== true || cardPrNumber(card) === undefined) return;
678
+ const { rejudgeAsyncResultCard } = await import('../task-agent/rejudge-on-head.js');
679
+ const result = await (deps.rejudgeCard ?? ((id, path) => rejudgeAsyncResultCard(id, path, deps.external)))(card.id, statePath);
680
+ const { requireSuccessfulRejudge } = await import('../task-agent/rejudge-on-head.js');
681
+ requireSuccessfulRejudge(result);
682
+ try { debug.log('task-agent', 'terminal-rejudge', { runId, card: card.id }); } catch { /* fail-soft */ }
683
+ }
684
+
661
685
  export async function superviseRun(opts: SuperviseRunOptions): Promise<SupervisorJobResult[]> {
662
686
  let results = [...opts.initial];
663
687
  let history: SupervisorRound[] = [];
@@ -808,6 +832,16 @@ export async function superviseRun(opts: SuperviseRunOptions): Promise<Superviso
808
832
  } catch (error) {
809
833
  try { debug.log('task-agent', 'shadow-move-failed', { runId, stopReason: decision.stopReason, error: String(error) }); } catch { /* fail-open */ }
810
834
  }
835
+ if (opts.taskAgentRejudge !== false) {
836
+ const rejudgeOptions = { ...(opts.taskAgentStatePath ? { statePath: opts.taskAgentStatePath } : {}), ...(opts.taskAgentRejudge ? { rejudgeCard: opts.taskAgentRejudge } : {}), ...(opts.taskAgentRejudgeExternal ? { external: opts.taskAgentRejudgeExternal } : {}) };
837
+ try { await rejudgeTerminalTaskCard(runId, rejudgeOptions); }
838
+ catch (error) {
839
+ try { await rejudgeTerminalTaskCard(runId, rejudgeOptions); }
840
+ catch (retryError) {
841
+ try { debug.log('task-agent', 'terminal-rejudge-failed', { runId, stopReason: decision.stopReason, error: String(retryError), firstError: String(error), attempts: 2 }); } catch { /* fail-open */ }
842
+ }
843
+ }
844
+ }
811
845
  }
812
846
  return results;
813
847
  }
@@ -426,6 +426,7 @@ function changedTestCountNote(
426
426
 
427
427
  const IMPORTER_GREP_PATHS = ['*.test.ts', '*.test.tsx', '*.test.mts', '*.test.mtsx', '*.test.cts', '*.test.ctsx', '*.test.js', '*.test.jsx', '*.test.mjs', '*.test.mjsx', '*.test.cjs', '*.test.cjsx'];
428
428
  const SOURCE_PATH_RE = /^src\/.+\.(?:[cm]?[jt]sx?)$/;
429
+ const SCRIPT_SOURCE_PATH_RE = /^scripts\/.+\.(?:[cm]?[jt]sx?)$/;
429
430
  const SOURCE_FILE_RE = /\.(?:[cm]?[jt]sx?)$/;
430
431
 
431
432
  function buildGateImporterTestIndex(
@@ -433,7 +434,7 @@ function buildGateImporterTestIndex(
433
434
  cwd: string,
434
435
  runCommand: SelfGateCliDeps['runCommand'],
435
436
  ): ImporterTestIndex | null | { readonly lookupFailed: string } {
436
- const sources = changed.filter((path) => SOURCE_PATH_RE.test(path) && !isTestPath(path));
437
+ const sources = changed.filter((path) => (SOURCE_PATH_RE.test(path) || SCRIPT_SOURCE_PATH_RE.test(path)) && !isTestPath(path));
437
438
  if (sources.length === 0) return null;
438
439
  const result = (runCommand ?? defaultRunCommand)('git', ['grep', '-l', '-e', '', '--', ...IMPORTER_GREP_PATHS], cwd);
439
440
  if ((result.status !== 0 && result.status !== 1) || result.error) {
@@ -596,7 +597,10 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
596
597
  if (options.shards !== undefined && (!Number.isSafeInteger(options.shards) || options.shards < 1)) throw new Error('invalid --shards count');
597
598
  const selection = (deps.changedFiles ?? ((dir, input) => defaultChangedFiles(dir, input, deps.runCommand)))(cwd, options);
598
599
  const exists = deps.exists ?? ((path) => existsSync(`${cwd}/${path}`));
599
- const importerIndex = buildGateImporterTestIndex(selection.files, cwd, deps.runCommand);
600
+ // Caller: src/release-loop/light-rc.ts runLightRc → runSelfGateCli(dir, { ..., includeImporterTests: true }).
601
+ // scripts/ joins the existing importer index only for that opt-in path.
602
+ const importerIndex = buildGateImporterTestIndex(options.includeImporterTests
603
+ ? selection.files : selection.files.filter((path) => !SCRIPT_SOURCE_PATH_RE.test(path)), cwd, deps.runCommand);
600
604
  const lookupFailed = importerIndex && 'lookupFailed' in importerIndex ? importerIndex.lookupFailed : undefined;
601
605
  const availableIndex = importerIndex && !('lookupFailed' in importerIndex) ? importerIndex : undefined;
602
606
  const routeIndex = availableIndex?.testPaths
@@ -51,6 +51,9 @@ import { hasGoalStepCodeName } from './goal-step-code-name.js';
51
51
  import { targetScopedGoalText } from './goal-text-path-scope.js';
52
52
  import { parseReleaseNoteSection } from '../release-loop/release-note.js';
53
53
  import { declaredNewFilePaths } from './goal-new-paths.js';
54
+ import { classifyGoalCommandExecution, type GoalCommandExecutionResult } from './goal-command-execution.js';
55
+ export { classifyGoalCommandExecution } from './goal-command-execution.js';
56
+ export type { GoalCommandExecutionKind, GoalCommandExecutionResult, GoalCommandExecutionClassification } from './goal-command-execution.js';
54
57
 
55
58
  // ⭐ `situation`/`complication` 은 **선택**이다 — 안 오면 `scqaNarrative` 가 종전 문면을 쓴다.
56
59
  // ⛔ 필수로 만들지 않는다: 그러면 인핸싱이 폴백으로 떨어진 저작(LLM 실패·`disabled`)이 전부 죽는다.
@@ -439,33 +442,6 @@ export function canonicalStructureFinding(
439
442
  return finding;
440
443
  }
441
444
 
442
- export type GoalCommandExecutionKind = 'success' | 'missing-command' | 'argument-problem' | 'execution-failure';
443
-
444
- export interface GoalCommandExecutionResult {
445
- readonly status: number | null;
446
- readonly stderr: string;
447
- }
448
-
449
- export interface GoalCommandExecutionClassification {
450
- readonly kind: GoalCommandExecutionKind;
451
- readonly log: string;
452
- }
453
-
454
- /** Classifies an already-observed Commander result without executing a command. */
455
- export function classifyGoalCommandExecution(result: GoalCommandExecutionResult): GoalCommandExecutionClassification {
456
- // A clean exit is authoritative: stderr text from a successful command is not a failure signal.
457
- if (result.status === 0) return { kind: 'success', log: 'command completed successfully' };
458
-
459
- const stderr = result.stderr;
460
- if (stderr.includes('unknown command') || stderr.includes('unknown option')) {
461
- return { kind: 'missing-command', log: 'command is not recognized; this may be not yet implemented rather than a defect' };
462
- }
463
- if (stderr.includes('missing required argument') || stderr.includes('required option')) {
464
- return { kind: 'argument-problem', log: 'command requires a missing argument or option' };
465
- }
466
- return { kind: 'execution-failure', log: `command execution failed with code ${String(result.status)}` };
467
- }
468
-
469
445
  /** Optional repository-bounded source reader. Legacy null means the referenced path is missing. */
470
446
  export interface GoalFileLintDeps {
471
447
  readReferencedFile?: (repositoryRelativePath: string) => string | null | ReferencedFileReadResult;
@@ -1725,7 +1701,8 @@ export function lintGoalFile(document: string, branch: string, deps: GoalFileLin
1725
1701
  }
1726
1702
  if (deps.readReferencedFile) {
1727
1703
  // AUTHOR-NEW-FILE-PATHS — 문서가 «새로 만들 파일»로 선언한 경로. 실행 쪽 범위 검사와 같은 줄을 읽는다.
1728
- const declaredNew = new Set(declaredNewFilePaths(document));
1704
+ // 저작과 실행은 같은 Original ask 범위만 선언으로 인정한다. 문서의 다른 절은 근거일 뿐이다.
1705
+ const declaredNew = new Set(declaredNewFilePaths(extractVerbatimOriginalAsk(document)?.ask ?? ''));
1729
1706
  for (const reference of tracedPathReferences(document)) {
1730
1707
  const result = deps.readReferencedFile(reference.path);
1731
1708
  const normalized = typeof result === 'string' ? { kind: 'ok' as const, contents: result }
@@ -0,0 +1,26 @@
1
+ export type GoalCommandExecutionKind = 'success' | 'missing-command' | 'argument-problem' | 'execution-failure';
2
+
3
+ export interface GoalCommandExecutionResult {
4
+ readonly status: number | null;
5
+ readonly stderr: string;
6
+ }
7
+
8
+ export interface GoalCommandExecutionClassification {
9
+ readonly kind: GoalCommandExecutionKind;
10
+ readonly log: string;
11
+ }
12
+
13
+ /** Classifies an already-observed Commander result without executing a command. */
14
+ export function classifyGoalCommandExecution(result: GoalCommandExecutionResult): GoalCommandExecutionClassification {
15
+ // A clean exit is authoritative: stderr text from a successful command is not a failure signal.
16
+ if (result.status === 0) return { kind: 'success', log: 'command completed successfully' };
17
+
18
+ const stderr = result.stderr;
19
+ if (stderr.includes('unknown command') || stderr.includes('unknown option')) {
20
+ return { kind: 'missing-command', log: 'command is not recognized; this may be not yet implemented rather than a defect' };
21
+ }
22
+ if (stderr.includes('missing required argument') || stderr.includes('required option')) {
23
+ return { kind: 'argument-problem', log: 'command requires a missing argument or option' };
24
+ }
25
+ return { kind: 'execution-failure', log: `command execution failed with code ${String(result.status)}` };
26
+ }
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * ⭐ AUTHOR-NEW-FILE-PATHS — 저작 골 문서가 «새로 만들 파일»로 선언한 경로.
3
3
  *
4
- * 저작(TRACED PATHS 존재 검사)과 실행(orchestrator 범위 검사)이 **같은 줄**을 읽어야
5
- * 저작 통과·실행 보류가 다시 갈리지 않는다(TC 13:50). 줄 인식은 실행 쪽
6
- * src/self-implement/orchestrator.ts 의 DECLARED_NEW_FILES_LINE 와 같은 꼴을 쓴다.
4
+ * 저작(TRACED PATHS 존재 검사)과 실행(orchestrator 범위 검사)이 **같은 함수**로
5
+ * 신설 경로를 읽어야 저작 통과·실행 보류가 다시 갈리지 않는다(TC 13:50).
7
6
  */
8
7
 
9
8
  // SCOPE-NEW-FILES-LINE — 칸 문면은 새로 만들 파일을 «신설:» 줄로 따로 적는다(OP D칸 규칙).
10
- // orchestrator.ts 의 DECLARED_NEW_FILES_LINE 와 같은 정규식이다(한쪽만 읽으면 저작과 실행이 갈린다).
9
+ // 저작과 실행이 이 함수를 공유한다(한쪽만 읽으면 저작과 실행이 갈린다).
11
10
  export const DECLARED_NEW_FILES_LINE = /^\s*(?:신설|new\s*files?)\s*[::]\s*(.*)$/i;
12
11
 
13
12
  /** 목록 기호 «- »·굵게 «**» 는 장식이다 — 그것만 벗기고 같은 정규식으로 문는다. */
@@ -32,7 +31,7 @@ export function declaredNewFilePaths(document: string): string[] {
32
31
  const match = DECLARED_NEW_FILES_LINE.exec(bareDeclarationLine(rawLine));
33
32
  if (!match) continue;
34
33
  for (const fragment of match[1]!.split(/[·,]/)) {
35
- // 실행 쪽 범위 검사(parseDeclaredTargetPathsFromAsk)와 같은 정규화 — 등 코드, ./ 접두.
34
+ // 저작·실행 공통 정규화 — 등 코드, ./ 접두.
36
35
  const piece = fragment.trim().replace(/^`+|`+$/g, '').replace(/\\/g, '/').replace(/^\.\//, '');
37
36
  if (!isRepositoryFileCandidate(piece)) continue;
38
37
  paths.push(piece);