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
@@ -18,6 +18,22 @@ function observeGoalAuthor(
18
18
  debug.log(category === 'goal-author' ? 'harness.author' : 'harness.author.clarify', event, data);
19
19
  }
20
20
  import { getUserConfig } from '../user-config.js';
21
+
22
+ export type GoalAuthorGrade = 'full' | 'lite';
23
+ export type GoalAuthorGradeSource = 'flag' | 'env' | 'config' | 'default';
24
+ export interface GoalAuthorGradeSelection { grade: GoalAuthorGrade; source: GoalAuthorGradeSource }
25
+
26
+ /**
27
+ * Precedence flag > env (`ELANOUS_AUTHOR_GRADE`) > config > default(full). Malformed env or
28
+ * configuration values are ignored and keep the full pipeline. The env seam lets the Pod carry the
29
+ * grade without a CLI flag an older in-Pod elanous would reject (AUTHOR-LITE2-POD).
30
+ */
31
+ export function resolveGoalAuthorGrade(flag?: GoalAuthorGrade, configured?: unknown, env: unknown = process.env.ELANOUS_AUTHOR_GRADE): GoalAuthorGradeSelection {
32
+ if (flag !== undefined) return { grade: flag, source: 'flag' };
33
+ if (env === 'lite' || env === 'full') return { grade: env, source: 'env' };
34
+ if (configured === 'lite' || configured === 'full') return { grade: configured, source: 'config' };
35
+ return { grade: 'full', source: 'default' };
36
+ }
21
37
  import type { ObserveOnlySource } from './observe-only.js';
22
38
  import type { Question } from '../ask-user-question/types.js';
23
39
  import { requestTestScenario as defaultRequestTestScenario, type TestScenarioRequestInput, type TestScenarioRequestResult } from './test-scenario-request.js';
