elanous 0.2.14 → 0.2.15

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 (293) 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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-33ffcc49ab45123b.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-74383f890f075511.js → page-0e2c57fe6add4b2a.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-e05a77df7e005b04.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-b8c49f6b4fa2df91.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-976813c91d7394ea.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/consult/{page-fb260b9283dbf646.js → page-28b9448091776c06.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-60c7a8e1a4fe62ce.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-2a52b316cb143d9c.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-c3ce483c8d5db370.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-96f074f6538e8a2c.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-e97be5334d83a9f5.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-955ec2da3be4c560.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-c1343f9499d86f3f.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-d23b12bb92242558.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-04250f0e2c0d03df.js → page-e2ee2896811e98d0.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-5efbf81124c474ae.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-85f1a1fc0a82738c.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-8ac8d71c2adf9c8a.js → page-da6c08aae0f92768.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-153c2b3bcc89e8de.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-94dc383d3d54db1a.js → page-87a11e6a05f15199.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/setup/{page-b3759cc14da80908.js → page-7d4182964407c09c.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-0d6d23b40263a7ea.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-f5597e894c02cd33.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/trace/{page-35bdeae2e2c17147.js → page-73735b64bfe1ae67.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-8fcec4c9ea2e7f70.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-9817c7f9007466cc.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-134c1bc762aee62c.js → page-97f9f43172a3a90e.js} +1 -1
  53. package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +1 -0
  54. package/apps/pwa/out/approvals/index.html +1 -1
  55. package/apps/pwa/out/approvals/index.txt +14 -14
  56. package/apps/pwa/out/autopilot/index.html +1 -1
  57. package/apps/pwa/out/autopilot/index.txt +14 -14
  58. package/apps/pwa/out/board/index.html +1 -1
  59. package/apps/pwa/out/board/index.txt +14 -14
  60. package/apps/pwa/out/botlab/index.html +1 -1
  61. package/apps/pwa/out/botlab/index.txt +14 -14
  62. package/apps/pwa/out/bots/index.html +1 -1
  63. package/apps/pwa/out/bots/index.txt +14 -14
  64. package/apps/pwa/out/ceo/index.html +1 -0
  65. package/apps/pwa/out/ceo/index.txt +33 -0
  66. package/apps/pwa/out/chat/index.html +1 -1
  67. package/apps/pwa/out/chat/index.txt +14 -14
  68. package/apps/pwa/out/consult/index.html +1 -1
  69. package/apps/pwa/out/consult/index.txt +14 -14
  70. package/apps/pwa/out/control/index.html +1 -1
  71. package/apps/pwa/out/control/index.txt +14 -14
  72. package/apps/pwa/out/design-check/index.html +1 -1
  73. package/apps/pwa/out/design-check/index.txt +14 -14
  74. package/apps/pwa/out/editor/index.html +1 -1
  75. package/apps/pwa/out/editor/index.txt +14 -14
  76. package/apps/pwa/out/exec/index.html +1 -1
  77. package/apps/pwa/out/exec/index.txt +14 -14
  78. package/apps/pwa/out/field/index.html +1 -1
  79. package/apps/pwa/out/field/index.txt +14 -14
  80. package/apps/pwa/out/healthz +1 -1
  81. package/apps/pwa/out/index.html +1 -1
  82. package/apps/pwa/out/index.txt +14 -14
  83. package/apps/pwa/out/inside/index.html +1 -1
  84. package/apps/pwa/out/inside/index.txt +14 -14
  85. package/apps/pwa/out/intake/index.html +1 -1
  86. package/apps/pwa/out/intake/index.txt +14 -14
  87. package/apps/pwa/out/live/index.html +1 -1
  88. package/apps/pwa/out/live/index.txt +14 -14
  89. package/apps/pwa/out/loops/index.html +1 -1
  90. package/apps/pwa/out/loops/index.txt +14 -14
  91. package/apps/pwa/out/market/index.html +1 -1
  92. package/apps/pwa/out/market/index.txt +14 -14
  93. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  94. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  95. package/apps/pwa/out/morning/index.html +1 -1
  96. package/apps/pwa/out/morning/index.txt +14 -14
  97. package/apps/pwa/out/observatory/index.html +1 -1
  98. package/apps/pwa/out/observatory/index.txt +14 -14
  99. package/apps/pwa/out/ops/checklist/index.html +1 -1
  100. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  101. package/apps/pwa/out/ops/release/index.html +1 -1
  102. package/apps/pwa/out/ops/release/index.txt +14 -14
  103. package/apps/pwa/out/ops/seats/index.html +1 -1
  104. package/apps/pwa/out/ops/seats/index.txt +14 -14
  105. package/apps/pwa/out/outputs/index.html +1 -1
  106. package/apps/pwa/out/outputs/index.txt +14 -14
  107. package/apps/pwa/out/reflection/index.html +1 -1
  108. package/apps/pwa/out/reflection/index.txt +15 -15
  109. package/apps/pwa/out/scheduler/index.html +1 -1
  110. package/apps/pwa/out/scheduler/index.txt +14 -14
  111. package/apps/pwa/out/sessions/index.html +1 -1
  112. package/apps/pwa/out/sessions/index.txt +14 -14
  113. package/apps/pwa/out/settings/devices/index.html +1 -1
  114. package/apps/pwa/out/settings/devices/index.txt +14 -14
  115. package/apps/pwa/out/settings/index.html +1 -1
  116. package/apps/pwa/out/settings/index.txt +14 -14
  117. package/apps/pwa/out/setup/done/index.html +1 -1
  118. package/apps/pwa/out/setup/done/index.txt +14 -14
  119. package/apps/pwa/out/setup/index.html +1 -1
  120. package/apps/pwa/out/setup/index.txt +14 -14
  121. package/apps/pwa/out/share/index.html +1 -1
  122. package/apps/pwa/out/share/index.txt +14 -14
  123. package/apps/pwa/out/showroom/index.html +1 -1
  124. package/apps/pwa/out/showroom/index.txt +14 -14
  125. package/apps/pwa/out/tasks/index.html +1 -1
  126. package/apps/pwa/out/tasks/index.txt +14 -14
  127. package/apps/pwa/out/term/index.html +1 -1
  128. package/apps/pwa/out/term/index.txt +14 -14
  129. package/apps/pwa/out/today/index.html +1 -1
  130. package/apps/pwa/out/today/index.txt +14 -14
  131. package/apps/pwa/out/trace/index.html +1 -1
  132. package/apps/pwa/out/trace/index.txt +14 -14
  133. package/apps/pwa/out/vault/index.html +1 -1
  134. package/apps/pwa/out/vault/index.txt +14 -14
  135. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  136. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  137. package/apps/pwa/out/workflows/index.html +1 -1
  138. package/apps/pwa/out/workflows/index.txt +14 -14
  139. package/apps/pwa/out/workspace/index.html +1 -1
  140. package/apps/pwa/out/workspace/index.txt +14 -14
  141. package/apps/pwa/out/worktrees/index.html +1 -1
  142. package/apps/pwa/out/worktrees/index.txt +14 -14
  143. package/graphs/heal/heal-loop.yaml +1 -1
  144. package/graphs/implement-loop.yaml +1 -1
  145. package/graphs/release/recipes.yaml +3 -0
  146. package/graphs/release/release-loop.yaml +4 -2
  147. package/package.json +1 -1
  148. package/scripts/brand/check.ts +7 -5
  149. package/scripts/ci-typecheck-changed.ts +50 -18
  150. package/scripts/docs/generate-graph-anatomy.ts +315 -0
  151. package/scripts/expose-rubric-suggest.ts +18 -12
  152. package/scripts/lecture-note/node.ts +31 -11
  153. package/scripts/lessons/handbook-page.ts +30 -3
  154. package/scripts/marketing/generate-ax-brief.ts +140 -0
  155. package/scripts/marketing/generate-feature-map.ts +118 -0
  156. package/scripts/release-loop/checklist-gate-node.ts +53 -14
  157. package/scripts/release-loop/gate-node.ts +80 -14
  158. package/scripts/release-loop/vault-note-node.ts +240 -8
  159. package/scripts/release-story/draft.ts +57 -9
  160. package/scripts/rhythm/daily.ts +263 -76
  161. package/scripts/rhythm/weekly.ts +30 -9
  162. package/scripts/smart-merge/resync-shadow.ts +147 -0
  163. package/src/agent-env/profile.ts +161 -0
  164. package/src/agent-substrate/pr-reviewer.ts +2 -0
  165. package/src/ask-user-question/pending-questions.ts +5 -0
  166. package/src/auth/github-app-token.ts +112 -13
  167. package/src/autopilot/build/llm-conflict-merge.ts +32 -6
  168. package/src/briefing/brief-items.ts +121 -0
  169. package/src/claims/claims-render.ts +14 -5
  170. package/src/cli/agent-env-cli.ts +21 -0
  171. package/src/cli/decisions-cli.ts +9 -0
  172. package/src/cli/doctor-cli.ts +23 -1
  173. package/src/cli/doctor-node-modules.ts +147 -0
  174. package/src/cli/intake-cli.ts +46 -17
  175. package/src/cli/pod-cli.ts +2 -0
  176. package/src/cli/release-cli.ts +61 -27
  177. package/src/cli/seat-requests-cli.ts +52 -0
  178. package/src/context-bus/context-now-public.ts +16 -6
  179. package/src/context-bus/context-now.ts +3 -1
  180. package/src/context-bus/coord-events.ts +6 -4
  181. package/src/decisions/decision-cards.ts +33 -7
  182. package/src/decisions/proact-meter.ts +286 -0
  183. package/src/decisions/proact1-lite.ts +86 -0
  184. package/src/decisions/web-push-decision-cards.ts +1 -1
  185. package/src/domains/alert-audit.ts +271 -0
  186. package/src/domains/core-tools.ts +4 -3
  187. package/src/domains/ops-facts-tool.ts +31 -1
  188. package/src/harness/harness-cli-command.ts +111 -15
  189. package/src/harness/harness-incidents.ts +2 -0
  190. package/src/harness/harness-pod-dispatch.ts +100 -11
  191. package/src/harness/harness-queue.ts +380 -50
  192. package/src/harness/helper-repair.ts +87 -0
  193. package/src/harness/helper-scan.ts +16 -4
  194. package/src/harness/launch-stamp.ts +236 -0
  195. package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
  196. package/src/harness/ledger-write-guard.ts +35 -0
  197. package/src/harness/pod-memory-policy.ts +15 -0
  198. package/src/harness/repository-name.ts +55 -0
  199. package/src/hq/fence-audit.ts +90 -0
  200. package/src/hq/hq.ts +62 -4
  201. package/src/hq/lease.ts +4 -4
  202. package/src/hq/move-plan.ts +126 -0
  203. package/src/hq/promote.ts +31 -0
  204. package/src/index.ts +77 -7
  205. package/src/intake-plane/check.ts +63 -0
  206. package/src/intake-plane/digest.ts +37 -15
  207. package/src/intake-plane/front-classifier.ts +6 -0
  208. package/src/intake-plane/items.ts +3 -2
  209. package/src/intake-plane/lens.ts +177 -0
  210. package/src/intake-plane/route.ts +145 -1
  211. package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
  212. package/src/launch-head/seat-goal-queue.ts +198 -0
  213. package/src/loops/budget.ts +36 -5
  214. package/src/loops/checker.ts +2 -2
  215. package/src/loops/orchestrator/author-depth.ts +65 -9
  216. package/src/loops/orchestrator/author-input-check.ts +8 -3
  217. package/src/loops/orchestrator/author-ledger.ts +3 -1
  218. package/src/loops/orchestrator/seat-share.ts +56 -0
  219. package/src/loops/orchestrator/tick.ts +11 -4
  220. package/src/loops/orchestrator/traffic.ts +117 -11
  221. package/src/loops/registry.ts +142 -61
  222. package/src/maturity/feature-maturity.ts +2 -1
  223. package/src/maturity/tui-slash-availability.ts +31 -0
  224. package/src/mss/logging/log-store.ts +35 -4
  225. package/src/nexus/api/http-server.ts +28 -14
  226. package/src/nexus/api/loop-edges.ts +173 -0
  227. package/src/nexus/api/schedules-read.ts +13 -0
  228. package/src/nexus/api/seat-requests.ts +79 -56
  229. package/src/prompt-enhance/enhance.ts +6 -1
  230. package/src/release-loop/auto-start.ts +6 -3
  231. package/src/release-loop/checklist.ts +13 -4
  232. package/src/release-loop/feature-store.ts +58 -31
  233. package/src/release-loop/merged-pr-checklist.ts +125 -0
  234. package/src/release-loop/placement.ts +86 -26
  235. package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
  236. package/src/seat-dispatch/seat-request-ledger.ts +133 -0
  237. package/src/seat-loop/seat-loop.ts +126 -10
  238. package/src/self-dev/dev-pipeline.ts +8 -4
  239. package/src/self-dev/draft-sweep.ts +90 -10
  240. package/src/self-dev/draft-triage-rules.ts +25 -8
  241. package/src/self-dev/orchestrate.ts +15 -1
  242. package/src/self-dev/review-provider-fallback.ts +6 -1
  243. package/src/self-dev/run-supervisor.ts +49 -2
  244. package/src/self-implement/frozen-merges.ts +3 -2
  245. package/src/self-implement/goal-author.ts +43 -5
  246. package/src/self-implement/goal-clarification-escalation.ts +5 -0
  247. package/src/self-implement/heal-intake.ts +120 -2
  248. package/src/self-implement/host-regate.ts +9 -2
  249. package/src/self-implement/l8-merge-queue.ts +132 -0
  250. package/src/self-implement/orchestrator.ts +187 -40
  251. package/src/self-implement/pipeline-shape.ts +1 -1
  252. package/src/self-implement/revert-guard.ts +42 -7
  253. package/src/self-implement/run-ledger.ts +2 -0
  254. package/src/self-implement/run-status-mapping.ts +2 -0
  255. package/src/self-implement/seams.ts +1 -1
  256. package/src/self-implement/self-implement-cli.ts +1 -0
  257. package/src/self-implement/speed-claims.ts +338 -0
  258. package/src/steward/triage.ts +74 -3
  259. package/src/steward/working-backwards.ts +4 -1
  260. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
  261. package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
  262. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
  263. package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
  264. package/src/task-orchestrator/surfaces/self-implement-pod.ts +147 -36
  265. package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
  266. package/src/telegram.ts +25 -4
  267. package/src/user-config.ts +29 -5
  268. package/src/version/packed-revision.json +1 -1
  269. package/src/workflow-runtime/nodes/bash.ts +9 -1
  270. package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
  271. package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
  272. package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
  273. package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
  274. package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +0 -1
  275. package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
  276. package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
  277. package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
  278. package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +0 -1
  279. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
  280. package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
  281. package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
  282. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +0 -1
  283. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
  284. package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
  285. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
  286. package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +0 -1
  287. package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +0 -1
  288. package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
  289. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
  290. package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +0 -1
  291. package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
  292. package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
  293. /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
@@ -30,7 +30,7 @@ import { LOGS_QUERY_SPEC, dispatchLogsQuery } from './logs-tool.js';
30
30
  import { MISSION_DECIDE_SPEC, dispatchMissionDecide } from '../autopilot/mission-decide-tool.js';
31
31
  import { COO_ADMIN_SPEC, dispatchCooAdmin } from './coo-admin-tool.js';
32
32
  import { RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, dispatchReleaseStatus, dispatchReleaseChange } from './release-tool.js';
33
- import { OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, dispatchOpsSeats, dispatchDecisionsPending } from './ops-facts-tool.js';
33
+ import { OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, PROACT_METER_SPEC, dispatchOpsSeats, dispatchDecisionsPending, dispatchProactMeter } from './ops-facts-tool.js';
34
34
 
35
35
  /** memory_recall — 크로스서피스 기억(자기 발송 원장 회상). 도메인 무관(domain 필터는 옵션).
36
36
  * finance-tools 에서 L2 로 이관(2026-07-08). */
@@ -142,7 +142,7 @@ const SELF_RECALL_BUDGET_SPEC: LLMToolSpec = {
142
142
  },
143
143
  },
144
144
  };
