elanous 0.2.38 → 0.2.40

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 (615) 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/{RrjXTwz-urRAbChP1bX02 → HjeK7zONHfR7op1hKXp4k}/_buildManifest.js +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{7954.4f7a8832663ee979.js → 1060.014f1f01a17aec26.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/{8911.bf5e712acb25fb6c.js → 1281.d81877e35b9068f4.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/{2104.70e086d5174a4736.js → 1350.59f40e39e02d91b1.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +286 -0
  10. package/apps/pwa/out/_next/static/chunks/1537-6bb97b5214928ea9.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +149 -0
  12. package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +90 -0
  14. package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +104 -0
  15. package/apps/pwa/out/_next/static/chunks/2047.8f23e210995d4548.js +2 -0
  16. package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/{1632.7d87f998e714bede.js → 2066.a77257c8c350770a.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/{8019.21ca554a821dad01.js → 2177.df8547f9d7215c4e.js} +3 -3
  20. package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/{3528.e4b8ab70d339f965.js → 2226.2cf61ec781827219.js} +2 -2
  22. package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{6493.27ad77de7aac7966.js → 2499.02fbfd18126c10c0.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/{7628.6b16863ac3972333.js → 2522.f2766b76d22c8bdf.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/{3086.fb14b60ce98b53d3.js → 2566.beaffc3a5e1fcd2e.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/{9299.24e2a7d9b6d1f91b.js → 261.0d471e8aded799f3.js} +3 -3
  27. package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +27 -0
  30. package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/2774-719820e3f5b2a3eb.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/2829-734fe8bf2eee7347.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{7524.ce973525d30b3d52.js → 3330.7443183b7e6fdbdf.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/{3124-561f7f037943cde2.js → 3746-f80a78f220bf37b5.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +24 -0
  45. package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +77 -0
  46. package/apps/pwa/out/_next/static/chunks/{bb6987f9.3647e1a23b0e3217.js → 3b355b3d.6f73c98bf6f9c749.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +36 -0
  48. package/apps/pwa/out/_next/static/chunks/{8994d93e.33ba491f95345fc2.js → 409c787b.e48c5bec8ec10456.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/{5041-5aada92ca215ffdd.js → 413-24c8284bda08241d.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +287 -0
  52. package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/4351.0f37a6db2b34d50b.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/{5638.107314dc479e2f37.js → 470.51a72a2348306455.js} +2 -2
  56. package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/{8487.291666583bebe640.js → 4769.9fd582071301f382.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/{8509.3a54d4e61893d7cb.js → 483.725e4a61be922788.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/{9fe1b809.738da85c8b17e3fb.js → 4864a2ca.1c1c280a7772c5e5.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +74 -0
  63. package/apps/pwa/out/_next/static/chunks/{8879.ac06755bcf3e44a1.js → 5383.bdfa5d886e39a826.js} +1 -1
  64. package/apps/pwa/out/_next/static/chunks/{6142.913b44e260a3f925.js → 5436.fdf6fa8ae3e9588d.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +8 -0
  70. package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +62 -0
  72. package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +94 -0
  76. package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/{7161-8f22db3ec5ed005e.js → 6051-0ae955eedb10adb9.js} +3 -3
  78. package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/{3291.f6c1005387d514bd.js → 6257.c0037b8949239f5e.js} +2 -2
  80. package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +32 -0
  85. package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/{5203.51e5d8b5f1dcfe53.js → 6749.0744ce6eb08d40e9.js} +3 -3
  87. package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/{1938.0e6c60ee91c4d7f5.js → 6908.06008c67f6380ea9.js} +2 -2
  90. package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/{de8b930d.d94c0f42acf807fc.js → 6cc1d1d6.9a98dd4622f1afc6.js} +1 -1
  93. package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/7219-15bdd9a80661af8e.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +93 -0
  96. package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +24 -0
  100. package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/7611.85f045bf232053ac.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/8099.1cccf31c1baa7e1c.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/{1026-44010f405704050c.js → 8272-025653884f9645d2.js} +1 -1
  109. package/apps/pwa/out/_next/static/chunks/{5307.d7328020227880c2.js → 8357.1aaa713b052a78b1.js} +4 -4
  110. package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +1 -0
  111. package/apps/pwa/out/_next/static/chunks/{4199-f7c6eb40cd0322f8.js → 8639-92c11d069792429e.js} +1 -1
  112. package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +1 -0
  113. package/apps/pwa/out/_next/static/chunks/8d173e38.18a2f7b4e1848bdb.js +1 -0
  114. package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +41 -0
  115. package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +1 -0
  116. package/apps/pwa/out/_next/static/chunks/9175.2de5209a5ce6f68a.js +1 -0
  117. package/apps/pwa/out/_next/static/chunks/9184.45afbf44fec87574.js +1 -0
  118. package/apps/pwa/out/_next/static/chunks/{eaad0cba.771200940c529df7.js → 9189505a.980427ac048c8c60.js} +1 -1
  119. package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +1 -0
  120. package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +1 -0
  121. package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +1 -0
  122. package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +79 -0
  123. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +1 -0
  124. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +1 -0
  125. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +1 -0
  126. package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +1 -0
  127. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-866aac5ff226a7f3.js → page-ba957ff875a026a0.js} +1 -1
  128. package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +1 -0
  129. package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +1 -0
  130. package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +1 -0
  131. package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +1 -0
  132. package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +1 -0
  133. package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +1 -0
  134. package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +1 -0
  135. package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +1 -0
  136. package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +1 -0
  137. package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +1 -0
  138. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +1 -0
  139. package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +1 -0
  140. package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +1 -0
  141. package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +1 -0
  142. package/apps/pwa/out/_next/static/chunks/app/live/page-4edb8df90f4537ce.js +1 -0
  143. package/apps/pwa/out/_next/static/chunks/app/live-run/page-83d27757e2b8060a.js +1 -0
  144. package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +1 -0
  145. package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +1 -0
  146. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +1 -0
  147. package/apps/pwa/out/_next/static/chunks/app/morning/page-fca2e59a81744d9e.js +1 -0
  148. package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +1 -0
  149. package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +1 -0
  150. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +1 -0
  151. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +1 -0
  152. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +1 -0
  153. package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +1 -0
  154. package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +1 -0
  155. package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +1 -0
  156. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +1 -0
  157. package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +1 -0
  158. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +1 -0
  159. package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +1 -0
  160. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +1 -0
  161. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +1 -0
  162. package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +1 -0
  163. package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +1 -0
  164. package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +1 -0
  165. package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +1 -0
  166. package/apps/pwa/out/_next/static/chunks/app/term/page-139d722de1dcd6c1.js +1 -0
  167. package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +1 -0
  168. package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +1 -0
  169. package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +1 -0
  170. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +1 -0
  171. package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +1 -0
  172. package/apps/pwa/out/_next/static/chunks/app/workspace/page-ccde99f07aaf71f7.js +1 -0
  173. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +1 -0
  174. package/apps/pwa/out/_next/static/chunks/{b7478fef.5917716286213fcc.js → b84cad00.594027ebe0531fe2.js} +1 -1
  175. package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +1 -0
  176. package/apps/pwa/out/_next/static/chunks/{b7459770.6539b11860eb8a9c.js → ca3702f8.2913182e73889fa2.js} +7 -7
  177. package/apps/pwa/out/_next/static/chunks/{3f0de3de-2340c532c00395db.js → d5d3cefc-373c4eb722ed848b.js} +1 -1
  178. package/apps/pwa/out/_next/static/chunks/{89d6c04f-6b8ab341748e468a.js → d8de0de2-dd0f4ac8e5ab1832.js} +1 -1
  179. package/apps/pwa/out/_next/static/chunks/{framework-4b12a946f352cee2.js → framework-002b472a66935d31.js} +1 -1
  180. package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +1 -0
  181. package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +1 -0
  182. package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +1 -0
  183. package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +1 -0
  184. package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +1 -0
  185. package/apps/pwa/out/approvals/index.html +1 -1
  186. package/apps/pwa/out/approvals/index.txt +22 -22
  187. package/apps/pwa/out/autopilot/index.html +1 -1
  188. package/apps/pwa/out/autopilot/index.txt +22 -22
  189. package/apps/pwa/out/board/index.html +1 -1
  190. package/apps/pwa/out/board/index.txt +22 -22
  191. package/apps/pwa/out/botlab/index.html +1 -1
  192. package/apps/pwa/out/botlab/index.txt +21 -21
  193. package/apps/pwa/out/bots/index.html +1 -1
  194. package/apps/pwa/out/bots/index.txt +21 -21
  195. package/apps/pwa/out/ceo/index.html +1 -1
  196. package/apps/pwa/out/ceo/index.txt +22 -22
  197. package/apps/pwa/out/chat/index.html +1 -1
  198. package/apps/pwa/out/chat/index.txt +21 -21
  199. package/apps/pwa/out/consult/index.html +1 -1
  200. package/apps/pwa/out/consult/index.txt +22 -22
  201. package/apps/pwa/out/control/index.html +1 -1
  202. package/apps/pwa/out/control/index.txt +22 -22
  203. package/apps/pwa/out/decisions/index.html +1 -1
  204. package/apps/pwa/out/decisions/index.txt +21 -21
  205. package/apps/pwa/out/design-check/index.html +1 -1
  206. package/apps/pwa/out/design-check/index.txt +22 -22
  207. package/apps/pwa/out/editor/index.html +1 -1
  208. package/apps/pwa/out/editor/index.txt +22 -22
  209. package/apps/pwa/out/exec/index.html +1 -1
  210. package/apps/pwa/out/exec/index.txt +22 -22
  211. package/apps/pwa/out/field/index.html +1 -1
  212. package/apps/pwa/out/field/index.txt +22 -22
  213. package/apps/pwa/out/healthz +1 -1
  214. package/apps/pwa/out/index.html +1 -1
  215. package/apps/pwa/out/index.txt +21 -21
  216. package/apps/pwa/out/inside/index.html +1 -1
  217. package/apps/pwa/out/inside/index.txt +21 -21
  218. package/apps/pwa/out/intake/index.html +1 -1
  219. package/apps/pwa/out/intake/index.txt +22 -22
  220. package/apps/pwa/out/live/index.html +1 -1
  221. package/apps/pwa/out/live/index.txt +22 -22
  222. package/apps/pwa/out/live-run/index.html +1 -1
  223. package/apps/pwa/out/live-run/index.txt +22 -22
  224. package/apps/pwa/out/loops/index.html +1 -1
  225. package/apps/pwa/out/loops/index.txt +21 -21
  226. package/apps/pwa/out/market/index.html +1 -1
  227. package/apps/pwa/out/market/index.txt +21 -21
  228. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  229. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  230. package/apps/pwa/out/morning/index.html +1 -1
  231. package/apps/pwa/out/morning/index.txt +22 -22
  232. package/apps/pwa/out/observatory/index.html +1 -1
  233. package/apps/pwa/out/observatory/index.txt +22 -22
  234. package/apps/pwa/out/ops/checklist/index.html +1 -1
  235. package/apps/pwa/out/ops/checklist/index.txt +21 -21
  236. package/apps/pwa/out/ops/release/index.html +1 -1
  237. package/apps/pwa/out/ops/release/index.txt +21 -21
  238. package/apps/pwa/out/ops/seats/index.html +1 -1
  239. package/apps/pwa/out/ops/seats/index.txt +21 -21
  240. package/apps/pwa/out/outputs/index.html +1 -1
  241. package/apps/pwa/out/outputs/index.txt +21 -21
  242. package/apps/pwa/out/reflection/index.html +1 -1
  243. package/apps/pwa/out/reflection/index.txt +22 -22
  244. package/apps/pwa/out/scheduler/index.html +1 -1
  245. package/apps/pwa/out/scheduler/index.txt +22 -22
  246. package/apps/pwa/out/sessions/index.html +1 -1
  247. package/apps/pwa/out/sessions/index.txt +21 -21
  248. package/apps/pwa/out/settings/devices/index.html +1 -1
  249. package/apps/pwa/out/settings/devices/index.txt +22 -22
  250. package/apps/pwa/out/settings/index.html +1 -1
  251. package/apps/pwa/out/settings/index.txt +22 -22
  252. package/apps/pwa/out/setup/done/index.html +1 -1
  253. package/apps/pwa/out/setup/done/index.txt +22 -22
  254. package/apps/pwa/out/setup/index.html +1 -1
  255. package/apps/pwa/out/setup/index.txt +22 -22
  256. package/apps/pwa/out/share/index.html +1 -1
  257. package/apps/pwa/out/share/index.txt +22 -22
  258. package/apps/pwa/out/showroom/index.html +1 -1
  259. package/apps/pwa/out/showroom/index.txt +21 -21
  260. package/apps/pwa/out/tasks/index.html +1 -1
  261. package/apps/pwa/out/tasks/index.txt +22 -22
  262. package/apps/pwa/out/term/index.html +1 -1
  263. package/apps/pwa/out/term/index.txt +22 -22
  264. package/apps/pwa/out/today/index.html +1 -1
  265. package/apps/pwa/out/today/index.txt +22 -22
  266. package/apps/pwa/out/trace/index.html +1 -1
  267. package/apps/pwa/out/trace/index.txt +22 -22
  268. package/apps/pwa/out/vault/index.html +1 -1
  269. package/apps/pwa/out/vault/index.txt +22 -22
  270. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  271. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  272. package/apps/pwa/out/workflows/index.html +1 -1
  273. package/apps/pwa/out/workflows/index.txt +22 -22
  274. package/apps/pwa/out/workspace/index.html +1 -1
  275. package/apps/pwa/out/workspace/index.txt +22 -22
  276. package/apps/pwa/out/worktrees/index.html +1 -1
  277. package/apps/pwa/out/worktrees/index.txt +22 -22
  278. package/graphs/demo/persona-marketer/fixtures/marketer-scenario.yaml +10 -0
  279. package/graphs/demo/persona-marketer/persona-marketer.yaml +25 -0
  280. package/graphs/demo/persona-marketer/recipes.yaml +19 -0
  281. package/graphs/video/explainer-line.yaml +4 -1
  282. package/package.json +1 -1
  283. package/scripts/dead-code/report.ts +169 -0
  284. package/scripts/lib/morning-report-options.ts +3 -4
  285. package/scripts/lib/tui-regress-evidence.ts +56 -0
  286. package/scripts/morning-report.ts +5 -1
  287. package/scripts/public-export.ts +15 -7
  288. package/scripts/release-loop/export-check-node.ts +21 -3
  289. package/scripts/release-loop/gate-node.ts +379 -48
  290. package/scripts/release-loop/gate-shard-dispatch.ts +128 -0
  291. package/scripts/release-loop/gate-shard-receipts.ts +89 -0
  292. package/scripts/release-loop/gate-wall.ts +5 -5
  293. package/scripts/release-loop/preflight.ts +6 -2
  294. package/scripts/release-loop/resume-release.ts +20 -5
  295. package/scripts/release-loop/resume.ts +48 -0
  296. package/scripts/release-loop/version-node.ts +7 -2
  297. package/scripts/release-story/draft.ts +1 -1
  298. package/scripts/research-bench/README.md +53 -0
  299. package/scripts/research-bench/questions.json +66 -0
  300. package/scripts/research-bench/rubric.md +33 -0
  301. package/scripts/research-bench/run.ts +523 -0
  302. package/scripts/tui-regress.ts +85 -10
  303. package/src/acp/core-turn-bridge.ts +8 -0
  304. package/src/agent/autonomous-tools.ts +10 -3
  305. package/src/agent/monad-agent-turn.ts +4 -1
  306. package/src/agent/registry.ts +26 -2
  307. package/src/agent/shared-app-tools.ts +1 -1
  308. package/src/agent/surface-ux/build.ts +12 -3
  309. package/src/agent/surface-ux/types.ts +1 -1
  310. package/src/agent/task-notification.ts +75 -6
  311. package/src/agent/types.ts +3 -0
  312. package/src/agent-mission/driver.ts +29 -3
  313. package/src/agent-mission/mission-cli.ts +2 -1
  314. package/src/agent-substrate/review-context-pack.ts +365 -0
  315. package/src/agent-substrate/self-review-cli.ts +226 -17
  316. package/src/ask-user-question/index.ts +6 -0
  317. package/src/ask-user-question/inline-resolver.ts +49 -0
  318. package/src/auto-research/question-ladder-contract.ts +22 -3
  319. package/src/auto-research/research-turn.ts +350 -17
  320. package/src/autopilot/agent-loop.ts +1 -1
  321. package/src/autopilot/pty-control-loop.ts +8 -1
  322. package/src/boot/daemon-tools/index.ts +4 -3
  323. package/src/boot/daemon-tools/self-implement.ts +270 -2
  324. package/src/boot/daemon-tools/types.ts +2 -0
  325. package/src/budget/llm-cost.ts +44 -5
  326. package/src/budget/unified-usage.ts +17 -1
  327. package/src/capture/tui-self-report.ts +14 -0
  328. package/src/chat/index.ts +33 -7
  329. package/src/cli/agent-cli.ts +43 -8
  330. package/src/cli/intake-cli.ts +22 -1
  331. package/src/cli/lesson-cli.ts +19 -3
  332. package/src/cli/log-event-names.ts +1 -0
  333. package/src/cli/pod-cli.ts +17 -2
  334. package/src/cli/pty-takeover-cli.ts +67 -7
  335. package/src/cli/release-cli.ts +52 -6
  336. package/src/cli/repo-cli.ts +2 -1
  337. package/src/cli/tasks-cli.ts +36 -9
  338. package/src/control-plane/ledger.ts +21 -4
  339. package/src/core-turn/run-core-turn.ts +44 -3
  340. package/src/core-turn/run-goal-loop.ts +152 -8
  341. package/src/core-turn/types.ts +9 -0
  342. package/src/dashboard/chat/acp-chat.ts +2 -2
  343. package/src/dashboard/classic-sigquit.ts +16 -0
  344. package/src/dashboard/index.ts +321 -27
  345. package/src/dashboard/input/chat-main-interaction-opts.ts +3 -0
  346. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +7 -2
  347. package/src/dashboard/input/chat-main-slash-command.ts +7 -0
  348. package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
  349. package/src/dashboard/input/dashboard-priority-key-route.ts +21 -5
  350. package/src/dashboard/input/external-input-host.ts +2 -0
  351. package/src/dashboard/sidetab-runtime.ts +228 -0
  352. package/src/dashboard/slash-runtime/ask-clarification.ts +7 -1
  353. package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -8
  354. package/src/dashboard/slash-runtime/help-from-registry.ts +2 -2
  355. package/src/dashboard/term-slash-runtime.ts +16 -18
  356. package/src/dashboard/turn-preamble.ts +2 -0
  357. package/src/domains/knowledge.ts +1 -1
  358. package/src/esc-abort-gate.ts +33 -1
  359. package/src/execution-loop/launch-gate.ts +206 -1
  360. package/src/graph-runner/graph-cli.ts +6 -4
  361. package/src/graph-runner/runner.ts +31 -9
  362. package/src/graph-wizard/generate.ts +16 -7
  363. package/src/graph-wizard/mission-compose.ts +201 -0
  364. package/src/grok/agent-search.ts +8 -2
  365. package/src/grok/credential.ts +5 -0
  366. package/src/grounding/ref-index.ts +11 -0
  367. package/src/grounding/ref-vector.ts +61 -0
  368. package/src/grounding/sources-cli.ts +2 -2
  369. package/src/grounding/sources.ts +15 -1
  370. package/src/harness/harness-cli-command.ts +38 -9
  371. package/src/harness/harness-pod-dispatch.ts +7 -1
  372. package/src/index.ts +18 -4
  373. package/src/input/external-input-queue.ts +10 -0
  374. package/src/intake-plane/collect-telegram-saved.ts +36 -1
  375. package/src/intake-plane/intake-to-tasks.ts +246 -3
  376. package/src/lessons/friction-ledger.ts +154 -0
  377. package/src/live-supply/collector.ts +149 -0
  378. package/src/live-supply/probes.ts +52 -0
  379. package/src/live-supply/snapshot.ts +40 -0
  380. package/src/llm.ts +431 -76
  381. package/src/loops/orchestrator/tick.ts +8 -0
  382. package/src/nexus/api/harness-api.ts +2 -1
  383. package/src/pty/snapshot.py +132 -0
  384. package/src/pty-shell/pty-control-ipc.ts +29 -2
  385. package/src/pty-shell/pty-manifest.ts +59 -3
  386. package/src/pty-shell/registry.ts +2 -0
  387. package/src/pty-shell/tui-run-state.ts +80 -3
  388. package/src/release-loop/gate-host-reservation.ts +13 -4
  389. package/src/release-loop/gate-shard-ledger.ts +68 -0
  390. package/src/release-loop/merged-pr-checklist.ts +25 -2
  391. package/src/release-loop/rehearsal-shard-plan.ts +6 -1
  392. package/src/release-loop/rehearsal.ts +27 -0
  393. package/src/research/edge-decision.ts +124 -15
  394. package/src/research/gap-followup.ts +249 -0
  395. package/src/research/lenses/counter-evidence.ts +100 -0
  396. package/src/research/lenses/working-backwards.ts +89 -0
  397. package/src/research/plugin-discovery.ts +242 -0
  398. package/src/research-bridge/invoke.ts +4 -2
  399. package/src/research-bridge/types.ts +2 -0
  400. package/src/self-dev/ask-launch-flow.ts +34 -106
  401. package/src/self-dev/dev-cli.ts +2 -0
  402. package/src/self-dev/dev-pipeline.ts +8 -4
  403. package/src/self-dev/graph-journey-route.ts +10 -2
  404. package/src/self-dev/launch-preflight-text.ts +2 -2
  405. package/src/self-dev/launch-preflight.ts +38 -7
  406. package/src/self-dev/orchestrate-cli.ts +1 -0
  407. package/src/self-dev/run-supervisor.ts +19 -0
  408. package/src/self-implement/device-first.ts +8 -2
  409. package/src/self-implement/doctor-visit.ts +76 -58
  410. package/src/self-implement/gate-artifacts.ts +3 -0
  411. package/src/self-implement/gate-baseline.ts +75 -4
  412. package/src/self-implement/gate-cli.ts +317 -9
  413. package/src/self-implement/gate-receipt-registry.ts +180 -0
  414. package/src/self-implement/gate-remote.ts +145 -22
  415. package/src/self-implement/goal-author-clarification.ts +178 -24
  416. package/src/self-implement/goal-author-decompose.ts +24 -15
  417. package/src/self-implement/goal-author.ts +9 -5
  418. package/src/self-implement/host-regate.ts +120 -45
  419. package/src/self-implement/impl-findings-feedback.ts +188 -0
  420. package/src/self-implement/knowhow-pack.ts +189 -10
  421. package/src/self-implement/off-diff-evidence.ts +124 -0
  422. package/src/self-implement/orchestrator.ts +85 -7
  423. package/src/self-implement/run-ledger.ts +46 -8
  424. package/src/self-implement/run-stop.ts +12 -0
  425. package/src/self-implement/seams.ts +6 -3
  426. package/src/self-implement/self-implement-cli.ts +4 -2
  427. package/src/session/chat.ts +89 -15
  428. package/src/session-runtime/provider-circuit.ts +140 -0
  429. package/src/shell-runner/file-engine.ts +1 -0
  430. package/src/shell-runner/types.ts +4 -0
  431. package/src/sidetab-core/model.ts +176 -0
  432. package/src/skills/tools/schedule-wakeup.ts +23 -1
  433. package/src/skills/tools/shell.ts +51 -5
  434. package/src/steward/directive-audit.ts +354 -0
  435. package/src/surface/child-process.ts +31 -0
  436. package/src/surface/tui-stderr-owner.ts +48 -0
  437. package/src/task-agent/cover.ts +30 -12
  438. package/src/task-agent/judge.ts +56 -0
  439. package/src/task-agent/postland-card.ts +35 -2
  440. package/src/task-agent/rejudge-on-head.ts +46 -2
  441. package/src/task-agent/repair-cycle.ts +1 -1
  442. package/src/task-agent/task-hand.ts +129 -24
  443. package/src/task-agent/tier-split.ts +105 -21
  444. package/src/task-orchestrator/surfaces/pod-command-job.ts +330 -2
  445. package/src/task-orchestrator/surfaces/pod-config-snapshot.ts +123 -0
  446. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +27 -2
  447. package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +11 -8
  448. package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +1 -1
  449. package/src/task-orchestrator/surfaces/pod-source-receive.ts +74 -6
  450. package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +2 -1
  451. package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +56 -15
  452. package/src/task-orchestrator/surfaces/self-implement-pod.ts +186 -33
  453. package/src/tool-runtime/skill-exec-runtime.ts +95 -5
  454. package/src/tui/entry.ts +15 -2
  455. package/src/tui/inline-choice.ts +188 -0
  456. package/src/tui/sidetab/list.ts +2 -176
  457. package/src/tui/slash/term.ts +12 -1
  458. package/src/tui-client/dashboard-session.ts +7 -1
  459. package/src/user-config.ts +34 -4
  460. package/src/ux-sim/classic-tui-seat-scenarios.ts +1 -7
  461. package/src/ux-sim/fake-land-queue.ts +88 -0
  462. package/src/ux-sim/gate-shard-host-reroute.ts +82 -0
  463. package/src/ux-sim/goal-interim-trace.ts +24 -0
  464. package/src/ux-sim/next-tui-seat-scenarios.ts +8 -13
  465. package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +4 -1
  466. package/src/version/packed-revision.json +1 -1
  467. package/src/video-pipeline/recipes/explainer.ts +54 -2
  468. package/src/web-search/grok.ts +32 -6
  469. package/src/web-search/index.ts +8 -7
  470. package/src/workflow-runtime/node-catalog.ts +53 -4
  471. package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +0 -27
  472. package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/214.186a430da28338f1.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +0 -93
  481. package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +0 -104
  482. package/apps/pwa/out/_next/static/chunks/2415.738b777642a02233.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +0 -24
  492. package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +0 -36
  494. package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/3389.012c70767a872f9b.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +0 -287
  498. package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +0 -94
  504. package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +0 -32
  514. package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +0 -90
  515. package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +0 -79
  517. package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +0 -74
  520. package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +0 -286
  523. package/apps/pwa/out/_next/static/chunks/5300.677c97e0393742e6.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +0 -8
  527. package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +0 -62
  539. package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/7488.966b116789f3d41a.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/7573.2376e13c9c21f1f8.js +0 -77
  544. package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +0 -149
  554. package/apps/pwa/out/_next/static/chunks/9223.cbcc88e0e9950426.js +0 -41
  555. package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +0 -24
  556. package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/9581.da63ddf41acfad46.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +0 -2
  560. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/outputs/page-291ab19fe2acb173.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/app/term/page-6b140f77593576eb.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +0 -1
  615. /package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → HjeK7zONHfR7op1hKXp4k}/_ssgManifest.js +0 -0
@@ -6,6 +6,7 @@ import { Database } from 'bun:sqlite';
6
6
  import { checklistDevVersion, listChecklist, type ChecklistItem } from '../../release-loop/checklist.js';
7
7
  import { readTrafficSignal } from '../../seat-loop/traffic-signal.js';
8
8
  import { runHoldHygiene, type HygieneCell, type HygieneDeps } from '../../steward/hold-hygiene.js';
9
+ import { runDirectiveAudit, type DirectiveAuditDeps } from '../../steward/directive-audit.js';
9
10
  import { effectiveInstanceRoot } from '../../instance/resolve.js';
10
11
  import { getUserConfig, ORCHESTRATOR_DEFAULTS, resolveConfiguredAgentRoleLlm, type OrchestratorLoopConfig } from '../../user-config.js';
11
12
  import { rubricPriority } from '../../release-loop/rubric.js';
@@ -70,6 +71,7 @@ export interface TickDeps {
70
71
  heartbeatIntervalMs?: number;
71
72
  hygieneCells?: () => HygieneCell[];
72
73
  hygienePaths?: Pick<HygieneDeps, 'trafficPath' | 'metaPath' | 'dispatcherDir'>;
74
+ directiveAudit?: DirectiveAuditDeps;
73
75
  cards?: () => TaskCard[]; split?: SplitAdapter; placeCell?: PlaceAdapter;
74
76
  degradation?: () => { llm: boolean; board: boolean };
75
77
  gridHosts?: readonly GridHost[];
@@ -960,6 +962,12 @@ export async function runOrchestratorNode(node: Node, deps: TickDeps = {}): Prom
960
962
  const sent = runHoldHygiene({ root, now, cells, ...deps.hygienePaths });
961
963
  for (const notice of sent) observe('exchange', `hold-hygiene:${notice.key}`, 1, SEAT_LOOP_IDS[notice.to]);
962
964
  } catch (error) { observe('hold-hygiene-failed', String(error).slice(0, 160), 0); }
965
+ // COORD-DIRECTIVE-AUDIT — 지시 원장 행마다 로직 점검(원장·로그·config)을 실제로 돌린다.
966
+ try {
967
+ const audit = runDirectiveAudit({ root, now: currentTime, ...deps.directiveAudit });
968
+ if (audit.alerts.length > 0) observe('directive-audit', `alerts:${audit.alerts.length}`, audit.alerts.length);
969
+ for (const proposal of audit.proposals) observe('directive-audit', `repair-proposal:${proposal.id}`, 1);
970
+ } catch (error) { observe('directive-audit-failed', String(error).slice(0, 160), 0); }
963
971
  }
964
972
  (deps.print ?? console.log)(reportLine(state));
965
973
  }
@@ -9,6 +9,7 @@ import { queryRunScreenKey } from '../../self-implement/run-ledger.js';
9
9
  import { logsDbPath } from '../../mss/logging/log-store.js';
10
10
  import { redactSecretText } from '../../debug/log.js';
11
11
  import { runAskLaunchFlow, type AskLaunchFlowResult } from '../../self-dev/ask-launch-flow.js';
12
+ import { defaultGoalAuthorSelfResolve } from '../../self-implement/goal-author-clarification.js';
12
13
  import * as askIo from '../../self-dev/ask-launch-io.js';
13
14
  import { DAEMON_HARNESS_ASK_ENTRANCE } from '../../self-dev/entrance-registry.js';
14
15
  import { prepareAskLaunch } from '../../self-dev/launch-preflight.js';
@@ -211,12 +212,12 @@ function defaultAskDeps(rows: Awaited<ReturnType<typeof askIo.readAskPreflightLo
211
212
  print: () => {},
212
213
  log: (event: string, data: Record<string, unknown>, level?: 'info' | 'warn' | 'error') => debug.log('harness-http', event, data, { level }),
213
214
  readLine: async () => '',
214
- readClarification: async () => '',
215
215
  readFile: (file: string) => readFileSync(file, 'utf8'),
216
216
  writeFile: (file: string, data: string) => writeFileSync(file, data, 'utf8'),
217
217
  cwd: () => process.cwd(),
218
218
  now: () => Date.now(),
219
219
  isInteractive: () => false,
220
+ selfResolveClarification: defaultGoalAuthorSelfResolve,
220
221
  buildPreflightDeps: askIo.buildAskPreflightDeps,
221
222
  priorBlockSamples: () => askIo.priorBlockSamplesFrom(rows),
222
223
  recentAuthoringSamples: () => askIo.recentAuthoringSamplesFrom(rows),
@@ -0,0 +1,132 @@
1
+ """Capture PTY state and retain the live descriptor for replay comparison."""
2
+
3
+ from dataclasses import dataclass
4
+ import fcntl
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ import re
9
+ import select
10
+ import struct
11
+ import tempfile
12
+ import termios
13
+
14
+
15
+ DEFAULT_SNAPSHOT_ID = "pty_dee629e3"
16
+ _SCHEMA = 1
17
+ _LIVE_FDS: dict[str, int] = {}
18
+
19
+
20
+ @dataclass(eq=True)
21
+ class Snapshot:
22
+ snapshot_id: str
23
+ rows: int
24
+ cols: int
25
+ termios_attributes: list
26
+ output: str
27
+
28
+
29
+ @dataclass(eq=True)
30
+ class ReplayState:
31
+ snapshot: Snapshot
32
+ live_rows: int
33
+ live_cols: int
34
+ live_termios_attributes: list
35
+ matches_live: bool
36
+
37
+
38
+ def _location(snapshot_id: str) -> Path:
39
+ if not isinstance(snapshot_id, str) or not re.fullmatch(r"[A-Za-z0-9_-]+", snapshot_id):
40
+ raise ValueError("invalid snapshot id")
41
+ directory = Path(os.environ.get("ELANOUS_PTY_SNAPSHOT_DIR", tempfile.gettempdir() + "/elanous-pty-snapshots"))
42
+ return directory / (snapshot_id + ".json")
43
+
44
+
45
+ def _size(fd: int) -> tuple[int, int]:
46
+ rows, cols, _, _ = struct.unpack("HHHH", fcntl.ioctl(fd, termios.TIOCGWINSZ, b"\0" * 8))
47
+ return rows, cols
48
+
49
+
50
+ def _serialize_attrs(attrs: list) -> list:
51
+ return attrs[:6] + [[
52
+ {"bytes": entry.hex()} if isinstance(entry, bytes) else entry
53
+ for entry in attrs[6]
54
+ ]]
55
+
56
+
57
+ def _deserialize_attrs(value: object) -> list:
58
+ if not isinstance(value, list) or len(value) != 7 or any(
59
+ type(item) is not int for item in value[:6]
60
+ ) or not isinstance(value[6], list):
61
+ raise ValueError("invalid termios attributes")
62
+ controls = []
63
+ for item in value[6]:
64
+ if type(item) is int and 0 <= item <= 255:
65
+ controls.append(item)
66
+ elif isinstance(item, dict) and set(item) == {"bytes"} and isinstance(item["bytes"], str):
67
+ try:
68
+ controls.append(bytes.fromhex(item["bytes"]))
69
+ except ValueError as error:
70
+ raise ValueError("invalid termios control byte") from error
71
+ else:
72
+ raise ValueError("invalid termios control byte")
73
+ return value[:6] + [controls]
74
+
75
+
76
+ def snapshot(pty_fd: int, snapshot_id: str = DEFAULT_SNAPSHOT_ID) -> Snapshot:
77
+ """Drain currently available output from a PTY master and save its state."""
78
+ path = _location(snapshot_id)
79
+ rows, cols = _size(pty_fd)
80
+ attrs = termios.tcgetattr(pty_fd)
81
+ output = bytearray()
82
+ while select.select([pty_fd], [], [], 0)[0]:
83
+ chunk = os.read(pty_fd, 65536)
84
+ if not chunk:
85
+ break
86
+ output.extend(chunk)
87
+ captured = Snapshot(snapshot_id, rows, cols, attrs, output.decode("utf-8"))
88
+ payload = {
89
+ "schema": _SCHEMA,
90
+ "snapshot_id": snapshot_id,
91
+ "rows": rows,
92
+ "cols": cols,
93
+ "termios_attributes": _serialize_attrs(attrs),
94
+ "output": captured.output,
95
+ }
96
+ path.parent.mkdir(parents=True, exist_ok=True)
97
+ with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=path.parent, delete=False) as file:
98
+ temporary = Path(file.name)
99
+ try:
100
+ json.dump(payload, file, ensure_ascii=False)
101
+ except BaseException:
102
+ temporary.unlink(missing_ok=True)
103
+ raise
104
+ temporary.replace(path)
105
+ _LIVE_FDS[snapshot_id] = pty_fd
106
+ return captured
107
+
108
+
109
+ def restore(snapshot_id: str = DEFAULT_SNAPSHOT_ID) -> ReplayState:
110
+ """Load a validated snapshot and compare it with its live PTY."""
111
+ path = _location(snapshot_id)
112
+ try:
113
+ payload = json.loads(path.read_text(encoding="utf-8"))
114
+ except (json.JSONDecodeError, UnicodeError) as error:
115
+ raise ValueError("invalid snapshot document") from error
116
+ if not isinstance(payload, dict) or set(payload) != {
117
+ "schema", "snapshot_id", "rows", "cols", "termios_attributes", "output"
118
+ } or type(payload["schema"]) is not int or payload["schema"] != _SCHEMA or payload["snapshot_id"] != snapshot_id or any(
119
+ type(payload[field]) is not int or not 0 <= payload[field] <= 65535 for field in ("rows", "cols")
120
+ ) or not isinstance(payload["output"], str):
121
+ raise ValueError("snapshot schema or id mismatch")
122
+ attrs = _deserialize_attrs(payload["termios_attributes"])
123
+ saved = Snapshot(snapshot_id, payload["rows"], payload["cols"], attrs, payload["output"])
124
+ if snapshot_id not in _LIVE_FDS:
125
+ raise RuntimeError("no live PTY registered for snapshot")
126
+ fd = _LIVE_FDS[snapshot_id]
127
+ live_rows, live_cols = _size(fd)
128
+ live_attrs = termios.tcgetattr(fd)
129
+ return ReplayState(
130
+ saved, live_rows, live_cols, live_attrs,
131
+ (live_rows, live_cols, live_attrs) == (saved.rows, saved.cols, saved.termios_attributes),
132
+ )
@@ -15,7 +15,7 @@ import { encodeSgrMouse, mouseModeOffReason, type PtyMouseInput } from './pty-mo
15
15
  import { startPtyRecording, stopPtyRecording } from './pty-recording.js';
16
16
  import { withExternalKeyCommand } from '../input/external-input-queue.js';
17
17
 
18
- const OWNER_PTY_CONTROL_ACTIONS = ['takeover', 'release', 'input-text', 'input-key', 'input-mouse', 'resize', 'snapshot', 'rename', 'terminate', 'capabilities', 'record-start', 'record-stop'] as const;
18
+ const OWNER_PTY_CONTROL_ACTIONS = ['takeover', 'release', 'input-text', 'input-key', 'input-mouse', 'resize', 'snapshot', 'rename', 'terminate', 'stop-turn', 'capabilities', 'record-start', 'record-stop'] as const;
19
19
  export type PtyControlAction = typeof OWNER_PTY_CONTROL_ACTIONS[number];
20
20
  type PtyControlStatus = 'success' | 'unknown-pty' | 'denied' | 'failed' | 'write-failed' | 'owner-unreachable';
21
21
 
@@ -42,7 +42,8 @@ export type PtyControlPayload =
42
42
  | { readonly cols: number; readonly rows: number }
43
43
  | { readonly ansi: boolean }
44
44
  | { readonly nickname: string }
45
- | { readonly cols: number; readonly rows: number; readonly title?: string };
45
+ | { readonly cols: number; readonly rows: number; readonly title?: string }
46
+ | { readonly force: true; readonly actorIdentity: string; readonly reason: string };
46
47
 
47
48
  export interface PtyControlRequestOptions {
48
49
  /** 요청자가 주장하는 쓰기 주체. `'agent'` 는 run-identity 일치를 요구한다(`resolveRemoteControlActor`). */
@@ -609,6 +610,19 @@ async function processPtyControlRequestsOnce(getHandle: (id: string) => PtyContr
609
610
  else if (!isOwnerPtyControlAction(row.action)) result = { status: 'denied', reason: 'unsupported-action' };
610
611
  else if (row.action === 'capabilities') result = { status: 'success', actions: OWNER_PTY_CONTROL_ACTIONS };
611
612
  else if (row.action === 'terminate') result = await terminatePty(handle);
613
+ else if (row.action === 'stop-turn') {
614
+ const payload = parsePayload(row);
615
+ const targetRunId = getPtyManifest(row.pty_id)?.runId?.trim() ?? '';
616
+ const requesterRunId = row.requester_run_id?.trim() ?? '';
617
+ if (requested === null) result = { status: 'denied', reason: 'invalid-actor' };
618
+ else if (payload === null || typeof payload !== 'object' || Array.isArray(payload) || !('force' in payload) || payload.force !== true) result = { status: 'denied', reason: 'force-required' };
619
+ else if (!('actorIdentity' in payload) || typeof payload.actorIdentity !== 'string' || !payload.actorIdentity.trim()) result = { status: 'denied', reason: 'actor-identity-required' };
620
+ else if (!('reason' in payload) || typeof payload.reason !== 'string' || !payload.reason.trim()) result = { status: 'denied', reason: 'reason-required' };
621
+ else if (!requesterRunId || !targetRunId) result = { status: 'denied', reason: 'run-identity-unavailable' };
622
+ else if (requesterRunId === targetRunId) result = { status: 'denied', reason: 'self-turn-refused' };
623
+ else if (!handle.stopTurn) result = { status: 'denied', reason: 'force-stop-unsupported' };
624
+ else result = handle.stopTurn(true) ? { status: 'success' } : { status: 'denied', reason: 'no-active-turn' };
625
+ }
612
626
  else if (row.action === 'record-start' || row.action === 'record-stop') {
613
627
  if (requested === null) result = { status: 'denied', reason: 'invalid-actor' };
614
628
  else if (row.action === 'record-stop') {
@@ -733,6 +747,19 @@ async function processPtyControlRequestsOnce(getHandle: (id: string) => PtyContr
733
747
  result = { status: 'failed', reason: 'control-processing-error' };
734
748
  debug.log('pty.takeover', 'control-processing-error', { id: row.pty_id, action: row.action, actor: requested ?? row.actor, error: (error as Error).message });
735
749
  }
750
+ if (row.action === 'stop-turn') {
751
+ const payload = parsePayload(row);
752
+ debug.log('pty.turn', 'force-stop-settled', {
753
+ ptyId: row.pty_id,
754
+ actor: requested ?? row.actor ?? 'unknown',
755
+ actorIdentity: payload && 'actorIdentity' in payload && typeof payload.actorIdentity === 'string' ? payload.actorIdentity.trim() || null : null,
756
+ reason: payload && 'reason' in payload && typeof payload.reason === 'string' ? payload.reason.trim() || null : null,
757
+ requesterRunId: row.requester_run_id?.trim() || null,
758
+ targetRunId: getPtyManifest(row.pty_id)?.runId?.trim() || null,
759
+ outcome: result.status,
760
+ refusal: result.reason ?? null,
761
+ });
762
+ }
736
763
  d.run("UPDATE pty_control_requests SET status=?, result_json=?, updated_at=? WHERE request_id=? AND status='processing'", [result.status, JSON.stringify(result), Date.now(), row.request_id]);
737
764
  debug.log('pty.arbiter', 'control-settled', { id: row.pty_id, action: row.action, actor: requested ?? row.actor, outcome: result.status, reason: result.reason });
738
765
  }
@@ -104,6 +104,10 @@ export interface PtyManifestRow {
104
104
  /** TUI 턴 상태 — TUI 가 턴 시작·끝에 스스로 적는다(화면 글자 추정 아님). 없으면 기록 안 하는 행/옛 판. */
105
105
  turnState?: PtyTurnState;
106
106
  turnAt?: number;
107
+ /** 마지막 진전 시각(epoch ms) — 스트림 텍스트·도구 시작/끝·상태 줄에서 갱신(SURVIVAL-PROGRESS-CLOCK). */
108
+ progressAt?: number;
109
+ /** Esc 로 턴 중단을 청한 시각(epoch ms) — 이후 busy 가 지속되면 abort-ignored 판정 재료. */
110
+ abortRequestedAt?: number;
107
111
  /** 행 소유 프로세스가 «기동 때» 실은 코드 판 — 트리 HEAD(`codeSha`)와 다른 축이다. */
108
112
  build?: PtyBuildIdentity;
109
113
  }
@@ -128,6 +132,7 @@ let _dbFailed = false;
128
132
  let _dbPathForTesting: string | null = null;
129
133
  const _lastSnapshotAt = new Map<string, number>();
130
134
  const _lastFrameAt = new Map<string, number>();
135
+ const _lastProgressAt = new Map<string, number>();
131
136
 
132
137
  export function setPtyManifestDbPathForTesting(path: string | null): void {
133
138
  _db?.close();
@@ -136,6 +141,7 @@ export function setPtyManifestDbPathForTesting(path: string | null): void {
136
141
  _dbPathForTesting = path;
137
142
  _lastSnapshotAt.clear();
138
143
  _lastFrameAt.clear();
144
+ _lastProgressAt.clear();
139
145
  }
140
146
 
141
147
  function db(): Database | null {
@@ -172,6 +178,11 @@ function db(): Database | null {
172
178
  }
173
179
 
174
180
  export function migratePtyManifestSchema(d: Database): void {
181
+ // 이행 문장 전체를 한 트랜잭션에 묶는다 — 문장별 autocommit fsync(첫 열기 ~35회)를 없애
182
+ // 매니페스트 DB 를 처음 여는 비용을 크게 줄인다. 이미 트랜잭션 안이면 그대로 진행한다.
183
+ let ownsTxn = false;
184
+ try { d.run('BEGIN'); ownsTxn = true; } catch { /* caller transaction */ }
185
+ try {
175
186
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN nickname TEXT'); } catch { /* already exists */ }
176
187
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN purpose TEXT'); } catch { /* already exists */ }
177
188
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN seat TEXT'); } catch { /* already exists */ }
@@ -201,12 +212,19 @@ export function migratePtyManifestSchema(d: Database): void {
201
212
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN last_control_at INTEGER'); } catch { /* already exists */ }
202
213
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN turn_state TEXT'); } catch { /* already exists */ }
203
214
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN turn_at INTEGER'); } catch { /* already exists */ }
215
+ try { d.run('ALTER TABLE pty_manifest ADD COLUMN progress_at INTEGER'); } catch { /* already exists */ }
216
+ try { d.run('ALTER TABLE pty_manifest ADD COLUMN abort_requested_at INTEGER'); } catch { /* already exists */ }
204
217
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN build_version TEXT'); } catch { /* already exists */ }
205
218
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN build_sha TEXT'); } catch { /* already exists */ }
206
219
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN build_source TEXT'); } catch { /* already exists */ }
207
220
  try { d.run('ALTER TABLE pty_manifest ADD COLUMN build_code_root TEXT'); } catch { /* already exists */ }
208
221
  try { d.run('ALTER TABLE pty_manifest DROP COLUMN chain_depth'); } catch { /* legacy schema has no duplicate column */ }
209
222
  try { d.run('UPDATE pty_manifest SET closed_at=updated_at WHERE alive=0 AND closed_at=0'); } catch { /* fail-soft */ }
223
+ } finally {
224
+ if (ownsTxn) {
225
+ try { d.run('COMMIT'); } catch { try { d.run('ROLLBACK'); } catch { /* fail-soft */ } }
226
+ }
227
+ }
210
228
  }
211
229
 
212
230
  function codeShaForWorkdir(workdir: string | undefined): string {
@@ -290,6 +308,7 @@ export function upsertPtyManifest(row: {
290
308
  // 행을 (다시) 세우면 snapshot·frame 칸이 비워진다 — 같은 프로세스의 쓰기 스로틀도 비워 다음 쓰기가 바로 닿게 한다.
291
309
  _lastSnapshotAt.delete(row.id);
292
310
  _lastFrameAt.delete(row.id);
311
+ _lastProgressAt.delete(row.id);
293
312
  const d = db(); if (!d) return;
294
313
  const runId = getHarnessRunId(process.env);
295
314
  const runIdSource = getHarnessRunIdSource();
@@ -343,7 +362,7 @@ export function upsertPtyManifest(row: {
343
362
  terminal_origin_reason=excluded.terminal_origin_reason, external_tool_name=excluded.external_tool_name,
344
363
  started_at=excluded.started_at, alive=1, exit_code=NULL,
345
364
  snapshot='', snapshot_at=0, frame='', frame_at=0, closed_at=0, updated_at=excluded.updated_at,
346
- turn_state=NULL, turn_at=NULL, build_version=NULL, build_sha=NULL, build_source=NULL, build_code_root=NULL`,
365
+ turn_state=NULL, turn_at=NULL, progress_at=NULL, abort_requested_at=NULL, build_version=NULL, build_sha=NULL, build_source=NULL, build_code_root=NULL`,
347
366
  values,
348
367
  );
349
368
  d.run('COMMIT');
@@ -434,11 +453,46 @@ export function updatePtyManifestSession(id: string, sessionId: string, now: num
434
453
  }
435
454
  }
436
455
 
437
- /** 턴 상태 기록 — 행이 없거나 db 를 못 열면 false(호출자가 관측을 남긴다). */
456
+ /** 턴 상태 기록 — 행이 없거나 db 를 못 열면 false(호출자가 관측을 남긴다). 새 busy 턴은 이전 Esc 청구를 비운다. */
438
457
  export function updatePtyManifestTurn(id: string, turn: PtyTurnState, at: number): boolean {
439
458
  try {
440
459
  const d = db(); if (!d) return false;
441
- return d.run('UPDATE pty_manifest SET turn_state=?, turn_at=? WHERE id=?', [turn, at, id]).changes > 0;
460
+ const result = turn === 'busy'
461
+ ? d.run('UPDATE pty_manifest SET turn_state=?, turn_at=?, progress_at=?, abort_requested_at=NULL WHERE id=?', [turn, at, at, id])
462
+ : d.run('UPDATE pty_manifest SET turn_state=?, turn_at=? WHERE id=?', [turn, at, id]);
463
+ if ((result.changes ?? 0) > 0 && turn === 'busy') _lastProgressAt.set(id, at);
464
+ return (result.changes ?? 0) > 0;
465
+ } catch {
466
+ return false;
467
+ }
468
+ }
469
+
470
+ /** 진전 기록 스로틀 — 같은 행은 이만큼 안에 다시 쓰지 않는다. */
471
+ export const PTY_MANIFEST_PROGRESS_THROTTLE_MS = 5_000;
472
+
473
+ /**
474
+ * 마지막 진전 시각 기록(SURVIVAL-PROGRESS-CLOCK). 같은 행을 5초 안에 다시 쓰면 생략하지만
475
+ * «실패가 아니다» — true 를 돌려 호출자의 실패 관측을 일으키지 않는다. 실패(0행·db 오류)만 false.
476
+ */
477
+ export function updatePtyManifestProgress(id: string, at: number): boolean {
478
+ try {
479
+ const last = _lastProgressAt.get(id);
480
+ if (last !== undefined && at - last < PTY_MANIFEST_PROGRESS_THROTTLE_MS) return true;
481
+ const d = db(); if (!d) return false;
482
+ const result = d.run('UPDATE pty_manifest SET progress_at=?, updated_at=? WHERE id=?', [at, at, id]);
483
+ const ok = (result.changes ?? 0) > 0;
484
+ if (ok) _lastProgressAt.set(id, at);
485
+ return ok;
486
+ } catch {
487
+ return false;
488
+ }
489
+ }
490
+
491
+ /** Esc 로 턴 중단을 청한 시각 기록 — busy 턴이 이 뒤로 계속되면 abort-ignored 판정 재료가 된다. */
492
+ export function updatePtyManifestAbortRequested(id: string, at: number): boolean {
493
+ try {
494
+ const d = db(); if (!d) return false;
495
+ return (d.run('UPDATE pty_manifest SET abort_requested_at=?, updated_at=? WHERE id=?', [at, at, id]).changes ?? 0) > 0;
442
496
  } catch {
443
497
  return false;
444
498
  }
@@ -903,6 +957,8 @@ function mapRow(r: Record<string, unknown>): PtyManifestRow {
903
957
  closedAt: Number(r.closed_at ?? 0),
904
958
  codeSha: String(r.code_sha ?? ''),
905
959
  ...(r.turn_state === 'busy' || r.turn_state === 'idle' ? { turnState: r.turn_state, turnAt: Number(r.turn_at ?? 0) } : {}),
960
+ ...(r.progress_at === null || r.progress_at === undefined ? {} : { progressAt: Number(r.progress_at) }),
961
+ ...(r.abort_requested_at === null || r.abort_requested_at === undefined ? {} : { abortRequestedAt: Number(r.abort_requested_at) }),
906
962
  ...(typeof r.build_sha === 'string' ? { build: mapBuild(r) } : {}),
907
963
  updatedAt: Number(r.updated_at),
908
964
  };
@@ -180,6 +180,8 @@ export interface PtyControlTarget {
180
180
  isAlive(): boolean;
181
181
  /** Registry-backed PTY targets expose termination; non-PTY control targets omit it. */
182
182
  kill?(signal?: NodeJS.Signals): void;
183
+ /** TUI owners may expose an explicit, forceful cancellation of the active turn. */
184
+ stopTurn?(force: true): boolean;
183
185
  canWrite(actor?: PtyWriteActor): boolean;
184
186
  write(chars: string, actor?: PtyWriteActor): void;
185
187
  resize(cols: number, rows: number): void;
@@ -27,7 +27,7 @@ import {
27
27
  versionedDashboardInstallDir,
28
28
  } from './stale-install-check.js';
29
29
  import {
30
- updatePtyManifestBuild, updatePtyManifestTurn,
30
+ updatePtyManifestAbortRequested, updatePtyManifestBuild, updatePtyManifestProgress, updatePtyManifestTurn,
31
31
  type PtyBuildIdentity, type PtyManifestRow, type PtyTurnState,
32
32
  } from './pty-manifest.js';
33
33
 
@@ -231,6 +231,10 @@ export type PtyTurnView = PtyTurnState | 'unknown';
231
231
  export interface PtyRunStateView {
232
232
  readonly turn: PtyTurnView;
233
233
  readonly turnAt?: number;
234
+ /** 마지막 진전 시각(SURVIVAL-PROGRESS-CLOCK) — 기록 없는 행·옛 판은 undefined. */
235
+ readonly progressAt?: number;
236
+ /** Esc 로 턴 중단을 청한 시각 — 이 뒤로 busy 가 지속되면 abort-ignored 판정 재료. */
237
+ readonly abortRequestedAt?: number;
234
238
  readonly build: PtyBuildIdentity | null;
235
239
  readonly installed: string | null;
236
240
  readonly stale: boolean | null;
@@ -244,7 +248,7 @@ export interface DescribeRunStateDeps {
244
248
 
245
249
  /** manifest 행 하나의 턴 ⊕ 판 ⊕ stale. 행이 없거나 죽었으면 턴은 unknown. */
246
250
  export function describePtyRunState(
247
- row: Pick<PtyManifestRow, 'alive' | 'startedAt' | 'kind' | 'turnState' | 'turnAt' | 'build'> | null,
251
+ row: Pick<PtyManifestRow, 'alive' | 'startedAt' | 'kind' | 'turnState' | 'turnAt' | 'progressAt' | 'abortRequestedAt' | 'build'> | null,
248
252
  deps: DescribeRunStateDeps = {},
249
253
  ): PtyRunStateView {
250
254
  let installed: InstalledRecord = {};
@@ -265,6 +269,8 @@ export function describePtyRunState(
265
269
  const turnView = ptyTurnViewOfRow(row);
266
270
  return {
267
271
  ...turnView,
272
+ ...(row?.alive && row.progressAt !== undefined ? { progressAt: row.progressAt } : {}),
273
+ ...(row?.alive && row.abortRequestedAt !== undefined ? { abortRequestedAt: row.abortRequestedAt } : {}),
268
274
  build: build ?? null,
269
275
  installed: installed.sha ?? null,
270
276
  stale: staleness.stale,
@@ -446,6 +452,50 @@ export const spawnTuiRelaunch = (sessionId: string): Promise<void> => {
446
452
  });
447
453
  };
448
454
 
455
+ // ── SURVIVAL-PROGRESS-CLOCK — «마지막 진전 시각»로 느림·정체·Esc 무시를 가른다 ──
456
+ //
457
+ // `turnAt` 은 턴 값이 «바뀔 때만» 적히므로 busy 경과만으로는 «일하는 중»인지 «죽었다»인지를 못 가른다.
458
+ // 진전 신호(스트림 텍스트·도구 시작/끝·상태 줄)가 `progress_at` 를 계속 올리면 긴 턴도 «느리지만 가는 중»이
459
+ // 되고, 진전이 끊기면 «정체», Esc 청구 뒤에도 busy 가 지속되면 «Esc 무시»로 판정한다.
460
+
461
+ export const DEFAULT_TURN_STALL_MS = 12 * 60 * 1000;
462
+ export const DEFAULT_SLOW_TURN_MS = 20 * 60 * 1000;
463
+ export const DEFAULT_ABORT_IGNORE_MS = 30 * 1000;
464
+
465
+ export type TurnProgressJudgment = 'progressing' | 'slow' | 'stalled' | 'abort-ignored' | 'unknown';
466
+
467
+ export interface TurnProgressInput {
468
+ readonly turn: PtyTurnView;
469
+ readonly turnAt?: number;
470
+ readonly progressAt?: number;
471
+ readonly abortRequestedAt?: number;
472
+ readonly now: number;
473
+ /** 정체 임계 — 진전이 이만큼 끊기면 stalled. 기본 12분. */
474
+ readonly stallMs?: number;
475
+ /** 느린 턴 임계 — 턴 경과가 이만큼이면 slow. 기본 20분. */
476
+ readonly slowTurnMs?: number;
477
+ /** Esc 무시 임계 — 청구 뒤 이만큼 busy 면 abort-ignored. 기본 30초. */
478
+ readonly abortIgnoreMs?: number;
479
+ }
480
+
481
+ /** 순수 판정: busy 턴의 진전·정체·Esc 무시를 임계로 가른다. 재료가 없으면 unknown(«괜찮다»가 아니다). */
482
+ export function judgeTurnProgress(input: TurnProgressInput): TurnProgressJudgment {
483
+ const { turn, turnAt, progressAt, abortRequestedAt, now } = input;
484
+ if (turn !== 'busy') return 'unknown';
485
+ if (!Number.isFinite(now)) return 'unknown';
486
+ // Esc 청구 뒤에도 busy 가 지속되면 무시 — 정체보다 구체적이므로 먼저 가른다.
487
+ if (abortRequestedAt !== undefined && Number.isFinite(abortRequestedAt)) {
488
+ if (now - abortRequestedAt >= (input.abortIgnoreMs ?? DEFAULT_ABORT_IGNORE_MS)) return 'abort-ignored';
489
+ }
490
+ // 진전 시각이 없으면 턴 시작(turnAt)이 진전의 0초다.
491
+ const since = progressAt !== undefined && Number.isFinite(progressAt) ? progressAt : turnAt;
492
+ if (since === undefined || !Number.isFinite(since)) return 'unknown';
493
+ const elapsed = Math.max(0, now - since);
494
+ if (elapsed >= (input.stallMs ?? DEFAULT_TURN_STALL_MS)) return 'stalled';
495
+ if (turnAt !== undefined && Number.isFinite(turnAt) && now - turnAt >= (input.slowTurnMs ?? DEFAULT_SLOW_TURN_MS)) return 'slow';
496
+ return 'progressing';
497
+ }
498
+
449
499
  // ── 기록(TUI) ──
450
500
 
451
501
  export interface TuiRunStateRecorderDeps {
@@ -453,6 +503,10 @@ export interface TuiRunStateRecorderDeps {
453
503
  readonly build?: () => PtyBuildIdentity;
454
504
  readonly writeBuild?: (id: string, build: PtyBuildIdentity) => boolean;
455
505
  readonly writeTurn?: (id: string, turn: PtyTurnState, at: number) => boolean;
506
+ /** 진전 시각 기록 — 생략하면 `updatePtyManifestProgress`(같은 행 5초 스로틀). */
507
+ readonly writeProgress?: (id: string, at: number) => boolean;
508
+ /** Esc 청구 시각 기록 — 생략하면 `updatePtyManifestAbortRequested`. */
509
+ readonly writeAbortRequested?: (id: string, at: number) => boolean;
456
510
  readonly log?: (event: string, data: Record<string, unknown>) => void;
457
511
  /**
458
512
  * herdr pane 신고기. 생략 시: `writeTurn` 을 주입하지 않은 기본 녹음기만
@@ -476,6 +530,8 @@ interface ActiveRecorder {
476
530
  readonly surfaceId: string;
477
531
  readonly now: () => number;
478
532
  readonly writeTurn: (id: string, turn: PtyTurnState, at: number) => boolean;
533
+ readonly writeProgress: (id: string, at: number) => boolean;
534
+ readonly writeAbortRequested: (id: string, at: number) => boolean;
479
535
  readonly log: (event: string, data: Record<string, unknown>) => void;
480
536
  readonly herdr: HerdrAgentReporterLike | null;
481
537
  /** manifest 에 실제로 적힌 마지막 턴. 실패면 안 바뀐다(재시도). */
@@ -495,6 +551,8 @@ export function startTuiRunStateRecorder(surfaceId: string, deps: TuiRunStateRec
495
551
  const now = deps.now ?? Date.now;
496
552
  const log = deps.log ?? defaultLog;
497
553
  const writeTurn = deps.writeTurn ?? updatePtyManifestTurn;
554
+ const writeProgress = deps.writeProgress ?? updatePtyManifestProgress;
555
+ const writeAbortRequested = deps.writeAbortRequested ?? updatePtyManifestAbortRequested;
498
556
  // 기본 신고기는 기본 manifest 쓰기와 짝일 때만. writeTurn 주입(시험)은 herdr 를 안 만든다.
499
557
  const herdr = deps.herdr !== undefined
500
558
  ? deps.herdr
@@ -508,7 +566,7 @@ export function startTuiRunStateRecorder(surfaceId: string, deps: TuiRunStateRec
508
566
  log(ok ? 'build-recorded' : 'build-write-failed', { ptyId: surfaceId, sha: build.sha, version: build.version, source: build.source });
509
567
  }
510
568
  // 기동 순서: 판 → manifest 초기 idle → herdr 신고. herdr start 가 idle 을 신고하므로 초기 idle 을 turn 으로 다시 안 보낸다.
511
- const recorder: ActiveRecorder = { surfaceId, now, writeTurn, log, herdr, herdrLast: herdr ? 'idle' : undefined };
569
+ const recorder: ActiveRecorder = { surfaceId, now, writeTurn, writeProgress, writeAbortRequested, log, herdr, herdrLast: herdr ? 'idle' : undefined };
512
570
  active = recorder;
513
571
  writeTurnState(recorder, 'idle');
514
572
  if (herdr) { try { herdr.start(); } catch { /* 신고 실패가 녹음기를 깨지 않는다 */ } }
@@ -614,6 +672,25 @@ export function reportTuiTurn(turn: PtyTurnState): void {
614
672
  writeTurnState(active, turn);
615
673
  }
616
674
 
675
+ /** 진전 신호(스트림 텍스트·도구 시작/끝·상태 줄)에서 부른다 — 마지막 진전 시각을 올린다. 녹음기가 없으면 아무것도 안 한다. */
676
+ export function reportTuiProgress(): void {
677
+ if (!active) return;
678
+ const at = active.now();
679
+ let ok = false;
680
+ try { ok = active.writeProgress(active.surfaceId, at); } catch { ok = false; }
681
+ // 실패(0행·예외)는 관측으로만 남긴다 — 진전 기록이 턴을 막지 않는다. 스로틀 생략(false 아님 실패만)은 조용히 지나간다.
682
+ if (!ok) active.log('progress-write-failed', { ptyId: active.surfaceId, at });
683
+ }
684
+
685
+ /** Esc 게이트가 턴 중단을 청할 때 부른다 — 청구 시각을 남긴다(busy 가 지속되면 abort-ignored 판정 재료). */
686
+ export function reportTuiAbortRequested(): void {
687
+ if (!active) return;
688
+ const at = active.now();
689
+ let ok = false;
690
+ try { ok = active.writeAbortRequested(active.surfaceId, at); } catch { ok = false; }
691
+ if (!ok) active.log('abort-requested-write-failed', { ptyId: active.surfaceId, at });
692
+ }
693
+
617
694
  function writeTurnState(recorder: ActiveRecorder, turn: PtyTurnState): void {
618
695
  const at = recorder.now();
619
696
  let ok = false;
@@ -11,11 +11,13 @@ interface GateHostClaim {
11
11
  hosts: string[];
12
12
  pid: number;
13
13
  pidStartedAt: number | null;
14
+ /** Wall-clock claim time. Claims written before this field existed stay readable (optional on read). */
15
+ reservedAt?: number;
14
16
  }
15
17
 
16
18
  export type GateHostReservation =
17
19
  | { status: 'free' }
18
- | { status: 'reserved' | 'unknown'; reason: string; runId?: string };
20
+ | { status: 'reserved' | 'unknown'; reason: string; runId?: string; reservedAt?: number };
19
21
 
20
22
  const hostName = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
21
23
  const claimsDir = (root: string) => join(root, 'release', 'gate-host-reservations');
@@ -44,7 +46,7 @@ export function reserveReleaseGateHosts(root: string, runId: string, hosts: read
44
46
  const token = randomUUID();
45
47
  const pidStartedAt = psProcessStartMs(process.pid);
46
48
  if (pidStartedAt === null) throw new Error('release gate owner start time unmeasured');
47
- const claim: GateHostClaim = { token, runId, hosts: unique, pid: process.pid, pidStartedAt };
49
+ const claim: GateHostClaim = { token, runId, hosts: unique, pid: process.pid, pidStartedAt, reservedAt: Date.now() };
48
50
  const path = join(dir, `${token}.json`);
49
51
  const temp = join(dir, `.${token}.tmp`);
50
52
  try {
@@ -82,7 +84,9 @@ export function inspectReleaseGateHost(root: string, host: string): GateHostRese
82
84
  || !Number.isSafeInteger(claim.pid) || claim.pid <= 0 || typeof claim.token !== 'string'
83
85
  || typeof claim.runId !== 'string' || !claim.runId
84
86
  || (claim.pidStartedAt !== null && (!Number.isSafeInteger(claim.pidStartedAt)
85
- || claim.pidStartedAt <= 0 || claim.pidStartedAt > Date.now()))) throw new Error('invalid claim');
87
+ || claim.pidStartedAt <= 0 || claim.pidStartedAt > Date.now()))
88
+ || (claim.reservedAt !== undefined && (!Number.isSafeInteger(claim.reservedAt)
89
+ || claim.reservedAt <= 0 || claim.reservedAt > Date.now()))) throw new Error('invalid claim');
86
90
  } catch (error) { return { status: 'unknown', reason: `reservation unreadable: ${name}: ${String(error)}` }; }
87
91
  const live = claimAlive(claim);
88
92
  if (live === 'dead') {
@@ -91,7 +95,12 @@ export function inspectReleaseGateHost(root: string, host: string): GateHostRese
91
95
  continue;
92
96
  }
93
97
  if (!claim.hosts.includes(host) && !claim.hosts.includes('*')) continue;
94
- return { status: live === 'unknown' ? 'unknown' : 'reserved', reason: `release gate ${claim.runId} holds ${host}`, runId: claim.runId };
98
+ return {
99
+ status: live === 'unknown' ? 'unknown' : 'reserved',
100
+ reason: `release gate ${claim.runId} holds ${host}`,
101
+ runId: claim.runId,
102
+ ...(claim.reservedAt !== undefined ? { reservedAt: claim.reservedAt } : {}),
103
+ };
95
104
  }
96
105
  return { status: 'free' };
97
106
  }
@@ -0,0 +1,68 @@
1
+ // One append-only observation per gate shard transition. The live board is a projection, not evidence for reuse.
2
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { releaseLedgerRoot } from '../instance/resolve.js';
5
+
6
+ export interface GateShardLedgerRow {
7
+ v: 1;
8
+ version: string;
9
+ phase: 'cut' | 'baseline';
10
+ shardId: string;
11
+ files: string[];
12
+ host: string | null;
13
+ executor: 'pod' | 'host';
14
+ imageDigest: string | null;
15
+ commit: string;
16
+ envFingerprint: string;
17
+ startedAt: string | null;
18
+ endedAt: string | null;
19
+ observedAt: string;
20
+ state: 'pending' | 'running' | 'done' | 'retry' | 'timeout' | 'failed';
21
+ rc: number | null;
22
+ pass: number | null;
23
+ fail: number | null;
24
+ errors: number | null;
25
+ ran: number | null;
26
+ failureIds: string[] | null;
27
+ errorIds: string[] | null;
28
+ attribution: 'complete' | 'unmeasured';
29
+ logPath: string | null;
30
+ junitPath: string | null;
31
+ stopReason: string | null;
32
+ attemptId: string | null;
33
+ }
34
+
35
+ export function gateShardLedgerPath(version: string, root = releaseLedgerRoot()): string {
36
+ if (!/^\d+\.\d+\.\d+(?:[-.][A-Za-z0-9.-]+)?$/.test(version)) throw new Error('invalid release version');
37
+ return join(root, 'release', version, 'gate-logs', 'shard-ledger.jsonl');
38
+ }
39
+
40
+ /** Each newline is a complete event, so an observer can read while the gate is running. */
41
+ export function appendGateShardLedger(path: string, row: GateShardLedgerRow): void {
42
+ if (!/^[a-f0-9]{40}$/i.test(row.commit) || !/^[a-f0-9]{64}$/i.test(row.envFingerprint)) throw new Error('invalid shard ledger identity');
43
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(row.shardId) || row.files.some((file) => file.startsWith('/') || file.split('/').includes('..'))) throw new Error('invalid shard ledger path');
44
+ mkdirSync(dirname(path), { recursive: true });
45
+ appendFileSync(path, `${JSON.stringify(row)}\n`, { mode: 0o600 });
46
+ }
47
+
48
+ export function readGateShardLedger(path: string): { rows: GateShardLedgerRow[]; malformed: number } | null {
49
+ if (!existsSync(path)) return null;
50
+ const rows: GateShardLedgerRow[] = [];
51
+ let malformed = 0;
52
+ for (const line of readFileSync(path, 'utf8').split('\n')) {
53
+ if (!line.trim()) continue;
54
+ try {
55
+ const row = JSON.parse(line) as GateShardLedgerRow;
56
+ if (row.v !== 1 || typeof row.shardId !== 'string' || !Array.isArray(row.files) || typeof row.observedAt !== 'string') throw new Error('invalid row');
57
+ rows.push(row);
58
+ } catch { malformed++; }
59
+ }
60
+ return { rows, malformed };
61
+ }
62
+
63
+ /** The newest event for each phase/shard. Preserve every attempt in the underlying ledger. */
64
+ export function latestGateShardLedger(rows: readonly GateShardLedgerRow[]): GateShardLedgerRow[] {
65
+ const latest = new Map<string, GateShardLedgerRow>();
66
+ for (const row of rows) latest.set(`${row.phase}:${row.shardId}`, row);
67
+ return [...latest.values()].sort((a, b) => a.phase.localeCompare(b.phase) || a.shardId.localeCompare(b.shardId, undefined, { numeric: true }));
68
+ }