elanous 0.2.36 → 0.2.37
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.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → 8kITumG9GAZ1LwT9POrAR}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1224.2ceca6c180d5130b.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{4286.b70c4520306817e8.js → 1262.a89bb27a5b2f844e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3410.7f9af5ab77cbe65f.js → 1746.3563617ea64023a3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1ed5e8af.cf424815c7c0efad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9250-072df22eaadaa6b5.js → 2002-a43a3f380df873ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/210.90b2cbd64926cf34.js +32 -0
- package/apps/pwa/out/_next/static/chunks/{3727.7949ccaacaa71242.js → 2283.edd9627b7bdfb546.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/2318-450e75d07605b8fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4615.8525eb0add35eba6.js → 2375.bf364c85dbb6f085.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2479-fe7e188bd9402d14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +27 -0
- package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5711.957b095705c12560.js → 3199.6a1a9b0e6cd01dc6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2986.45d7652e4f13557e.js → 3578.4e9f9948a9a6f2f4.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4036.d54e55e1ebf5acdc.js +90 -0
- package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +226 -0
- package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +287 -0
- package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7481.39f7a4654907bf6e.js → 4313.76f232c304a15bde.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4324.c99ba3e83eb1b691.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4337e36d.f42c3b391b3d8f20.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{6943.c6b24124853a47b2.js → 4383.18e5c54db3a22c88.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3757-180308bc61021b05.js → 4725-34bed25720cc326b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4833-7934c592c5569f00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{771.c3cdb6c1f7f47c02.js → 4931.95cc6dbd230774ca.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/4937-28d437999b77c9dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{552.b7d061ad91eeac51.js → 5046.7fd5cbd859299448.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +104 -0
- package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +1 -0
- package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9f31e478.fefcbcd3c388ebc5.js → 58edce7d.f1b0c72acf13b123.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +93 -0
- package/apps/pwa/out/_next/static/chunks/{3136.30aaaa5fa03662a3.js → 5952.294fc574a8625184.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{da5ecc5c-b005ad8cac737d57.js → 5f6af561-f90b7a67f4f75d13.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +74 -0
- package/apps/pwa/out/_next/static/chunks/{3324.a09703033abcbd2f.js → 6252.240578f10952e091.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8904.7d51af2dc033965c.js → 6680.6a693c28aae4cd19.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{275-02ea4c2a33980422.js → 6745-5a63492933959dd9.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{528.38ce20ef8aa251ff.js → 6819.be5c9c4d67f22922.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6877-84d47b7e85d0c41e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6971-fb5f9dc0a2cb5d2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3a3d9966.cc4d4585a2708e7c.js → 709a918c.4fd3b4e2a32cb4e3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +41 -0
- package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4304.e9b2512834a9e23e.js → 7424.c0fe2a595d4232b5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/757-4fbaf50942b8c424.js +1 -0
- package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2683.c9773a0c589b031b.js → 7867.e8725745ddd1d940.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/{6466-92e65a48490b68a9.js → 7893-e444a1b0809cd1e4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{ccae1c4b-0cf414f5eff1b10d.js → 7c5ed21f-f286708e57cf88f7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{3840-5825d56d46976a92.js → 8080-40d781e7704d3671.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +77 -0
- package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +1 -0
- package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +286 -0
- package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +79 -0
- package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{873d6c12.09bd499f57171543.js → 8c58d09b.e8724a299658d86f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{575.d929793a53f27c4b.js → 9151.a2c51e93f9156dae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9175-7d383df15a82e2be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +1 -0
- package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +94 -0
- package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{190.285c832bd9626f8c.js → 958.53a8dcf4729e6997.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-5c112c6d7c33656e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-e2de2e59111c9aad.js → page-3337c6a1328c2674.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-f2b2e4bf6e981450.js → page-89acd5d68c0bc560.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-2af16e62c12f22df.js → page-fe986fe79b107546.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-1e5da297557d2354.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-88a1edbff62eaf1d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-443364c9e4a23b31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-b9af2ac1c4737ce8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-fb9e639c107f5f72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-ac2c1e6169cfce57.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c1c80e345fa9d6f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{053a429e.9a63436527cc751c.js → d9815f84.4918f682a45a8502.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{ceba0382.650712b56b85b582.js → de8446d3.31b73eb239fecd7b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +1 -0
- package/apps/pwa/out/_next/static/css/{b5d88eba78a3d7bf.css → be9019f696217521.css} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/drafts/recipes.yaml +27 -7
- package/graphs/work/draft-close.yaml +54 -0
- package/graphs/work/green-close.yaml +37 -0
- package/graphs/work/recipes.yaml +46 -1
- package/graphs/work/run-research.yaml +35 -0
- package/package.json +1 -1
- package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
- package/scripts/boundary-proof-mapping-audit.ts +88 -0
- package/scripts/codex-fallback-live-probe.ts +17 -15
- package/scripts/fleet/node-setup.sh +32 -2
- package/scripts/fleet/run-light-worker.sh +9 -0
- package/scripts/gate-0226-failure-census.ts +507 -0
- package/scripts/goals/authoring-metrics.ts +258 -0
- package/scripts/graph-units/draft-close-node.ts +222 -0
- package/scripts/hook-order-sweep.ts +7 -4
- package/scripts/land-global-checks.ts +69 -1
- package/scripts/measure-nl-routing-corpus.ts +207 -2
- package/scripts/release-loop/docs-node.ts +140 -0
- package/scripts/release-loop/gate-closure.ts +48 -0
- package/scripts/release-loop/gate-diff.ts +2 -1
- package/scripts/release-loop/known-issues-node.ts +6 -4
- package/scripts/release-loop/notes-repair.ts +216 -0
- package/scripts/release-loop/prepare-node.ts +61 -5
- package/scripts/release-loop/resume-release.ts +114 -4
- package/scripts/release-loop/resume-test-traffic.ts +10 -0
- package/scripts/release-loop/version-node.ts +81 -12
- package/scripts/train/green-leg.ts +176 -0
- package/scripts/tui-continue-real-evidence.ts +47 -0
- package/scripts/ux-sim.ts +1 -1
- package/src/acp/client.ts +31 -5
- package/src/acp/core-turn-bridge.ts +6 -0
- package/src/acp/stderr-summary.ts +49 -0
- package/src/agent/monad-agent-turn.ts +1 -0
- package/src/agent/terminal-surface.ts +217 -0
- package/src/agent-substrate/pr-reviewer.ts +11 -3
- package/src/agent-substrate/self-review-cli.ts +343 -31
- package/src/autopilot/lineage/sources.ts +1 -1
- package/src/autopilot/pr-manager.ts +64 -13
- package/src/boot/daemon-runtime.ts +35 -3
- package/src/boot/daemon-tools/pty-shell.ts +2 -1
- package/src/budget/run-usage-rollup.ts +12 -7
- package/src/budget/unified-usage.ts +20 -0
- package/src/capture/tui-self-report.ts +32 -3
- package/src/cli/autopilot-cli.ts +24 -9
- package/src/cli/decisions-cli.ts +7 -4
- package/src/cli/doctor-cli.ts +116 -7
- package/src/cli/logs-cli.ts +357 -2
- package/src/cli/memory-cli.ts +30 -1
- package/src/cli/msg-cli.ts +15 -11
- package/src/cli/pr-granularity.ts +5 -3
- package/src/cli/release-checklist-read-cli.ts +109 -4
- package/src/cli/release-cli.ts +43 -10
- package/src/cli/self-update.ts +23 -5
- package/src/cli/self-utterances-cli.ts +13 -16
- package/src/cli/tasks-cli.ts +309 -11
- package/src/cli/usage-cli.ts +12 -10
- package/src/context-bus/claude-memory-import.ts +99 -0
- package/src/context-bus/long-term-memory.ts +125 -0
- package/src/core-turn/run-goal-loop.ts +5 -2
- package/src/core-turn/types.ts +2 -0
- package/src/dashboard/chat/acp-chat.ts +9 -0
- package/src/dashboard/index.ts +97 -13
- package/src/dashboard/input/agent-wake.ts +43 -0
- package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
- package/src/dashboard/replay-preview.ts +25 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +17 -8
- package/src/dashboard/synchronized-frame-output.ts +74 -0
- package/src/display/coordinator.ts +45 -12
- package/src/display/resize-listener.ts +41 -20
- package/src/display/synchronized-output-region.ts +60 -0
- package/src/domains/surface-events.ts +80 -1
- package/src/exec-requests/planner.ts +37 -2
- package/src/execution-loop/launch-gate.ts +4 -3
- package/src/git-fs/cli-stderr.ts +45 -0
- package/src/git-fs/gh-cli.ts +6 -3
- package/src/git-fs/git-cli.ts +11 -5
- package/src/graph-runner/graph-cli.ts +26 -2
- package/src/graph-runner/graph-run-api.ts +3 -2
- package/src/graph-runner/runner.ts +17 -5
- package/src/graph-wizard/generate.ts +29 -6
- package/src/harness/harness-cli-command.ts +179 -33
- package/src/harness/review-adapter.ts +26 -6
- package/src/index.ts +116 -13
- package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
- package/src/llm/route-decision.ts +13 -0
- package/src/llm-tool-name.ts +37 -0
- package/src/llm-tool-schema.ts +35 -0
- package/src/llm.ts +123 -11
- package/src/loops/checker.ts +100 -0
- package/src/loops/consensus.ts +26 -2
- package/src/loops/loop-cli.ts +9 -4
- package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
- package/src/loops/orchestrator/tick.ts +49 -4
- package/src/model-tier/llm-tier-map.ts +12 -17
- package/src/msg/msg-store.ts +17 -0
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/graph-wizard-api.ts +27 -2
- package/src/nexus/api/grid.ts +24 -4
- package/src/nexus/api/http-server.ts +1 -1
- package/src/nexus/api/ops-api.ts +14 -6
- package/src/nexus/state/state.ts +7 -1
- package/src/oauth/codex-account-store.ts +34 -1
- package/src/ops/loops-map.ts +46 -0
- package/src/ops/overview-cache.ts +22 -4
- package/src/ops/overview.ts +41 -5
- package/src/policy/llm-policy.ts +3 -1
- package/src/prompt-enhance/enhance.ts +4 -4
- package/src/pty-shell/availability.ts +7 -0
- package/src/pty-shell/herdr-agent-report.ts +40 -31
- package/src/pty-shell/herdr-prune-stale.ts +17 -0
- package/src/pty-shell/herdr-sequence-lease.ts +60 -0
- package/src/pty-shell/pty-control-ipc.ts +187 -18
- package/src/pty-shell/registry.ts +25 -15
- package/src/pty-shell/tui-run-state.ts +235 -1
- package/src/release-loop/checklist.ts +50 -5
- package/src/release-loop/feature-store.ts +9 -9
- package/src/release-loop/landed-but-yellow.ts +93 -12
- package/src/repl/index.ts +8 -0
- package/src/seat-loop/seat-report.ts +1 -0
- package/src/self-dev/ask-launch-flow.ts +86 -10
- package/src/self-dev/decompose-proposal.ts +29 -18
- package/src/self-dev/dev-pipeline.ts +29 -7
- package/src/self-dev/draft-sweep.ts +241 -16
- package/src/self-dev/draft-triage-rules.ts +19 -5
- package/src/self-dev/review-provider-fallback.ts +45 -7
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-run.ts +50 -3
- package/src/self-implement/doctor-visit.ts +83 -0
- package/src/self-implement/gate-artifacts.ts +6 -2
- package/src/self-implement/gate-baseline.ts +64 -1
- package/src/self-implement/gate-cli.ts +93 -9
- package/src/self-implement/gate-remote.ts +28 -4
- package/src/self-implement/gate-scope.ts +13 -1
- package/src/self-implement/goal-author.ts +333 -18
- package/src/self-implement/goal-supervisor-fill.ts +75 -26
- package/src/self-implement/importer-test-index.ts +215 -2
- package/src/self-implement/knowhow-pack.ts +5 -5
- package/src/self-implement/orchestrator.ts +318 -25
- package/src/self-implement/review-round-brief.ts +300 -0
- package/src/self-implement/rework-policy.ts +14 -0
- package/src/self-implement/run-ledger.ts +170 -6
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/running-runs.ts +17 -3
- package/src/self-implement/seams.ts +12 -6
- package/src/self-implement/stop-autoheal.ts +1 -0
- package/src/self-implement/test-seams.ts +13 -1
- package/src/self-implement/train-reach-grouping.ts +8 -2
- package/src/session/chat.ts +7 -1
- package/src/session/id-prefix.ts +60 -0
- package/src/session/index.ts +4 -0
- package/src/session-runtime/index.ts +9 -1
- package/src/skills/tools/pty.ts +5 -0
- package/src/status/metrics.ts +12 -1
- package/src/steward/rejudge-tick.ts +12 -5
- package/src/steward/resync-tick.ts +31 -4
- package/src/task-agent/actions.ts +199 -4
- package/src/task-agent/boundary-proof-mapping.ts +12 -0
- package/src/task-agent/boundary-proof-producer.ts +147 -0
- package/src/task-agent/completion-evidence.ts +2 -1
- package/src/task-agent/green-close-recipe.ts +239 -0
- package/src/task-agent/green-sweep.ts +64 -10
- package/src/task-agent/live-moves.ts +48 -3
- package/src/task-agent/map-host-adapter.ts +134 -0
- package/src/task-agent/map-reduce.ts +158 -0
- package/src/task-agent/map-run-ledger.ts +226 -0
- package/src/task-agent/parent-host.ts +21 -11
- package/src/task-agent/rejudge-on-head.ts +72 -5
- package/src/task-agent/repair-cycle.ts +41 -0
- package/src/task-agent/repair-next-move.ts +12 -2
- package/src/task-agent/review-prior-fix.ts +12 -2
- package/src/task-agent/shadow.ts +188 -5
- package/src/task-agent/task-hand.ts +158 -13
- package/src/task-agent/task-parent-role-llm.ts +9 -0
- package/src/task-agent/tier-split.ts +4 -3
- package/src/task-agent/work-order.ts +53 -2
- package/src/task-orchestrator/dispatcher.ts +46 -1
- package/src/task-orchestrator/generator-prompt.ts +6 -0
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +50 -4
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +15 -9
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +217 -36
- package/src/task-orchestrator/surfaces/self-implement.ts +4 -0
- package/src/testing/linux-env.ts +177 -0
- package/src/tui/chat/goal-interim.ts +5 -1
- package/src/tui/entry.ts +258 -4
- package/src/tui/input/at-picker.ts +55 -0
- package/src/tui/slash/resume.ts +35 -12
- package/src/tui/slash/signals.ts +114 -0
- package/src/tui/slash/term-next.ts +71 -0
- package/src/tui-client/local-chat-turn.ts +4 -3
- package/src/user-config.ts +19 -1
- package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
- package/src/ux-sim/classic-tui-seat-scenarios.ts +158 -4
- package/src/ux-sim/fake-gh.ts +174 -0
- package/src/ux-sim/fake-kubectl.ts +142 -0
- package/src/ux-sim/fake-llm.ts +6 -1
- package/src/ux-sim/next-tui-seat-scenarios.ts +124 -6
- package/src/ux-sim/resume-picker-device.ts +173 -0
- package/src/ux-sim/session-id-seed.ts +60 -0
- package/src/ux-sim/skills-tools-shell-device.ts +156 -0
- package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
- package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/109.f196abf3484bb97f.js +0 -287
- package/apps/pwa/out/_next/static/chunks/1201.afbfc5b1c5e5cbe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1517.9275b89572098ff3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1574.ad11f8db8396ab45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1614.7a8c5c40aee0261f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1871-f1eb29d8dbb4065b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2193.0ea24c4a7aa1d004.js +0 -36
- package/apps/pwa/out/_next/static/chunks/223-ad865c1e80491d78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2265-f0d90b6dbec05f81.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2267.eb4a62a209f09ec4.js +0 -149
- package/apps/pwa/out/_next/static/chunks/2341-86cd06c0cf01aa33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2352.9abc42422bade876.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2397-c1653a816061842d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2500.cbc1257b774aae52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2709.8e1889034255fc5f.js +0 -24
- package/apps/pwa/out/_next/static/chunks/2812ca6a.3b9f4a7a08885744.js +0 -1
- package/apps/pwa/out/_next/static/chunks/284.ac77c0db55bfd3a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2941.3c3546f96bc6efa7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2976.b000698a3c6512dc.js +0 -24
- package/apps/pwa/out/_next/static/chunks/3031-85328a92fcce4da6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3148-98efd4869d660a87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3155.2587a55196ecb394.js +0 -226
- package/apps/pwa/out/_next/static/chunks/3277.785db1cf09f98601.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3320-f9c77381f85a2c0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3386-91049feabff9a94e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/339.80afb7e8839372af.js +0 -8
- package/apps/pwa/out/_next/static/chunks/3538.a445e50bb359d068.js +0 -32
- package/apps/pwa/out/_next/static/chunks/3540c645.2595a6af01a97672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3621.581b2054cc14bee4.js +0 -286
- package/apps/pwa/out/_next/static/chunks/3676.8c28d16fd84ca843.js +0 -1
- package/apps/pwa/out/_next/static/chunks/375-b84540853523fe52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3969-81054381eaec4c4c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4067-bf9aa60547972937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4116.a84a92a2492c080e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4176.e5729c93c29151eb.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4227-e6c84686bc8946ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4327.b9e08a71c0009348.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4946.bd106eafd668f248.js +0 -148
- package/apps/pwa/out/_next/static/chunks/5085-bce4904931767ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5125.2eb9968b83767193.js +0 -93
- package/apps/pwa/out/_next/static/chunks/5128-75b733fd74a9a1eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5489f3fe.4bb1458b09f6dead.js +0 -122
- package/apps/pwa/out/_next/static/chunks/5560-2a4648f6a30f16e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5772-e29e14f8ff127122.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5887.5b2f5b44ce0f7e9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5899-4c112a2d908cf97f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/599.c39c571b6733f2a2.js +0 -41
- package/apps/pwa/out/_next/static/chunks/6041.1b65efb796ebc2a5.js +0 -79
- package/apps/pwa/out/_next/static/chunks/6057-765c0a26028c86b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6142.f289e0d039f453a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6199-6bcd61d2a3910e04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6236.cdf7293e710a9b2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6387-237e079dac9a3ae6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6448-b6d272d074276e14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6566.926a1d1b7c911442.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6580-cdc0e4a634ce4708.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6659-b242dc31b20b3f29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6690.c5d9b321b1f79384.js +0 -1
- package/apps/pwa/out/_next/static/chunks/671-e9b6ad0c771c5514.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6862.bcbf89dd0f4496b3.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6912-7b375ee48732c767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6925.562d532110879886.js +0 -1
- package/apps/pwa/out/_next/static/chunks/727.7deb87749b0a343a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7545.b1aa42cd15503319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-416d298c2dc4a9ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7784.1f0ac4a16d190cf4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7870-309de463786ac1fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7888-e29b151bb6a3bb8b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7891.03f910124a737985.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7905-6ba78d9234608f79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8037.e254fd797aa224d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8207.f527886b7c464529.js +0 -1
- package/apps/pwa/out/_next/static/chunks/856.50bda61439d465e6.js +0 -62
- package/apps/pwa/out/_next/static/chunks/9031-92e9b487edb681ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9060.5410d9f6a95f6287.js +0 -90
- package/apps/pwa/out/_next/static/chunks/914.8abefd48d579547b.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9187-c87a2207d4ac5d46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9289.3e92f278821e18e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9319.7816d3cc741cd34f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9327-e016bb9f1674d24f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9365.3de48bf75193ff9a.js +0 -77
- package/apps/pwa/out/_next/static/chunks/9383.5b5e9aa56b5fea97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9387-cb929573ef436f39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9489.c27becacc3283dec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9514.131c52cc75fe6439.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9536.28962663c7734376.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9686-12286f5bbf15a5a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9776.289d40cc5a4c342a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9789.60f723d18097ec30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/989.f0c17523c5e61f65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9972-1df393aa8d4f7d52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4105e5bbbb043024.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5d4fa75c2acb113e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a79a9bdb3b2142bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f1c2047484992a35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-15ffc49b2d8c70e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-67d53a3fc8734135.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-e30c8dc696491336.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-2136e04fa77696e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-fecd2814790c2e12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-280aa4a98fc5bb44.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c14e96b65fb4b883.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-187dac74825ed6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-6bc7246ea8c92448.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-083747f1d4518f61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-15ca95a16ec96e1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a8ac031b3810eb64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-8835f93110494cda.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5b92b7f75df20948.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-468eddf2b1a84ecb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-18b40867a77a5793.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-fe934f2979f5a4a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-3eec435537357170.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ff88cbfd2aa99fc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a5b39fb780c70ab5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ae465dde1d1b3933.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e7abc34a48a6f71e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-cab9674fde9ead26.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-1da49bc8712284d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5e8aa657551ac912.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fcc36f36c6dc1fe2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d22db22f2a7296ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d12dc7b1773878f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-99debec9ea7fbec0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-2a088a78bad7e924.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-2e8ed57db7e69993.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4b3a16213c2ae8fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f098747ddf38c98b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-c15958835534404b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-68279e3c843c1fd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a34a74453367a39d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8767a7db2155a5bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-bd5976683460d7f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-b7364835e7f2ef54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-311af16b3ee0ffca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fb670757274ae990.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f9a61e4d.1fc2c1dbdd49fe46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-6d497cbf1eef04c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-012f5edec932f337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-da5aeb809edc94a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-5ee319af6f749dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-4bc84653a718d6c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-8a96cf6775c73d6e.js +0 -1
- /package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → 8kITumG9GAZ1LwT9POrAR}/_ssgManifest.js +0 -0
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// `reportTuiTurn` 을 부른다. 기록 실패는 `debug.log('pty.tui-state', …)` — 조용히 삼키지 않는다.
|
|
12
12
|
// 판독(CLI 쪽): `describePtyRunState` 가 행 ⊕ 설치본 기록 ⊕ (체크아웃이면) 그 뿌리의 HEAD 로 판정한다.
|
|
13
13
|
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
14
15
|
import { readFileSync } from 'node:fs';
|
|
15
16
|
import { homedir } from 'node:os';
|
|
16
17
|
import { dirname, join, resolve } from 'node:path';
|
|
@@ -307,6 +308,144 @@ export function formatTurnCell(cell: PtyTurnCell): string {
|
|
|
307
308
|
return `${cell.state},${cell.sinceSec === null ? '?' : cell.sinceSec}s`;
|
|
308
309
|
}
|
|
309
310
|
|
|
311
|
+
// ── H9 자기 재기동 — «재기동 필요» ∧ 유휴 ∧ 사람 입력 없음 ⇒ 스스로 재기동 ──
|
|
312
|
+
//
|
|
313
|
+
// 정본: seat-state/OPC/TUI-HOMEOSTASIS-SLICES-2026-10-16.md (TUI-H9-IDLE-SELF-RESTART).
|
|
314
|
+
// 판정(`decideSelfRestart`)은 순수 함수 — 실제 «새 세션 ⊕ 자리 맥락 재주입» 은 주입된 수행자(`restart`)가 한다.
|
|
315
|
+
// 낡음 판정은 기존 `BuildStaleness` 를 그대로 재사용한다(판정·문면을 두벌로 만들지 않는다).
|
|
316
|
+
// 사람 입력 대기 신호는 입력 축(대시보드)이 준다 — 이 모듈은 입력 큐를 소유하지 않는다.
|
|
317
|
+
|
|
318
|
+
/** 유휴 유예: 이만큼 조용했을 때만 재기동한다 — 턴 사이 «쉼»에서 세션을 새로 띄우지 않는다. */
|
|
319
|
+
export const DEFAULT_SELF_RESTART_IDLE_MS = 90_000;
|
|
320
|
+
|
|
321
|
+
export type SelfRestartReason =
|
|
322
|
+
| 'stale-idle-no-input'
|
|
323
|
+
| 'not-stale'
|
|
324
|
+
| 'staleness-unknown'
|
|
325
|
+
| 'turn-busy'
|
|
326
|
+
| 'turn-unknown'
|
|
327
|
+
| 'still-warm'
|
|
328
|
+
| 'human-input-pending';
|
|
329
|
+
|
|
330
|
+
export interface SelfRestartDecisionDeps {
|
|
331
|
+
/** 재기동 필요인가 — 미지(null)면 재기동하지 않는다. */
|
|
332
|
+
readonly stale: boolean | null;
|
|
333
|
+
readonly staleReason?: BuildStaleReason;
|
|
334
|
+
readonly turn: PtyTurnView;
|
|
335
|
+
/** 지금 턴 상태에 머문 시간(ms) — 모르면 null. */
|
|
336
|
+
readonly idleMs: number | null;
|
|
337
|
+
readonly humanInputPending: boolean;
|
|
338
|
+
readonly minIdleMs?: number;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface SelfRestartDecision {
|
|
342
|
+
readonly restart: boolean;
|
|
343
|
+
readonly reason: SelfRestartReason;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** 판정선: 세 조건(재기동 필요 · 유휴 · 사람 입력 없음)이 모두 참일 때만 참. 거짓인 첫 조건을 이유로 낸다. */
|
|
347
|
+
export function decideSelfRestart(deps: SelfRestartDecisionDeps): SelfRestartDecision {
|
|
348
|
+
if (deps.stale === null) return { restart: false, reason: 'staleness-unknown' };
|
|
349
|
+
if (!deps.stale) return { restart: false, reason: 'not-stale' };
|
|
350
|
+
if (deps.turn === 'busy') return { restart: false, reason: 'turn-busy' };
|
|
351
|
+
if (deps.turn === 'unknown') return { restart: false, reason: 'turn-unknown' };
|
|
352
|
+
const minIdleMs = deps.minIdleMs ?? DEFAULT_SELF_RESTART_IDLE_MS;
|
|
353
|
+
if (deps.idleMs === null || deps.idleMs < minIdleMs) return { restart: false, reason: 'still-warm' };
|
|
354
|
+
if (deps.humanInputPending) return { restart: false, reason: 'human-input-pending' };
|
|
355
|
+
return { restart: true, reason: 'stale-idle-no-input' };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** 재기동 계획 — 새 세션 ⊕ 자리 맥락 재주입 명령까지. 세션 id 를 모르면 명령은 null(판정 근거는 남긴다). */
|
|
359
|
+
export interface TuiSelfRestartPlan {
|
|
360
|
+
readonly surfaceId: string;
|
|
361
|
+
readonly staleReason: BuildStaleReason;
|
|
362
|
+
readonly idleMs: number;
|
|
363
|
+
readonly relaunchCommand: string | null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export interface TuiSelfRestartDeps {
|
|
367
|
+
/** 낡음 판정 재료 — 생략하면 이 프로세스의 기동 판 ⊕ 설치본 ⊕ 체크아웃 HEAD 로 잰다. */
|
|
368
|
+
readonly staleness?: () => BuildStaleness;
|
|
369
|
+
/** 사람이 지금 입력 중인가 — 생략하면 «모름» 으로 보고 재기동하지 않는다(조용한 세션을 뺏지 않는다). */
|
|
370
|
+
readonly humanInputPending?: () => boolean;
|
|
371
|
+
readonly sessionId?: string;
|
|
372
|
+
/**
|
|
373
|
+
* 지금 이 TUI 가 «소유한» 세션 — 틱마다 다시 읽어 `sessionId` 보다 우선한다.
|
|
374
|
+
* 대화가 갈아 끼워지는 TUI 는 기동 시점 값을 고정할 수 없어 이 칸을 쓴다(읽기 실패 → null → 재기동 안 함).
|
|
375
|
+
*/
|
|
376
|
+
readonly readSessionId?: () => string | null;
|
|
377
|
+
readonly minIdleMs?: number;
|
|
378
|
+
readonly intervalMs?: number;
|
|
379
|
+
/** 실제 재기동 수행자(새 세션 ⊕ 자리 맥락 재주입). 생략 시 설치본 `elanous --resume <세션>`. */
|
|
380
|
+
readonly restart?: (plan: TuiSelfRestartPlan) => void;
|
|
381
|
+
/**
|
|
382
|
+
* 낡은 TUI 가 좌석을 «놓는» 동작 — 새 세션을 띄우기 직전에 부른다.
|
|
383
|
+
* 생략해도 이 녹음기는 그 좌석 행 쓰기를 스스로 놓는다(두 TUI 가 한 좌석을 겹쳐 쓰지 않게).
|
|
384
|
+
*/
|
|
385
|
+
readonly yieldSeat?: (plan: TuiSelfRestartPlan) => void;
|
|
386
|
+
/** 유휴 감시 등록 — 틱을 받아 해제 함수를 준다. 생략 시 unref 된 setInterval. */
|
|
387
|
+
readonly watch?: (tick: () => void) => () => void;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const stalenessFromRunningBuild = (): BuildStaleness => {
|
|
391
|
+
const build = runningBuild();
|
|
392
|
+
const installed = readCurrentInstallRecord();
|
|
393
|
+
let checkoutHead: string | undefined;
|
|
394
|
+
if (build.source === 'checkout' && build.codeRoot) checkoutHead = readCheckoutHead(build.codeRoot);
|
|
395
|
+
return judgeBuildStaleness({ build, installed, ...(checkoutHead ? { checkoutHead } : {}) });
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
const defaultWatch = (tick: () => void, intervalMs: number): (() => void) => {
|
|
399
|
+
const timer = setInterval(tick, intervalMs);
|
|
400
|
+
(timer as unknown as { unref?: () => void }).unref?.();
|
|
401
|
+
return () => { clearInterval(timer); };
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/** 재기동 인자: 새 세션은 `--resume <세션>` 으로 그 좌석의 자리 맥락을 다시 받는다. */
|
|
405
|
+
export const TUI_RELAUNCH_FLAG = '--resume';
|
|
406
|
+
|
|
407
|
+
export interface TuiRelaunchChild {
|
|
408
|
+
on(event: 'error', listener: (error: Error) => void): unknown;
|
|
409
|
+
on(event: 'spawn', listener: () => void): unknown;
|
|
410
|
+
unref(): void;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** 프로세스 띄우기 seam — 무대는 여기만 가짜로 바꿔 «생산 argv·옵션» 을 그대로 지난다. */
|
|
414
|
+
export type TuiRelaunchSpawn = (
|
|
415
|
+
command: string,
|
|
416
|
+
args: readonly string[],
|
|
417
|
+
options: { detached: boolean; stdio: 'ignore' },
|
|
418
|
+
) => TuiRelaunchChild;
|
|
419
|
+
|
|
420
|
+
const defaultRelaunchSpawn: TuiRelaunchSpawn = (command, args, options) =>
|
|
421
|
+
spawn(command, args, options) as unknown as TuiRelaunchChild;
|
|
422
|
+
|
|
423
|
+
let relaunchSpawn: TuiRelaunchSpawn = defaultRelaunchSpawn;
|
|
424
|
+
|
|
425
|
+
/** 무대용 spawn 교체 — 인자 없이 부르면 생산 기본값(spawn)으로 되돌린다. */
|
|
426
|
+
export function setTuiRelaunchSpawnForTesting(next?: TuiRelaunchSpawn): void {
|
|
427
|
+
relaunchSpawn = next ?? defaultRelaunchSpawn;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** 기본 재기동 수행자: 설치본 `elanous --resume <세션>` — 새 세션이 그 좌석의 자리 맥락으로 다시 뜬다. */
|
|
431
|
+
export const spawnTuiRelaunch = (sessionId: string): Promise<void> => {
|
|
432
|
+
return new Promise<void>((resolve, reject) => {
|
|
433
|
+
const child = relaunchSpawn(process.env.ELANOUS_BIN || 'elanous', [TUI_RELAUNCH_FLAG, sessionId], { detached: true, stdio: 'ignore' });
|
|
434
|
+
let settled = false;
|
|
435
|
+
child.on('error', (error: Error) => {
|
|
436
|
+
if (settled) return;
|
|
437
|
+
settled = true;
|
|
438
|
+
reject(error);
|
|
439
|
+
});
|
|
440
|
+
child.on('spawn', () => {
|
|
441
|
+
if (settled) return;
|
|
442
|
+
settled = true;
|
|
443
|
+
child.unref();
|
|
444
|
+
resolve();
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
};
|
|
448
|
+
|
|
310
449
|
// ── 기록(TUI) ──
|
|
311
450
|
|
|
312
451
|
export interface TuiRunStateRecorderDeps {
|
|
@@ -326,6 +465,11 @@ export interface TuiRunStateRecorderDeps {
|
|
|
326
465
|
* 생략 시: 기본 신고기(herdr 미주입)일 때만 실 `process` · 주입 herdr 는 지킴이 0. `null` 이면 안 건다.
|
|
327
466
|
*/
|
|
328
467
|
readonly signals?: HerdrSignalTarget | null;
|
|
468
|
+
/**
|
|
469
|
+
* H9 자기 재기동(낡은 판 ∧ 유휴 ∧ 사람 입력 없음 ⇒ 새 세션 ⊕ 자리 맥락 재주입).
|
|
470
|
+
* 생략하면 감시를 안 건다 — 이 축을 켜는 것은 호출자(생산 진입점)의 결정이다.
|
|
471
|
+
*/
|
|
472
|
+
readonly selfRestart?: TuiSelfRestartDeps;
|
|
329
473
|
}
|
|
330
474
|
|
|
331
475
|
interface ActiveRecorder {
|
|
@@ -336,6 +480,8 @@ interface ActiveRecorder {
|
|
|
336
480
|
readonly herdr: HerdrAgentReporterLike | null;
|
|
337
481
|
/** manifest 에 실제로 적힌 마지막 턴. 실패면 안 바뀐다(재시도). */
|
|
338
482
|
last?: PtyTurnState;
|
|
483
|
+
/** idle 로 «바뀐» 시각 — 유휴 경과의 0초. busy 로 가면 다시 모른다. */
|
|
484
|
+
idleSince?: number;
|
|
339
485
|
/** herdr 에 마지막으로 알린 턴. manifest 성패와 따로 둔다. */
|
|
340
486
|
herdrLast?: PtyTurnState;
|
|
341
487
|
}
|
|
@@ -366,6 +512,88 @@ export function startTuiRunStateRecorder(surfaceId: string, deps: TuiRunStateRec
|
|
|
366
512
|
active = recorder;
|
|
367
513
|
writeTurnState(recorder, 'idle');
|
|
368
514
|
if (herdr) { try { herdr.start(); } catch { /* 신고 실패가 녹음기를 깨지 않는다 */ } }
|
|
515
|
+
// H9 유휴 감시: 판·턴·사람 입력을 매 틱 다시 재고, 재기동 성공 뒤에만 좌석을 양도한다.
|
|
516
|
+
const selfRestart = deps.selfRestart;
|
|
517
|
+
let unwatch: () => void = () => {};
|
|
518
|
+
if (selfRestart) {
|
|
519
|
+
const minIdleMs = selfRestart.minIdleMs ?? DEFAULT_SELF_RESTART_IDLE_MS;
|
|
520
|
+
const restartedFor = new Set<BuildStaleReason>();
|
|
521
|
+
let restartInFlight = false;
|
|
522
|
+
const tick = (): void => {
|
|
523
|
+
if (active !== recorder) return;
|
|
524
|
+
let staleness: BuildStaleness;
|
|
525
|
+
try { staleness = (selfRestart.staleness ?? stalenessFromRunningBuild)(); } catch (error) {
|
|
526
|
+
log('self-restart-staleness-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
// 사람 입력 신호가 없거나 읽을 수 없으면 모름으로 닫는다 — 명시적 false만 «사람 입력 없음»이다.
|
|
530
|
+
let humanInputPending = true;
|
|
531
|
+
if (selfRestart.humanInputPending) {
|
|
532
|
+
try { humanInputPending = selfRestart.humanInputPending() !== false; } catch { humanInputPending = true; }
|
|
533
|
+
}
|
|
534
|
+
// 재기동은 «지금 이 TUI 가 보여 주는 대화» 로만 잇는다 — 읽기 실패는 null(대체 세션을 지어내지 않는다).
|
|
535
|
+
let sessionId: string | null = selfRestart.sessionId ?? null;
|
|
536
|
+
if (selfRestart.readSessionId) {
|
|
537
|
+
try { sessionId = selfRestart.readSessionId(); } catch { sessionId = null; }
|
|
538
|
+
}
|
|
539
|
+
const idleMs = recorder.idleSince === undefined ? null : Math.max(0, now() - recorder.idleSince);
|
|
540
|
+
const decision = decideSelfRestart({
|
|
541
|
+
stale: staleness.stale,
|
|
542
|
+
staleReason: staleness.reason,
|
|
543
|
+
turn: recorder.last ?? 'unknown',
|
|
544
|
+
idleMs,
|
|
545
|
+
humanInputPending,
|
|
546
|
+
minIdleMs,
|
|
547
|
+
});
|
|
548
|
+
if (!decision.restart || restartedFor.has(staleness.reason) || restartInFlight) return;
|
|
549
|
+
if (!sessionId) {
|
|
550
|
+
log('self-restart-no-session', { ptyId: surfaceId, staleReason: staleness.reason });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const plan: TuiSelfRestartPlan = {
|
|
554
|
+
surfaceId,
|
|
555
|
+
staleReason: staleness.reason,
|
|
556
|
+
idleMs: idleMs ?? 0,
|
|
557
|
+
relaunchCommand: sessionId ? buildStaleInstallRelaunchCommand(sessionId) : null,
|
|
558
|
+
};
|
|
559
|
+
log('self-restart', { ptyId: surfaceId, reason: decision.reason, staleReason: plan.staleReason, idleMs: plan.idleMs, relaunchCommand: plan.relaunchCommand });
|
|
560
|
+
restartInFlight = true;
|
|
561
|
+
const handoff = (): void => {
|
|
562
|
+
// 새 세션이 실제로 생성된 뒤에만 낡은 TUI 가 좌석 행 쓰기를 놓는다.
|
|
563
|
+
if (selfRestart.yieldSeat) {
|
|
564
|
+
try {
|
|
565
|
+
selfRestart.yieldSeat(plan);
|
|
566
|
+
} catch (error) {
|
|
567
|
+
log('self-restart-yield-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (active === recorder) active = null;
|
|
571
|
+
restartedFor.add(staleness.reason);
|
|
572
|
+
restartInFlight = false;
|
|
573
|
+
log('self-restart-handover', { ptyId: surfaceId, staleReason: plan.staleReason, relaunchCommand: plan.relaunchCommand });
|
|
574
|
+
};
|
|
575
|
+
const failed = (error: unknown): void => {
|
|
576
|
+
restartInFlight = false;
|
|
577
|
+
log('self-restart-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
|
|
578
|
+
};
|
|
579
|
+
try {
|
|
580
|
+
if (selfRestart.restart) {
|
|
581
|
+
selfRestart.restart(plan);
|
|
582
|
+
handoff();
|
|
583
|
+
} else {
|
|
584
|
+
void spawnTuiRelaunch(sessionId).then(handoff, failed);
|
|
585
|
+
}
|
|
586
|
+
} catch (error) {
|
|
587
|
+
failed(error);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
try {
|
|
591
|
+
const watch = selfRestart.watch ?? ((fn: () => void) => defaultWatch(fn, selfRestart.intervalMs ?? Math.max(5_000, Math.round(minIdleMs / 3))));
|
|
592
|
+
unwatch = watch(tick);
|
|
593
|
+
} catch (error) {
|
|
594
|
+
log('self-restart-watch-failed', { ptyId: surfaceId, error: error instanceof Error ? error.message : String(error) });
|
|
595
|
+
}
|
|
596
|
+
}
|
|
369
597
|
// raw 모드 전 ctrl+c(SIGINT 기본 죽음)는 'exit' 훅을 안 돌린다 — 신호에서도 반납한다.
|
|
370
598
|
const signals = deps.signals !== undefined ? deps.signals : (deps.herdr === undefined ? process : null);
|
|
371
599
|
let unguard: () => void = () => {};
|
|
@@ -373,6 +601,7 @@ export function startTuiRunStateRecorder(surfaceId: string, deps: TuiRunStateRec
|
|
|
373
601
|
try { unguard = guardHerdrReleaseOnSignal(() => herdr.release(), signals); } catch { /* 지킴이 실패가 녹음기를 깨지 않는다 */ }
|
|
374
602
|
}
|
|
375
603
|
return () => {
|
|
604
|
+
try { unwatch(); } catch { /* 종료 경로를 깨지 않는다 */ }
|
|
376
605
|
try { unguard(); } catch { /* 종료 경로를 깨지 않는다 */ }
|
|
377
606
|
if (herdr) { try { herdr.release(); } catch { /* 종료 경로를 깨지 않는다 */ } }
|
|
378
607
|
if (active === recorder) active = null;
|
|
@@ -389,7 +618,12 @@ function writeTurnState(recorder: ActiveRecorder, turn: PtyTurnState): void {
|
|
|
389
618
|
const at = recorder.now();
|
|
390
619
|
let ok = false;
|
|
391
620
|
try { ok = recorder.writeTurn(recorder.surfaceId, turn, at); } catch { ok = false; }
|
|
392
|
-
if (ok)
|
|
621
|
+
if (ok) {
|
|
622
|
+
recorder.last = turn;
|
|
623
|
+
// 유휴 경과의 0초는 «idle 로 바뀐 시각» — busy 로 가면 다시 모른다.
|
|
624
|
+
if (turn === 'idle') recorder.idleSince = at;
|
|
625
|
+
else recorder.idleSince = undefined;
|
|
626
|
+
}
|
|
393
627
|
// 실패는 관측으로 남긴다 — 바깥 제어자는 그 사이 «unknown/낡은 값»을 본다. 성공은 남기지 않는다(턴마다 두 줄은 소음).
|
|
394
628
|
else recorder.log('turn-write-failed', { ptyId: recorder.surfaceId, turn, at });
|
|
395
629
|
// herdr 는 manifest 쓰기 성패와 무관하게, 값이 바뀔 때 알린다(같은 값 재시도는 한 번만).
|
|
@@ -37,6 +37,8 @@ export interface ChecklistItem {
|
|
|
37
37
|
/** 이 칸이 근거로 삼는 문서(저장소 상대 경로, 선택적으로 `#절`). 없으면 칸에 실리지 않는다. */
|
|
38
38
|
refs?: string[];
|
|
39
39
|
evidence?: string;
|
|
40
|
+
/** Public known-issue wording, independent from the internal checklist title. */
|
|
41
|
+
knownIssue?: string;
|
|
40
42
|
disposition?: ChecklistDisposition;
|
|
41
43
|
kind?: ChecklistKind;
|
|
42
44
|
/** Whether the seat dispatcher should launch a coding task. Missing in old rows means code. */
|
|
@@ -67,6 +69,49 @@ export interface Checklist {
|
|
|
67
69
|
history: ChecklistHistory[];
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
export type ChecklistEvidenceState = 'observed' | 'missing' | 'unmeasurable';
|
|
73
|
+
export type ChecklistEvidenceStageId = 'E1' | 'E3' | 'E4' | 'E5';
|
|
74
|
+
export interface ChecklistEvidenceStage {
|
|
75
|
+
id: ChecklistEvidenceStageId;
|
|
76
|
+
state: ChecklistEvidenceState;
|
|
77
|
+
source: string | null;
|
|
78
|
+
measuredAt: string | null;
|
|
79
|
+
reason: string;
|
|
80
|
+
verificationCommand?: string | null;
|
|
81
|
+
}
|
|
82
|
+
export interface ChecklistEvidenceChain {
|
|
83
|
+
id: string;
|
|
84
|
+
version: string;
|
|
85
|
+
stages: ChecklistEvidenceStage[];
|
|
86
|
+
greenEligible: boolean;
|
|
87
|
+
reason: string | null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Build a read-only, fail-closed evidence verdict. Claims are observed only when a source adapter verifies them. */
|
|
91
|
+
export function checklistEvidenceChain(input: {
|
|
92
|
+
item: ChecklistItem;
|
|
93
|
+
version: string;
|
|
94
|
+
stages: readonly ChecklistEvidenceStage[];
|
|
95
|
+
}): ChecklistEvidenceChain {
|
|
96
|
+
const required: ChecklistEvidenceStageId[] = ['E1', 'E3', 'E4', 'E5'];
|
|
97
|
+
const byId = new Map(input.stages.map((stage) => [stage.id, stage]));
|
|
98
|
+
const stages = required.map((id) => {
|
|
99
|
+
const stage = byId.get(id);
|
|
100
|
+
if (!stage) return { id, state: 'unmeasurable' as const, source: null, measuredAt: null, reason: '출처 어댑터 결과가 없다' };
|
|
101
|
+
if (stage.state === 'observed' && (!stage.source?.trim() || !stage.measuredAt || !Number.isFinite(Date.parse(stage.measuredAt))))
|
|
102
|
+
return { ...stage, state: 'unmeasurable' as const, reason: 'observed 판정에 유효한 출처와 측정 시각이 없다' };
|
|
103
|
+
return stage;
|
|
104
|
+
});
|
|
105
|
+
const unresolved = stages.filter((stage) => stage.state !== 'observed');
|
|
106
|
+
return {
|
|
107
|
+
id: input.item.id,
|
|
108
|
+
version: input.version,
|
|
109
|
+
stages,
|
|
110
|
+
greenEligible: unresolved.length === 0,
|
|
111
|
+
reason: unresolved.length ? `${unresolved.map((stage) => `${stage.id}=${stage.state}: ${stage.reason}`).join('; ')}` : null,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
70
115
|
export function devVersion(): string {
|
|
71
116
|
return (JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf8')) as { version: string }).version;
|
|
72
117
|
}
|
|
@@ -405,7 +450,7 @@ export function addItem(v: string, input: { id: string; title: string; refs?: st
|
|
|
405
450
|
});
|
|
406
451
|
}
|
|
407
452
|
|
|
408
|
-
export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; refs?: string[]; owner?: string; disposition?: ChecklistDisposition; kind?: ChecklistKind; dispatchKind?: ChecklistDispatchKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean | null }, by: string): Checklist {
|
|
453
|
+
export function setItem(v: string, id: string, patch: { status?: ChecklistStatus; evidence?: string; knownIssue?: string | null; refs?: string[]; owner?: string; disposition?: ChecklistDisposition; kind?: ChecklistKind; dispatchKind?: ChecklistDispatchKind; priority?: ChecklistItem['priority']; predecessors?: string[]; deadlineVersion?: string; ceoMinutes?: number; ceoDate?: string; accelerator?: boolean | null }, by: string): Checklist {
|
|
409
454
|
return setItemGuarded(v, id, patch, by);
|
|
410
455
|
}
|
|
411
456
|
|
|
@@ -501,13 +546,13 @@ function setItemGuarded(v: string, id: string, patch: Parameters<typeof setItem>
|
|
|
501
546
|
throw new CliUserError(`지금 주인: ${item.owner} — --force 로만 바꾼다`, 'release checklist claim <id> --by <자리> --force');
|
|
502
547
|
}
|
|
503
548
|
}
|
|
504
|
-
const fields = (['evidence', 'owner', 'status', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const).filter((field) => patch[field] !== undefined && JSON.stringify(patch[field]) !== JSON.stringify(item[field] ?? null));
|
|
549
|
+
const fields = (['evidence', 'knownIssue', 'owner', 'status', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const).filter((field) => patch[field] !== undefined && JSON.stringify(patch[field]) !== JSON.stringify(item[field] ?? null));
|
|
505
550
|
if (fields.length === 0) return false;
|
|
506
551
|
const at = new Date().toISOString();
|
|
507
552
|
for (const field of fields) {
|
|
508
553
|
const from = item[field];
|
|
509
554
|
const to = patch[field]!;
|
|
510
|
-
if (field === 'accelerator' && to === null) delete item
|
|
555
|
+
if ((field === 'accelerator' || field === 'knownIssue') && to === null) delete (item as unknown as Record<string, unknown>)[field];
|
|
511
556
|
else (item as unknown as Record<string, unknown>)[field] = to;
|
|
512
557
|
item.updatedAt = at;
|
|
513
558
|
item.updatedBy = by;
|
|
@@ -585,7 +630,7 @@ export interface ChecklistGate {
|
|
|
585
630
|
undecided: string[];
|
|
586
631
|
blocked: string[];
|
|
587
632
|
moved: string[];
|
|
588
|
-
knownIssues: Array<{ id: string; title: string; evidence: string }>;
|
|
633
|
+
knownIssues: Array<{ id: string; title: string; evidence: string; knownIssue?: string }>;
|
|
589
634
|
/** Post-publication cells excluded from the pre-publication judgement. */
|
|
590
635
|
postpub?: string[];
|
|
591
636
|
/** Screen cells whose `짝:` line is missing or has an untracked ⏳ — a warning; it never changes `ok`. */
|
|
@@ -616,7 +661,7 @@ export function checklistGate(v: string): ChecklistGate {
|
|
|
616
661
|
if (item.status !== 'yellow') continue;
|
|
617
662
|
if (item.disposition === 'block') result.blocked.push(item.id);
|
|
618
663
|
else if (item.disposition === 'move') result.moved.push(item.id);
|
|
619
|
-
else if (item.disposition === 'known-issue') result.knownIssues.push({ id: item.id, title: item.title, evidence: item.evidence ?? '' });
|
|
664
|
+
else if (item.disposition === 'known-issue') result.knownIssues.push({ id: item.id, title: item.title, evidence: item.evidence ?? '', ...(item.knownIssue?.trim() ? { knownIssue: item.knownIssue.trim() } : {}) });
|
|
620
665
|
else result.undecided.push(item.id);
|
|
621
666
|
}
|
|
622
667
|
result.ok = result.red.length === 0 && result.undecided.length === 0 && result.blocked.length === 0;
|
|
@@ -36,7 +36,7 @@ const walWait = new Int32Array(new SharedArrayBuffer(4));
|
|
|
36
36
|
const SCHEMA_COLUMNS: Record<string, readonly string[]> = {
|
|
37
37
|
imported_versions: ['json_hash', 'imported_at'],
|
|
38
38
|
events: ['reason', 'generation'],
|
|
39
|
-
assignments: ['priority', 'predecessors', 'deadline_version', 'ceo_minutes', 'ceo_date', 'accelerator', 'refs', 'dispatch_kind'],
|
|
39
|
+
assignments: ['priority', 'predecessors', 'deadline_version', 'ceo_minutes', 'ceo_date', 'accelerator', 'refs', 'dispatch_kind', 'known_issue'],
|
|
40
40
|
release_schedules: ['freeze_from', 'freeze_until', 'publish_at'],
|
|
41
41
|
};
|
|
42
42
|
function schemaCurrent(db: Database): boolean {
|
|
@@ -81,7 +81,7 @@ function open(root = releaseLedgerRoot()): Database {
|
|
|
81
81
|
if (!eventColumns.some((column) => column.name === 'generation')) db.exec('ALTER TABLE events ADD COLUMN generation INTEGER');
|
|
82
82
|
db.exec('CREATE TABLE IF NOT EXISTS release_schedules (version TEXT PRIMARY KEY, cut_at TEXT NOT NULL, land_by TEXT, updated_at TEXT NOT NULL, updated_by TEXT NOT NULL)');
|
|
83
83
|
const assignmentColumns = db.query('PRAGMA table_info(assignments)').all() as Array<{ name: string }>;
|
|
84
|
-
for (const [name, sql] of [['priority', 'TEXT'], ['predecessors', 'TEXT'], ['deadline_version', 'TEXT'], ['ceo_minutes', 'INTEGER'], ['ceo_date', 'TEXT'], ['accelerator', 'INTEGER'], ['refs', 'TEXT'], ['dispatch_kind', 'TEXT']] as const) {
|
|
84
|
+
for (const [name, sql] of [['priority', 'TEXT'], ['predecessors', 'TEXT'], ['deadline_version', 'TEXT'], ['ceo_minutes', 'INTEGER'], ['ceo_date', 'TEXT'], ['accelerator', 'INTEGER'], ['refs', 'TEXT'], ['dispatch_kind', 'TEXT'], ['known_issue', 'TEXT']] as const) {
|
|
85
85
|
if (!assignmentColumns.some((column) => column.name === name)) db.exec(`ALTER TABLE assignments ADD COLUMN ${name} ${sql}`);
|
|
86
86
|
}
|
|
87
87
|
const scheduleColumns = db.query('PRAGMA table_info(release_schedules)').all() as Array<{ name: string }>;
|
|
@@ -178,7 +178,7 @@ function evidenceRefs(db: Database, version: string, id: string): string[] {
|
|
|
178
178
|
return (db.query('SELECT ref FROM evidence WHERE feature_id = ? AND version = ? ORDER BY rowid').all(id, version) as Array<{ ref: string }>).map((row) => row.ref);
|
|
179
179
|
}
|
|
180
180
|
function items(db: Database, version: string): ChecklistItem[] {
|
|
181
|
-
const rows = db.query(`SELECT f.id, COALESCE(a.title_override, f.title) AS title, a.owner, a.kind, a.dispatch_kind, a.priority, a.predecessors, a.deadline_version, a.ceo_minutes, a.ceo_date, a.accelerator, a.refs, a.status, a.disposition, a.evidence, a.updated_at, a.updated_by
|
|
181
|
+
const rows = db.query(`SELECT f.id, COALESCE(a.title_override, f.title) AS title, a.owner, a.kind, a.dispatch_kind, a.priority, a.predecessors, a.deadline_version, a.ceo_minutes, a.ceo_date, a.accelerator, a.refs, a.status, a.disposition, a.evidence, a.known_issue, a.updated_at, a.updated_by
|
|
182
182
|
FROM assignments a JOIN features f ON f.id = a.feature_id WHERE a.version = ? ORDER BY a.rowid`).all(version) as Array<Record<string, string | number | null>>;
|
|
183
183
|
return rows.map((r) => {
|
|
184
184
|
const evidence = [r.evidence, ...evidenceRefs(db, version, r.id as string)].filter((value) => value !== null).join('\n');
|
|
@@ -190,7 +190,7 @@ function items(db: Database, version: string): ChecklistItem[] {
|
|
|
190
190
|
...(r.ceo_minutes !== null ? { ceoMinutes: r.ceo_minutes as number } : {}), ...(r.ceo_date !== null ? { ceoDate: r.ceo_date as string } : {}),
|
|
191
191
|
...(r.accelerator === 1 ? { accelerator: true as const } : {}),
|
|
192
192
|
...(r.refs !== null && r.refs !== undefined ? { refs: JSON.parse(r.refs as string) as string[] } : {}),
|
|
193
|
-
...(r.evidence !== null || evidence ? { evidence } : {}), ...(r.disposition !== null ? { disposition: r.disposition as ChecklistItem['disposition'] } : {}),
|
|
193
|
+
...(r.evidence !== null || evidence ? { evidence } : {}), ...(r.known_issue !== null ? { knownIssue: r.known_issue as string } : {}), ...(r.disposition !== null ? { disposition: r.disposition as ChecklistItem['disposition'] } : {}),
|
|
194
194
|
updatedAt: r.updated_at as string, updatedBy: r.updated_by as string };
|
|
195
195
|
});
|
|
196
196
|
}
|
|
@@ -203,9 +203,9 @@ function putItem(db: Database, version: string, item: ChecklistItem): void {
|
|
|
203
203
|
if (!feature) db.query('INSERT INTO features (id, title, owner, kind, created_at) VALUES (?, ?, ?, ?, ?)').run(item.id, item.title, item.owner ?? null, item.kind ?? null, item.updatedAt);
|
|
204
204
|
// features.owner/kind follow the latest assignment so details() and the checklist never disagree after a set.
|
|
205
205
|
else if (item.owner !== undefined || item.kind !== undefined) db.query('UPDATE features SET owner = COALESCE(?, owner), kind = COALESCE(?, kind) WHERE id = ?').run(item.owner ?? null, item.kind ?? null, item.id);
|
|
206
|
-
db.query(`INSERT INTO assignments (feature_id, version, status, disposition, evidence, title_override, owner, kind, dispatch_kind, priority, predecessors, deadline_version, ceo_minutes, ceo_date, accelerator, refs, updated_at, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
207
|
-
ON CONFLICT(feature_id, version) DO UPDATE SET status=excluded.status, disposition=excluded.disposition, evidence=excluded.evidence, owner=excluded.owner, kind=excluded.kind, dispatch_kind=excluded.dispatch_kind, priority=excluded.priority, predecessors=excluded.predecessors, deadline_version=excluded.deadline_version, ceo_minutes=excluded.ceo_minutes, ceo_date=excluded.ceo_date, accelerator=excluded.accelerator, refs=excluded.refs, updated_at=excluded.updated_at, updated_by=excluded.updated_by`)
|
|
208
|
-
.run(item.id, version, item.status, item.disposition ?? null, item.evidence ?? null, feature && feature.title !== item.title ? item.title : null, item.owner ?? null, item.kind ?? null, item.dispatchKind ?? null, item.priority ?? null, item.predecessors ? JSON.stringify(item.predecessors) : null, item.deadlineVersion ?? null, item.ceoMinutes ?? null, item.ceoDate ?? null, item.accelerator === true ? 1 : null, item.refs?.length ? JSON.stringify(item.refs) : null, item.updatedAt, item.updatedBy);
|
|
206
|
+
db.query(`INSERT INTO assignments (feature_id, version, status, disposition, evidence, title_override, owner, kind, dispatch_kind, priority, predecessors, deadline_version, ceo_minutes, ceo_date, accelerator, refs, known_issue, updated_at, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
207
|
+
ON CONFLICT(feature_id, version) DO UPDATE SET status=excluded.status, disposition=excluded.disposition, evidence=excluded.evidence, owner=excluded.owner, kind=excluded.kind, dispatch_kind=excluded.dispatch_kind, priority=excluded.priority, predecessors=excluded.predecessors, deadline_version=excluded.deadline_version, ceo_minutes=excluded.ceo_minutes, ceo_date=excluded.ceo_date, accelerator=excluded.accelerator, refs=excluded.refs, known_issue=excluded.known_issue, updated_at=excluded.updated_at, updated_by=excluded.updated_by`)
|
|
208
|
+
.run(item.id, version, item.status, item.disposition ?? null, item.evidence ?? null, feature && feature.title !== item.title ? item.title : null, item.owner ?? null, item.kind ?? null, item.dispatchKind ?? null, item.priority ?? null, item.predecessors ? JSON.stringify(item.predecessors) : null, item.deadlineVersion ?? null, item.ceoMinutes ?? null, item.ceoDate ?? null, item.accelerator === true ? 1 : null, item.refs?.length ? JSON.stringify(item.refs) : null, item.knownIssue ?? null, item.updatedAt, item.updatedBy);
|
|
209
209
|
}
|
|
210
210
|
function jsonHash(contents: string | Buffer): string { return createHash('sha256').update(contents).digest('hex'); }
|
|
211
211
|
|
|
@@ -274,7 +274,7 @@ function importOnDb(db: Database, version: string): boolean {
|
|
|
274
274
|
record(db, version, { at: new Date().toISOString(), by: item.updatedBy, id: item.id, field: 'reimport', from: null, to: item, released: data.released, dev: data.dev });
|
|
275
275
|
continue;
|
|
276
276
|
}
|
|
277
|
-
const fields = ['title', 'status', 'evidence', 'owner', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const;
|
|
277
|
+
const fields = ['title', 'status', 'evidence', 'knownIssue', 'owner', 'disposition', 'kind', 'dispatchKind', 'priority', 'predecessors', 'deadlineVersion', 'ceoMinutes', 'ceoDate', 'accelerator', 'refs'] as const;
|
|
278
278
|
const baseEvidence = (db.query('SELECT evidence FROM assignments WHERE feature_id = ? AND version = ?').get(item.id, version) as { evidence: string | null }).evidence ?? undefined;
|
|
279
279
|
const preserveRefs = item.evidence === baseEvidence || item.evidence === current.evidence;
|
|
280
280
|
const differs = fields.some((field) => field === 'evidence'
|
|
@@ -441,7 +441,7 @@ export function set(version: string, id: string, patch: Partial<ChecklistItem>,
|
|
|
441
441
|
return mutate(version, released, dev, (data) => {
|
|
442
442
|
const item = data.items.find((i) => i.id === id);
|
|
443
443
|
if (!item) throw new CliUserError(`없는 칸: ${id}`, 'list 로 칸 목록을 본다');
|
|
444
|
-
const fields = (['evidence', 'owner', 'status', 'disposition', 'kind'] as const).filter((field) => patch[field] !== undefined && patch[field] !== item[field]);
|
|
444
|
+
const fields = (['evidence', 'knownIssue', 'owner', 'status', 'disposition', 'kind'] as const).filter((field) => patch[field] !== undefined && patch[field] !== item[field]);
|
|
445
445
|
if (!fields.length) return false;
|
|
446
446
|
const at = new Date().toISOString();
|
|
447
447
|
for (const field of fields) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ownerMatches, type ChecklistItem } from './checklist.js';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
2
4
|
|
|
3
5
|
export interface MergedChecklistPr { number: number; title: string; body: string | null; mergedAt: string }
|
|
4
6
|
type PrMatchBasis = 'cell-line' | 'title-strong' | 'mention';
|
|
@@ -113,35 +115,106 @@ export function landedButYellow(items: readonly ChecklistItem[], prs: readonly M
|
|
|
113
115
|
return rows.sort((a, b) => Number(b.newer) - Number(a.newer));
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
export interface MergedPrRunResult { status: number | null; stdout: string; stderr: string }
|
|
118
|
+
export interface MergedPrRunResult { status: number | null; stdout: string; stderr: string; timedOut?: boolean }
|
|
117
119
|
/** One `gh` invocation; resolves when the process exits. */
|
|
118
|
-
export type MergedPrRunner = (command: string, args: readonly string[], cwd: string) => Promise<MergedPrRunResult>;
|
|
120
|
+
export type MergedPrRunner = (command: string, args: readonly string[], cwd: string, timeoutMs?: number) => Promise<MergedPrRunResult>;
|
|
119
121
|
|
|
120
122
|
export const MERGED_PR_LIMIT = 400;
|
|
121
123
|
/** Concurrent `gh pr list` calls once the first page is full (LANDED-YELLOW-FAST · 10-21 prod: 24 sequential calls ≈ 145s). */
|
|
122
124
|
export const MERGED_PR_CONCURRENCY = 6;
|
|
123
125
|
const DAY_MS = 86_400_000;
|
|
124
126
|
|
|
127
|
+
export interface MergedChecklistFetch {
|
|
128
|
+
prs: MergedChecklistPr[];
|
|
129
|
+
truncated: boolean;
|
|
130
|
+
calls: number;
|
|
131
|
+
/** Set when a time budget stopped the window walk before every window was fetched. */
|
|
132
|
+
stopReason: 'time-budget-exceeded' | undefined;
|
|
133
|
+
/** UTC windows not yet fetched when `stopReason` is set. */
|
|
134
|
+
remainingWindows: string[];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const FETCH_CACHE_TTL_MS = 10 * 60_000;
|
|
138
|
+
interface PersistedFetchCache { schema: 1; entries: Record<string, { savedAt: number; result: MergedChecklistFetch }> }
|
|
139
|
+
|
|
140
|
+
function cacheRead(path: string, key: string, nowMs: number): MergedChecklistFetch | undefined {
|
|
141
|
+
try {
|
|
142
|
+
if (!existsSync(path)) return undefined;
|
|
143
|
+
const cache = JSON.parse(readFileSync(path, 'utf8')) as PersistedFetchCache;
|
|
144
|
+
const entry = cache?.schema === 1 ? cache.entries?.[key] : undefined;
|
|
145
|
+
if (!entry || !Number.isFinite(entry.savedAt) || nowMs - entry.savedAt < 0 || nowMs - entry.savedAt >= FETCH_CACHE_TTL_MS) return undefined;
|
|
146
|
+
if (!Array.isArray(entry.result?.prs) || entry.result.stopReason !== undefined) return undefined;
|
|
147
|
+
return { ...entry.result, calls: 0 };
|
|
148
|
+
} catch { return undefined; }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function cacheWrite(path: string, key: string, result: MergedChecklistFetch, nowMs: number): void {
|
|
152
|
+
try {
|
|
153
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
154
|
+
let entries: PersistedFetchCache['entries'] = {};
|
|
155
|
+
try {
|
|
156
|
+
const cache = JSON.parse(readFileSync(path, 'utf8')) as PersistedFetchCache;
|
|
157
|
+
if (cache?.schema === 1 && cache.entries && typeof cache.entries === 'object') entries = cache.entries;
|
|
158
|
+
} catch { /* replace an unreadable cache with a fresh one */ }
|
|
159
|
+
for (const [entryKey, entry] of Object.entries(entries)) if (!Number.isFinite(entry.savedAt) || nowMs - entry.savedAt >= FETCH_CACHE_TTL_MS) delete entries[entryKey];
|
|
160
|
+
entries[key] = { savedAt: nowMs, result };
|
|
161
|
+
const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
162
|
+
writeFileSync(temp, `${JSON.stringify({ schema: 1, entries } satisfies PersistedFetchCache)}\n`, { mode: 0o600 });
|
|
163
|
+
renameSync(temp, path);
|
|
164
|
+
} catch { /* cache is an optimization; a failed write must not fail the read-only command */ }
|
|
165
|
+
}
|
|
166
|
+
|
|
125
167
|
/**
|
|
126
168
|
* Merged PRs since `since` (UTC date). One call answers when it returns fewer than the cap. Otherwise the range
|
|
127
169
|
* splits into UTC-day windows fetched with a concurrency cap; a full window is halved down to one second as before,
|
|
128
170
|
* and a full one-second window probes one extra row to tell exactly 400 from truncation.
|
|
171
|
+
* With `timeBudgetMs`, each gh request is bounded by the remaining time; a slow initial request or window returns the
|
|
172
|
+
* partial result with `stopReason: 'time-budget-exceeded'` and UTC window starts in `remainingWindows`.
|
|
129
173
|
* The returned set and the truncation flag match the former sequential bisection; only call shape and time differ.
|
|
174
|
+
* A completed result is cached across CLI processes for ten minutes; a partial (`stopReason`) result is never cached.
|
|
130
175
|
*/
|
|
131
176
|
export async function fetchMergedChecklistPrs(since: string, now: Date, run: MergedPrRunner, cwd: string,
|
|
132
|
-
{ concurrency = MERGED_PR_CONCURRENCY }: { concurrency?: number } = {}): Promise<
|
|
177
|
+
{ concurrency = MERGED_PR_CONCURRENCY, timeBudgetMs, cacheFile }: { concurrency?: number; timeBudgetMs?: number; cacheFile?: string } = {}): Promise<MergedChecklistFetch> {
|
|
178
|
+
const cacheKey = JSON.stringify([since, now.toISOString().slice(0, 10), cwd, concurrency,
|
|
179
|
+
process.env.GH_REPO ?? process.env.GITHUB_REPOSITORY ?? '']);
|
|
180
|
+
const cached = cacheFile ? cacheRead(cacheFile, cacheKey, Date.now()) : undefined;
|
|
181
|
+
if (cached) return cached;
|
|
182
|
+
const deadline = timeBudgetMs === undefined ? undefined : performance.now() + timeBudgetMs;
|
|
183
|
+
const over = (): boolean => deadline !== undefined && performance.now() >= deadline;
|
|
184
|
+
const cacheable = (result: MergedChecklistFetch): MergedChecklistFetch => {
|
|
185
|
+
if (result.stopReason !== undefined || !cacheFile) return result;
|
|
186
|
+
cacheWrite(cacheFile, cacheKey, result, Date.now());
|
|
187
|
+
return result;
|
|
188
|
+
};
|
|
133
189
|
let calls = 0;
|
|
134
|
-
const fetch = async (lower: string, upper?: string, limit = MERGED_PR_LIMIT): Promise<MergedChecklistPr[]> => {
|
|
190
|
+
const fetch = async (lower: string, upper?: string, limit = MERGED_PR_LIMIT): Promise<MergedChecklistPr[] | null> => {
|
|
135
191
|
const search = `merged:>=${lower}${upper ? ` merged:<${upper}` : ''}`;
|
|
192
|
+
if (over()) return null;
|
|
136
193
|
calls++;
|
|
137
|
-
const
|
|
194
|
+
const args = ['pr', 'list', '--state', 'merged', '--search', search, '--limit', String(limit), '--json', 'number,title,body,mergedAt'];
|
|
195
|
+
const remainingMs = deadline === undefined ? undefined : Math.max(1, Math.ceil(deadline - performance.now()));
|
|
196
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
197
|
+
const request = run('gh', args, cwd, remainingMs);
|
|
198
|
+
let r: MergedPrRunResult | null;
|
|
199
|
+
try {
|
|
200
|
+
r = remainingMs === undefined ? await request : await Promise.race([
|
|
201
|
+
request,
|
|
202
|
+
new Promise<null>((resolve) => { timer = setTimeout(() => resolve(null), remainingMs); }),
|
|
203
|
+
]);
|
|
204
|
+
} finally { if (timer !== undefined) clearTimeout(timer); }
|
|
205
|
+
if (r === null || r.timedOut) return null;
|
|
138
206
|
if (r.status !== 0) throw new Error(`병합 PR 조회(gh) 실패 rc=${r.status}: ${(r.stderr || r.stdout).trim().slice(0, 400)}`);
|
|
139
207
|
const page = JSON.parse(r.stdout.trim()) as MergedChecklistPr[];
|
|
140
208
|
if (!Array.isArray(page)) throw new Error('병합 PR 조회(gh): 배열이 아니다');
|
|
141
209
|
return page;
|
|
142
210
|
};
|
|
211
|
+
const lower = Date.parse(`${since}T00:00:00Z`);
|
|
212
|
+
const upper = Math.max(lower + 1000, Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
|
|
213
|
+
const windows: Array<[number, number]> = [];
|
|
214
|
+
for (let start = lower; start < upper; start += DAY_MS) windows.push([start, Math.min(start + DAY_MS, upper)]);
|
|
143
215
|
const first = await fetch(since);
|
|
144
|
-
if (first
|
|
216
|
+
if (first === null) return cacheable({ prs: [], truncated: false, calls, stopReason: 'time-budget-exceeded', remainingWindows: windows.map(([start]) => new Date(start).toISOString()) });
|
|
217
|
+
if (first.length < MERGED_PR_LIMIT) return cacheable({ prs: first, truncated: false, calls, stopReason: undefined, remainingWindows: [] });
|
|
145
218
|
// Bounded pool: at most `concurrency` gh processes at once; a failure stops new calls and rejects.
|
|
146
219
|
let active = 0;
|
|
147
220
|
let failed: unknown;
|
|
@@ -155,14 +228,20 @@ export async function fetchMergedChecklistPrs(since: string, now: Date, run: Mer
|
|
|
155
228
|
finally { active--; if (failed !== undefined) for (const wake of waiting.splice(0)) wake(); else waiting.shift()?.(); }
|
|
156
229
|
};
|
|
157
230
|
let truncated = false;
|
|
231
|
+
const remainingWindows: string[] = [];
|
|
158
232
|
const visit = async (start: number, end: number): Promise<MergedChecklistPr[]> => {
|
|
159
233
|
const from = new Date(start).toISOString();
|
|
160
234
|
const to = new Date(end).toISOString();
|
|
161
|
-
|
|
235
|
+
// The budget is checked when the window actually starts (inside the pool), not when `visit` is mapped —
|
|
236
|
+
// every window is mapped at once, so a check here would run before the first fetch resolves.
|
|
237
|
+
const page = await limited(async () => (over() ? null : fetch(from, to)));
|
|
238
|
+
if (page === null) { remainingWindows.push(from); return []; }
|
|
162
239
|
if (page.length < MERGED_PR_LIMIT) return page;
|
|
163
240
|
if (end - start <= 1000) {
|
|
164
241
|
// The second-precision search cannot be subdivided; probe one extra row to distinguish exactly 400 from a real truncation.
|
|
242
|
+
if (over()) { remainingWindows.push(from); return page.slice(0, MERGED_PR_LIMIT); }
|
|
165
243
|
const probe = await limited(() => fetch(from, to, MERGED_PR_LIMIT + 1));
|
|
244
|
+
if (probe === null) { remainingWindows.push(from); return page.slice(0, MERGED_PR_LIMIT); }
|
|
166
245
|
if (probe.length > MERGED_PR_LIMIT) truncated = true;
|
|
167
246
|
return probe.slice(0, MERGED_PR_LIMIT);
|
|
168
247
|
}
|
|
@@ -170,15 +249,17 @@ export async function fetchMergedChecklistPrs(since: string, now: Date, run: Mer
|
|
|
170
249
|
const [left, right] = await Promise.all([visit(start, middle), visit(middle, end)]);
|
|
171
250
|
return [...left, ...right];
|
|
172
251
|
};
|
|
173
|
-
const lower = Date.parse(`${since}T00:00:00Z`);
|
|
174
|
-
const upper = Math.max(lower + 1000, Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1));
|
|
175
|
-
const windows: Array<[number, number]> = [];
|
|
176
|
-
for (let start = lower; start < upper; start += DAY_MS) windows.push([start, Math.min(start + DAY_MS, upper)]);
|
|
177
252
|
const settled = await Promise.allSettled(windows.map(([start, end]) => visit(start, end)));
|
|
178
253
|
const rejected = settled.find((result): result is PromiseRejectedResult => result.status === 'rejected');
|
|
179
254
|
if (rejected) throw failed ?? rejected.reason;
|
|
180
255
|
// Chronological window order, first occurrence wins — the same de-duplication as before.
|
|
181
256
|
const unique = new Map<number, MergedChecklistPr>();
|
|
182
257
|
for (const result of settled) for (const pr of (result as PromiseFulfilledResult<MergedChecklistPr[]>).value) if (!unique.has(pr.number)) unique.set(pr.number, pr);
|
|
183
|
-
|
|
258
|
+
remainingWindows.sort();
|
|
259
|
+
if (remainingWindows.length) {
|
|
260
|
+
// The broad first page is usable partial evidence if the detailed window walk runs out of budget.
|
|
261
|
+
for (const pr of first) if (!unique.has(pr.number)) unique.set(pr.number, pr);
|
|
262
|
+
truncated = true;
|
|
263
|
+
}
|
|
264
|
+
return cacheable({ prs: [...unique.values()], truncated, calls, stopReason: remainingWindows.length ? 'time-budget-exceeded' : undefined, remainingWindows });
|
|
184
265
|
}
|
package/src/repl/index.ts
CHANGED
|
@@ -211,6 +211,14 @@ function buildReplTools(): {
|
|
|
211
211
|
searchPlannerState: planner,
|
|
212
212
|
turnIndex: undefined,
|
|
213
213
|
ptyDashboardOn: false,
|
|
214
|
+
// Match the CLI agent path: native Agent fallback needs the parent's
|
|
215
|
+
// catalog and dispatcher so children can actually use inherited tools.
|
|
216
|
+
agentHostTools: specs,
|
|
217
|
+
agentDispatchTool: dispatch,
|
|
218
|
+
buildChildToolCatalog: (childCwd: string) => {
|
|
219
|
+
const cli = require('../cli/agent-cli.js') as typeof import('../cli/agent-cli.js');
|
|
220
|
+
return cli.buildCliAgentTools(undefined, undefined, childCwd);
|
|
221
|
+
},
|
|
214
222
|
getToolRuntime: () => undefined,
|
|
215
223
|
dispatchToolRuntime: async (n) => ({ error: `runtime unavailable in REPL: ${n}` }),
|
|
216
224
|
dispatchPluginTool: async (n) => ({ ok: false as const, error: `plugin unavailable in REPL: ${n}` }),
|
|
@@ -134,6 +134,7 @@ export async function seatReport(seat: string, deps: SeatReportDeps = {}): Promi
|
|
|
134
134
|
const [{ resolveRepositoryName }, { githubDraftSweepAdapters }] = await Promise.all([
|
|
135
135
|
import('../harness/repository-name.js'), import('../harness/harness-cli-command.js'),
|
|
136
136
|
]);
|
|
137
|
+
// The collector folds repeated PR pages before this report formats the census.
|
|
137
138
|
return collectDraftMetrics(resolveRepositoryName({}), githubDraftSweepAdapters(), clock);
|
|
138
139
|
}))(now);
|
|
139
140
|
draftDetail = ` · draft 재고 ${metrics.inventory} · 최장 나이 ${metrics.oldestAgeHours === null ? '해당 없음' : `${metrics.oldestAgeHours.toFixed(1)}h`} · needs-owner ${metrics.needsOwner} · 48h 전환율 ${metrics.conversion48h === null ? '표본 없음' : `${(metrics.conversion48h * 100).toFixed(1)}% (${metrics.converted48h}/${metrics.cohort48h})`}`;
|