145
- export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC];
145
+ export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, PROACT_METER_SPEC];
146
146
 
147
147
  export interface CoreTools {
148
148
  specs: LLMToolSpec[];
@@ -159,10 +159,11 @@ export function buildCoreTools(recallSearch?: RecallSearch): CoreTools {
159
159
  names,
160
160
  dispatch: async (name, args, onPartial, context) => {
161
161
  const h = CORE_TOOL_HANDLERS[name];
162
- if (!h && name !== 'release_change' && name !== 'ops_seats' && name !== 'decisions_pending') return { error: `unknown core tool: ${name}` };
162
+ if (!h && name !== 'release_change' && name !== 'ops_seats' && name !== 'decisions_pending' && name !== 'proact_meter') return { error: `unknown core tool: ${name}` };
163
163
  if (name === 'release_change') return dispatchReleaseChange(args, context);
164
164
  if (name === 'ops_seats') return dispatchOpsSeats(args, context);
165
165
  if (name === 'decisions_pending') return dispatchDecisionsPending(context);
166
+ if (name === 'proact_meter') return dispatchProactMeter(args, context);
166
167
  if (name === 'memory_recall' || name === 'self_recall') {
167
168
  return runRecall(name, args, context ?? { surface: 'skill' }, recallSearch, onPartial);
168
169
  }
@@ -2,6 +2,7 @@ import type { LLMToolSpec } from '../llm.js';
2
2
  import type { ToolRuntimeContext } from '../tool-runtime/types.js';
3
3
  import { debug } from '../debug/log.js';
4
4
  import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
5
+ import { readProactMeter, type ProactMeter } from '../decisions/proact-meter.js';
5
6
  import { buildSeatsBoard, todayKst, type SeatsSources } from '../nexus/api/ops-seats.js';
6
7
  import { liveSeatsSources } from '../nexus/api/ops-seats-sources.js';
7
8
 
@@ -11,6 +12,12 @@ export const OPS_SEATS_SPEC: LLMToolSpec = {
11
12
  parameters: { type: 'object', properties: { date: { type: 'string', description: '조회할 KST 날짜 YYYY-MM-DD. 생략하면 KST 오늘.' } }, required: [] },
12
13
  };
13
14
 
15
+ export const PROACT_METER_SPEC: LLMToolSpec = {
16
+ name: 'proact_meter',
17
+ description: '오너 전용 읽기 — 선제성 기준선. 지난 7 KST일마다 대표가 시키지 않고 시스템이 먼저 낸 수(offered), 그중 채택된 수(adopted), 대표가 먼저 물어서야 드러난 수(asked). 표본은 결정·지시·조율·루프 원문 링크를 가진다. 못 읽은 원천은 sources.read=false 이며 0이 아니다.',
18
+ parameters: { type: 'object', properties: { days: { type: 'number', description: 'KST 일수. 생략하면 7.' } }, required: [] },
19
+ };
20
+
14
21
  export const DECISIONS_PENDING_SPEC: LLMToolSpec = {
15
22
  name: 'decisions_pending',
16
23
  description: '오너 전용 읽기 — 결정 원장의 열린 결정을 기한 순으로 조회한다. 결정 선택·기록은 하지 않는다.',
@@ -21,13 +28,14 @@ export interface OpsFactsDeps {
21
28
  seatsSources?: () => SeatsSources;
22
29
  listDecisions?: (filters: { status: 'open' }) => DecisionEntry[];
23
30
  today?: () => string;
31
+ readProact?: (days: number) => ProactMeter;
24
32
  }
25
33
 
26
34
  function ownerVerified(context?: ToolRuntimeContext): boolean {
27
35
  return context?.requestOrigin !== 'external-agent' && !!context?.sessionId && !!context?.verifiedOwner?.id.trim();
28
36
  }
29
37
 
30
- function logCall(tool: 'ops_seats' | 'decisions_pending', ok: boolean, nulls: number): void {
38
+ function logCall(tool: 'ops_seats' | 'decisions_pending' | 'proact_meter', ok: boolean, nulls: number): void {
31
39
  debug.log('ops.facts', 'call', { tool, ok, nulls });
32
40
  }
33
41
 
@@ -56,6 +64,28 @@ export async function dispatchOpsSeats(args: Record<string, unknown>, context?:
56
64
  }
57
65
  }
58
66
 
67
+ /** Seven KST days. An unreadable ledger stays sources.read=false; it is not a zero day. */
68
+ export function dispatchProactMeter(args: Record<string, unknown>, context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
69
+ if (!ownerVerified(context)) {
70
+ logCall('proact_meter', false, 0);
71
+ return { error: '오너 확인이 필요한 조회다' };
72
+ }
73
+ const days = args.days === undefined ? 7 : args.days;
74
+ if (typeof days !== 'number' || !Number.isInteger(days) || days < 1 || days > 31) {
75
+ logCall('proact_meter', false, 0);
76
+ return { error: 'days 는 1 이상 31 이하의 정수여야 한다' };
77
+ }
78
+ try {
79
+ const meter = (deps.readProact ?? ((n: number) => readProactMeter({ days: n })))(days);
80
+ const unread = Object.values(meter.sources).filter(source => !source.read).length;
81
+ logCall('proact_meter', true, unread);
82
+ return meter;
83
+ } catch {
84
+ logCall('proact_meter', false, 1);
85
+ return { error: '선제성 원장을 읽지 못했다', days: null };
86
+ }
87
+ }
88
+
59
89
  /** Only the five requested fields leave the decision ledger. Undated entries follow dated entries. */
60
90
  export function dispatchDecisionsPending(context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
61
91
  if (!ownerVerified(context)) {
@@ -21,7 +21,7 @@ import { resolveChildLlmEffort, resolveImplementationChildModel } from '../self-
21
21
  import { resolveHarnessTarget } from '../self-implement/harness-target-options.js';
22
22
  import { queryRunningRuns } from '../self-implement/running-runs.js';
23
23
  import { DevPipelineError } from '../self-dev/dev-pipeline.js';
24
- import { runDraftSweep, type DraftSweepAdapters, type DraftSweepResult, type SweepDraft, type SweepMergedPr } from '../self-dev/draft-sweep.js';
24
+ import { runDraftSweep, sweepFailureReason, type DraftSweepAdapters, type DraftSweepResult, type SweepDraft, type SweepMergedPr, type SweepReviewGate } from '../self-dev/draft-sweep.js';
25
25
  import { debug } from '../debug/log.js';
26
26
  import { dispatchTask, type DispatchTaskInput, type DispatchTaskDeps } from '../execution-loop/dispatch-task.js';
27
27
  import { launchRequestId, preLaunchGate, type PreLaunchGateDeps } from '../execution-loop/launch-gate.js';
@@ -30,8 +30,10 @@ import { CODEX_PROVIDER, GROK_PROVIDER, decideBudget, decideLaunchBudget, readBu
30
30
  import { DEFAULT_FALLBACK_CHAIN } from '../oauth/fallback-chain.js';
31
31
  import { installDeliverableVerifyCliCommand, type InstallDeliverableVerifyCliDeps } from './deliverable-verify-cli.js';
32
32
  import { getUserConfig } from '../user-config.js';
33
+ import { resolveRepositoryName } from './repository-name.js';
33
34
  import { installHarnessCliSinkHook } from './harness-cli-sink.js';
34
35
  import { addHarnessQueue, listHarnessQueue, queueSeatForCwd, reconcileHarnessQueue, removeHarnessQueue, tickHarnessQueue, type HarnessQueueDeps, type QueueSeat } from './harness-queue.js';
36
+ import { parseDoorSince, queryLaunchDoors, renderLaunchDoors, type DoorTable } from './launch-stamp.js';
35
37
  import { resolveHarnessSubstrate, type ResolvedHarnessSubstrate } from './harness-substrate-default.js';
36
38
  import { runHarnessPlanRfc } from './harness-plan-rfc.js';
37
39
  import { classifyGarbage, isGarbageProcessTarget, type GarbageProcess } from './process-garbage.js';
@@ -333,7 +335,7 @@ function assertHarnessChildLlmModel(opts: HarnessAskSayChildLlmOptions): void {
333
335
  }
334
336
 
335
337
  /** Shared post-parse gate: validate child model before any ask/say early return (including `--dry-run`). */
336
- type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?: string; after?: string; autoMerge?: boolean; base?: string; json?: boolean; target?: string; source?: string; seat?: QueueSeat };
338
+ type HarnessSubstrateOpts = { substrate?: 'local' | 'pod'; podPool?: string; podMemory?: 'lite' | 'standard' | 'high'; after?: string; autoMerge?: boolean; base?: string; json?: boolean; target?: string; source?: string; seat?: QueueSeat };
337
339
  function resolveLaunchSubstrate(opts: HarnessSubstrateOpts): ResolvedHarnessSubstrate {
338
340
  const resolved = resolveHarnessSubstrate({ flag: opts, config: getUserConfig(), env: process.env });
339
341
  debug.log('harness.substrate', 'resolved', resolved);
@@ -552,9 +554,23 @@ function hasHarnessPodJobApplied(runId: string | undefined, output: string): boo
552
554
  } finally { store.close(); }
553
555
  }
554
556
 
557
+ /** 10-05 PODPROVIDER — say which child provider a local run will use (an unnamed choice follows config). */
558
+ function announceLocalChildProvider(opts: unknown): void {
559
+ const child = opts as HarnessAskSayChildLlmOptions;
560
+ if (!child.childLlmProvider?.trim()) return;
561
+ console.error(`child provider 요청 = ${child.childLlmProvider.trim()}${child.childLlmModel?.trim() ? `/${child.childLlmModel.trim()}` : ''} (local)`);
562
+ }
563
+
564
+ /** `harness.authorOnPod` — 명시 true 만 켠다. 읽기 실패는 끔(호스트 저작)으로 둔다. */
565
+ function harnessAuthorOnPod(): boolean {
566
+ try { return getUserConfig().harness?.authorOnPod === true; }
567
+ catch { return false; }
568
+ }
569
+
555
570
  /** ⭐ 런 계약의 실행 칸 = pod — 호스트는 그래프를 안 돌리고 Pod 로 보낸다(harness-pod-dispatch.ts). */
556
571
  async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-say', input: string, pool: string,
557
- recordDispatch: (input: DispatchTaskInput, deps?: DispatchTaskDeps) => ReturnType<typeof dispatchTask> = dispatchTask): Promise<void> {
572
+ recordDispatch: (input: DispatchTaskInput, deps?: DispatchTaskDeps) => ReturnType<typeof dispatchTask> = dispatchTask,
573
+ authorOnPod = false): Promise<void> {
558
574
  const o = opts as HarnessSubstrateOpts;
559
575
  if (o.target !== undefined) {
560
576
  debug.log('harness.pod', 'target-refused', { target: o.target });
@@ -588,7 +604,13 @@ async function onPod(opts: unknown, entrance: 'cli-harness-ask' | 'cli-harness-s
588
604
  const { dispatchHarnessOnPod } = await import('./harness-pod-dispatch.js');
589
605
  let output = '';
590
606
  let runId: string | undefined;
591
- const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}), ...(o.seat ? { seat: o.seat } : {}) },
607
+ const child = opts as HarnessAskSayChildLlmOptions;
608
+ // 10-05 PODPROVIDER: say which child provider the Pod will use when the launch named one.
609
+ if (child.childLlmProvider?.trim()) console.error(`child provider 요청 = ${child.childLlmProvider.trim()}${child.childLlmModel?.trim() ? `/${child.childLlmModel.trim()}` : ''} (pod · 자격 없으면 발사 전 거부)`);
610
+ const status = await dispatchHarnessOnPod({ entrance, input, podPool: pool, ...(authorOnPod && entrance === 'cli-harness-say' ? { authorOnPod: true } : {}),
611
+ ...(child.childLlmProvider?.trim() ? { childLlmProvider: child.childLlmProvider.trim() } : {}),
612
+ ...(child.childLlmModel?.trim() ? { childLlmModel: child.childLlmModel.trim() } : {}),
613
+ ...(child.childLlmEffort?.trim() ? { childLlmEffort: child.childLlmEffort.trim() } : {}), ...(dispatchRecorded ? { dispatchRecorded: true } : {}), ...(o.podMemory ? { podMemory: o.podMemory } : {}), ...((opts as HarnessAskSayChildLlmOptions).goalType ? { goalType: (opts as HarnessAskSayChildLlmOptions).goalType } : {}), ...(o.after ? { after: o.after } : {}), ...(o.autoMerge === false ? { autoMerge: false } : {}), ...(o.base ? { base: o.base } : {}), ...(o.json ? { json: true } : {}), ...(o.source ? { source: o.source } : {}), ...(o.seat ? { seat: o.seat } : {}) },
592
614
  { onOutput: (text) => {
593
615
  runId ??= harnessPodRunId(output + text);
594
616
  output = (output + text).slice(-16_000);
@@ -1631,6 +1653,12 @@ export async function runHarnessBudget(opts: { json?: boolean } = {}, io: {
1631
1653
 
1632
1654
  const DRAFT_SWEEP_REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
1633
1655
 
1656
+ /** A review body still names a must-fix. «must-fix 0» and «must-fix: 0» are a cleared list, not a remaining one. */
1657
+ function mustFixRemains(text: string): boolean {
1658
+ if (!/(?:^|\n)\s*(?:[-*]\s*)?(?:must-fix|must fix)\b/i.test(text)) return false;
1659
+ return !/(?:^|\n)\s*(?:[-*]\s*)?(?:must-fix|must fix)\s*[:=]?\s*0\b/i.test(text);
1660
+ }
1661
+
1634
1662
  type GithubPull = {
1635
1663
  number: number;
1636
1664
  title: string;
@@ -1814,6 +1842,36 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
1814
1842
  }
1815
1843
  return undefined;
1816
1844
  };
1845
+ const reviewGateForDraft = (draft: SweepDraft, repository: string): SweepReviewGate | undefined => {
1846
+ const head = ghJson<{ head?: { sha?: string } }>(['api', `repos/${repository}/pulls/${draft.number}`], execute);
1847
+ const sha = head?.head?.sha;
1848
+ if (!sha) return undefined;
1849
+ const comments: string[] = [];
1850
+ for (let page = 1; page <= 100; page++) {
1851
+ const batch = ghJson<Array<{ body?: string }>>(['api', `repos/${repository}/issues/${draft.number}/comments?per_page=100&page=${page}`], execute);
1852
+ if (!Array.isArray(batch) || batch.length > 100) throw new Error('Incomplete review comment inventory');
1853
+ comments.push(...batch.map((comment) => comment.body ?? ''));
1854
+ if (batch.length < 100) break;
1855
+ if (page === 100) throw new Error('Review comment inventory exceeded pagination limit');
1856
+ }
1857
+ const reviews = ghJson<Array<{ state?: string; body?: string }>>(['api', `repos/${repository}/pulls/${draft.number}/reviews?per_page=100`], execute);
1858
+ if (!Array.isArray(reviews) || reviews.length > 100) throw new Error('Incomplete review inventory');
1859
+ const text = [...comments, ...reviews.map((review) => review.body ?? '')].join('\n');
1860
+ const status = ghJson<{ state?: string }>(['api', `repos/${repository}/commits/${sha}/status`], execute);
1861
+ const checkRuns = ghJson<{ check_runs?: Array<{ conclusion?: string | null }> }>(
1862
+ ['api', `repos/${repository}/commits/${sha}/check-runs?per_page=100`], execute);
1863
+ if (!checkRuns || !Array.isArray(checkRuns.check_runs)) throw new Error('Incomplete check-run inventory');
1864
+ const conclusions = checkRuns.check_runs.map((run) => run.conclusion ?? null);
1865
+ const gatePass = (status?.state === 'success' || status?.state === undefined)
1866
+ && conclusions.length > 0 && conclusions.every((conclusion) => conclusion === 'success' || conclusion === 'skipped' || conclusion === 'neutral');
1867
+ const gateFail = status?.state === 'failure' || status?.state === 'error'
1868
+ || conclusions.some((conclusion) => conclusion === 'failure' || conclusion === 'cancelled' || conclusion === 'timed_out' || conclusion === 'action_required');
1869
+ return {
1870
+ ...(reviews.some((review) => review.state === 'APPROVED') || /(?:^|\n)\s*VERDICT:\s*PASS\b/i.test(text) ? { review: 'pass' as const } : {}),
1871
+ ...(mustFixRemains(text) ? { mustFix: true } : {}),
1872
+ ...(gatePass && !gateFail ? { gate: 'pass' as const } : gateFail ? { gate: 'fail' as const } : {}),
1873
+ };
1874
+ };
1817
1875
  const landingCommentsFor = (repository: string, number: number): string[] => {
1818
1876
  const comments: string[] = [];
1819
1877
  for (let page = 1; page <= 100; page++) {
@@ -1902,6 +1960,7 @@ export function githubDraftSweepAdapters(execute: GhExecute = executeGh, git: Dr
1902
1960
  execute(['pr', 'close', String(number), '--repo', repository, '--comment', comment]);
1903
1961
  },
1904
1962
  getClaimOwner: async (repository, number) => claimCommentOwner(repository, number, execute),
1963
+ getReviewGate: async (draft, repository) => reviewGateForDraft(draft, repository),
1905
1964
  };
1906
1965
  }
1907
1966
 
@@ -1936,7 +1995,10 @@ function installHarnessDraftSweepCommand(harnessCmd: Command, deps: HarnessDraft
1936
1995
  .action(async (number: string, opts: { owner?: string; note?: string; release?: boolean; repo?: string }) => {
1937
1996
  try {
1938
1997
  const execute = deps.execute ?? executeGh;
1939
- const repository = opts.repo ?? deps.repository?.() ?? execute(['repo', 'view', '--json', 'nameWithOwner', '--jq', '.nameWithOwner']).trim();
1998
+ const repository = resolveRepositoryName({
1999
+ repo: opts.repo ?? deps.repository?.(),
2000
+ executeGh: (args) => execute(args),
2001
+ });
1940
2002
  if (!DRAFT_SWEEP_REPOSITORY.test(repository) || repository.includes('..')) throw new HarnessCliInputError(`invalid --repo (expected owner/name): ${repository}`);
1941
2003
  if (!/^[1-9]\d*$/.test(number) || !Number.isSafeInteger(Number(number))) throw new HarnessCliInputError(`invalid PR number: ${number}`);
1942
2004
  const owner = opts.owner?.trim();
@@ -1973,21 +2035,32 @@ function installHarnessDraftSweepCommand(harnessCmd: Command, deps: HarnessDraft
1973
2035
  .action(async (opts: { apply?: boolean; json?: boolean; repo?: string }) => {
1974
2036
  try {
1975
2037
  const write = deps.write ?? console.log;
1976
- const repository = opts.repo ?? deps.repository?.() ?? execFileSync('gh', ['repo', 'view', '--json', 'nameWithOwner', '--jq', '.nameWithOwner'],
1977
- { encoding: 'utf8', timeout: 30_000, stdio: ['ignore', 'pipe', 'pipe'], env: process.env }).trim();
2038
+ const execute = deps.execute ?? executeGh;
2039
+ const repository = resolveRepositoryName({
2040
+ repo: opts.repo ?? deps.repository?.(),
2041
+ executeGh: (args) => execute(args),
2042
+ });
1978
2043
  if (!DRAFT_SWEEP_REPOSITORY.test(repository) || repository.includes('..')) {
1979
2044
  throw new HarnessCliInputError(`invalid --repo (expected owner/name): ${repository}`);
1980
2045
  }
1981
2046
  const result: DraftSweepResult = await runDraftSweep({ repository, apply: opts.apply === true, adapters: deps.adapters ?? githubDraftSweepAdapters(deps.execute) });
1982
- if (opts.json) write(JSON.stringify(result));
2047
+ const reason = result.error ? sweepFailureReason(result.error) : undefined;
2048
+ if (opts.json) write(JSON.stringify(reason ? { ...result, error: reason } : result));
1983
2049
  else {
1984
- write(`draft sweep ${result.repository}: ${result.complete ? 'complete' : `incomplete (${result.error})`} · ${result.apply ? 'apply' : 'dry-run'}`);
1985
- for (const entry of result.entries) write(`#${entry.number} ${entry.action}: ${entry.reason}${entry.statusLabel ? ` → ${entry.statusLabel}` : ''}${entry.error ? ` ERROR: ${entry.error}` : ''}`);
1986
- if (result.complete) write(`못 본 초안 ${result.unobserved ?? 0} · 이번에 닫음 ${result.closed ?? 0}${result.apply ? '' : '(dry-run)'} · 처리 중 ${result.claimed ?? 0} · 표식 만료 ${result.claimExpired ?? 0}`);
2050
+ write(`draft sweep ${result.repository}: ${result.complete ? 'complete' : `incomplete (${reason})`} · ${result.apply ? 'apply' : 'dry-run'}`);
2051
+ for (const entry of result.entries) write(`#${entry.number} ${entry.action}: ${entry.reason}${entry.statusLabel ? ` → ${entry.statusLabel}` : ''}${entry.error ? ` ERROR: ${sweepFailureReason(entry.error)}` : ''}`);
2052
+ if (result.complete) {
2053
+ const daily = result.daily
2054
+ ? ` · 24h 넘음 ${result.daily.over24h} · 정리 대상 ${result.daily.closable} · 수확 대기 ${result.daily.harvestable} · 막힘 ${result.daily.blocked}`
2055
+ : '';
2056
+ write(`못 본 초안 ${result.unobserved ?? 0} · 이번에 닫음 ${result.closed ?? 0}${result.apply ? '' : '(dry-run)'} · 처리 중 ${result.claimed ?? 0} · 표식 만료 ${result.claimExpired ?? 0}${daily}`);
2057
+ }
2058
+ if (reason) write(reason);
1987
2059
  }
1988
2060
  if (!result.complete || result.entries.some((entry) => entry.error)) process.exitCode = 1;
1989
2061
  } catch (error) {
1990
2062
  console.error(humanErrorLine(error));
2063
+ try { debug.log('drafts.cleanup', 'failed', { reason: sweepFailureReason(error) }); } catch { /* observation is fail-soft */ }
1991
2064
  process.exitCode = 1;
1992
2065
  }
1993
2066
  });
@@ -2248,6 +2321,27 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2248
2321
  .command('harness')
2249
2322
  .description('dev-harness worktree 수명 — 워크트리 생성(worktree add)·조회(worktrees)·정리(clean)·프로세스 관찰(processes)');
2250
2323
  installHarnessCliSinkHook(harnessCmd, deps.registerSink, deps.resolveSurface);
2324
+ harnessCmd.command('doors').description('기간 안 런을 입구×대기열 경유 표로 읽는다')
2325
+ .option('--since <window>', '조회 창 (30m · 12h · 1d, 기본 1d)', '1d')
2326
+ .option('--json', '구조화 출력')
2327
+ .action((opts: { since?: string; json?: boolean }) => {
2328
+ const parsed = parseDoorSince(opts.since);
2329
+ if ('error' in parsed) {
2330
+ console.error(`❌ ${parsed.error}`);
2331
+ process.exitCode = 2;
2332
+ return;
2333
+ }
2334
+ let table: DoorTable;
2335
+ try {
2336
+ table = queryLaunchDoors({ sinceMs: parsed.sinceMs });
2337
+ } catch (error) {
2338
+ console.error(`❌ doors: ${error instanceof Error ? error.message : String(error)}`);
2339
+ process.exitCode = 1;
2340
+ return;
2341
+ }
2342
+ if (opts.json) console.log(JSON.stringify(table));
2343
+ else console.log(renderLaunchDoors(table));
2344
+ });
2251
2345
  harnessCmd.command('incidents').description('최근 하니스 Pod 종료 사고 원장을 읽는다')
2252
2346
  .option('--since <minutes>', '최근 몇 분의 종료를 볼지 (기본 15분)', '15')
2253
2347
  .option('--json', '구조화 출력')
@@ -2294,7 +2388,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2294
2388
  console.log(`${row.id} ${row.seat} ${row.kind} queued`);
2295
2389
  }));
2296
2390
  queue.command('list').description('대기·발사 원장 조회').action(() => queueAction(async () => {
2297
- for (const row of listHarnessQueue(queueDeps)) console.log(`${row.id} ${row.seat} ${row.status} ${row.kind} ${row.input}`);
2391
+ for (const row of listHarnessQueue(queueDeps)) console.log(`${row.id} ${row.seat} ${row.status} ${row.kind} ${row.input}${row.waitingReason ? ` · ${row.waitingReason}` : ''}`);
2298
2392
  }));
2299
2393
  queue.command('remove <id>').description('대기 중 또는 종료 확인된 항목 제거').action((id: string) => queueAction(async () => {
2300
2394
  if (!await removeHarnessQueue(id, queueDeps)) throw new Error(`queue item active or not found: ${id}`);
@@ -2303,7 +2397,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2303
2397
  queue.command('reconcile <id>').description('불확정 발사를 확인하고 종료 또는 미발사 증거가 있으면 예약 해소').action((id: string) => queueAction(async () => {
2304
2398
  console.log(`${id} ${await reconcileHarnessQueue(id, queueDeps)}`);
2305
2399
  }));
2306
- queue.command('tick').description('맨 앞 한 건의 자리 몫과 Pod 풀 여유를 확인하고 발사').action(() => queueAction(async () => {
2400
+ queue.command('tick').description('자리별 맨 앞 한 건을 라운드로빈으로 돌며 자리 몫·Pod 풀 여유·마무리 관문을 확인하고 한 건 발사').action(() => queueAction(async () => {
2307
2401
  const result = await tickHarnessQueue(queueDeps);
2308
2402
  console.log(`${result.outcome}${result.item ? ` ${result.item.id}` : ''}: ${result.reason}`);
2309
2403
  }));
@@ -2328,7 +2422,7 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2328
2422
  process.exitCode = 2;
2329
2423
  return Promise.resolve();
2330
2424
  }
2331
- return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : ask(goalPath, normalizeHarnessAskSayOptions(stampedOpts));
2425
+ return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-ask', goalPath, resolved.pool!, deps.podDispatchTask) : (announceLocalChildProvider(stampedOpts), ask(goalPath, normalizeHarnessAskSayOptions(stampedOpts)));
2332
2426
  },
2333
2427
  deps.launchGate,
2334
2428
  );
@@ -2353,7 +2447,9 @@ export function installHarnessCliCommand(program: Command, deps: HarnessCliComma
2353
2447
  process.exitCode = 2;
2354
2448
  return Promise.resolve();
2355
2449
  }
2356
- return resolved.substrate === 'pod' ? onPod(stampedOpts, 'cli-harness-say', sentence.join(' '), resolved.pool!, deps.podDispatchTask) : say(sentence, normalizeHarnessAskSayOptions(stampedOpts));
2450
+ return resolved.substrate === 'pod'
2451
+ ? onPod(stampedOpts, 'cli-harness-say', sentence.join(' '), resolved.pool!, deps.podDispatchTask, harnessAuthorOnPod())
2452
+ : (announceLocalChildProvider(stampedOpts), say(sentence, normalizeHarnessAskSayOptions(stampedOpts)));
2357
2453
  },
2358
2454
  deps.launchGate,
2359
2455
  );
@@ -2,6 +2,7 @@ import { Database } from 'bun:sqlite';
2
2
  import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { redactSecretText } from '../debug/log.js';
5
+ import { refuseProductionLedgerWriteInTest } from './ledger-write-guard.js';
5
6
 
6
7
  export interface RunExit {
7
8
  runId: string;
@@ -83,6 +84,7 @@ export function readRunExits(root: string): RunExit[] {
83
84
 
84
85
  /** SQLite's process-owned write lock serializes ledger checks and appends across processes. */
85
86
  export function recordRunExit(row: RunExit, root: string): void {
87
+ if (refuseProductionLedgerWriteInTest(root, 'incidents')) return;
86
88
  if (!row.runId?.trim()) return;
87
89
  const at = Date.parse(row.at);
88
90
  if (!Number.isFinite(at)) throw new Error('invalid incident exit time');
@@ -14,6 +14,11 @@ import { findGitDir } from '../git-fs/locate.js';
14
14
  import { debug } from '../debug/log.js';
15
15
  import { ELANOUS_ENTRY_SCRIPT } from '../self-implement/seams.js';
16
16
  import { parsePodSourceSpec } from '../task-orchestrator/surfaces/pod-source-spec.js';
17
+ import { declaredGoalType, type GoalType } from '../self-implement/goal-author.js';
18
+ import { appendRunLedgerEntry } from '../self-implement/run-ledger.js';
19
+ import { HARNESS_RUN_ID_ENV } from './harness-space.js';
20
+ import { podMemoryLimitFor, type PodMemoryTier } from '../task-orchestrator/surfaces/self-implement-pod.js';
21
+ import { selectPodMemoryTier } from './pod-memory-policy.js';
17
22
 
18
23
  export interface HarnessPodDispatchInput {
19
24
  readonly entrance: 'cli-harness-ask' | 'cli-harness-say';
@@ -23,8 +28,9 @@ export interface HarnessPodDispatchInput {
23
28
  readonly seat?: 'OP' | 'TC' | 'MK' | 'UX';
24
29
  /** Host already recorded the observe-only dispatch; suppress a second decision in the Pod. */
25
30
  readonly dispatchRecorded?: boolean;
26
- /** Pod 메모리 등급(standard|high) — 오케스트레이터 환경 `ELANOUS_POD_MEMORY_TIER` 로 Job 까지 간다. */
27
- readonly podMemory?: string;
31
+ /** Pod 메모리 등급(lite|standard|high) — 오케스트레이터 환경 `ELANOUS_POD_MEMORY_TIER` 로 Job 까지 간다. */
32
+ readonly podMemory?: PodMemoryTier;
33
+ readonly goalType?: GoalType;
28
34
  /** L7c — Pod queue predecessor (goal ID or PR) — `ELANOUS_POD_AFTER` carries it to every Pod Job admission. */
29
35
  readonly after?: string;
30
36
  readonly autoMerge?: boolean;
@@ -32,6 +38,50 @@ export interface HarnessPodDispatchInput {
32
38
  readonly json?: boolean;
33
39
  /** 사람이 고른 원천 spec — `commit:` · `pr:` · `worktree:` · `files:`. 검증은 발사 전. */
34
40
  readonly source?: string;
41
+ /** `--child-llm-provider/--child-llm-model/--child-llm-effort` — carried to `self orchestrate` so the Pod child uses them (10-05 PODPROVIDER). */
42
+ readonly childLlmProvider?: string;
43
+ readonly childLlmModel?: string;
44
+ readonly childLlmEffort?: string;
45
+ /** `harness.authorOnPod` — say 문장을 Pod 안에서 저작부터 돌린다. 호스트는 영수증만 남긴다. */
46
+ readonly authorOnPod?: boolean;
47
+ }
48
+
49
+ /** 골 본문에 넣을 문장 — 코드 펜스가 문장을 삼키지 않게 백틱 런을 늘린다. */
50
+ export function fenceVerbatimSentence(sentence: string): { fence: string; body: string } {
51
+ const body = sentence.replace(/\r\n/g, '\n');
52
+ const longest = body.match(/`+/g)?.reduce((n, run) => Math.max(n, run.length), 0) ?? 0;
53
+ return { fence: '`'.repeat(Math.max(3, longest + 1)), body };
54
+ }
55
+
56
+ /** Pod 가 문장 그대로 `harness say` 를 돌리게 하는 골. 자격·풀은 기존 Pod 주입을 그대로 쓴다. */
57
+ export function authorOnPodGoal(sentence: string): string {
58
+ const { fence, body } = fenceVerbatimSentence(sentence);
59
+ return [
60
+ '# 골: Pod 안에서 harness say 저작',
61
+ '',
62
+ '- GoalType: implement',
63
+ '- 대상 경로: src/harness/harness-pod-dispatch.ts',
64
+ '',
65
+ '## 할 일',
66
+ '',
67
+ '이 Pod 안에서, 그리고 이 Pod 안에서만, 아래 펜스의 문장을 한 글자도 바꾸지 않고',
68
+ '`elanous harness say --substrate local` 의 인자로 실행한다.',
69
+ '그 한 발이 골 저작·리뷰·게이트·구현·PR 까지 이 Pod 안에서 끝낸다.',
70
+ '호스트에는 발사 영수증과 원장만 남는다. 호스트에서 저작·리뷰·게이트를 지휘하지 않는다.',
71
+ '저작 자격은 이 Job 에 이미 주입된 Pod 자격 규칙을 그대로 쓴다.',
72
+ '',
73
+ '## 문장 (verbatim)',
74
+ '',
75
+ fence,
76
+ body,
77
+ fence,
78
+ '',
79
+ '## 수용 기준',
80
+ '',
81
+ '- 위 펜스 문장만으로 `elanous harness say --substrate local` 한 발이 이 Pod 에서 저작부터 PR 까지 간다.',
82
+ '- 그 명령의 종료 코드가 0 이 아니면 이 골은 실패다.',
83
+ '',
84
+ ].join('\n');
35
85
  }
36
86
 
37
87
  export function podOrchestrateArgs(input: HarnessPodDispatchInput, goalFile: string): string[] {
@@ -44,6 +94,9 @@ export function podOrchestrateArgs(input: HarnessPodDispatchInput, goalFile: str
44
94
  ...(input.base ? ['--base', input.base] : []),
45
95
  ...(input.json ? ['--json'] : []),
46
96
  ...(input.source ? ['--pod-source', input.source] : []),
97
+ ...(input.childLlmProvider?.trim() ? ['--child-llm-provider', input.childLlmProvider.trim()] : []),
98
+ ...(input.childLlmModel?.trim() ? ['--child-llm-model', input.childLlmModel.trim()] : []),
99
+ ...(input.childLlmEffort?.trim() ? ['--child-llm-effort', input.childLlmEffort.trim()] : []),
47
100
  '--concurrency', '1',
48
101
  ];
49
102
  }
@@ -63,34 +116,69 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
63
116
  return 2;
64
117
  }
65
118
  }
119
+ const env = { ...process.env };
66
120
  let tempDir: string | undefined;
67
- let goalFile: string;
68
- let goalChars: number;
121
+ let goalFile = '';
122
+ let goalText: string;
69
123
  if (input.entrance === 'cli-harness-ask') {
70
124
  goalFile = resolve(cwd, input.input);
71
- goalChars = (deps.readFile ?? ((p) => readFileSync(p, 'utf8')))(goalFile).length;
125
+ goalText = (deps.readFile ?? ((p) => readFileSync(p, 'utf8')))(goalFile);
126
+ } else if (input.authorOnPod) {
127
+ const goal = authorOnPodGoal(input.input);
128
+ goalText = goal;
129
+ const root = findGitDir(cwd)?.root ?? cwd;
130
+ const runsDir = join(root, 'docs', 'goals');
131
+ mkdirSync(runsDir, { recursive: true });
132
+ tempDir = mkdtempSync(join(runsDir, '.pod-author-'));
133
+ goalFile = join(tempDir, 'GOAL-pod-author-2026-10-05.md');
134
+ writeFileSync(goalFile, goal, { mode: 0o600 });
135
+ const runId = env[HARNESS_RUN_ID_ENV]?.trim();
136
+ const receipt = { entrance: input.entrance, host: 'receipt-only', authorOnPod: true, goalFile };
137
+ debug.log('harness.substrate', 'author-on-pod-receipt', receipt);
138
+ if (runId) {
139
+ try { appendRunLedgerEntry({ runId, event: 'author-on-pod-receipt', data: receipt }); }
140
+ catch (error) { debug.log('harness.substrate', 'author-on-pod-receipt-unwritten', { reason: error instanceof Error ? error.message : String(error) }); }
141
+ }
72
142
  } else {
73
143
  const goal = input.input;
144
+ goalText = goal;
74
145
  const runsDir = selfDevRunsDir();
75
146
  mkdirSync(runsDir, { recursive: true });
76
147
  tempDir = mkdtempSync(join(runsDir, 'pod-goal-'));
77
148
  goalFile = join(tempDir, 'goal.txt');
78
149
  writeFileSync(goalFile, goal, { mode: 0o600 });
79
- goalChars = goal.length;
80
150
  }
151
+ const goalChars = goalText.length;
81
152
  const args = podOrchestrateArgs(input, goalFile);
82
153
  debug.log('harness.substrate', 'goal-file', { entrance: input.entrance, mode: tempDir ? 'temp' : 'path' });
83
- const env = { ...process.env };
154
+ const goalType = input.goalType ?? declaredGoalType(goalText);
155
+ // A process-level memory tier is already an explicit selection; do not replace it with a goal-type default.
156
+ const existing = podMemoryLimitFor(goalText, env);
157
+ const selection = !input.podMemory && ['option', 'goal-line', 'pwa-auto', 'advise'].includes(existing.source)
158
+ ? { tier: existing.tier, reason: 'existing-tier' as const }
159
+ : goalType || input.podMemory ? selectPodMemoryTier(goalType ?? 'implement', input.podMemory, existing.tier) : undefined;
160
+ if (selection) {
161
+ env.ELANOUS_POD_MEMORY_TIER = selection.tier;
162
+ env.ELANOUS_POD_MEMORY_REASON = selection.reason;
163
+ } else {
164
+ delete env.ELANOUS_POD_MEMORY_REASON;
165
+ }
166
+ if (goalType) env.ELANOUS_POD_GOAL_TYPE = goalType;
167
+ else delete env.ELANOUS_POD_GOAL_TYPE;
84
168
  if (input.seat) env.ELANOUS_HARNESS_SEAT = input.seat;
85
169
  delete env.ELANOUS_POD_GOAL_DOC;
86
170
  delete env.ELANOUS_DISPATCH_RECORDED;
87
171
  if (input.dispatchRecorded) env.ELANOUS_DISPATCH_RECORDED = '1';
88
- if (input.podMemory) env.ELANOUS_POD_MEMORY_TIER = input.podMemory;
172
+ debug.log('harness.substrate', 'memory-selected', { entrance: input.entrance, goalType,
173
+ tier: selection?.tier ?? existing.tier, reason: selection?.reason ?? existing.source,
174
+ memoryLimit: podMemoryLimitFor(goalText, env).limit });
89
175
  delete env.ELANOUS_POD_AFTER;
90
176
  if (input.after) env.ELANOUS_POD_AFTER = input.after;
91
- if (input.entrance === 'cli-harness-ask') {
177
+ if (input.authorOnPod) env.ELANOUS_POD_AUTHOR_ON_POD = '1';
178
+ else delete env.ELANOUS_POD_AUTHOR_ON_POD;
179
+ if (input.entrance === 'cli-harness-ask' || input.authorOnPod) {
92
180
  const root = findGitDir(cwd)?.root;
93
- const path = resolve(cwd, input.input);
181
+ const path = input.authorOnPod ? goalFile : resolve(cwd, input.input);
94
182
  const relativePath = root ? relative(realpathSync(root), realpathSync(path)) : '';
95
183
  if (relativePath && relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)) {
96
184
  env.ELANOUS_POD_GOAL_DOC = relativePath;
@@ -100,6 +188,7 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
100
188
  }
101
189
  debug.log('harness.substrate', 'dispatch-pod', {
102
190
  entrance: input.entrance, podPool: input.podPool ?? null, podMemory: input.podMemory ?? null, after: input.after ?? null, autoMerge: input.autoMerge !== false,
191
+ childLlmProvider: input.childLlmProvider ?? null, childLlmModel: input.childLlmModel ?? null,
103
192
  goalChars, ...(input.entrance === 'cli-harness-ask' ? { goalPath: input.input } : {}),
104
193
  });
105
194
  const run = deps.run ?? ((cmd: string, a: readonly string[], childEnv: NodeJS.ProcessEnv) => new Promise<number>((resolveStatus) => {
@@ -119,7 +208,7 @@ export function dispatchHarnessOnPod(input: HarnessPodDispatchInput, deps: PodDi
119
208
  child.once('error', () => { cleanup(); resolveStatus(1); });
120
209
  child.once('close', (code, signal) => { cleanup(); resolveStatus(code ?? ((signal ?? forwarded) === 'SIGINT' ? 130 : 143)); });
121
210
  }));
122
- const cleanup = () => { if (tempDir) rmSync(tempDir, { recursive: true, force: true }); };
211
+ const cleanup = () => { if (tempDir && !input.authorOnPod) rmSync(tempDir, { recursive: true, force: true }); };
123
212
  try {
124
213
  const status = run(process.execPath, args, env);
125
214
  if (typeof status === 'number' || status === null) {