@@ -45,6 +61,7 @@ type GoalEnhancement = Omit<Pick<EnhanceResult, 'original' | 'checklist' | 'verb
45
61
  export interface GoalAuthorDeps {
46
62
  ground: (ask: string, groundingDeps?: GoalAuthorGroundingDeps) => Promise<CodebaseGrounding>;
47
63
  persistentGrounding?: GoalAuthorGroundingDeps;
64
+ authorGrade?: GoalAuthorGradeSelection;
48
65
  enhance: (ask: string, opts?: EnhanceOpts) => Promise<GoalEnhancement>;
49
66
  /** Additive enhancer options supplied by the file-writing entrypoint. */
50
67
  enhanceOpts?: EnhanceOpts;
@@ -4925,6 +4942,17 @@ async function authorGoalWithSupersededRootIntent(
4925
4942
  }
4926
4943
  // ⭐ 이 저작 «한 번»의 id. goalId 보다 «먼저» 있어야 ground·enhance 의 소요를 그 골에 붙일 수 있다.
4927
4944
  const authorRunId = generateGoalId();
4945
+ const authorGrade = deps.authorGrade ?? resolveGoalAuthorGrade(undefined, getUserConfig().harness?.authorGrade);
4946
+ const lite = authorGrade.grade === 'lite';
4947
+ // lite 는 판정선을 남기는 대신 단계를 덜 한다 — 요청에 반증 줄이 하나도 없으면 판정선 없는 골이 나가므로 시작 전에 멈춘다.
4948
+ if (lite && !ask.split(/\r?\n/u).some((line) => /판정선|판정 신호|반증|decision signal/iu.test(line))) {
4949
+ try { observeGoalAuthor('goal-author', 'author-grade-refused', { authorRunId, reason: 'lite-without-falsifiable-signal' }); }
4950
+ catch { /* observation is fail-soft */ }
4951
+ throw new Error('lite authoring needs a falsifiable signal line (판정선/반증) in the ask — add one or use --author-grade full');
4952
+ }
4953
+ const skippedPhases = lite ? ['adversarial-review', 'goal-steps-decomposed', 'persistent-grounding'] : [];
4954
+ try { observeGoalAuthor('goal-author', 'author-grade', { authorRunId, grade: authorGrade.grade, source: authorGrade.source, skippedPhases }); }
4955
+ catch { /* observation is fail-soft */ }
4928
4956
  const authorStartedAt = Date.now();
4929
4957
  const enhanceTokens: GoalAuthorTokens = { inputTokens: null, outputTokens: null };
4930
4958
  const onEnhanceUsage = (usage: LLMUsage): void => {
@@ -4934,18 +4962,20 @@ async function authorGoalWithSupersededRootIntent(
4934
4962
  };
4935
4963
  let facts: CodebaseGrounding | null = null;
4936
4964
  let groundingError = false;
4937
- const persistentGrounding = resolveGoalAuthorPersistentGrounding('persistentGrounding' in deps ? deps.persistentGrounding : undefined);
4938
- const groundStartedAt = startGoalAuthorPhase('ground', authorRunId, deps.onProgress);
4939
- try {
4940
- facts = await deps.ground(ask, persistentGrounding.deps);
4941
- } catch {
4942
- groundingError = true;
4943
- } finally {
4944
- endGoalAuthorPhase('ground', groundStartedAt, authorRunId, deps.onProgress);
4945
- observeGoalAuthorPersistentGroundingResult(
4946
- persistentGrounding.decision,
4947
- goalAuthorPersistentGroundingMetrics(facts, groundingError, authorRunId),
4948
- );
4965
+ if (!lite) {
4966
+ const persistentGrounding = resolveGoalAuthorPersistentGrounding('persistentGrounding' in deps ? deps.persistentGrounding : undefined);
4967
+ const groundStartedAt = startGoalAuthorPhase('ground', authorRunId, deps.onProgress);
4968
+ try {
4969
+ facts = await deps.ground(ask, persistentGrounding.deps);
4970
+ } catch {
4971
+ groundingError = true;
4972
+ } finally {
4973
+ endGoalAuthorPhase('ground', groundStartedAt, authorRunId, deps.onProgress);
4974
+ observeGoalAuthorPersistentGroundingResult(
4975
+ persistentGrounding.decision,
4976
+ goalAuthorPersistentGroundingMetrics(facts, groundingError, authorRunId),
4977
+ );
4978
+ }
4949
4979
  }
4950
4980
 
4951
4981
  const enhanceStartedAt = startGoalAuthorPhase('enhance', authorRunId, deps.onProgress);
@@ -4989,6 +5019,57 @@ async function authorGoalWithSupersededRootIntent(
4989
5019
  }
4990
5020
  enhancement = { ...enhancement, original: ask };
4991
5021
 
5022
+ if (lite) {
5023
+ const goalType = deps.goalType ?? 'implement';
5024
+ const goalId = deps.goalId ?? generateGoalId();
5025
+ const signal = decisionSignalSection(null, ask, enhancement.decisionSignal);
5026
+ const falsifiableSignal = signal.filled > 0 ? signal.lines : [
5027
+ ...signal.lines,
5028
+ ...ask.split(/\r?\n/u).filter((line) => /판정선|판정 신호|반증|decision signal/iu.test(line) && !signal.lines.some((rendered) => rendered.includes(line.trim()))),
5029
+ ];
5030
+ const targetPaths = [...new Set(declaredTargetPaths(ask))];
5031
+ const checklistIds = [...new Set(Array.from(ask.matchAll(/\b[A-Z][A-Z0-9]+-[A-Z0-9]+\b/g), (match) => match[0]))];
5032
+ const askFile = deps.askFile === undefined ? undefined : validateAskFile(deps.askFile);
5033
+ const inheritedRootIntent = deps.parentDocument === undefined ? null : parseRootIntent(deps.parentDocument);
5034
+ const rootIntent = validateRootIntent(deps.supersessionRootIntent ?? inheritedRootIntent ?? deps.rootIntent ?? goalSummary(ask));
5035
+ const document = [
5036
+ deps.goalTitle ?? goalSummary(ask),
5037
+ `- GoalId: ${goalId}`,
5038
+ `- RootIntent: ${rootIntent}`,
5039
+ `- GoalType: ${goalType}`,
5040
+ ...(askFile === undefined ? [] : [`- AskFile: ${askFile}`]),
5041
+ '',
5042
+ '## PROBLEM',
5043
+ `- 왜: ${enhancement.complication ?? goalSummary(ask)}`,
5044
+ '',
5045
+ '## WHAT TO BUILD',
5046
+ `- 무엇: ${enhancement.situation ?? goalSummary(ask)}`,
5047
+ ORIGINAL_ASK_MARKER,
5048
+ fenceFor(ask), ask, fenceFor(ask),
5049
+ '',
5050
+ '## ACCEPTANCE CRITERIA',
5051
+ ...enhancement.checklist.map(requestedCriterionLine),
5052
+ '',
5053
+ '## REQUIRED EVIDENCE',
5054
+ '- [requested] Verify the decision signal and the verbatim ask.',
5055
+ '',
5056
+ ...(goalType === 'implement' ? ['## TRACED PATHS', ...(targetPaths.length ? targetPaths.map((path) => `- ${path}`) : ['- Target paths: not declared in the ask.']), ''] : []),
5057
+ '## SCOPE BOUNDARY',
5058
+ `- 대상 경로: ${targetPaths.length ? targetPaths.join(' · ') : 'not declared'}`,
5059
+ `- 칸 id: ${checklistIds.length ? checklistIds.join(' · ') : 'not declared'}`,
5060
+ '',
5061
+ '## 답하지 못하는 것',
5062
+ '- No persistent grounding was performed in lite authoring.',
5063
+ '',
5064
+ '## 불변식',
5065
+ '- No persistent grounding was performed in lite authoring.',
5066
+ '',
5067
+ '## 판정 신호',
5068
+ ...falsifiableSignal,
5069
+ ].join('\n');
5070
+ return { document, facts: null, grounded: false, authorRunId };
5071
+ }
5072
+
4992
5073
  observeGoalAuthor('goal-author', 'constant-instruction-placement', {
4993
5074
  authorRunId,
4994
5075
  lines: CONSTANT_DOCUMENT_INSTRUCTION_POLICY.length,
@@ -11,9 +11,13 @@ import {
11
11
  writePendingQuestion,
12
12
  } from '../ask-user-question/pending-questions.js';
13
13
  import { debug } from '../debug/log.js';
14
+ import { writeGoalDocumentAtomic } from './goal-document-write.js';
14
15
  import {
15
16
  applyClarificationReply,
16
17
  parseGoalDocumentClarifications,
18
+ selfAnswerGoalDocumentClarifications,
19
+ type GoalClarificationSelfResolver,
20
+ type GoalClarificationSelfAnswerPath,
17
21
  } from './goal-author-clarification.js';
18
22
  import type { HitlDelivery } from '../hitl/types.js';
19
23
 
@@ -49,6 +53,8 @@ export interface GoalClarificationEscalationInput {
49
53
  resolveOriginDelivery?: (sessionId: string) => HitlDelivery | undefined;
50
54
  /** Non-blocking terminal fallback for a non-TUI delivery with no installed resolver. */
51
55
  fallback?: (message: string) => void;
56
+ selfResolveClarification?: GoalClarificationSelfResolver;
57
+ selfAnswerPath?: GoalClarificationSelfAnswerPath;
52
58
  /** Fail-soft pending persistence for terminal fallbacks; injectable for focused tests. */
53
59
  pendingQuestionPersistence?: {
54
60
  create: typeof createPendingQuestion;
@@ -404,6 +410,7 @@ export async function escalateGoalDocumentClarifications(
404
410
  const dispatchDelivery = delivery ?? 'modal';
405
411
  const recordedDelivery = input.resolvedDelivery ?? dispatchDelivery;
406
412
  let answersWritten = 0;
413
+ let selfAnswered = 0;
407
414
  let questionIds: string[] = [];
408
415
  const observeEscalation = (event: GoalClarificationObservationEvent, data: Record<string, unknown>): void => {
409
416
  writeEscalationObservation(event, { ...data, deliverySource, answersWritten, questionIds });
@@ -430,6 +437,35 @@ export async function escalateGoalDocumentClarifications(
430
437
  return result;
431
438
  }
432
439
 
440
+ if (input.selfAnswerPath) {
441
+ try {
442
+ const resolution = await selfAnswerGoalDocumentClarifications(document, {
443
+ path: input.selfAnswerPath,
444
+ ask: document,
445
+ resolver: input.selfResolveClarification,
446
+ });
447
+ if (resolution.newlyAnswered > 0) {
448
+ writeGoalDocumentAtomic(input.goalFile, resolution.document);
449
+ answersWritten += resolution.newlyAnswered;
450
+ selfAnswered += resolution.newlyAnswered;
451
+ document = resolution.document;
452
+ clarifications = parseGoalDocumentClarifications(document);
453
+ unanswered = clarifications.filter((clarification) => !clarification.answered);
454
+ deliveryIds = mapClarificationDeliveryIds(clarifications);
455
+ questionIds = [...new Set(unanswered.map((clarification) => clarification.questionId))];
456
+ }
457
+ if (unanswered.length === 0) {
458
+ const result = { unanswered: 0, escalated: 0, delivery: recordedDelivery, outcome: 'skipped' as const, answeredBy: 'agent' as const };
459
+ observeEscalation(result.outcome, { goalFile: input.goalFile, ...result, reason: 'self-answered' });
460
+ return result;
461
+ }
462
+ } catch (error) {
463
+ debug.log(GOAL_CLARIFICATION_ESCALATION_CATEGORY, 'self-answer-failed', {
464
+ path: input.selfAnswerPath, reason: error instanceof Error ? error.message : String(error),
465
+ });
466
+ }
467
+ }
468
+
433
469
  const dispatch = input.dispatch ?? dispatchAskUserQuestion;
434
470
  const pendingQuestionPersistence = input.pendingQuestionPersistence ?? {
435
471
  create: createPendingQuestion,
@@ -444,7 +480,7 @@ export async function escalateGoalDocumentClarifications(
444
480
  // ⛔⭐⭐⭐ 여기서부터의 조기 반환·폴백은 이 «누적값»을 쓴다 — 'none' 을 하드코딩하면
445
481
  // 앞 배치가 답했는데 뒤 배치가 취소·실패했을 때 「아무도 안 답했다」는 «거짓»이 된다
446
482
  // (무인 리뷰 R2 must-fix · 실측 형태: 관측이 거짓말한다).
447
- const seenProvenance = { explicit: new Set<'human' | 'agent'>(), unmarkedAnswer: false };
483
+ const seenProvenance = { explicit: new Set<'human' | 'agent'>(selfAnswered > 0 ? ['agent' as const] : []), unmarkedAnswer: false };
448
484
  const aggregate = (): GoalClarificationAnsweredBy => {
449
485
  const { explicit, unmarkedAnswer } = seenProvenance;
450
486
  // ⭐ 표시 없는 답도 «한 출처»로 센다 — legacy 경로는 사람이다(레포 안 리졸버는 이제 전부 명시한다).
@@ -0,0 +1,28 @@
1
+ /** Atomic goal-document replacement for self-answered clarifications (POD-SELF-ANSWER). */
2
+ import { chmodSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
3
+
4
+ export interface GoalDocumentWriteIo {
5
+ writeFileSync: (path: string, data: string, options?: { mode?: number }) => void;
6
+ renameSync: (from: string, to: string) => void;
7
+ rmSync: (path: string, options: { force: boolean }) => void;
8
+ statSync: (path: string) => { mode: number };
9
+ chmodSync: (path: string, mode: number) => void;
10
+ }
11
+
12
+ const realIo: GoalDocumentWriteIo = { writeFileSync, renameSync, rmSync, statSync, chmodSync };
13
+
14
+ /** Replace a goal document atomically (temp file next to it + rename) so a failed write never leaves a torn goal.
15
+ * The existing file's permission bits are kept, so a 0600 goal stays 0600 after replacement. */
16
+ export function writeGoalDocumentAtomic(path: string, document: string, io: GoalDocumentWriteIo = realIo): void {
17
+ let mode: number | undefined;
18
+ try { mode = io.statSync(path).mode & 0o777; } catch { mode = undefined; }
19
+ const temporary = `${path}.self-answer-${process.pid}-${Date.now()}.tmp`;
20
+ try {
21
+ io.writeFileSync(temporary, document, mode === undefined ? undefined : { mode });
22
+ if (mode !== undefined) io.chmodSync(temporary, mode);
23
+ io.renameSync(temporary, path);
24
+ } catch (error) {
25
+ try { io.rmSync(temporary, { force: true }); } catch { /* best effort */ }
26
+ throw error;
27
+ }
28
+ }
@@ -68,6 +68,10 @@ function liveHealRuns(root: string): Set<string> {
68
68
  /** A test process must never start the real heal loop — it spawns external grounding (10-05 load 150 incident). */
69
69
  export async function startHealLoop(file: string, options: HealLoopOptions, env: NodeJS.ProcessEnv = process.env,
70
70
  run: StartHealLoop = async (graph, args) => (await import('../graph-runner/runner.js')).runGraph(graph, args)): Promise<unknown> {
71
+ if (env.ELANOUS_POD_NAME) {
72
+ debug.log('heal.intake', 'skipped-in-pod', { ref: options.input.failureEvent.ref });
73
+ return undefined;
74
+ }
71
75
  if (env.NODE_ENV === 'test' || env.ELANOUS_TEST_HOME) {
72
76
  debug.log('heal.intake', 'loop-start-skipped-test', { runId: options.runId });
73
77
  return undefined;
@@ -30,6 +30,8 @@ export type HostRegateDeps = {
30
30
  acquire?: (repoRoot: string) => Promise<() => void>;
31
31
  log?: (event: 'passed' | 'failed' | 'unmeasured' | 'base-raced', data: Record<string, unknown>) => void;
32
32
  freezeRoot?: string;
33
+ /** Test seam for the operational freeze authority; production always uses the host root. */
34
+ prodFreezeRoot?: string;
33
35
  runExposeGate?: typeof runExposeGate;
34
36
  syncMergedChecklist?: typeof import('../release-loop/merged-pr-checklist.js').syncMergedPrChecklist;
35
37
  };
@@ -241,7 +243,7 @@ export async function runHostRegate(input: HostRegateInput, deps: HostRegateDeps
241
243
  // --match-head-commit pins the head. gh has no base pin, so the base was re-read just
242
244
  // above; the seconds between that read and the merge are checked after the fact below.
243
245
  if (input.verifyOnly) return result('passed');
244
- const landing = admitLandingMerge({ prNumber: input.prNumber, headCommit: input.headCommit, repoRoot: input.repoRoot, ...(input.goalFile ? { goalFile: input.goalFile } : {}) }, deps.freezeRoot, {}, input.resumed ? undefined : { prNumber: input.prNumber, repoRoot: input.repoRoot, headCommit: input.headCommit });
246
+ const landing = admitLandingMerge({ prNumber: input.prNumber, headCommit: input.headCommit, repoRoot: input.repoRoot, ...(input.goalFile ? { goalFile: input.goalFile } : {}) }, deps.freezeRoot, {}, input.resumed ? undefined : { prNumber: input.prNumber, repoRoot: input.repoRoot, headCommit: input.headCommit }, { prodFreezeRoot: deps.prodFreezeRoot });
245
247
  if (landing.kind !== 'merge') {
246
248
  debug.log('harness.merge', 'frozen', { pr: input.prNumber, ...(landing.kind === 'held' ? { reason: landing.freeze.reason, until: landing.freeze.until } : { resumedElsewhere: true }) });
247
249
  return { passed: true, failures: [], os: process.platform, status: 'frozen' };
@@ -129,12 +129,33 @@ export async function runL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}
129
129
  if (changed.some((file) => file.startsWith('-') || file.startsWith('/') || file.split('/').includes('..') || file.includes('\\'))) throw new Error('unsafe changed-file path');
130
130
  verdict.tests = files.filter((file) => /(?:^|\/)[^/]+\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/.test(file));
131
131
  const merge = execute('git', ['-c', 'user.name=elanous shadow', '-c', 'user.email=shadow@localhost', 'merge', '--no-ff', '--no-edit', item.head], temp);
132
+ let autoResolvedNextMd = false;
132
133
  if (merge.status !== 0) {
133
- const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp);
134
- if (!unresolved) throw new Error(`git merge failed: ${merge.stderr.trim() || `exit ${merge.status}`}`);
135
- verdict.verdict = 'conflict';
136
- verdict.detail = unresolved;
137
- } else {
134
+ const unresolved = run('git', ['diff', '--name-only', '--diff-filter=U'], temp).split('\n').filter(Boolean);
135
+ if (!unresolved.length) throw new Error(`git merge failed: ${merge.stderr.trim() || `exit ${merge.status}`}`);
136
+ if (unresolved.length === 1 && unresolved[0] === NEXT_MD_PATH) {
137
+ const stage = (n: number): string => {
138
+ const result = execute('git', ['show', `:${n}:${NEXT_MD_PATH}`], temp!);
139
+ if (result.status !== 0) throw new Error(`release/next.md stage ${n} unavailable: ${result.stderr}`);
140
+ return result.stdout;
141
+ };
142
+ // Stage 2 is main in this checkout; preserve the incoming PR (stage 3) and union main's additions.
143
+ const resolved = resolveNextMdConflict(stage(1), stage(2), stage(3));
144
+ if (resolved === null) {
145
+ verdict.verdict = 'conflict';
146
+ verdict.detail = 'release/next.md conflict is not append-only';
147
+ } else {
148
+ writeFileSync(join(temp, NEXT_MD_PATH), resolved);
149
+ run('git', ['add', '--', NEXT_MD_PATH], temp);
150
+ run('git', ['-c', 'user.name=elanous shadow', '-c', 'user.email=shadow@localhost', 'commit', '-m', 'Integrate PR for L8 shadow evaluation'], temp);
151
+ autoResolvedNextMd = true;
152
+ }
153
+ } else {
154
+ verdict.verdict = 'conflict';
155
+ verdict.detail = unresolved.join('\n');
156
+ }
157
+ }
158
+ if (merge.status === 0 || autoResolvedNextMd) {
138
159
  if (verdict.tests.length) {
139
160
  if (verdict.tests.some((file) => !existsSync(join(temp!, file)))) throw new Error('changed test path unavailable');
140
161
  run('bun', ['install', '--frozen-lockfile'], temp);
@@ -153,6 +174,9 @@ export async function runL8ShadowQueue(cwd: string, deps: L8ShadowQueueDeps = {}
153
174
  } else {
154
175
  verdict.verdict = 'pass';
155
176
  }
177
+ if (autoResolvedNextMd) {
178
+ verdict.detail = verdict.detail ? `release/next.md auto-resolved; ${verdict.detail}` : 'release/next.md auto-resolved';
179
+ }
156
180
  }
157
181
  } finally {
158
182
  try {
@@ -34,6 +34,7 @@ import { judgePredictionAccuracy } from './judge-prediction-accuracy.js';
34
34
  import { measureReviewFindingRecurrence, type ReviewFindingRecurrence } from './review-finding-recurrence.js';
35
35
  import { createHash } from 'node:crypto';
36
36
  import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
37
+ import { writeGoalDocumentAtomic } from './goal-document-write.js';
37
38
  import { hostname, loadavg } from 'node:os';
38
39
  import { resolveHostId } from '../platform/host-id.js';
39
40
  import { resolveInstanceName } from '../instance-identity.js';
@@ -242,7 +243,7 @@ export type { QuotaAccountAvailabilityEvidence };
242
243
  import { decideReworkSalvage, type ReworkSalvageEvidence } from './rework-salvage.js';
243
244
  import { type SupervisorGoalFillEvidence, fillUnverifiableGoalSlots, type SupervisorGoalFillResult } from './goal-supervisor-fill.js';
244
245
  import { escalateGoalDocumentClarifications, recordSupervisorPlanRevision, type GoalClarificationEscalationResult, type SupervisorPlanRevisionRelaxation, type SupervisorPlanRevisionResult } from './goal-clarification-escalation.js';
245
- import { parseGoalDocumentClarifications } from './goal-author-clarification.js';
246
+ import { parseGoalDocumentClarifications, selfAnswerGoalDocumentClarifications, type GoalClarificationSelfResolver } from './goal-author-clarification.js';
246
247
  import {
247
248
  createFileAskUserQuestionResolver,
248
249
  type AskUserQuestionDispatchContext,
@@ -1077,6 +1078,9 @@ export interface SelfImplementSeams {
1077
1078
  openPr: (opts: { title: string; body: string; head: string; base?: string; draft?: boolean; labels?: string[]; cwd: string }) => Promise<{ url: string; number: number }>;
1078
1079
  /** PR 생성 뒤 런의 라운드 대화를 게시한다. 실패는 PR 개설을 되돌리지 않는다. */
1079
1080
  postPrComment?: (opts: { number: number; body: string; cwd: string }) => Promise<void>;
1081
+ /** Read and edit the single marked status comment; missing seams fall back to individual comments. */
1082
+ findPrComment?: (opts: { number: number; marker: string; cwd: string; comments?: unknown }) => Promise<{ id: number; body: string } | undefined>;
1083
+ editPrComment?: (opts: { id: number; body: string; cwd: string }) => Promise<void>;
1080
1084
  addPrLabel?: (opts: { number: number; label: string; cwd: string }) => Promise<void>;
1081
1085
  readPrFiles?: (opts: { number: number; cwd: string; base?: string }) => Promise<string[]>;
1082
1086
  /** PR의 base/head SHA와 관측된 PR base를 한 응답에서 고정한다. 미주입·실패·빈 SHA면 검사 대상을 증명할 수 없어 fail-closed로 PR을 열어 둔다. */
@@ -1154,6 +1158,8 @@ export interface SelfImplementSeams {
1154
1158
  /** ⭐ 「이 표면에 답할 사람이 있나」. 기본은 실제 stdin 의 TTY 여부다 —
1155
1159
  * ⛔ 실물에 매이면 테스트가 «환경»을 검사하게 되므로 주입 가능하게 둔다. */
1156
1160
  stdinIsInteractive?: () => boolean;
1161
+ /** Optional author self-answer seam for non-TTY Pods; never installs a terminal HITL resolver. */
1162
+ selfResolveClarification?: GoalClarificationSelfResolver;
1157
1163
  }
1158
1164
 
1159
1165
  /** onProgress seam 이 통지하는 단계(관측·UX 라벨). */
@@ -3182,6 +3188,20 @@ function autoMergeEnabled(opts: Pick<SelfImplementOptions, 'completion' | 'autoM
3182
3188
  return completionIntentOf(opts) === 'auto-merge';
3183
3189
  }
3184
3190
 
3191
+ /**
3192
+ * FREEZE-POD — a Pod child never merges its own PR. The host's landing freeze is invisible inside the Pod, so an
3193
+ * auto-merge run there always stops at merge-ready and the launching host re-gates and merges (or holds it).
3194
+ * Local (non-Pod) runs are returned unchanged.
3195
+ */
3196
+ export function routePodMergeToHost<T extends Pick<SelfImplementOptions, 'completion' | 'autoMerge' | 'mergeByHost'>>(
3197
+ opts: T, env: NodeJS.ProcessEnv = process.env,
3198
+ ): { opts: T; reason: 'requested' | 'pod-child-auto-merge' | null } {
3199
+ const podChild = env.ELANOUS_SUBSTRATE === 'pod' && Boolean(env.ELANOUS_POD_NAME?.trim());
3200
+ if (!podChild || !autoMergeEnabled(opts)) return { opts, reason: null };
3201
+ if (opts.mergeByHost === true) return { opts, reason: 'requested' };
3202
+ return { opts: { ...opts, mergeByHost: true }, reason: 'pod-child-auto-merge' };
3203
+ }
3204
+
3185
3205
  const SELF_IMPLEMENT_LOG_CATEGORY = 'self-implement';
3186
3206
 
3187
3207
  /** Emits a run-aware auxiliary log without admitting it to the run ledger. */
@@ -3465,6 +3485,12 @@ export async function runSelfImplement(opts: SelfImplementOptions): Promise<Self
3465
3485
  }
3466
3486
  const identity = resolveRunIdentity({ explicit: opts.runId });
3467
3487
  const { runId } = identity;
3488
+ const podMerge = routePodMergeToHost(opts);
3489
+ if (podMerge.reason) {
3490
+ opts = podMerge.opts;
3491
+ try { debug.log('self-implement.pod', 'merge-routed-to-host', { job: process.env.ELANOUS_POD_NAME ?? null, runId, reason: podMerge.reason }); }
3492
+ catch { /* observation must not block the run */ }
3493
+ }
3468
3494
  const branch = opts.branchName ?? plannedSelfImplBranch(opts.feature, opts.goalId, runId);
3469
3495
  try {
3470
3496
  debug.log('self-implement.branch', 'planned', {
@@ -5364,6 +5390,7 @@ async function runSelfImplementInner(
5364
5390
  ? { resolvedDelivery: installDecision.delivery }
5365
5391
  : {}),
5366
5392
  ...(s.escalateGoalClarifications ? { dispatch: s.escalateGoalClarifications } : {}),
5393
+ ...(!stdinIsInteractive && !clarificationResolverLease.installed ? { selfAnswerPath: 'ask' as const, selfResolveClarification: s.selfResolveClarification } : {}),
5367
5394
  fallback: (message) => process.stdout.write(`${message}\n`),
5368
5395
  });
5369
5396
  } finally {
@@ -5440,9 +5467,11 @@ async function runSelfImplementInner(
5440
5467
  const omission = `\n\n[${omittedChars} characters omitted; original body length ${body.length}]`;
5441
5468
  prCommentBuffer.push(`${body.slice(0, MAX_PR_COMMENT_BODY_CHARS - omission.length)}${omission}`);
5442
5469
  };
5443
- const flushPrComments = async (pr: { number: number }): Promise<void> => {
5470
+ let statusCommentCreateAttempted = false;
5471
+ const flushPrComments = async (pr: { number: number }, salvageAction?: 'launched'): Promise<void> => {
5444
5472
  if (!prCommentBuffer.length) return;
5445
5473
  if (!s.postPrComment) {
5474
+ observe('pr.comment.mode', { number: pr.number, mode: 'seam-missing', buffered: prCommentBuffer.length });
5446
5475
  observe('pr.comment.seam-missing', { number: pr.number, buffered: prCommentBuffer.length }, { level: 'warn' });
5447
5476
  return;
5448
5477
  }
@@ -5450,24 +5479,39 @@ async function runSelfImplementInner(
5450
5479
  if (s.findPrComment && s.editPrComment) {
5451
5480
  try {
5452
5481
  const existing = await s.findPrComment({ number: pr.number, marker, cwd: wt.path });
5453
- const history = prCommentBuffer.join('\n\n');
5454
- const body = existing
5455
- ? existing.body.includes('</details>')
5456
- ? existing.body.replace(/<\/details>(?![\s\S]*<\/details>)/, `\n\n${history}\n</details>`)
5457
- : `${existing.body}\n\n<details>\n<summary>Round history</summary>\n\n${history}\n</details>`
5458
- : `${marker}\n<details>\n<summary>Round history</summary>\n\n${history}\n</details>`;
5459
- if (existing) await s.editPrComment({ id: existing.id, body, cwd: wt.path });
5460
- else await s.postPrComment({ number: pr.number, body, cwd: wt.path });
5482
+ if (!existing && statusCommentCreateAttempted) {
5483
+ observe('pr.comment.mode', { number: pr.number, mode: 'individual-fallback', reason: 'status-creation-uncertain', buffered: prCommentBuffer.length });
5484
+ } else {
5485
+ const history = prCommentBuffer.join('\n\n');
5486
+ const body = existing
5487
+ ? existing.body.includes('</details>')
5488
+ ? existing.body.replace(/<\/details>(?![\s\S]*<\/details>)/, `\n\n${history}\n</details>`)
5489
+ : `${existing.body}\n\n<details>\n<summary>Round history</summary>\n\n${history}\n</details>`
5490
+ : `${marker}\n<details>\n<summary>Round history</summary>\n\n${history}\n</details>`;
5491
+ if (existing) await s.editPrComment({ id: existing.id, body, cwd: wt.path });
5492
+ else {
5493
+ statusCommentCreateAttempted = true;
5494
+ await s.postPrComment({ number: pr.number, body, cwd: wt.path });
5495
+ }
5496
+ observe('pr.comment.mode', { number: pr.number, mode: existing ? 'status-updated' : 'status-created', buffered: prCommentBuffer.length });
5497
+ prCommentBuffer.length = 0;
5498
+ }
5461
5499
  } catch (error) {
5462
5500
  // Unknown lookup/edit outcome is not proof of absence: never create a second status comment.
5463
5501
  observe('pr.comment.status-failed', { number: pr.number, error: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
5502
+ observe('pr.comment.mode', { number: pr.number, mode: 'individual-fallback', reason: 'status-failed', buffered: prCommentBuffer.length });
5464
5503
  }
5504
+ } else {
5505
+ observe('pr.comment.mode', { number: pr.number, mode: 'individual-fallback', reason: 'status-seam-missing', buffered: prCommentBuffer.length });
5465
5506
  }
5466
- for (const body of prCommentBuffer) {
5507
+ if (!prCommentBuffer.length) return;
5508
+ const pending = prCommentBuffer.splice(0);
5509
+ for (const body of pending) {
5467
5510
  try {
5468
5511
  await s.postPrComment({ number: pr.number, body, cwd: wt.path });
5469
5512
  } catch (error) {
5470
- observe('pr.comment.post-failed', { number: pr.number, error: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
5513
+ // Posting may have succeeded remotely even if the response failed; do not retry an uncertain comment.
5514
+ observe('pr.comment.post-failed', { number: pr.number, ...(salvageAction && body.includes('Rework salvage status: launched.') ? { salvageAction } : {}), error: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
5471
5515
  }
5472
5516
  }
5473
5517
  };
@@ -5818,7 +5862,13 @@ async function runSelfImplementInner(
5818
5862
  };
5819
5863
  const postSalvageStatus = async (pr: { number: number } | undefined, action: 'launched' | 'parked', reason?: string): Promise<void> => {
5820
5864
  if (!pr) return;
5865
+ if (action === 'launched') {
5866
+ bufferPrComment(round, 'author', 'Rework salvage status: launched.', reason ? [`reason: ${reason}`] : []);
5867
+ await flushPrComments(pr, 'launched');
5868
+ return;
5869
+ }
5821
5870
  if (!s.postPrComment) {
5871
+ observe('pr.comment.mode', { number: pr.number, mode: 'seam-missing', salvageAction: action });
5822
5872
  observe('pr.comment.seam-missing', { number: pr.number, salvageAction: action }, { level: 'warn' });
5823
5873
  return;
5824
5874
  }
@@ -5827,6 +5877,7 @@ async function runSelfImplementInner(
5827
5877
  `Rework salvage status: ${action}.`,
5828
5878
  ...(reason ? ['', `- reason: ${reason}`] : []),
5829
5879
  ].join('\n');
5880
+ observe('pr.comment.mode', { number: pr.number, mode: 'individual', salvageAction: action });
5830
5881
  try {
5831
5882
  await s.postPrComment({ number: pr.number, body, cwd: wt.path });
5832
5883
  } catch (error) {
@@ -5996,7 +6047,7 @@ async function runSelfImplementInner(
5996
6047
  if (!s.launchReworkSalvage) throw new Error('launch seam unavailable');
5997
6048
  await s.launchReworkSalvage({ goalFile: opts.goalFile!, base: wt.branch, salvageAttempt: (opts.salvageAttempt ?? 0) + 1 });
5998
6049
  observe('rework-salvage', { action: 'launched', ...launchObservation });
5999
- await postSalvageStatus(pr, 'launched');
6050
+ await postSalvageStatus(pr, 'launched', environmentReason ?? (hardCapBlockedExtend ? 'hard-cap-extend' : undefined));
6000
6051
  return 'launched';
6001
6052
  } catch (error) {
6002
6053
  const reason = 'launch-failed';
@@ -6175,6 +6226,20 @@ async function runSelfImplementInner(
6175
6226
  if (relaxation) pendingPlanRevision = { ...pendingPlanRevision!, relaxation };
6176
6227
  if (opts.goalFile) {
6177
6228
  try {
6229
+ if (!(s.stdinIsInteractive ?? (() => Boolean(process.stdin.isTTY && process.stdout.isTTY)))()) {
6230
+ try {
6231
+ const source = readFileSync(opts.goalFile, 'utf8');
6232
+ // Always run the shared self-answer so every rework pass leaves one observation, even with nothing pending.
6233
+ const resolved = await selfAnswerGoalDocumentClarifications(source, {
6234
+ path: 'rework', ask: source, resolver: s.selfResolveClarification,
6235
+ });
6236
+ if (resolved.newlyAnswered > 0) writeGoalDocumentAtomic(opts.goalFile, resolved.document);
6237
+ } catch (error) {
6238
+ debug.log('self-implement.clarification-escalation', 'self-answer-failed', {
6239
+ path: 'rework', reason: error instanceof Error ? error.message : String(error),
6240
+ });
6241
+ }
6242
+ }
6178
6243
  const recorded: SupervisorPlanRevisionResult = recordSupervisorPlanRevision(opts.goalFile, { reason: parsedDecision.reason, round, ...(relaxation ? { relaxation } : {}) });
6179
6244
  pendingPlanRevision = { ...pendingPlanRevision!, ...(relaxation ? { relaxation } : {}), application: {
6180
6245
  status: recorded.status === 'applied' || recorded.status === 'already-applied' ? recorded.status : 'failed',
@@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { debug } from '../debug/log.js';
5
- import { parse as parsePrCommentMeta } from '../agent-substrate/pr-comment-meta.js';
5
+ import { expandRecords, parse as parsePrCommentMeta } from '../agent-substrate/pr-comment-meta.js';
6
6
 
7
7
  export interface PriorDraftPr {
8
8
  number: number;
@@ -85,9 +85,10 @@ export async function collectPriorDraftFindings({
85
85
  continue;
86
86
  }
87
87
  const prActivity = Date.parse(pr.state.toUpperCase() === 'CLOSED' ? pr.closedAt ?? pr.createdAt : pr.createdAt);
88
+ const records = comments.flatMap((comment) => expandRecords(comment.body).map((body) => ({ ...comment, body })));
88
89
  // A PR bearing the current run's comment is this run's PR, not a predecessor.
89
- if (comments.some((comment) => parsePrCommentMeta(comment.body)?.run === currentRunId)) continue;
90
- const reviewers = comments.flatMap((comment) => {
90
+ if (records.some((comment) => parsePrCommentMeta(comment.body)?.run === currentRunId)) continue;
91
+ const reviewers = records.flatMap((comment) => {
91
92
  const meta = parsePrCommentMeta(comment.body);
92
93
  return meta?.role === 'reviewer' && meta.run && meta.round !== undefined
93
94
  ? [{ comment, meta }] : [];
@@ -0,0 +1,76 @@
1
+ import { appendFileSync, readFileSync } from 'node:fs';
2
+ import { debug } from '../debug/log.js';
3
+
4
+ export interface SkillRun {
5
+ skill: string;
6
+ runId: string;
7
+ outcome: 'success' | 'failure';
8
+ failureKind?: string;
9
+ userCorrected: boolean;
10
+ retries: number;
11
+ durationMs: number;
12
+ at: string;
13
+ }
14
+
15
+ export interface SkillFixProposal {
16
+ skill: string;
17
+ failureKind: string;
18
+ evidenceRunIds: string[];
19
+ suggestion: string;
20
+ expectedEffect: string;
21
+ }
22
+
23
+ /** Record one run at the caller-supplied ledger path; never touch a skill file. */
24
+ export function appendSkillRun(ledgerPath: string, run: SkillRun): void {
25
+ appendFileSync(ledgerPath, `${JSON.stringify(run)}\n`, 'utf8');
26
+ }
27
+
28
+ /** Read the JSONL ledger in append order; an absent ledger has no runs. */
29
+ export function readSkillRuns(ledgerPath: string): SkillRun[] {
30
+ let text: string;
31
+ try {
32
+ text = readFileSync(ledgerPath, 'utf8');
33
+ } catch (error) {
34
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return [];
35
+ throw error;
36
+ }
37
+ return text.split('\n').filter((line) => line.trim() !== '').map((line) => JSON.parse(line) as SkillRun);
38
+ }
39
+
40
+ /** Shadow-only suggestions for recurring failures within the same skill and failure kind. */
41
+ export function proposeSkillFixes(
42
+ runs: readonly SkillRun[],
43
+ { threshold = 3 }: { threshold?: number } = {},
44
+ ): SkillFixProposal[] {
45
+ const grouped = new Map<string, Map<string, Map<string, SkillRun>>>();
46
+ for (const run of runs) {
47
+ if (run.outcome !== 'failure' || !run.failureKind) continue;
48
+ let kinds = grouped.get(run.skill);
49
+ if (!kinds) { kinds = new Map(); grouped.set(run.skill, kinds); }
50
+ let failures = kinds.get(run.failureKind);
51
+ if (!failures) { failures = new Map(); kinds.set(run.failureKind, failures); }
52
+ const previous = failures.get(run.runId);
53
+ if (!previous || run.at >= previous.at) failures.set(run.runId, run);
54
+ }
55
+
56
+ const proposals: SkillFixProposal[] = [];
57
+ for (const [skill, kinds] of grouped) {
58
+ for (const [failureKind, failures] of kinds) {
59
+ if (failures.size < threshold) continue;
60
+ const evidenceRunIds = [...failures.values()]
61
+ .map((run, index) => ({ run, index }))
62
+ .sort((a, b) => a.run.at.localeCompare(b.run.at) || a.index - b.index)
63
+ .slice(-3)
64
+ .map(({ run }) => run.runId);
65
+ proposals.push({
66
+ skill,
67
+ failureKind,
68
+ evidenceRunIds,
69
+ suggestion: `스킬 ${skill}의 ${failureKind} 실패 처리를 점검·개선하세요 — 같은 실패가 ${failures.size}번 반복됐습니다.`,
70
+ expectedEffect: `${skill}의 ${failureKind} 재발 감소`,
71
+ });
72
+ debug.log('skill.feedback', 'proposal', { skill, failureKind, runs: failures.size });
73
+ }
74
+ }
75
+ return proposals;
76
+ }