elanous 0.2.36 → 0.2.38
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 → RrjXTwz-urRAbChP1bX02}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{9250-072df22eaadaa6b5.js → 1026-44010f405704050c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4304.e9b2512834a9e23e.js → 1632.7d87f998e714bede.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +148 -0
- package/apps/pwa/out/_next/static/chunks/{7784.1f0ac4a16d190cf4.js → 2104.70e086d5174a4736.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6566.926a1d1b7c911442.js → 214.186a430da28338f1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +93 -0
- package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{5711.957b095705c12560.js → 2415.738b777642a02233.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4286.b70c4520306817e8.js → 3086.fb14b60ce98b53d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3148-98efd4869d660a87.js → 3124-561f7f037943cde2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +24 -0
- package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3727.7949ccaacaa71242.js → 3291.f6c1005387d514bd.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3389.012c70767a872f9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +287 -0
- package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8904.7d51af2dc033965c.js → 3528.e4b8ab70d339f965.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +94 -0
- package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3031-85328a92fcce4da6.js → 4199-f7c6eb40cd0322f8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +32 -0
- package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +90 -0
- package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +79 -0
- package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +74 -0
- package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{528.38ce20ef8aa251ff.js → 5203.51e5d8b5f1dcfe53.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +286 -0
- package/apps/pwa/out/_next/static/chunks/5300.677c97e0393742e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2683.c9773a0c589b031b.js → 5307.d7328020227880c2.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +1 -0
- package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +8 -0
- package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{552.b7d061ad91eeac51.js → 5638.107314dc479e2f37.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6142.913b44e260a3f925.js +64 -0
- package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6925.562d532110879886.js → 6493.27ad77de7aac7966.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{275-02ea4c2a33980422.js → 7161-8f22db3ec5ed005e.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3136.30aaaa5fa03662a3.js → 7488.966b116789f3d41a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4116.a84a92a2492c080e.js → 7524.ce973525d30b3d52.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7573.2376e13c9c21f1f8.js +77 -0
- package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3324.a09703033abcbd2f.js → 7628.6b16863ac3972333.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3410.7f9af5ab77cbe65f.js → 7954.4f7a8832663ee979.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{771.c3cdb6c1f7f47c02.js → 8019.21ca554a821dad01.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4615.8525eb0add35eba6.js → 8487.291666583bebe640.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6943.c6b24124853a47b2.js → 8879.ac06755bcf3e44a1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{575.d929793a53f27c4b.js → 8911.bf5e712acb25fb6c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9f31e478.fefcbcd3c388ebc5.js → 8994d93e.33ba491f95345fc2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{da5ecc5c-b005ad8cac737d57.js → 89d6c04f-6b8ab341748e468a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599.c39c571b6733f2a2.js → 9223.cbcc88e0e9950426.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +226 -0
- package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +24 -0
- package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8037.e254fd797aa224d1.js → 9581.da63ddf41acfad46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +2 -0
- package/apps/pwa/out/_next/static/chunks/{ceba0382.650712b56b85b582.js → 9fe1b809.738da85c8b17e3fb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-866aac5ff226a7f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-e7abc34a48a6f71e.js → page-291ab19fe2acb173.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-c15958835534404b.js → page-6b140f77593576eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3a3d9966.cc4d4585a2708e7c.js → b7459770.6539b11860eb8a9c.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/{053a429e.9a63436527cc751c.js → b7478fef.5917716286213fcc.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/bb6987f9.3647e1a23b0e3217.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{2812ca6a.3b9f4a7a08885744.js → de8b930d.d94c0f42acf807fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{873d6c12.09bd499f57171543.js → eaad0cba.771200940c529df7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.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/release/recipes.yaml +9 -0
- package/graphs/release/release-loop.yaml +26 -18
- 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/ask-op-inbox-once.ts +17 -0
- package/scripts/boundary-proof-mapping-audit.ts +88 -0
- package/scripts/brand/check.ts +13 -4
- 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-accepted-remaining-90.txt +91 -0
- package/scripts/gate-0226-failure-census.ts +546 -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/intake-collect-telegram-saved-stage.ts +48 -0
- package/scripts/land-global-checks.ts +69 -1
- package/scripts/measure-nl-routing-corpus.ts +207 -2
- package/scripts/provision-export-redactions.ts +15 -0
- package/scripts/public-copy-lint.ts +122 -0
- package/scripts/public-export.ts +69 -2
- package/scripts/ratchets/next-tui-entry-budget.ts +124 -0
- package/scripts/ratchets/train-side-invariants.sh +7 -1
- package/scripts/release-loop/cut-branch.ts +61 -12
- 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/gate-node.ts +161 -25
- package/scripts/release-loop/gate-partial.ts +82 -3
- package/scripts/release-loop/gate-prewarm-plan.ts +35 -0
- package/scripts/release-loop/gate-radius-live.ts +4 -2
- 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-cap.ts +2 -1
- package/scripts/release-loop/resume-release.ts +406 -7
- package/scripts/release-loop/resume-test-traffic.ts +10 -0
- package/scripts/release-loop/resume.ts +13 -5
- package/scripts/release-loop/traffic-node.ts +12 -4
- package/scripts/release-loop/traffic-transition.ts +53 -13
- package/scripts/release-loop/version-node.ts +109 -15
- 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/scripts/video-pipeline.ts +2 -2
- package/src/acp/client.ts +31 -5
- package/src/acp/core-turn-bridge.ts +12 -1
- package/src/acp/stderr-summary.ts +49 -0
- package/src/agent/monad-agent-turn.ts +1 -0
- package/src/agent/run-context.ts +1 -1
- package/src/agent/task-notification.ts +3 -1
- package/src/agent/terminal-surface.ts +217 -0
- package/src/agent-substrate/pr-reviewer.ts +11 -3
- package/src/agent-substrate/review-observation.ts +10 -0
- package/src/agent-substrate/self-review-cli.ts +537 -57
- package/src/auto-research/question-ladder-contract.ts +137 -0
- package/src/auto-research/research-turn.ts +134 -0
- package/src/autopilot/discovery/doc-lint.ts +1 -1
- 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/bots/screen-probe.ts +1 -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 +42 -5
- package/src/chat/index.ts +1 -0
- package/src/chat/tool-render/dispatch.ts +8 -1
- 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/guardian-cli.ts +3 -1
- package/src/cli/intake-cli.ts +48 -12
- package/src/cli/lesson-cli.ts +69 -9
- 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/pr-land-overlap-evidence.ts +38 -5
- package/src/cli/pr-land-refusal.ts +1 -1
- package/src/cli/pty-takeover-cli.ts +58 -5
- package/src/cli/release-checklist-read-cli.ts +112 -4
- package/src/cli/release-cli.ts +89 -18
- package/src/cli/review-carry.ts +77 -0
- package/src/cli/self-update.ts +24 -6
- package/src/cli/self-utterances-cli.ts +13 -16
- package/src/cli/tasks-card-show.ts +31 -1
- package/src/cli/tasks-cli.ts +370 -33
- package/src/cli/tasks-read-cli.ts +13 -5
- package/src/cli/usage-cli.ts +12 -10
- package/src/context-bus/claude-memory-import.ts +99 -0
- package/src/context-bus/context-now.ts +86 -2
- package/src/context-bus/long-term-memory.ts +125 -0
- package/src/core-turn/run-core-turn.ts +9 -2
- package/src/core-turn/run-goal-loop.ts +5 -2
- package/src/core-turn/types.ts +5 -0
- package/src/dashboard/chat/acp-chat.ts +9 -0
- package/src/dashboard/index.ts +115 -30
- package/src/dashboard/input/agent-wake.ts +43 -0
- package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
- package/src/dashboard/input/terminal-modal-router.ts +8 -159
- package/src/dashboard/replay-preview.ts +25 -0
- package/src/dashboard/side-basic-runtime.ts +61 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +19 -8
- package/src/dashboard/synchronized-frame-output.ts +74 -0
- package/src/dashboard/term-slash-runtime.ts +2 -0
- package/src/dashboard/turn-preamble.ts +28 -4
- 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/guardian-tick.ts +9 -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/git-fs/watcher.ts +27 -22
- package/src/git-fs/worktree.ts +19 -3
- package/src/graph-runner/graph-cli.ts +26 -2
- package/src/graph-runner/graph-grow.ts +37 -9
- package/src/graph-runner/graph-run-api.ts +3 -2
- package/src/graph-runner/runner.ts +34 -7
- package/src/graph-wizard/generate.ts +29 -6
- package/src/grok/credential.ts +107 -36
- package/src/harness/harness-cli-command.ts +179 -33
- package/src/harness/review-adapter.ts +26 -6
- package/src/hq/fence-wrapper.ts +1 -1
- package/src/hq/transfer.ts +1 -1
- package/src/index.ts +151 -18
- package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
- package/src/intake-plane/collect-telegram-saved.ts +54 -5
- package/src/interactive-terminal-modal.ts +3 -0
- package/src/keybindings.ts +17 -22
- package/src/llm/provider-credentials.ts +1 -1
- package/src/llm/route-decision.ts +13 -0
- package/src/llm-tool-name.ts +37 -0
- package/src/llm-tool-schema.ts +55 -0
- package/src/llm.ts +203 -21
- 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/inbox-once.ts +28 -0
- package/src/loops/orchestrator/inbox.ts +130 -1
- package/src/loops/orchestrator/tick.ts +55 -7
- package/src/maturity/feature-maturity.ts +1 -0
- 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/channel-ask-op.ts +96 -0
- package/src/nexus/api/discord-trigger-bot.ts +28 -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/api/telegram-trigger-bot.ts +24 -0
- package/src/nexus/api/terminals.ts +28 -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/patents/ledger.ts +18 -1
- package/src/policy/llm-policy.ts +3 -1
- package/src/preview/terminal.ts +4 -2
- package/src/prompt-cache/anthropic.ts +2 -0
- package/src/prompt-enhance/enhance.ts +9 -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/pty-manifest.ts +16 -2
- 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/gate-shards.ts +61 -0
- package/src/release-loop/landed-but-yellow.ts +93 -12
- package/src/release-loop/light-rc.ts +7 -4
- package/src/release-loop/rehearsal.ts +1 -1
- package/src/repl/index.ts +18 -0
- package/src/research/axis-fanout.ts +133 -0
- package/src/research/edge-decision.ts +88 -0
- package/src/research/output-router.ts +127 -0
- package/src/research-bridge/format.ts +16 -1
- package/src/research-bridge/index.ts +1 -0
- package/src/research-bridge/invoke.ts +41 -5
- 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/decompose.ts +45 -9
- package/src/self-dev/dev-pipeline.ts +29 -7
- package/src/self-dev/draft-residue.ts +221 -2
- package/src/self-dev/draft-sweep.ts +254 -16
- package/src/self-dev/draft-triage-rules.ts +19 -5
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/review-provider-fallback.ts +45 -7
- package/src/self-dev/run-supervisor.ts +22 -7
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-run.ts +310 -6
- package/src/self-implement/comment-only-source.ts +54 -0
- package/src/self-implement/doctor-visit.ts +108 -0
- package/src/self-implement/gate-artifacts.ts +6 -2
- package/src/self-implement/gate-baseline.ts +99 -1
- package/src/self-implement/gate-cli.ts +231 -82
- package/src/self-implement/gate-failure-signature.ts +44 -0
- package/src/self-implement/gate-remote.ts +30 -4
- package/src/self-implement/gate-scope.ts +13 -1
- package/src/self-implement/goal-author.ts +407 -21
- package/src/self-implement/goal-doc/section.ts +22 -0
- package/src/self-implement/goal-supervisor-fill.ts +110 -27
- package/src/self-implement/importer-test-index.ts +215 -2
- package/src/self-implement/knowhow-pack.ts +125 -12
- package/src/self-implement/l8-merge-queue.ts +198 -32
- package/src/self-implement/off-diff-evidence.ts +23 -0
- package/src/self-implement/orchestrator.ts +369 -33
- package/src/self-implement/pod-inplace-worktree.ts +82 -0
- package/src/self-implement/pod-warm-pool-profile.ts +2 -2
- package/src/self-implement/premise-review.ts +7 -5
- 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 +428 -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 +45 -16
- package/src/self-implement/self-implement-cli.ts +26 -2
- 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/channel-ask-op-envelope.ts +36 -0
- package/src/session/channel-op-reply-route.ts +200 -0
- package/src/session/chat.ts +10 -1
- package/src/session/id-prefix.ts +60 -0
- package/src/session/index.ts +6 -0
- package/src/session-runtime/index.ts +18 -23
- package/src/skills/runner.ts +7 -3
- package/src/skills/tools/dev-harness.ts +2 -2
- package/src/skills/tools/pty.ts +5 -0
- package/src/skills/tools/schedule-wakeup.ts +1 -6
- package/src/status/bar.ts +3 -3
- 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/storage/file-lock.ts +17 -4
- package/src/task-agent/actions.ts +199 -4
- package/src/task-agent/adopt-pr.ts +63 -2
- 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/evidence-command-normalize.ts +42 -0
- package/src/task-agent/export-redactions-provision.ts +43 -0
- 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 +74 -17
- package/src/task-agent/rejudge-on-head.ts +72 -5
- package/src/task-agent/repair-cycle.ts +108 -8
- package/src/task-agent/repair-next-move.ts +12 -2
- package/src/task-agent/repair-worker.ts +37 -1
- 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 +253 -20
- package/src/task-agent/task-parent-role-llm.ts +9 -0
- package/src/task-agent/tier-split.ts +40 -5
- 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-failure-reason.ts +31 -4
- 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 +77 -8
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +15 -9
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +338 -79
- package/src/task-orchestrator/surfaces/self-implement.ts +37 -2
- package/src/terminal/session-registry.ts +1 -0
- package/src/terminal/terminal-modal-router.ts +156 -0
- package/src/testing/linux-env.ts +177 -0
- package/src/tool-hints/gate.ts +6 -4
- package/src/tool-hints/signals.ts +33 -1
- package/src/tui/chat/background-job-status.ts +8 -2
- package/src/tui/chat/goal-interim.ts +5 -1
- package/src/tui/entry.ts +722 -30
- package/src/tui/input/at-picker.ts +55 -0
- package/src/tui/sidetab/attach.ts +304 -0
- package/src/tui/sidetab/list.ts +176 -0
- package/src/tui/sidetab/nl-tool.ts +136 -0
- package/src/tui/slash/resume-command.ts +23 -0
- package/src/tui/slash/resume.ts +38 -35
- package/src/tui/slash/signals.ts +114 -0
- package/src/tui/slash/term-next.ts +72 -0
- package/src/tui-client/local-chat-turn.ts +31 -9
- package/src/user-config.ts +30 -3
- 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 +17 -6
- package/src/ux-sim/next-tui-seat-scenarios.ts +176 -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/190.285c832bd9626f8c.js +0 -64
- 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/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/2986.45d7652e4f13557e.js +0 -2
- 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/3757-180308bc61021b05.js +0 -1
- package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3840-5825d56d46976a92.js +0 -1
- 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/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/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/6466-92e65a48490b68a9.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/727.7deb87749b0a343a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7481.39f7a4654907bf6e.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/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/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/live/page-e2de2e59111c9aad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f2b2e4bf6e981450.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/missions/[id]/page-2af16e62c12f22df.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/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/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/ccae1c4b-0cf414f5eff1b10d.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/scripts/migrate-monad-to-elanous.ts +0 -195
- package/scripts/rebrand-elanous.ts +0 -242
- /package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → RrjXTwz-urRAbChP1bX02}/_ssgManifest.js +0 -0
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
type AcceptanceReport,
|
|
33
33
|
} from './acceptance.js';
|
|
34
34
|
import { recordOpsEventSafe, type OpsEventInput } from '../domains/ops-log.js';
|
|
35
|
+
import { defaultAnomalyCluster, extractContainerExit, type AnomalyCluster } from '../loops/orchestrator/anomaly-cluster.js';
|
|
35
36
|
|
|
36
37
|
// ───────────────────────── Concurrency caps ────────────────────────
|
|
37
38
|
|
|
@@ -82,6 +83,9 @@ export interface DispatcherOptions {
|
|
|
82
83
|
* acceptance.checks reference fs or subprocess (file-exists /
|
|
83
84
|
* file-contains / shell-zero). */
|
|
84
85
|
acceptanceIo?: AcceptanceIo;
|
|
86
|
+
/** ANOMALY-CLUSTER — grouping state written at the failure record point; while a signature has 3+ runs
|
|
87
|
+
* and no cause verdict, relaunches of that goal·host are deferred instead of spawned. */
|
|
88
|
+
anomalyCluster?: AnomalyCluster;
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
export interface DispatchTickResult {
|
|
@@ -95,7 +99,7 @@ export interface DispatchTickResult {
|
|
|
95
99
|
}>;
|
|
96
100
|
/** Ready tasks we skipped this tick due to caps. Caller may `tick()`
|
|
97
101
|
* again after any adapter completes. */
|
|
98
|
-
deferred: Array<{ taskId: string; reason: 'cap' | 'no-adapter' | 'intake-sequential' }>;
|
|
102
|
+
deferred: Array<{ taskId: string; reason: 'cap' | 'no-adapter' | 'intake-sequential' | 'anomaly-hold' }>;
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
export class TaskDispatcher {
|
|
@@ -103,10 +107,13 @@ export class TaskDispatcher {
|
|
|
103
107
|
private readonly activeByKind = new Map<TaskSurfaceKind, number>();
|
|
104
108
|
private readonly abortByTask = new Map<string, AbortController>();
|
|
105
109
|
private readonly now: () => number;
|
|
110
|
+
/** Production default is the process-wide cluster so tick-recorded failures gate relaunch here. */
|
|
111
|
+
private readonly anomaly: AnomalyCluster;
|
|
106
112
|
|
|
107
113
|
constructor(private readonly opts: DispatcherOptions) {
|
|
108
114
|
this.caps = { ...DEFAULT_CONCURRENCY_CAPS, ...(opts.concurrencyCaps ?? {}) };
|
|
109
115
|
this.now = opts.now ?? Date.now;
|
|
116
|
+
this.anomaly = opts.anomalyCluster ?? defaultAnomalyCluster();
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
/**
|
|
@@ -147,6 +154,12 @@ export class TaskDispatcher {
|
|
|
147
154
|
deferred.push({ taskId: task.id, reason: 'cap' });
|
|
148
155
|
continue;
|
|
149
156
|
}
|
|
157
|
+
// ANOMALY-CLUSTER relaunch gate: the task's goal·host relaunch is deferred while its signature has
|
|
158
|
+
// 3+ runs and no cause verdict has cleared it. Goal = task id, host = surface kind.
|
|
159
|
+
if (this.anomaly.hold(task.id, kind).hold) {
|
|
160
|
+
deferred.push({ taskId: task.id, reason: 'anomaly-hold' });
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
150
163
|
const result = this.spawn(task, adapter);
|
|
151
164
|
dispatched.push({
|
|
152
165
|
taskId: task.id,
|
|
@@ -220,6 +233,24 @@ export class TaskDispatcher {
|
|
|
220
233
|
}
|
|
221
234
|
}
|
|
222
235
|
|
|
236
|
+
/** Records one failed attempt into the anomaly cluster (goal·host key, host excluded from signature). */
|
|
237
|
+
private recordAnomalyFailure(task: Task, observation: { runId: string; kind: string; stage: string; lastLines: string[] }): void {
|
|
238
|
+
try {
|
|
239
|
+
this.anomaly.record({
|
|
240
|
+
goal: task.id,
|
|
241
|
+
host: task.surface.kind,
|
|
242
|
+
runId: observation.runId,
|
|
243
|
+
kind: observation.kind,
|
|
244
|
+
containerExit: extractContainerExit(observation.lastLines.join('\n')),
|
|
245
|
+
stage: observation.stage,
|
|
246
|
+
lastLines: observation.lastLines,
|
|
247
|
+
at: new Date(this.now()).toISOString(),
|
|
248
|
+
});
|
|
249
|
+
} catch {
|
|
250
|
+
// Observability must not alter dispatch admission or completion.
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
223
254
|
private recordOpsEvent(input: OpsEventInput): void {
|
|
224
255
|
try {
|
|
225
256
|
(this.opts.recordOpsEvent ?? recordOpsEventSafe)(input);
|
|
@@ -322,6 +353,8 @@ export class TaskDispatcher {
|
|
|
322
353
|
this.recordOps(task, 'cycle_end', nextStatus, { execStatus: exec.status, durationMs: exec.durationMs });
|
|
323
354
|
|
|
324
355
|
if (nextStatus === 'done') {
|
|
356
|
+
// Cause verdict — the same goal·host produced a clean run, so the relaunch hold is cleared.
|
|
357
|
+
this.anomaly.release(task.id, kind, '원인 판정: 실행이 정상 완료됨');
|
|
325
358
|
this.opts.bus?.emit({
|
|
326
359
|
kind: 'task-completed',
|
|
327
360
|
taskId: task.id,
|
|
@@ -337,6 +370,12 @@ export class TaskDispatcher {
|
|
|
337
370
|
reason: 'user',
|
|
338
371
|
});
|
|
339
372
|
} else {
|
|
373
|
+
this.recordAnomalyFailure(task, {
|
|
374
|
+
runId: exec.id,
|
|
375
|
+
kind: exec.status === 'timeout' ? 'timeout' : 'exit',
|
|
376
|
+
stage: 'monitor',
|
|
377
|
+
lastLines: [exec.error?.message ?? '', `Reason: ${exec.error?.code ?? 'UNKNOWN'}`],
|
|
378
|
+
});
|
|
340
379
|
this.opts.bus?.emit({
|
|
341
380
|
kind: 'task-failed',
|
|
342
381
|
taskId: task.id,
|
|
@@ -368,6 +407,12 @@ export class TaskDispatcher {
|
|
|
368
407
|
this.opts.store?.saveTask(this.opts.graph.getTask(task.id)!);
|
|
369
408
|
} catch { /* ignore illegal transition */ }
|
|
370
409
|
this.recordOps(task, 'cycle_end', 'failed', { errorCode: 'ADAPTER_ERROR', error: err instanceof Error ? err.message.slice(0, 120) : String(err) });
|
|
410
|
+
this.recordAnomalyFailure(task, {
|
|
411
|
+
runId: res.executionId,
|
|
412
|
+
kind: 'adapter-error',
|
|
413
|
+
stage: 'monitor',
|
|
414
|
+
lastLines: [err instanceof Error ? err.message : String(err)],
|
|
415
|
+
});
|
|
371
416
|
this.opts.bus?.emit({
|
|
372
417
|
kind: 'task-failed',
|
|
373
418
|
taskId: task.id,
|
|
@@ -82,6 +82,8 @@ export function buildDecomposePrompt(
|
|
|
82
82
|
|
|
83
83
|
const lines: string[] = [];
|
|
84
84
|
const isGoalAuthorCoarse = opts.profile === 'goal-author-coarse';
|
|
85
|
+
const measuredDeviceInventory = input.context?.activeSummary?.match(/^Device inventory \(measured at goal-author repository root\): .*$/mu)?.[0];
|
|
86
|
+
const measuredDevice = measuredDeviceInventory?.match(/^Device inventory \(measured at goal-author repository root\): (missing|existing): (src\/ux-sim\/[^\s,]+)$/u);
|
|
85
87
|
lines.push(
|
|
86
88
|
'You are the monad-agent Task Orchestrator decomposition planner.',
|
|
87
89
|
isGoalAuthorCoarse
|
|
@@ -205,6 +207,10 @@ export function buildDecomposePrompt(
|
|
|
205
207
|
'## Device-first decomposition (DEVICE-FIRST-AUTHORING — follow strictly)',
|
|
206
208
|
'### Existing device inventory (read from this checkout; use these names before proposing a new device)',
|
|
207
209
|
...deviceInventory(),
|
|
210
|
+
...(measuredDeviceInventory
|
|
211
|
+
? ['### Goal-author device check (measured at the target root; takes precedence over checkout inventory)',
|
|
212
|
+
measuredDeviceInventory,
|
|
213
|
+
...(measuredDevice ? [`- Use the measured ${measuredDevice[1]} status for ${measuredDevice[2]} instead of this checkout's inventory when choosing the device branch.`] : [])] : []),
|
|
208
214
|
'- The objective\'s verification plan (검증 계획/TDD) section picks a stage and ladder tier (무대·층) — e.g. a `src/ux-sim/*` stage at L2 (fake-llm·fake-bin·fake-clock). BEFORE writing slices, CHECK whether the mock device or injected event definition that this verification plan needs ALREADY EXISTS: look in `src/ux-sim/*`, the §1 device table of `docs/manual/MANUAL-ux-simulation-devices-2026-08-23.md`, and the event-monkey event kinds.',
|
|
209
215
|
'- Two branches — exactly one applies per needed device:',
|
|
210
216
|
' - Device EXISTS: its name MUST go into the verification plan wording — the slice that tests the feature carries the device\'s exact name (device file / §1 table row / event kind) in its acceptance criteria and reuses the device; never re-create it.',
|
|
@@ -7,6 +7,8 @@ import { fromJsonl, type LogRecord } from '../../mss/logging/record.js';
|
|
|
7
7
|
|
|
8
8
|
const FILE_LIMIT = 5 * 1024 * 1024;
|
|
9
9
|
const TOTAL_LIMIT = 20 * 1024 * 1024;
|
|
10
|
+
export const POD_SALVAGE_BUNDLE_FILE_LIMIT = 160 * 1024 * 1024;
|
|
11
|
+
export const POD_SALVAGE_BUNDLE_TOTAL_LIMIT = 180 * 1024 * 1024;
|
|
10
12
|
|
|
11
13
|
type Artifact = { path: string; bytes: Buffer };
|
|
12
14
|
type Incomplete = { path: string; reason: string };
|
|
@@ -50,13 +52,16 @@ export function parsePodArtifactChunks(logs: string): ParseResult {
|
|
|
50
52
|
}
|
|
51
53
|
const artifacts: Artifact[] = [];
|
|
52
54
|
const error: Incomplete[] = [];
|
|
53
|
-
let
|
|
55
|
+
let artifactBytes = 0;
|
|
56
|
+
let salvageBundleBytes = 0;
|
|
54
57
|
for (const [token, group] of groups) {
|
|
55
58
|
let path = token;
|
|
56
59
|
let reason = group.reason;
|
|
57
60
|
try {
|
|
58
61
|
path = decodePath(token);
|
|
59
62
|
if (!safeRelativePath(path)) throw new Error('unsafe relative path');
|
|
63
|
+
const salvageBundle = /^pod-salvage\/[a-zA-Z0-9._-]+\.bundle$/.test(path);
|
|
64
|
+
const fileLimit = salvageBundle ? POD_SALVAGE_BUNDLE_FILE_LIMIT : FILE_LIMIT;
|
|
60
65
|
if (!reason && group.chunks.size !== group.total) reason = 'missing chunk';
|
|
61
66
|
if (!reason) {
|
|
62
67
|
let encoded = '';
|
|
@@ -64,15 +69,20 @@ export function parsePodArtifactChunks(logs: string): ParseResult {
|
|
|
64
69
|
const chunk = group.chunks.get(i);
|
|
65
70
|
if (!chunk) { reason = 'missing chunk'; break; }
|
|
66
71
|
encoded += chunk;
|
|
67
|
-
if (encoded.length >
|
|
72
|
+
if (encoded.length > Math.ceil(fileLimit * 4 / 3) + 1024 * 1024) { reason = 'file too large'; break; }
|
|
68
73
|
}
|
|
69
74
|
if (!reason) {
|
|
70
75
|
const compressed = Buffer.from(encoded, 'base64');
|
|
71
76
|
if (compressed.toString('base64') !== encoded) throw new Error('invalid base64');
|
|
72
|
-
const bytes = gunzipSync(compressed, { maxOutputLength:
|
|
73
|
-
if (bytes.length >
|
|
74
|
-
else if (
|
|
75
|
-
|
|
77
|
+
const bytes = gunzipSync(compressed, { maxOutputLength: fileLimit + 1 });
|
|
78
|
+
if (bytes.length > fileLimit) reason = 'file too large';
|
|
79
|
+
else if ((salvageBundle ? salvageBundleBytes : artifactBytes) + bytes.length >
|
|
80
|
+
(salvageBundle ? POD_SALVAGE_BUNDLE_TOTAL_LIMIT : TOTAL_LIMIT)) reason = 'total too large';
|
|
81
|
+
else {
|
|
82
|
+
artifacts.push({ path, bytes });
|
|
83
|
+
if (salvageBundle) salvageBundleBytes += bytes.length;
|
|
84
|
+
else artifactBytes += bytes.length;
|
|
85
|
+
}
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
88
|
} catch (e) { reason = e instanceof Error ? e.message : String(e); }
|
|
@@ -334,10 +334,31 @@ export interface PodTermination {
|
|
|
334
334
|
finishedAt: string | PodTerminationUnknown;
|
|
335
335
|
/** From `state.terminated.reason === 'OOMKilled'` only (never from exit code 137). */
|
|
336
336
|
oomKilled: boolean | PodTerminationUnknown;
|
|
337
|
+
/** A terminal verdict can race Kubernetes container status; one bounded Pod re-read records the outcome. */
|
|
338
|
+
collectionAttempts?: 2;
|
|
339
|
+
collectionFailureReason?: 'pod-still-running-after-retry' | 'pod-absent-after-retry' |
|
|
340
|
+
'pod-read-unavailable-after-retry' | 'terminated-fields-missing-after-retry' | 'pod-state-unknown-after-retry';
|
|
337
341
|
}
|
|
338
342
|
|
|
339
|
-
/**
|
|
340
|
-
export const POD_TERMINATION_JOB_JSONPATH = '{.status.conditions[?(@.type=="Failed")].reason}{"\\t"}{.spec.activeDeadlineSeconds}{"\\t"}{.metadata.uid}';
|
|
343
|
+
/** FailureTarget can precede Failed at the deadline. Read both in the same Job snapshot. */
|
|
344
|
+
export const POD_TERMINATION_JOB_JSONPATH = '{.status.conditions[?(@.type=="Failed")].reason}{"|"}{.status.conditions[?(@.type=="FailureTarget")].reason}{"\\t"}{.spec.activeDeadlineSeconds}{"\\t"}{.metadata.uid}';
|
|
345
|
+
|
|
346
|
+
export function podJobConditionReason(value: string | undefined): string {
|
|
347
|
+
const [failed, target] = (value ?? '').split('|');
|
|
348
|
+
return terminationWord(failed || target);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** Emitted by the Pod wrapper before the Job's hard deadline, scoped to this Job and child run. */
|
|
352
|
+
export function podDeadlineResultMarker(logs: string, job: string, runId: string): { stage: string; pr: number | null; branch: string | null } | null {
|
|
353
|
+
if (!/^[A-Za-z0-9_.:-]+$/.test(job) || !/^[A-Za-z0-9_.:-]+$/.test(runId)) return null;
|
|
354
|
+
const prefix = `ELANOUS_POD_DEADLINE_RESULT ${job} ${runId} stop-cause=deadline `;
|
|
355
|
+
for (const line of logs.split('\n').reverse()) {
|
|
356
|
+
if (!line.startsWith(prefix)) continue;
|
|
357
|
+
const match = /^stage=(clone|setup|run|unknown) pr=(unknown|[1-9]\d*) branch=(unknown|[A-Za-z0-9_./-]+)$/.exec(line.slice(prefix.length).trim());
|
|
358
|
+
if (match) return { stage: match[1]!, pr: match[2] === 'unknown' ? null : Number(match[2]), branch: match[3] === 'unknown' ? null : match[3]! };
|
|
359
|
+
}
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
341
362
|
/**
|
|
342
363
|
* One row per Pod of this Job name. The first three columns keep the classification read's layout
|
|
343
364
|
* (`creation · terminated.reason · terminated.exitCode`, empty when the child has no status yet), so the
|
|
@@ -384,7 +405,7 @@ export function readPodTermination(input: { kubectl: PodTerminationKubectl; name
|
|
|
384
405
|
const jobUid = input.jobUid && JOB_UID.test(input.jobUid) ? input.jobUid : undefined;
|
|
385
406
|
// A read without a uid, or with another uid, is not this launch's Job.
|
|
386
407
|
const jobOk = jobRead?.status === 0 && jobUid !== undefined && readUid === jobUid;
|
|
387
|
-
const jobConditionReason = jobOk ?
|
|
408
|
+
const jobConditionReason = jobOk ? podJobConditionReason(reasonText) : 'unknown';
|
|
388
409
|
const podsRead = 'podsRead' in input ? input.podsRead ?? null
|
|
389
410
|
: read(['-n', namespace, 'get', 'pods', '-l', `job-name=${job}`, '-o', `jsonpath=${POD_TERMINATION_PODS_JSONPATH}`]);
|
|
390
411
|
let podLookup: PodTermination['podLookup'] = 'unavailable';
|
|
@@ -444,7 +465,11 @@ export function parsePodTermination(value: unknown, job: string): PodTermination
|
|
|
444
465
|
|| !oneOf(row.podLookup, ['terminated', 'running', 'pending', 'unknown', 'absent', 'unavailable'])
|
|
445
466
|
|| !wordOrUnknown(row.containerReason) || !numberOrUnknown(row.exitCode)
|
|
446
467
|
|| !(row.finishedAt === 'unknown' || (typeof row.finishedAt === 'string' && terminationInstant(row.finishedAt) !== 'unknown'))
|
|
447
|
-
|| !boolOrUnknown(row.oomKilled)
|
|
468
|
+
|| !boolOrUnknown(row.oomKilled)
|
|
469
|
+
|| (row.collectionAttempts !== undefined && row.collectionAttempts !== 2)
|
|
470
|
+
|| (row.collectionFailureReason !== undefined && !oneOf(row.collectionFailureReason, [
|
|
471
|
+
'pod-still-running-after-retry', 'pod-absent-after-retry', 'pod-read-unavailable-after-retry',
|
|
472
|
+
'terminated-fields-missing-after-retry', 'pod-state-unknown-after-retry']))) return undefined;
|
|
448
473
|
// Facts are attributed through the uid only: a record claiming Job or Pod facts without one is contradictory.
|
|
449
474
|
if (row.jobUid === 'unknown' && (row.jobLookup !== 'unavailable' || row.podLookup !== 'unavailable')) return undefined;
|
|
450
475
|
return {
|
|
@@ -455,5 +480,7 @@ export function parsePodTermination(value: unknown, job: string): PodTermination
|
|
|
455
480
|
podLookup: row.podLookup as PodTermination['podLookup'], containerReason: row.containerReason as string,
|
|
456
481
|
exitCode: row.exitCode as PodTermination['exitCode'], finishedAt: row.finishedAt as string,
|
|
457
482
|
oomKilled: row.oomKilled as PodTermination['oomKilled'],
|
|
483
|
+
...(row.collectionAttempts === 2 ? { collectionAttempts: 2 as const } : {}),
|
|
484
|
+
...(row.collectionFailureReason ? { collectionFailureReason: row.collectionFailureReason as PodTermination['collectionFailureReason'] } : {}),
|
|
458
485
|
};
|
|
459
486
|
}
|
|
@@ -14,6 +14,7 @@ type ParseResult = Ledger[] | { error: Incomplete[]; ledgers: Ledger[] };
|
|
|
14
14
|
const NON_PROGRESS_EVENTS = new Set(['progress-delivery-outcome']);
|
|
15
15
|
const REEMIT_EXCLUDED_PREFIX = 'progress-delivery';
|
|
16
16
|
type Emit = (category: string, event: string, data: Record<string, unknown>) => void;
|
|
17
|
+
export interface PodLedgerMeasurement { ledgerRead: boolean; activityMeasured: boolean }
|
|
17
18
|
/** Heal-loop starter seam — defaults to the real `startHealLoop` inside `recordFailureEvent`. */
|
|
18
19
|
type StartHealLoop = Parameters<typeof recordFailureEvent>[2];
|
|
19
20
|
|
|
@@ -174,7 +175,7 @@ export function createPodLedgerFollower(opts: {
|
|
|
174
175
|
stallMinutes?: number;
|
|
175
176
|
now?: () => number;
|
|
176
177
|
onStall?: (message: string) => void;
|
|
177
|
-
}): { poll():
|
|
178
|
+
}): { poll(): PodLedgerMeasurement; readonly owned: boolean } {
|
|
178
179
|
const dir = opts.dir ?? runLedgerDir();
|
|
179
180
|
const log = opts.log ?? defaultPodReemit();
|
|
180
181
|
const emit: Emit = opts.emit ?? defaultPodReemit();
|
|
@@ -208,17 +209,17 @@ export function createPodLedgerFollower(opts: {
|
|
|
208
209
|
return {
|
|
209
210
|
get owned() { return owned; },
|
|
210
211
|
poll() {
|
|
211
|
-
if (disabled) return;
|
|
212
|
+
if (disabled) return { ledgerRead: true, activityMeasured: false };
|
|
212
213
|
if (!owned && existsSync(path)) {
|
|
213
214
|
disabled = true;
|
|
214
215
|
log('self-implement.pod', 'ledger-live-skipped', { runId: opts.runId, reason: 'host-ledger-exists' });
|
|
215
|
-
return;
|
|
216
|
+
return { ledgerRead: true, activityMeasured: false };
|
|
216
217
|
}
|
|
217
218
|
const since = `${Math.floor(lastActivityMeasuredAt / 1000)}.${String(lastActivityMeasuredAt % 1000).padStart(3, '0')}`;
|
|
218
219
|
const measurementStartedAt = now();
|
|
219
220
|
const until = `${Math.floor(measurementStartedAt / 1000)}.${String(measurementStartedAt % 1000).padStart(3, '0')}`;
|
|
220
221
|
const r = opts.exec(`s="\${ELANOUS_STATE_DIR:-$HOME/.elanous}"; f="$s/run-ledger/${opts.runId}.jsonl"; w="$s/worktrees"; if [ -f "$f" ]; then tail -c +${offset + 1} "$f" || exit 1; fi; target=''; matches=0; for tree in "$w"/*/*.worktrees/*; do [ -e "$tree/.git" ] || continue; owner=$(git -C "$tree" config --worktree --get elanous.harness.owner 2>/dev/null) || continue; [ "$owner" = 'dev:${opts.runId}' ] || continue; target="$tree"; matches=$((matches + 1)); done; if [ "$matches" -eq 1 ]; then activity=$(find "$target" -type d \\( -name .git -o -name node_modules \\) -prune -o -type f -newermt '@${since}' ! -newermt '@${until}' -printf . 2>/dev/null) && printf '\\nELANOUS_ACTIVITY %s\\n' "\${#activity}" || printf '\\nELANOUS_ACTIVITY_ERROR\\n'; else printf '\\nELANOUS_ACTIVITY_ERROR\\n'; fi`);
|
|
221
|
-
if (r.status !== 0) { log('self-implement.pod', 'ledger-live-unavailable', { runId: opts.runId, status: r.status, stderr: r.stderr.trim().slice(0, 200) }); checkStall(false, false); return; }
|
|
222
|
+
if (r.status !== 0) { log('self-implement.pod', 'ledger-live-unavailable', { runId: opts.runId, status: r.status, stderr: r.stderr.trim().slice(0, 200) }); checkStall(false, false); return { ledgerRead: false, activityMeasured: false }; }
|
|
222
223
|
const marker = r.stdout.lastIndexOf('\nELANOUS_ACTIVITY');
|
|
223
224
|
const activityLine = marker < 0 ? '' : r.stdout.slice(marker + 1);
|
|
224
225
|
const activityMatch = /^ELANOUS_ACTIVITY (0|[1-9]\d*)\n$/.exec(activityLine);
|
|
@@ -241,11 +242,11 @@ export function createPodLedgerFollower(opts: {
|
|
|
241
242
|
}
|
|
242
243
|
}
|
|
243
244
|
}
|
|
244
|
-
if (!ledgerBytes) { checkStall(true, activityMeasured); return; }
|
|
245
|
+
if (!ledgerBytes) { checkStall(true, activityMeasured); return { ledgerRead: true, activityMeasured }; }
|
|
245
246
|
offset += Buffer.byteLength(ledgerBytes);
|
|
246
247
|
const text = pending + ledgerBytes;
|
|
247
248
|
const cut = text.lastIndexOf('\n');
|
|
248
|
-
if (cut < 0) { pending = text; checkStall(true, activityMeasured); return; }
|
|
249
|
+
if (cut < 0) { pending = text; checkStall(true, activityMeasured); return { ledgerRead: true, activityMeasured }; }
|
|
249
250
|
const complete = text.slice(0, cut + 1);
|
|
250
251
|
pending = text.slice(cut + 1);
|
|
251
252
|
mkdirSync(dir, { recursive: true });
|
|
@@ -278,6 +279,7 @@ export function createPodLedgerFollower(opts: {
|
|
|
278
279
|
nextStallMinute = stallMinutes;
|
|
279
280
|
}
|
|
280
281
|
checkStall(true, activityMeasured);
|
|
282
|
+
return { ledgerRead: true, activityMeasured };
|
|
281
283
|
},
|
|
282
284
|
};
|
|
283
285
|
}
|
|
@@ -1,3 +1,100 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/** Capture a failed Job against its recorded placement, never the caller's current kubectl context. */
|
|
5
|
+
export function captureFailedPodJobs(input: {
|
|
6
|
+
runId: string;
|
|
7
|
+
job: string;
|
|
8
|
+
childRunId?: string;
|
|
9
|
+
ledgerPath: string;
|
|
10
|
+
artifactDir: string;
|
|
11
|
+
kubectl: (host: string, args: readonly string[]) => { status: number | null; stdout: string; stderr: string };
|
|
12
|
+
record: (event: string, data: Record<string, unknown>) => void;
|
|
13
|
+
now?: () => number;
|
|
14
|
+
}): void {
|
|
15
|
+
const { runId, job, ledgerPath, artifactDir, kubectl, record } = input;
|
|
16
|
+
const capturedAt = new Date((input.now ?? Date.now)()).toISOString();
|
|
17
|
+
const base = { runId, job, capturedAt };
|
|
18
|
+
const finish = (outcome: 'collected' | 'failed' | 'vanished', reason: string, extra: Record<string, unknown> = {}) =>
|
|
19
|
+
record('pod-failure-capture', { ...base, ...extra, outcome, reason });
|
|
20
|
+
if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(job)) { finish('failed', 'job-invalid'); return; }
|
|
21
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(runId)) { finish('failed', 'run-id-invalid'); return; }
|
|
22
|
+
let rows: Array<{ runId?: string; event?: string; data?: Record<string, unknown> }>;
|
|
23
|
+
try {
|
|
24
|
+
rows = readFileSync(ledgerPath, 'utf8').trim().split('\n').map(line => JSON.parse(line));
|
|
25
|
+
if (rows.some(row => !row || typeof row !== 'object' || Array.isArray(row))) throw new Error('invalid ledger row');
|
|
26
|
+
} catch { finish('failed', 'ledger-unavailable'); return; }
|
|
27
|
+
if (rows.some(row => row.runId !== runId)) { finish('failed', 'ledger-run-mismatch'); return; }
|
|
28
|
+
const placements = rows.filter(row => row.event === 'pod-job-applied' && row.data?.job === job).map(row => row.data!);
|
|
29
|
+
if (!placements.length || placements.some(p => !['host', 'context', 'namespace', 'childRunId'].every(key => typeof p[key] === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9.-]*$/.test(p[key] as string))
|
|
30
|
+
|| typeof p.jobUid !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9-]*$/.test(p.jobUid))) {
|
|
31
|
+
finish('failed', 'placement-incomplete'); return;
|
|
32
|
+
}
|
|
33
|
+
const selected = input.childRunId ? placements.filter(p => p.childRunId === input.childRunId) : placements;
|
|
34
|
+
if (!selected.length) { finish('failed', 'placement-incomplete'); return; }
|
|
35
|
+
const first = selected[0]!;
|
|
36
|
+
if (placements.some(p => ['host', 'context', 'namespace'].some(key => p[key] !== first[key]))
|
|
37
|
+
|| selected.some(p => ['childRunId', 'jobUid'].some(key => p[key] !== first[key]))) {
|
|
38
|
+
finish('failed', 'placement-conflict'); return;
|
|
39
|
+
}
|
|
40
|
+
const location = { host: first.host, context: first.context, namespace: first.namespace, childRunId: first.childRunId, jobUid: first.jobUid };
|
|
41
|
+
const evidenceDir = join(artifactDir, 'pod-failure-capture', runId, job, first.childRunId as string);
|
|
42
|
+
const previous = rows.find(row => row.event === 'pod-failure-capture' && row.data?.job === job && row.data?.outcome === 'collected'
|
|
43
|
+
&& row.data.childRunId === first.childRunId && row.data.jobUid === first.jobUid
|
|
44
|
+
&& row.data.host === first.host && row.data.context === first.context && row.data.namespace === first.namespace);
|
|
45
|
+
// A ledger row describes a prior collection; it does not guarantee that its files survived.
|
|
46
|
+
// Check all three expected paths before treating it as complete, then recapture any missing evidence.
|
|
47
|
+
if (previous && ([['jobEvidence', 'job.json'], ['podEvidence', 'pods.json'], ['logEvidence', 'child.log']] as const)
|
|
48
|
+
.every(([field, filename]) => {
|
|
49
|
+
const path = join(evidenceDir, filename);
|
|
50
|
+
if (previous.data?.[field] !== path) return false;
|
|
51
|
+
try { return statSync(path).isFile(); } catch { return false; }
|
|
52
|
+
})) return;
|
|
53
|
+
const args = ['--context', first.context as string, '-n', first.namespace as string] as const;
|
|
54
|
+
try {
|
|
55
|
+
const jobResult = kubectl(first.host as string, [...args, 'get', 'job', job, '-o', 'json']);
|
|
56
|
+
if (jobResult.status !== 0) {
|
|
57
|
+
finish(/\bNotFound\b|not found/i.test(jobResult.stderr) ? 'vanished' : 'failed',
|
|
58
|
+
/\bNotFound\b|not found/i.test(jobResult.stderr) ? 'job-not-found' : 'job-read-failed', location);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const jobBody = JSON.parse(jobResult.stdout) as { metadata?: { name?: string; uid?: string } };
|
|
62
|
+
if (jobBody.metadata?.name !== job || jobBody.metadata.uid !== first.jobUid) { finish('failed', 'job-identity-mismatch', location); return; }
|
|
63
|
+
mkdirSync(evidenceDir, { recursive: true });
|
|
64
|
+
const save = (filename: string, bytes: string, sameJob?: (existing: string) => boolean): string => {
|
|
65
|
+
const path = join(evidenceDir, filename);
|
|
66
|
+
try { writeFileSync(path, bytes, { flag: 'wx', mode: 0o600 }); }
|
|
67
|
+
catch (error) {
|
|
68
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
69
|
+
const existing = readFileSync(path, 'utf8');
|
|
70
|
+
if (existing !== bytes && !sameJob?.(existing)) throw error;
|
|
71
|
+
}
|
|
72
|
+
return path;
|
|
73
|
+
};
|
|
74
|
+
const jobEvidence = save('job.json', jobResult.stdout, existing => {
|
|
75
|
+
try {
|
|
76
|
+
const metadata = (JSON.parse(existing) as { metadata?: { name?: string; uid?: string } }).metadata;
|
|
77
|
+
return metadata?.name === job && metadata.uid === first.jobUid;
|
|
78
|
+
} catch { return false; }
|
|
79
|
+
});
|
|
80
|
+
const podResult = kubectl(first.host as string, [...args, 'get', 'pods', '-l', `job-name=${job}`, '-o', 'json']);
|
|
81
|
+
if (podResult.status !== 0) { finish('failed', 'pods-read-failed', { ...location, jobEvidence }); return; }
|
|
82
|
+
const pods = JSON.parse(podResult.stdout) as { items?: Array<{ metadata?: { name?: string; ownerReferences?: Array<{ uid?: string }> } }> };
|
|
83
|
+
if (!pods.items?.length || pods.items.some(p => !p.metadata?.ownerReferences?.some(owner => owner.uid === jobBody.metadata?.uid))) {
|
|
84
|
+
finish('failed', 'pod-owner-mismatch', { ...location, jobEvidence }); return;
|
|
85
|
+
}
|
|
86
|
+
const podEvidence = save('pods.json', podResult.stdout);
|
|
87
|
+
const podName = pods.items[0]?.metadata?.name;
|
|
88
|
+
if (pods.items.length !== 1 || !podName || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(podName)) {
|
|
89
|
+
finish('failed', 'pod-ambiguous', { ...location, jobEvidence, podEvidence }); return;
|
|
90
|
+
}
|
|
91
|
+
const logs = kubectl(first.host as string, [...args, 'logs', `pod/${podName}`, '-c', 'child']);
|
|
92
|
+
if (logs.status !== 0) { finish('failed', 'logs-read-failed', { ...location, jobEvidence, podEvidence }); return; }
|
|
93
|
+
const logEvidence = save('child.log', logs.stdout);
|
|
94
|
+
finish('collected', 'job-failed', { ...location, jobEvidence, podEvidence, logEvidence });
|
|
95
|
+
} catch { finish('failed', 'capture-error', location); }
|
|
96
|
+
}
|
|
97
|
+
|
|
1
98
|
const SECRET_FIELD = /token|secret|password|authorization|key/i;
|
|
2
99
|
const SAFE_FIELD = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
3
100
|
const SECRET_VALUE = /(?:\b(?:bearer|basic)\s+\S+|\b(?:token|secret|password|authorization|api[-_]?key|access[-_]?key|private[-_]?key)\b\s*(?:is|[=:])\s*["']?\S+|\b(?:sk-[A-Za-z0-9_-]{8,}|gh[pousr]_[A-Za-z0-9_]{8,}))\b/i;
|
|
@@ -193,7 +193,7 @@ export function parsePodPool(spec: string): PodPoolMember[] {
|
|
|
193
193
|
// `:N` 이 없으면 «상한 없음»이 아니라 기본 상한 2 다 — 그 사실(`capacityDefaulted`)을 멤버에 남긴다.
|
|
194
194
|
const capacityDefaulted = m[3] === undefined;
|
|
195
195
|
const capacity = capacityDefaulted ? POD_POOL_DEFAULT_CAPACITY : Number(m[3]);
|
|
196
|
-
// POOL-LITE (10-07): `:0` means «this member is off» — a
|
|
196
|
+
// POOL-LITE (10-07): `:0` means «this member is off» — a zero-capacity entry once threw here and broke every launch.
|
|
197
197
|
// It is dropped below (never chosen); only a pool with no live member left is an error.
|
|
198
198
|
if (!Number.isInteger(capacity) || capacity < 0) throw new Error(`--pod-pool: 상한은 1 이상 — 「${part}」`);
|
|
199
199
|
if (m[5] && Number(m[5]) > 65535) throw new Error(`--pod-pool: 레지스트리 포트는 65535 이하 — 「${part}」`);
|
|
@@ -243,14 +243,27 @@ export function podWorkLabels(identity: PodWorkIdentity): Record<string, string>
|
|
|
243
243
|
};
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* MINIH-MODE (0.2.37) — where this pool's standard Jobs run: `home` (1 standard Job) or `external` (2).
|
|
248
|
+
* The location is declared explicitly by the launch (`minihLocationFrom` reads `ELANOUS_MINIH_LOCATION`); when it
|
|
249
|
+
* is absent the configured member capacities decide, exactly as before.
|
|
250
|
+
*/
|
|
247
251
|
export type MinihLocation = 'home' | 'external';
|
|
248
252
|
|
|
253
|
+
/** The declared location for a launch: `home` / `external` only. Absent, empty or unknown → null (configured capacity). */
|
|
254
|
+
export function minihLocationFrom(value: string | undefined | null): MinihLocation | null {
|
|
255
|
+
const normalized = value?.trim().toLowerCase();
|
|
256
|
+
return normalized === 'home' || normalized === 'external' ? normalized : null;
|
|
257
|
+
}
|
|
258
|
+
|
|
249
259
|
export function minihStandardCapacity(location: MinihLocation): number {
|
|
250
260
|
return location === 'home' ? 1 : 2;
|
|
251
261
|
}
|
|
252
262
|
|
|
253
|
-
/**
|
|
263
|
+
/**
|
|
264
|
+
* A drain withholds new slots until occupancy falls to the target; it never evicts Running Pods.
|
|
265
|
+
* `PodPoolScheduler.tryAcquire` applies this to new standard-Job admission when a location is declared.
|
|
266
|
+
*/
|
|
254
267
|
export function decideMinihDrain(location: MinihLocation, runningPods: number, pendingPods = 0): {
|
|
255
268
|
capacity: number;
|
|
256
269
|
runningPods: number;
|
|
@@ -281,8 +294,8 @@ export interface PodPlacement {
|
|
|
281
294
|
}
|
|
282
295
|
|
|
283
296
|
/**
|
|
284
|
-
* Preferred members for a placement kind: lite → the smallest-capacity member(s)
|
|
285
|
-
* Capacity is the operator's own statement of member size (`pool-node-
|
|
297
|
+
* Preferred members for a placement kind: lite → the smallest-capacity member(s), standard → the largest.
|
|
298
|
+
* Capacity is the operator's own statement of member size (`pool-node-a@node-a:40,pool-node-b@node-b:6`), so no new spec syntax.
|
|
286
299
|
* When every member has the same capacity there is no small/big member and no preference (empty list).
|
|
287
300
|
*/
|
|
288
301
|
export function preferredMembers(members: readonly PodPoolMember[], kind: PodPlacementKind | null | undefined): string[] {
|
|
@@ -582,6 +595,26 @@ export class PodPoolScheduler {
|
|
|
582
595
|
recommended: Math.min(this.members.reduce((n, item) => n + item.capacity, 0), snapshots.reduce((n, item) => n + item.recommended, 0)),
|
|
583
596
|
waiting: snapshots.flatMap((item) => item.waiting) };
|
|
584
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* MINIH-MODE — withhold NEW standard-Job admission while the declared location has no free standard slot.
|
|
600
|
+
* Occupancy = the measured node-c Pods (Running ⊕ Pending; a Pending Pod already holds a slot) ⊕ this process's
|
|
601
|
+
* node-c inflight slots. Nothing here deletes, evicts or re-schedules a Pod: an excess above the location's capacity
|
|
602
|
+
* leaves only as those Pods finish, and until then new admissions wait. `lite` Jobs are not the standard
|
|
603
|
+
* admission this policy gates, and no declared location keeps the configured member capacities.
|
|
604
|
+
*/
|
|
605
|
+
private minihHold(kind: PodPlacementKind | null, location: MinihLocation | null, reading: Record<string, MemberOccupancy>): { capacity: number; occupied: number; draining: boolean } | null {
|
|
606
|
+
if (!location || kind === 'lite') return null;
|
|
607
|
+
// A launch pool can contain node-b and WSL beside node-c. Their Pods do not occupy node-c's physical slots.
|
|
608
|
+
const minihMembers = this.members.filter((m) => m.capacity > 0 && (m.sshHost === 'node-c' || m.context === 'pool-node-c'));
|
|
609
|
+
if (minihMembers.length === 0) return null;
|
|
610
|
+
// Lite Jobs also hold a physical node-c slot while running, even though this policy never blocks their admission.
|
|
611
|
+
const inflight = minihMembers.reduce((count, m) => count + (this.inflight.get(m.context) ?? 0), 0);
|
|
612
|
+
// The measurement reports Running and Pending as one occupied count, and decideMinihDrain judges only their sum.
|
|
613
|
+
const occupied = minihMembers
|
|
614
|
+
.reduce((count, m) => count + (reading[m.context]?.occupied ?? 0), inflight);
|
|
615
|
+
const decision = decideMinihDrain(location, occupied);
|
|
616
|
+
return decision.availableSlots > 0 ? null : { capacity: decision.capacity, occupied, draining: decision.drainNewAdmissions };
|
|
617
|
+
}
|
|
585
618
|
/**
|
|
586
619
|
* Pick the member with the largest free share: (capacity − measured cluster occupancy − this process's inflight) / capacity.
|
|
587
620
|
* POOL-SPREAD (10-06): comparing absolute free slots always picked the big member (node-b 25 vs node-c 4), so the small
|
|
@@ -593,9 +626,10 @@ export class PodPoolScheduler {
|
|
|
593
626
|
* member the 0.2.15 gate launched its shards one at a time.
|
|
594
627
|
* Ties keep the configured member order. Returns null when every readable member is full.
|
|
595
628
|
*/
|
|
596
|
-
async tryAcquire(skip?: ReadonlySet<string>, onEmpty?: (reason: string) => void, mode: { selfCapped?: boolean; kind?: PodPlacementKind | null; onPlacement?: (placement: PodPlacement) => void; signal?: AbortSignal } = {}): Promise<PodPoolMember | null> {
|
|
629
|
+
async tryAcquire(skip?: ReadonlySet<string>, onEmpty?: (reason: string) => void, mode: { selfCapped?: boolean; kind?: PodPlacementKind | null; /** MINIH-MODE: a declared location gates new standard-Job admission (home 1 · external 2). */ minihLocation?: MinihLocation | null; onPlacement?: (placement: PodPlacement) => void; signal?: AbortSignal } = {}): Promise<PodPoolMember | null> {
|
|
597
630
|
const selfCapped = mode.selfCapped === true;
|
|
598
631
|
const kind = mode.kind ?? null;
|
|
632
|
+
const minihLocation = mode.minihLocation ?? null;
|
|
599
633
|
// POOL-LITE: lite goals try the small member first, implementation goals the big one; full → overflow by free share.
|
|
600
634
|
const preferred = new Set(preferredMembers(this.members, kind));
|
|
601
635
|
let reading: Record<string, MemberOccupancy>;
|
|
@@ -608,6 +642,9 @@ export class PodPoolScheduler {
|
|
|
608
642
|
}
|
|
609
643
|
// A caller may abandon a slow occupancy read. Never acquire its lease after cancellation.
|
|
610
644
|
if (mode.signal?.aborted) throw mode.signal.reason ?? new Error('pod pool acquisition aborted');
|
|
645
|
+
// MINIH-MODE: only node-c candidates are held. Other pool members retain their own configured capacity.
|
|
646
|
+
// Running/Pending Pods already in the pool stay exactly as they are.
|
|
647
|
+
const minihHeld = this.minihHold(kind, minihLocation, reading);
|
|
611
648
|
const free: Record<string, number> = {};
|
|
612
649
|
const measured: Record<string, boolean> = {};
|
|
613
650
|
let best: { member: PodPoolMember; slots: number; measured: boolean; preferred: boolean } | null = null;
|
|
@@ -621,6 +658,10 @@ export class PodPoolScheduler {
|
|
|
621
658
|
for (const m of this.members) {
|
|
622
659
|
// A capacity-0 member is off — never chosen (and no division by zero in the share).
|
|
623
660
|
if (m.capacity <= 0) continue;
|
|
661
|
+
if (minihHeld && (m.sshHost === 'node-c' || m.context === 'pool-node-c')) {
|
|
662
|
+
free[m.context] = 0;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
624
665
|
// LAUNCH-STALL: a member this launch cannot fit is skipped for this call only (other launches may fit it).
|
|
625
666
|
if (skip?.has(m.context)) continue;
|
|
626
667
|
const inflight = this.inflight.get(m.context) ?? 0;
|
|
@@ -635,7 +676,12 @@ export class PodPoolScheduler {
|
|
|
635
676
|
if (better(cand)) best = cand;
|
|
636
677
|
}
|
|
637
678
|
if (!best) {
|
|
638
|
-
|
|
679
|
+
if (minihHeld) {
|
|
680
|
+
const reason = `node-c 드레인: ${minihLocation} 표준 상한 ${minihHeld.capacity} · 점유 ${minihHeld.occupied}${minihHeld.draining
|
|
681
|
+
? ' — 초과분이 빠질 때까지 새 입장 보류(기존 Pod 유지)' : ' — 상한이 찼다'}`;
|
|
682
|
+
debug.log('pod.pool', 'node-c-drain-hold', { location: minihLocation, capacity: minihHeld.capacity, occupied: minihHeld.occupied, draining: minihHeld.draining, kind });
|
|
683
|
+
onEmpty?.(reason);
|
|
684
|
+
} else onEmpty?.(`no free slot · free=${JSON.stringify(free)} · measured=${JSON.stringify(measured)}${selfCapped ? ' · selfCapped' : ''}${skip?.size ? ` · skipped=${[...skip].join(',')}` : ''}${kind ? ` · kind=${kind}` : ''}`);
|
|
639
685
|
return null;
|
|
640
686
|
}
|
|
641
687
|
const chosen: { member: PodPoolMember; slots: number; measured: boolean; preferred: boolean } = best;
|
|
@@ -842,6 +888,29 @@ export function syncPoolImage(member: PodPoolMember, image: string, localCommit:
|
|
|
842
888
|
|
|
843
889
|
export type PoolKubectl = (args: readonly string[], input?: string) => { status: number | null; stdout: string; stderr: string };
|
|
844
890
|
|
|
891
|
+
/** Read identities for a shadow decision; this never reserves or changes a Pod. */
|
|
892
|
+
export function snapshotWarmPoolMembers(members: readonly PodPoolMember[], kubectl: PoolKubectl):
|
|
893
|
+
Record<string, Array<{ name: string; uid: string; node: string | null }>> | null {
|
|
894
|
+
const result: Record<string, Array<{ name: string; uid: string; node: string | null }>> = {};
|
|
895
|
+
for (const member of members) {
|
|
896
|
+
const observed = kubectl(['--context', member.context, '-n', 'elanous-test', 'get', 'pods', '-l', 'elanous.kind=warm-worker', '-o', 'json']);
|
|
897
|
+
if (observed.status !== 0) return null;
|
|
898
|
+
try {
|
|
899
|
+
const parsed: unknown = JSON.parse(observed.stdout);
|
|
900
|
+
if (!parsed || typeof parsed !== 'object' || !('items' in parsed) || !Array.isArray(parsed.items)) return null;
|
|
901
|
+
const rows: Array<{ name: string; uid: string; node: string | null }> = [];
|
|
902
|
+
for (const item of parsed.items) {
|
|
903
|
+
if (!item || typeof item !== 'object' || !item.metadata || typeof item.metadata.name !== 'string'
|
|
904
|
+
|| typeof item.metadata.uid !== 'string' || !item.metadata.name || !item.metadata.uid) return null;
|
|
905
|
+
const node = typeof item.spec?.nodeName === 'string' ? item.spec.nodeName : null;
|
|
906
|
+
rows.push({ name: item.metadata.name, uid: item.metadata.uid, node });
|
|
907
|
+
}
|
|
908
|
+
result[member.context] = rows.sort((a, b) => a.uid.localeCompare(b.uid));
|
|
909
|
+
} catch { return null; }
|
|
910
|
+
}
|
|
911
|
+
return result;
|
|
912
|
+
}
|
|
913
|
+
|
|
845
914
|
/**
|
|
846
915
|
* 발사 전 점검 — 노드마다 «컨텍스트가 닿나 · elanous-test 네임스페이스가 있나».
|
|
847
916
|
* ⛔ 안 되는 노드는 «조용히» 빼지 않는다 — 이유를 낸다. 하나도 안 되면 ok=false.
|
|
@@ -926,7 +995,7 @@ export async function syncPoolImages(members: readonly PodPoolMember[], image: s
|
|
|
926
995
|
const remoteBuild = deps.remoteBuild ?? (script ? defaultRemoteBuild(script, image) : null);
|
|
927
996
|
// The registry-less build fallback below calls this ship path, including its remote import result check.
|
|
928
997
|
const ship = deps.ship ?? ((m: PodPoolMember, img: string, commit: string | null, remote: RemoteRun) =>
|
|
929
|
-
syncPoolImage(m, img, commit, { run: remote, inspect: deps.inspect, transfer: deps.transfer }));
|
|
998
|
+
syncPoolImage(m, img, commit, { run: remote, inspect: deps.inspect, transfer: deps.transfer, lock: deps.lock }));
|
|
930
999
|
const results = new Map<string, PoolImageSync>();
|
|
931
1000
|
// pack 한 번 ⊕ 나눠 쓰기 — 원격 멤버가 둘 이상일 때만(하나뿐이면 종전대로 build.sh 가 스스로 pack 한다).
|
|
932
1001
|
// 주입된 remoteBuild(시험)에는 진짜 pack 을 붙이지 않는다 — pack 도 주입해야 쓴다.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/** Fenced warm-Pod lease bridge. The Job remains authoritative. */
|
|
2
2
|
import { spawn, spawnSync } from 'node:child_process';
|
|
3
3
|
import { isAbsolute, join } from 'node:path';
|
|
4
|
-
import { constants, closeSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { constants, closeSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
5
5
|
import { tmpdir, userInfo } from 'node:os';
|
|
6
|
-
import { dlopen, read } from 'bun:ffi';
|
|
6
|
+
import { dlopen, read, type Pointer } from 'bun:ffi';
|
|
7
7
|
import { createHash, randomUUID } from 'node:crypto';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
9
|
import { debug } from '../../debug/log.js';
|
|
@@ -165,7 +165,8 @@ const prepareLockLib = process.platform === 'linux' || process.platform === 'dar
|
|
|
165
165
|
[process.platform === 'linux' ? '__errno_location' : '__error']: { args: [], returns: 'ptr' },
|
|
166
166
|
}) : null;
|
|
167
167
|
const prepareFlock = prepareLockLib?.symbols.flock ?? null;
|
|
168
|
-
const
|
|
168
|
+
const prepareErrnoLocation = prepareLockLib?.symbols[process.platform === 'linux' ? '__errno_location' : '__error'];
|
|
169
|
+
const readPrepareErrno = (): number => (prepareErrnoLocation ? read.i32(prepareErrnoLocation() as Pointer) : -1);
|
|
169
170
|
const LOCK_EX = 2;
|
|
170
171
|
const LOCK_NB = 4;
|
|
171
172
|
const LOCK_UN = 8;
|
|
@@ -190,7 +191,7 @@ async function withPrepareLease<T>(candidate: WarmWorkerCandidate, directory: st
|
|
|
190
191
|
while (!acquired) {
|
|
191
192
|
if (signal?.aborted) throw new Error('warm prepare lock wait cancelled');
|
|
192
193
|
if (prepareFlock(fd, LOCK_EX | LOCK_NB) === 0) { acquired = true; break; }
|
|
193
|
-
const errno =
|
|
194
|
+
const errno = readPrepareErrno();
|
|
194
195
|
// EWOULDBLOCK/EAGAIN: Linux 11, Darwin 35. EINTR is safe to retry.
|
|
195
196
|
if (errno !== 11 && errno !== 35 && errno !== 4)
|
|
196
197
|
throw new Error(`warm prepare flock failed (errno ${errno})`);
|
|
@@ -818,11 +819,16 @@ if (import.meta.main) {
|
|
|
818
819
|
// Read-only: join the cold CLI legs and the warm gate legs both processes wrote to logs.db.
|
|
819
820
|
try {
|
|
820
821
|
const storeMod = await import('../../mss/logging/log-store.js');
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
822
|
+
const dbPath = storeMod.logsDbPath();
|
|
823
|
+
// No logs.db yet means no leg was ever measured: an empty pair list, not a failed query.
|
|
824
|
+
if (!existsSync(dbPath)) process.stdout.write(JSON.stringify({ warmReuseTimePairs: [] }) + '\n');
|
|
825
|
+
else {
|
|
826
|
+
const store = storeMod.LogStore.openReadOnly(dbPath);
|
|
827
|
+
try { process.stdout.write(JSON.stringify({ warmReuseTimePairs: readWarmReuseTimePairs(store) }) + '\n'); }
|
|
828
|
+
finally { store.close(); }
|
|
829
|
+
}
|
|
830
|
+
} catch (error) {
|
|
831
|
+
process.stdout.write(JSON.stringify({ warmReuseTimePairs: null, reason: 'logs-unavailable', detail: String(error) }) + '\n');
|
|
826
832
|
process.exitCode = 2;
|
|
827
833
|
}
|
|
828
834
|
}
|