elanous 0.2.35 → 0.2.36
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/chunks/{d06f9160.a3d9062f95de8ad7.js → 053a429e.9a63436527cc751c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/109.f196abf3484bb97f.js +287 -0
- package/apps/pwa/out/_next/static/chunks/{4346.8601952e73bf5803.js → 1201.afbfc5b1c5e5cbe4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1517.9275b89572098ff3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1574.ad11f8db8396ab45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1614.7a8c5c40aee0261f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1871-f1eb29d8dbb4065b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/190.285c832bd9626f8c.js +64 -0
- package/apps/pwa/out/_next/static/chunks/2193.0ea24c4a7aa1d004.js +36 -0
- package/apps/pwa/out/_next/static/chunks/223-ad865c1e80491d78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2265-f0d90b6dbec05f81.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2267.eb4a62a209f09ec4.js +149 -0
- package/apps/pwa/out/_next/static/chunks/2341-86cd06c0cf01aa33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2352.9abc42422bade876.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2397-c1653a816061842d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3103.0df8da390948bb84.js → 2500.cbc1257b774aae52.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{7644.17e37ba6f1d4226e.js → 2683.c9773a0c589b031b.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/2709.8e1889034255fc5f.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{4718-17d2af636796a330.js → 275-02ea4c2a33980422.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/2812ca6a.3b9f4a7a08885744.js +1 -0
- package/apps/pwa/out/_next/static/chunks/284.ac77c0db55bfd3a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8714.ff0b7ce3ec3e3ae2.js → 2941.3c3546f96bc6efa7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2976.b000698a3c6512dc.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{9214.63a510d39178e997.js → 2986.45d7652e4f13557e.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3031-85328a92fcce4da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3136.30aaaa5fa03662a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3677-4215f826f6aa68e5.js → 3148-98efd4869d660a87.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3155.2587a55196ecb394.js +226 -0
- package/apps/pwa/out/_next/static/chunks/3277.785db1cf09f98601.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3320-f9c77381f85a2c0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324.a09703033abcbd2f.js +34 -0
- package/apps/pwa/out/_next/static/chunks/3386-91049feabff9a94e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/339.80afb7e8839372af.js +8 -0
- package/apps/pwa/out/_next/static/chunks/3410.7f9af5ab77cbe65f.js +115 -0
- package/apps/pwa/out/_next/static/chunks/3538.a445e50bb359d068.js +32 -0
- package/apps/pwa/out/_next/static/chunks/3540c645.2595a6af01a97672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3621.581b2054cc14bee4.js +286 -0
- package/apps/pwa/out/_next/static/chunks/3676.8c28d16fd84ca843.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9512.5a76207a730e0491.js → 3727.7949ccaacaa71242.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/375-b84540853523fe52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8557-e7898769499cd56f.js → 3757-180308bc61021b05.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +74 -0
- package/apps/pwa/out/_next/static/chunks/{359-2250789a2d3499e7.js → 3840-5825d56d46976a92.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3969-81054381eaec4c4c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{da5176c6.6773b6c5994e1d99.js → 3a3d9966.cc4d4585a2708e7c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4067-bf9aa60547972937.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4116.a84a92a2492c080e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4176.e5729c93c29151eb.js +104 -0
- package/apps/pwa/out/_next/static/chunks/4227-e6c84686bc8946ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9952.5daf5f94063a2b3e.js → 4286.b70c4520306817e8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4304.e9b2512834a9e23e.js +23 -0
- package/apps/pwa/out/_next/static/chunks/4327.b9e08a71c0009348.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{938.66ad1ef0e5abae1c.js → 4615.8525eb0add35eba6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4946.bd106eafd668f248.js +148 -0
- package/apps/pwa/out/_next/static/chunks/5085-bce4904931767ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5125.2eb9968b83767193.js +93 -0
- package/apps/pwa/out/_next/static/chunks/5128-75b733fd74a9a1eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6236.e18cced2d5ba6afb.js → 528.38ce20ef8aa251ff.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/5489f3fe.4bb1458b09f6dead.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{3408.21d1570602f4b6c0.js → 552.b7d061ad91eeac51.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5560-2a4648f6a30f16e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5711.957b095705c12560.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1530.0e612369b6fd7ea0.js → 575.d929793a53f27c4b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5772-e29e14f8ff127122.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5887.5b2f5b44ce0f7e9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5899-4c112a2d908cf97f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/599.c39c571b6733f2a2.js +41 -0
- package/apps/pwa/out/_next/static/chunks/6041.1b65efb796ebc2a5.js +79 -0
- package/apps/pwa/out/_next/static/chunks/6057-765c0a26028c86b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6142.f289e0d039f453a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6199-6bcd61d2a3910e04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6236.cdf7293e710a9b2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6387-237e079dac9a3ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6448-b6d272d074276e14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6466-92e65a48490b68a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6566.926a1d1b7c911442.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6580-cdc0e4a634ce4708.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6659-b242dc31b20b3f29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6690.c5d9b321b1f79384.js +1 -0
- package/apps/pwa/out/_next/static/chunks/671-e9b6ad0c771c5514.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6862.bcbf89dd0f4496b3.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6912-7b375ee48732c767.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6925.562d532110879886.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7385.50a308a477bdfc21.js → 6943.c6b24124853a47b2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{630.fe5a46428d63b013.js → 727.7deb87749b0a343a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2018.a741297bce759490.js → 7481.39f7a4654907bf6e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7545.b1aa42cd15503319.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7551-416d298c2dc4a9ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8444.4c26b379ca2b9596.js → 771.c3cdb6c1f7f47c02.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/7784.1f0ac4a16d190cf4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7870-309de463786ac1fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7888-e29b151bb6a3bb8b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3589.5444a06bcf6e775c.js → 7891.03f910124a737985.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7905-6ba78d9234608f79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8037.e254fd797aa224d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8207.f527886b7c464529.js +1 -0
- package/apps/pwa/out/_next/static/chunks/856.50bda61439d465e6.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{b2f6ba4d.fb56fdbbf798df33.js → 873d6c12.09bd499f57171543.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5101.e32282718f99ed6f.js → 8904.7d51af2dc033965c.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9031-92e9b487edb681ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9060.5410d9f6a95f6287.js +90 -0
- package/apps/pwa/out/_next/static/chunks/{5637.281e5dec4ea6ecf1.js → 914.8abefd48d579547b.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9187-c87a2207d4ac5d46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4705-194e64e47ca0fbdb.js → 9250-072df22eaadaa6b5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9289.3e92f278821e18e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9319.7816d3cc741cd34f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9327-e016bb9f1674d24f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9365.3de48bf75193ff9a.js +77 -0
- package/apps/pwa/out/_next/static/chunks/9383.5b5e9aa56b5fea97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9387-cb929573ef436f39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9489.c27becacc3283dec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8637.47f5b35afcae5e6b.js → 9514.131c52cc75fe6439.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9536.28962663c7734376.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9686-12286f5bbf15a5a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9776.289d40cc5a4c342a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1674.2a65fd5668e1cc5f.js → 9789.60f723d18097ec30.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/989.f0c17523c5e61f65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9972-1df393aa8d4f7d52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9f31e478.fefcbcd3c388ebc5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-96750aba7430959f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4105e5bbbb043024.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5d4fa75c2acb113e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-a79a9bdb3b2142bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f1c2047484992a35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-15ffc49b2d8c70e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-67d53a3fc8734135.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-e30c8dc696491336.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-2136e04fa77696e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-fecd2814790c2e12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-280aa4a98fc5bb44.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c14e96b65fb4b883.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-187dac74825ed6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-6bc7246ea8c92448.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-083747f1d4518f61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-96750aba7430959f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-15ca95a16ec96e1f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a8ac031b3810eb64.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-8835f93110494cda.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-e2de2e59111c9aad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f2b2e4bf6e981450.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5b92b7f75df20948.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-468eddf2b1a84ecb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2af16e62c12f22df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-18b40867a77a5793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-fe934f2979f5a4a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-3eec435537357170.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ff88cbfd2aa99fc8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a5b39fb780c70ab5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ae465dde1d1b3933.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e7abc34a48a6f71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-cab9674fde9ead26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-1da49bc8712284d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5e8aa657551ac912.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fcc36f36c6dc1fe2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d22db22f2a7296ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d12dc7b1773878f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-99debec9ea7fbec0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-96750aba7430959f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-2a088a78bad7e924.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-2e8ed57db7e69993.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4b3a16213c2ae8fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f098747ddf38c98b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-c15958835534404b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-68279e3c843c1fd7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a34a74453367a39d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8767a7db2155a5bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-bd5976683460d7f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-b7364835e7f2ef54.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-311af16b3ee0ffca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fb670757274ae990.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{0368f25d-ad84af92fefee68f.js → ccae1c4b-0cf414f5eff1b10d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{cbe38a15.5a032598e39970c6.js → ceba0382.650712b56b85b582.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{b6705a4f-b9a9047de9eecfd0.js → da5ecc5c-b005ad8cac737d57.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{56d372f2.681ab4ec4c998fae.js → f9a61e4d.1fc2c1dbdd49fe46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-6d497cbf1eef04c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-012f5edec932f337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-da5aeb809edc94a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-5ee319af6f749dfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-4bc84653a718d6c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-8a96cf6775c73d6e.js +1 -0
- package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → uC-N0emedeNOFuaBQFv-I}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/docs/goal-context/05-authoring-lessons.jsonl +47 -0
- package/docs/goal-context/README.md +3 -0
- package/graphs/default-loop.yaml +6 -1
- package/graphs/implement-loop.yaml +24 -0
- package/graphs/release/recipes.yaml +6 -0
- package/graphs/release/release-loop.yaml +17 -4
- package/graphs/wizard/recipes.yaml +44 -0
- package/graphs/wizard/wizard-loop.yaml +44 -0
- package/graphs/work/research-report.yaml +2 -0
- package/package.json +1 -1
- package/scripts/ci-typecheck-changed.ts +139 -5
- package/scripts/coord-role-pack.ts +1 -1
- package/scripts/first10-gate.ts +416 -0
- package/scripts/fleet/wsl-node-setup.sh +432 -0
- package/scripts/release-loop/docs-node.ts +204 -8
- package/scripts/release-loop/gate-node.ts +249 -53
- package/scripts/release-loop/node-verdict.ts +2 -0
- package/scripts/release-loop/resume-cap.ts +14 -34
- package/scripts/release-loop/resume-release.ts +37 -11
- package/scripts/release-loop/traffic-node.ts +59 -0
- package/scripts/release-loop/traffic-transition.ts +159 -0
- package/scripts/rhythm/daily.ts +137 -3
- package/scripts/ux-sim.ts +53 -0
- package/skills/README.md +1 -0
- package/src/acp/core-turn-bridge.ts +48 -6
- package/src/agent/delegate-hint.ts +16 -0
- package/src/agent/runner.ts +82 -2
- package/src/agent/task-notification.ts +56 -10
- package/src/agent/types.ts +4 -0
- package/src/agent-substrate/pr-reviewer.ts +55 -3
- package/src/agent-substrate/review-intent.ts +29 -2
- package/src/agent-substrate/review-observation.ts +3 -0
- package/src/agent-substrate/self-review-cli.ts +168 -16
- package/src/autopilot/mission-codebase-gate.ts +141 -12
- package/src/autopilot/pr-manager.ts +25 -5
- package/src/boot/daemon-tools/self-implement.ts +1 -0
- package/src/budget/usage-outcomes.ts +8 -4
- package/src/capture/tui-self-report.ts +102 -0
- package/src/chat/goal-continue-verdict.ts +41 -0
- package/src/chat/index.ts +30 -4
- package/src/chat/internal-turn-origin.ts +1 -0
- package/src/chat/paste-fold.ts +86 -0
- package/src/chat/tool-render/block.ts +32 -0
- package/src/chat/tool-render/dispatch.ts +2 -20
- package/src/chat/tool-render/result-text.ts +98 -0
- package/src/chat/turn-typeahead.ts +70 -7
- package/src/cli/config-audit.ts +119 -0
- package/src/cli/doctor-cli.ts +22 -0
- package/src/cli/guardian-cli.ts +10 -0
- package/src/cli/herdr-cli.ts +34 -0
- package/src/cli/light-entry-route.ts +6 -3
- package/src/cli/log-event-names.ts +2 -0
- package/src/cli/make-cli.ts +247 -11
- package/src/cli/msg-cli.ts +117 -3
- package/src/cli/pr-cli.ts +29 -16
- package/src/cli/pr-land-base-read.ts +11 -2
- package/src/cli/pr-land-heavy-remote.ts +6 -3
- package/src/cli/pr-land-overlap-evidence.ts +23 -4
- package/src/cli/pr-land-recovery.ts +117 -1
- package/src/cli/pty-takeover-cli.ts +8 -7
- package/src/cli/release-cli.ts +21 -5
- package/src/cli/release-single-flight.ts +1 -1
- package/src/cli/tasks-card-show.ts +162 -0
- package/src/cli/tasks-cli.ts +574 -356
- package/src/cli/tasks-read-cli.ts +197 -0
- package/src/core-turn/run-core-turn.ts +4 -2
- package/src/core-turn/run-goal-loop.ts +73 -16
- package/src/core-turn/types.ts +6 -1
- package/src/dashboard/context/keys.ts +8 -31
- package/src/dashboard/index.ts +202 -49
- package/src/dashboard/input/agent-wake.ts +7 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +146 -3
- package/src/dashboard/input/chat-main-slash-command.ts +4 -2
- package/src/dashboard/input/goal-continue.ts +210 -0
- package/src/dashboard/input/goal-interim.ts +2 -0
- package/src/dashboard/input/internal-turn.ts +2 -8
- package/src/dashboard/input/seat-autowake.ts +9 -7
- package/src/dashboard/input/turn-autoheal.ts +52 -0
- package/src/dashboard/seat-boot-preamble.ts +132 -5
- package/src/dashboard/slash-runtime/chat-persona.ts +65 -8
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +3 -2
- package/src/dashboard/slash-runtime/model-choices.ts +15 -0
- package/src/dashboard/stale-install-check.ts +3 -99
- package/src/debug/batch-queue.ts +119 -0
- package/src/debug/tui-log-tag.ts +67 -0
- package/src/display/agent-surface.ts +7 -0
- package/src/domains/standalone-log-sink.ts +15 -2
- package/src/esc-abort-gate.ts +69 -0
- package/src/git-fs/worktree.ts +95 -1
- package/src/graph-runner/graph-cli.ts +18 -0
- package/src/graph-runner/runner.ts +20 -11
- package/src/graph-wizard/generate.ts +108 -4
- package/src/graph-wizard/steps.ts +15 -0
- package/src/grounding/proactive-ground.ts +86 -0
- package/src/harness/control-inbox.ts +49 -1
- package/src/harness/harness-cli-command.ts +51 -7
- package/src/harness/harness-incidents.ts +52 -1
- package/src/harness/pod-control-send.ts +33 -5
- package/src/harness/self-send-remote.ts +69 -0
- package/src/harness/self-send-routing-guard.ts +6 -0
- package/src/harness/self-send-target.ts +4 -3
- package/src/index.ts +327 -12
- package/src/input-core/context-key-runtime.ts +21 -0
- package/src/light-cli.ts +6 -1
- package/src/llm-wellformed.ts +70 -0
- package/src/llm.ts +35 -4
- package/src/loops/msg-wake.ts +35 -39
- package/src/loops/orchestrator/inbox.ts +59 -11
- package/src/loops/orchestrator/knowhow-pack.ts +364 -0
- package/src/loops/orchestrator/tick.ts +5 -1
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/models/prompts.ts +17 -2
- package/src/msg/msg-post-office.ts +116 -0
- package/src/mss/logging/log-store.ts +79 -22
- package/src/nexus/api/decisions-route.ts +38 -18
- package/src/nexus/api/graph-wizard-api.ts +20 -2
- package/src/nexus/boot/register-mcp-clients.ts +263 -38
- package/src/nexus/onboarding/first-boot-wizard.ts +56 -8
- package/src/onboarding/first-run.ts +37 -6
- package/src/onboarding/web-first.ts +43 -11
- package/src/ops/overview-collect.ts +12 -6
- package/src/ops/overview.ts +3 -3
- package/src/persona/loader.ts +11 -0
- package/src/persona/types.ts +8 -0
- package/src/prompt-library/session-guidance.ts +14 -0
- package/src/provider-summary.ts +8 -0
- package/src/pty-shell/herdr-agent-report.ts +118 -9
- package/src/pty-shell/herdr-prompt.ts +266 -0
- package/src/pty-shell/pty-control-ipc.ts +151 -8
- package/src/pty-shell/pty-special-keys.ts +42 -2
- package/src/pty-shell/stale-install-check.ts +99 -0
- package/src/pty-shell/tui-run-state.ts +14 -2
- package/src/release-loop/auto-start.ts +20 -2
- package/src/release-loop/checklist.ts +76 -4
- package/src/release-loop/rehearsal.ts +36 -20
- package/src/repl/index.ts +19 -1
- package/src/seat-loop/knowhow-lint.ts +1 -1
- package/src/self-dev/decompose-proposal.ts +172 -20
- package/src/self-dev/decompose.ts +61 -0
- package/src/self-dev/dev-cli.ts +37 -3
- package/src/self-dev/dev-pipeline.ts +8 -2
- package/src/self-dev/draft-triage-rules.ts +31 -0
- package/src/self-dev/glm-credential-guard.ts +77 -0
- package/src/self-dev/graph-journey-nodes.ts +1 -0
- package/src/self-dev/graph-journey-route.ts +12 -2
- package/src/self-dev/orchestrate-cli.ts +68 -3
- package/src/self-dev/orchestrate.ts +116 -17
- package/src/self-dev/run-supervisor.ts +55 -2
- package/src/self-dev/run-types.ts +1 -1
- package/src/self-health/autoheal.ts +153 -0
- package/src/self-health/signatures.ts +6 -0
- package/src/self-implement/device-first.ts +116 -0
- package/src/self-implement/doctor-visit.ts +239 -1
- package/src/self-implement/evidence-command-budget.ts +80 -0
- package/src/self-implement/goal-author-cli.ts +1 -0
- package/src/self-implement/goal-author-decompose.ts +265 -4
- package/src/self-implement/goal-author.ts +33 -8
- package/src/self-implement/goal-elaborate.ts +176 -22
- package/src/self-implement/goal-refine.ts +3 -0
- package/src/self-implement/graph-variant.ts +40 -6
- package/src/self-implement/headless-elanous-driver.ts +19 -3
- package/src/self-implement/off-diff-evidence.ts +58 -6
- package/src/self-implement/orchestrator.ts +222 -21
- package/src/self-implement/pod-warm-pool-profile.ts +1 -0
- package/src/self-implement/pwa-import-graph.ts +1 -1
- package/src/self-implement/rework-policy.ts +30 -0
- package/src/self-implement/run-ledger.ts +108 -0
- package/src/self-implement/run-ttl.ts +38 -24
- package/src/self-implement/scope-adjacency.ts +3 -1
- package/src/self-implement/seams.ts +60 -4
- package/src/self-implement/wiring-check.ts +358 -0
- package/src/shell-env-bootstrap.ts +11 -4
- package/src/skills/runner.ts +4 -2
- package/src/skills/tools/agent.ts +290 -48
- package/src/skills/tools/schedule-wakeup.ts +23 -4
- package/src/skills/tools/shell.ts +61 -6
- package/src/skills/tools/terminal-modal-inject.ts +1 -2
- package/src/skills/tools/terminal-modal.ts +10 -8
- package/src/skills/url-router.ts +41 -1
- package/src/smart-wizard/run.ts +66 -0
- package/src/status/bar.ts +34 -6
- package/src/status/status-provider-info.ts +37 -6
- package/src/steward/rejudge-tick.ts +10 -2
- package/src/steward/ta-repair-event-collect.ts +17 -6
- package/src/task-agent/actions.ts +3 -1
- package/src/task-agent/land-queue.ts +21 -8
- package/src/task-agent/live-moves.ts +22 -6
- package/src/task-agent/mission.ts +30 -0
- package/src/task-agent/rejudge-on-head.ts +15 -0
- package/src/task-agent/repair-cycle.ts +138 -32
- package/src/task-agent/repair-next-move.ts +31 -3
- package/src/task-agent/repair-outcomes.ts +29 -0
- package/src/task-agent/repair-worker.ts +14 -4
- package/src/task-agent/shadow.ts +41 -3
- package/src/task-agent/task-card-prefix.ts +2 -0
- package/src/task-agent/task-hand.ts +477 -22
- package/src/task-agent/task-parent-role-llm.ts +36 -0
- package/src/task-agent/tier-split.ts +78 -9
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +9 -0
- package/src/task-orchestrator/surfaces/pod-child-reuse.ts +146 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +86 -5
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +215 -3
- package/src/task-orchestrator/surfaces/pod-launch-history.ts +133 -0
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +11 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +76 -4
- package/src/task-orchestrator/surfaces/pod-provenance.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-repair-commit.ts +18 -3
- package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +16 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-host.ts +224 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +201 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-provider.ts +444 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-runner.ts +387 -0
- package/src/task-orchestrator/surfaces/pod-si-warm-transport.ts +157 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +485 -36
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +3 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +396 -19
- package/src/task-orchestrator/surfaces/self-implement.ts +42 -2
- package/src/tool-runtime/index.ts +15 -2
- package/src/tool-runtime/mcp-proxy-bootstrap.ts +29 -0
- package/src/tty-foreground.ts +155 -0
- package/src/tui/chat/goal-interim.ts +173 -0
- package/src/tui/chat/log-view.ts +205 -18
- package/src/tui/chat/self-health-status.ts +232 -0
- package/src/tui/entry.ts +370 -81
- package/src/tui/input/composer.ts +8 -1
- package/src/tui/slash/compact-command.ts +7 -0
- package/src/tui/slash/compact.ts +1 -7
- package/src/tui/slash/resume.ts +75 -0
- package/src/tui-client/local-chat-turn.ts +45 -2
- package/src/tui-client/next-dashboard-runtime.ts +8 -0
- package/src/tui-client/next-loop-command.ts +70 -0
- package/src/tui-client/next-wakeup.ts +191 -0
- package/src/tui.ts +37 -0
- package/src/ui/modal-adapter.ts +2 -2
- package/src/ux-sim/classic-tui-seat-scenarios.ts +516 -0
- package/src/ux-sim/cli-config-audit-device.ts +121 -0
- package/src/ux-sim/dashboard-event-loop-stall-monitor-device.ts +238 -0
- package/src/ux-sim/event-monkey.ts +3 -2
- package/src/ux-sim/fake-bin.ts +45 -0
- package/src/ux-sim/fake-herdr.ts +238 -0
- package/src/ux-sim/fake-llm.ts +111 -6
- package/src/ux-sim/fault-glm-no-credential.ts +116 -0
- package/src/ux-sim/fault-nextmove-dispatch.ts +102 -1
- package/src/ux-sim/fault-scope-escape-lift.ts +33 -6
- package/src/ux-sim/fault-tui-400-autoheal.ts +297 -0
- package/src/ux-sim/goal-device.ts +619 -0
- package/src/ux-sim/in-process-cli.ts +22 -5
- package/src/ux-sim/mock-authoring-first-run-child.ts +35 -0
- package/src/ux-sim/mock-authoring-first-run.ts +97 -0
- package/src/ux-sim/mock-seat-pty-frame-child.ts +29 -0
- package/src/ux-sim/mock-seat-pty-frame.ts +297 -0
- package/src/ux-sim/mock-seat-pty-sequence-child.ts +21 -0
- package/src/ux-sim/mock-seat-pty-sequence.ts +206 -0
- package/src/ux-sim/next-tui-seat-scenarios.ts +631 -0
- package/src/ux-sim/onboarding-first-run-device.ts +269 -0
- package/src/ux-sim/pty-key-modifiers-device.ts +85 -0
- package/src/ux-sim/subagent-tools-device.ts +81 -0
- package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +250 -0
- package/src/ux-sim/tty-foreground-steal-child.ts +21 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +0 -1
- package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +0 -32
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +0 -41
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +0 -74
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1993-c0007b7ad0c0ef41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1c735de4.f993a30bbd3c57c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +0 -148
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2625.5d870aa999f33db1.js +0 -34
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +0 -77
- package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +0 -62
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +0 -90
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +0 -36
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +0 -79
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +0 -122
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4441-875b97f5821ed58a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +0 -24
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5154.a118f3957ed14e36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +0 -23
- package/apps/pwa/out/_next/static/chunks/5483-dd5d7697f6bdb106.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +0 -287
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +0 -1
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +0 -8
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +0 -93
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6b89c8bd.24df687a820500b3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +0 -149
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +0 -104
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8219.baa715d1deee80e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8256.e3ac8421a71f82f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +0 -226
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +0 -64
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +0 -115
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/967-2aa39b2d04b4c690.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-406b47745b53f269.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-47c5d4db587266bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-dc0a532f5b9196ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +0 -1
- /package/apps/pwa/out/_next/static/{T1mBQqmkwKDLGtyisDCOd → uC-N0emedeNOFuaBQFv-I}/_ssgManifest.js +0 -0
|
@@ -9,6 +9,7 @@ export type NodeVerdict = 'pass' | 'flaky' | 'fail';
|
|
|
9
9
|
export interface GraphContext {
|
|
10
10
|
nodeId?: string;
|
|
11
11
|
runId?: string;
|
|
12
|
+
startedAt?: string;
|
|
12
13
|
input: { version: string; previousVersion: string; [key: string]: unknown };
|
|
13
14
|
outputs: Record<string, Record<string, unknown>>;
|
|
14
15
|
}
|
|
@@ -24,6 +25,7 @@ export function readGraphContext(env: NodeJS.ProcessEnv = process.env): GraphCon
|
|
|
24
25
|
|| typeof input.previousVersion !== 'string' || !/^\d+\.\d+\.\d+$/.test(input.previousVersion)) throw new Error('version and previousVersion required');
|
|
25
26
|
return { ...(typeof context.nodeId === 'string' ? { nodeId: context.nodeId } : {}),
|
|
26
27
|
...(typeof context.runId === 'string' ? { runId: context.runId } : {}),
|
|
28
|
+
...(typeof context.startedAt === 'string' ? { startedAt: context.startedAt } : {}),
|
|
27
29
|
input: input as GraphContext['input'], outputs: context.outputs && typeof context.outputs === 'object' ? context.outputs as GraphContext['outputs'] : {} };
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -1,56 +1,36 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { DecisionLedger } from '../../src/decisions/decision-ledger.js';
|
|
5
4
|
import type { GraphRunState } from '../../src/graph-runner/runner.js';
|
|
6
5
|
import { isReleaseVersion } from './release-version.js';
|
|
7
6
|
|
|
8
|
-
export const RELEASE_RESUME_LIMIT = 2;
|
|
9
|
-
|
|
10
7
|
/** Called under the OP seat flock, before any branch-tip or graph-run mutation. */
|
|
11
8
|
export function reserveReleaseResumeAttempt(state: GraphRunState, root: string, options: {
|
|
12
9
|
now?: () => Date;
|
|
13
|
-
|
|
14
|
-
/** Under the OP flock, check capacity before tip preflight without consuming an attempt. */
|
|
10
|
+
/** Under the OP flock, preview the next observed count before tip preflight. */
|
|
15
11
|
checkOnly?: boolean;
|
|
16
|
-
|
|
17
|
-
noDecision?: boolean;
|
|
18
|
-
} = {}): { attempt: number; path: string } {
|
|
12
|
+
} = {}): { attempt: number; path: string; priorCountKnown: boolean } {
|
|
19
13
|
const version = (state.input as { version?: unknown } | undefined)?.version;
|
|
20
14
|
if (typeof version !== 'string' || !isReleaseVersion(version) || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(state.runId))
|
|
21
|
-
throw new Error('release resume
|
|
15
|
+
throw new Error('release resume count requires a valid saved version and run id');
|
|
22
16
|
const path = join(root, 'release', version, 'resume-attempts', `${state.runId}.json`);
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
return new DecisionLedger({ stateDir: root, now: options.now }).raiseOnce({
|
|
26
|
-
title: `${v} release resume limit: ${id}`, category: 'scope',
|
|
27
|
-
scqa: { s: `Release ${v} run ${id} has reached its two-resume limit.`, c: reason,
|
|
28
|
-
q: 'Should this release be deferred or moved to the next version?' },
|
|
29
|
-
options: [{ key: 'a', label: 'Defer this release', consequence: 'Stop this run and reschedule the release.' },
|
|
30
|
-
{ key: 'b', label: 'Move to next version', consequence: 'Stop this run and carry the work to a new release.' }],
|
|
31
|
-
recommendation: { skipped: true, reason: 'OP must choose the release disposition.' },
|
|
32
|
-
raisedBy: { agent: 'release-resume-cap', track: 'TC' }, refs: [ref],
|
|
33
|
-
}, ref).id;
|
|
34
|
-
});
|
|
35
|
-
const blocked = (reason: string): never => {
|
|
36
|
-
if (options.noDecision) throw new Error(`release resume limit reached: ${reason}`);
|
|
37
|
-
throw new Error(`release resume limit reached — decision ${card(version, state.runId, reason)}: ${reason}`);
|
|
38
|
-
};
|
|
39
|
-
if (options.noDecision && !options.checkOnly) throw new Error('noDecision requires checkOnly');
|
|
40
|
-
if (!existsSync(path) && state.resume) blocked('saved run was resumed before attempt counting; prior count is unknown');
|
|
41
|
-
const prior = existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) as unknown : { version, runId: state.runId, attempts: [] };
|
|
17
|
+
const hadLedger = existsSync(path);
|
|
18
|
+
const prior = hadLedger ? JSON.parse(readFileSync(path, 'utf8')) as unknown : { version, runId: state.runId, attempts: [] };
|
|
42
19
|
if (!prior || typeof prior !== 'object' || Array.isArray(prior)) throw new Error('release resume attempts ledger invalid');
|
|
43
|
-
const saved = prior as { version?: unknown; runId?: unknown; attempts?: unknown };
|
|
20
|
+
const saved = prior as { version?: unknown; runId?: unknown; attempts?: unknown; priorCountKnown?: unknown };
|
|
44
21
|
if (saved.version !== version || saved.runId !== state.runId || !Array.isArray(saved.attempts)
|
|
45
|
-
|| saved.attempts.some(value => typeof value !== 'string' || !Number.isFinite(Date.parse(value)))
|
|
22
|
+
|| saved.attempts.some(value => typeof value !== 'string' || !Number.isFinite(Date.parse(value)))
|
|
23
|
+
|| (saved.priorCountKnown !== undefined && typeof saved.priorCountKnown !== 'boolean'))
|
|
46
24
|
throw new Error('release resume attempts ledger invalid');
|
|
47
|
-
|
|
48
|
-
|
|
25
|
+
// Existing ledgers predate this field and began before the first resume.
|
|
26
|
+
// A legacy resumed run with no ledger must carry its unknown history forward.
|
|
27
|
+
const priorCountKnown = hadLedger ? saved.priorCountKnown !== false : !state.resume;
|
|
28
|
+
if (options.checkOnly) return { attempt: saved.attempts.length + 1, path, priorCountKnown };
|
|
49
29
|
const at = (options.now ?? (() => new Date()))().toISOString();
|
|
50
|
-
const next = { version, runId: state.runId, attempts: [...saved.attempts, at] };
|
|
30
|
+
const next = { version, runId: state.runId, priorCountKnown, attempts: [...saved.attempts, at] };
|
|
51
31
|
mkdirSync(dirname(path), { recursive: true });
|
|
52
32
|
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
53
33
|
try { writeFileSync(temp, `${JSON.stringify(next)}\n`, { mode: 0o600 }); renameSync(temp, path); }
|
|
54
34
|
finally { try { unlinkSync(temp); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; } }
|
|
55
|
-
return { attempt: next.attempts.length, path };
|
|
35
|
+
return { attempt: next.attempts.length, path, priorCountKnown };
|
|
56
36
|
}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// The saved version-release output is re-pointed at the new branch tip (fast-forward only, state backed up first), so
|
|
3
3
|
// every later node reads the repaired commit without anyone hand-editing the run state.
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
5
6
|
import { constants, copyFileSync, existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
6
7
|
import { join } from 'node:path';
|
|
7
8
|
import { debug } from '../../src/debug/log.js';
|
|
8
9
|
import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
|
|
9
|
-
import { lastJsonObject, runGraph, verifyReleaseCommandRoot, type GraphRunState } from '../../src/graph-runner/runner.js';
|
|
10
|
+
import { lastJsonObject, runGraph, verifyReleaseCommandRoot, verifyReleaseRecipeSnapshot, type GraphRunState } from '../../src/graph-runner/runner.js';
|
|
10
11
|
import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
|
|
11
12
|
import { releaseRunUniverse } from './release-universe.js';
|
|
12
13
|
import { isReleaseVersion } from './release-version.js';
|
|
@@ -47,7 +48,7 @@ export interface ResumeReleaseOptions {
|
|
|
47
48
|
export interface ResumeReleasePlan {
|
|
48
49
|
runId: string; version: string; fromNode: string; commandRoot: string; tip: TipRefresh;
|
|
49
50
|
partial: PartialOutcome; gatePlan: { mode: 'not-run' | 'partial' | 'full'; reason?: string };
|
|
50
|
-
nextAttempt: number;
|
|
51
|
+
nextAttempt: number; priorCountKnown: boolean; preflight: 'passed';
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
|
|
@@ -84,7 +85,7 @@ export function refreshReleaseBranchTip(runId: string, deps: ResumeReleaseDeps =
|
|
|
84
85
|
return refreshReleaseBranchTipInternal(runId, deps, from, waiverCommit, false);
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
function refreshReleaseBranchTipInternal(runId: string, deps: ResumeReleaseDeps, from: string | undefined, waiverCommit: string | undefined, dryRun: boolean): TipRefresh {
|
|
88
|
+
function refreshReleaseBranchTipInternal(runId: string, deps: ResumeReleaseDeps, from: string | undefined, waiverCommit: string | undefined, dryRun: boolean, expectedTip?: string): TipRefresh {
|
|
88
89
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
89
90
|
const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(runId)}.json`);
|
|
90
91
|
const state = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
@@ -108,6 +109,7 @@ function refreshReleaseBranchTipInternal(runId: string, deps: ResumeReleaseDeps,
|
|
|
108
109
|
const git = deps.git ?? gitRunner(deps.repo ?? process.cwd());
|
|
109
110
|
const advertised = git(['ls-remote', '--heads', 'origin', `refs/heads/${branch}`]).split(/\s+/)[0] ?? '';
|
|
110
111
|
if (!/^[0-9a-f]{40}$/i.test(advertised)) throw new Error(`${branch} is not on origin — this run was not cut as a release branch`);
|
|
112
|
+
if (expectedTip && advertised.toLowerCase() !== expectedTip.toLowerCase()) throw new Error(`${branch} moved during pin preflight (${expectedTip} → ${advertised}) — retry resume`);
|
|
111
113
|
let to: string;
|
|
112
114
|
if (dryRun) {
|
|
113
115
|
// Use the advertised object id directly: a preflight must not create/update local remote-tracking refs.
|
|
@@ -152,6 +154,26 @@ function refreshReleaseBranchTipInternal(runId: string, deps: ResumeReleaseDeps,
|
|
|
152
154
|
return { version, branch, from: recorded, to, changed: true, statePath, backup };
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
/** Check the saved graph and recipes before fetch, backup, or run-ledger writes. */
|
|
158
|
+
function verifyResumeRecipePin(commandRoot: string, snapshot: GraphRunState['graphSnapshot'], version: string): void {
|
|
159
|
+
try { verifyReleaseRecipeSnapshot(commandRoot, snapshot); }
|
|
160
|
+
catch (error) {
|
|
161
|
+
if (!snapshot) throw error;
|
|
162
|
+
const mismatches: string[] = [];
|
|
163
|
+
for (const [name, savedSha] of [
|
|
164
|
+
['release-loop.yaml', snapshot.graphSha], ['recipes.yaml', snapshot.recipesSha],
|
|
165
|
+
] as const) {
|
|
166
|
+
const file = join(commandRoot, 'graphs', 'release', name);
|
|
167
|
+
let currentSha: string;
|
|
168
|
+
try { currentSha = createHash('sha256').update(readFileSync(file)).digest('hex'); }
|
|
169
|
+
catch { continue; }
|
|
170
|
+
if (currentSha !== savedSha) mismatches.push(`${file} saved=${savedSha} current=${currentSha}`);
|
|
171
|
+
}
|
|
172
|
+
if (!mismatches.length) throw error;
|
|
173
|
+
throw new Error(`release recipe source differs from saved pin: ${mismatches.join('; ')}\nremedy: start a new pinned run with eln release run --version ${version}`, { cause: error });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
155
177
|
/** GATE-PARTIAL outcome of a resume — applied (plan written) or the reason it fell back to a full gate. `auto` marks the
|
|
156
178
|
* default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
|
|
157
179
|
export type PartialOutcome = { requested: false }
|
|
@@ -318,7 +340,7 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
|
|
|
318
340
|
const waiver = original && (opts.waive !== undefined || opts.reason !== undefined) ? releaseResumeWaiver(original, opts) : undefined;
|
|
319
341
|
if (original && opts.acceptedRegressions !== undefined && !waiver)
|
|
320
342
|
releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from);
|
|
321
|
-
const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now, checkOnly: true
|
|
343
|
+
const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now, checkOnly: true });
|
|
322
344
|
let waiverCommit: string | undefined;
|
|
323
345
|
if (waiver) {
|
|
324
346
|
const output = lastJsonObject(savedRun.nodes.find((node) => node.nodeId === 'version-release')?.output);
|
|
@@ -331,6 +353,7 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
|
|
|
331
353
|
waiverCommit = recorded;
|
|
332
354
|
}
|
|
333
355
|
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
|
|
356
|
+
verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, tip.version);
|
|
334
357
|
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
|
|
335
358
|
const partial = preparePartial(tip, opts, deps, true);
|
|
336
359
|
const fromIndex = savedRun.path.indexOf(opts.from);
|
|
@@ -340,7 +363,7 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
|
|
|
340
363
|
: partial.requested && partial.applied ? { mode: 'partial' as const }
|
|
341
364
|
: { mode: 'full' as const, reason: partial.requested ? partial.reason : opts.partial === false ? 'partial recheck disabled' : 'no partial plan applies' };
|
|
342
365
|
return { runId: opts.runId, version: tip.version, fromNode: opts.from, commandRoot, tip, partial,
|
|
343
|
-
gatePlan, nextAttempt: attempt.attempt,
|
|
366
|
+
gatePlan, nextAttempt: attempt.attempt, priorCountKnown: attempt.priorCountKnown, preflight: 'passed' };
|
|
344
367
|
}
|
|
345
368
|
|
|
346
369
|
async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
|
|
@@ -368,7 +391,7 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
368
391
|
const acceptedRegressions = original && opts.acceptedRegressions !== undefined && !waiver
|
|
369
392
|
? releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from) : undefined;
|
|
370
393
|
if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
|
|
371
|
-
// Validate request arguments before the
|
|
394
|
+
// Validate request arguments before the tip refresh; the count is observational.
|
|
372
395
|
reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now, checkOnly: true });
|
|
373
396
|
// A waived verdict may reuse its original commit. A moved tip still requires a full re-gate.
|
|
374
397
|
if (waiver) {
|
|
@@ -383,13 +406,16 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
383
406
|
if (advertised?.toLowerCase() !== recorded.toLowerCase()) throw new Error('release branch moved — re-gate the new tip before waiving a node');
|
|
384
407
|
waiverCommit = recorded;
|
|
385
408
|
}
|
|
386
|
-
//
|
|
387
|
-
const
|
|
388
|
-
|
|
409
|
+
// The read-only tip and recipe pin preflight must pass before fetch or a saved-run rewrite.
|
|
410
|
+
const previewTip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
|
|
411
|
+
verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, previewTip.version);
|
|
412
|
+
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), previewTip.to, deps.git);
|
|
413
|
+
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, false, previewTip.to);
|
|
389
414
|
const partial = preparePartial(tip, opts, deps);
|
|
390
|
-
// A failed preflight does not consume
|
|
415
|
+
// A failed preflight does not consume an observed release resume attempt.
|
|
391
416
|
const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now });
|
|
392
|
-
debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt,
|
|
417
|
+
debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt,
|
|
418
|
+
priorCountKnown: attempt.priorCountKnown, ledger: attempt.path });
|
|
393
419
|
// Resume the graph at the path it was started from (its snapshot is used); only tests inject another path.
|
|
394
420
|
const graphPath = deps.graphPath ?? savedGraphPath(tip.statePath) ?? GRAPH;
|
|
395
421
|
const savedSource = JSON.parse(readFileSync(tip.statePath, 'utf8')) as GraphRunState;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { debug } from '../../src/debug/log.js';
|
|
4
|
+
import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
|
|
5
|
+
import { releaseLoopConfig } from './unattended-release.js';
|
|
6
|
+
import { errorResult, finishNode, readGraphContext } from './node-verdict.js';
|
|
7
|
+
import { applyReleaseTrafficEvent, trafficOptionsFromConfig, type ReleaseTrafficEvent, type TrafficTransitionOptions } from './traffic-transition.js';
|
|
8
|
+
|
|
9
|
+
const EVENTS: Record<string, ReleaseTrafficEvent> = {
|
|
10
|
+
'traffic-gate-passed': 'gate-passed',
|
|
11
|
+
'traffic-verify-complete': 'verify-complete',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface TrafficNodeDeps {
|
|
15
|
+
options?: TrafficTransitionOptions;
|
|
16
|
+
report?: (body: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function reportFailure(body: string): void {
|
|
20
|
+
const result = spawnSync('eln', ['msg', 'post', '--from', 'MK', '--to', 'OP', '--kind', 'report', '--body', body], { encoding: 'utf8', timeout: 10_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
21
|
+
if (result.status !== 0 || result.error) process.stderr.write(`traffic OP mail failed: ${result.stderr || result.error || result.status}\n`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Traffic transitions are warning-only for the release graph, but their node result remains truthful. */
|
|
25
|
+
export async function runTrafficNode(node = process.argv[2], env: NodeJS.ProcessEnv = process.env, deps: TrafficNodeDeps = {}): Promise<number> {
|
|
26
|
+
let version = '';
|
|
27
|
+
let runId = '';
|
|
28
|
+
try {
|
|
29
|
+
const context = readGraphContext(env);
|
|
30
|
+
version = context.input.version;
|
|
31
|
+
runId = context.runId ?? '';
|
|
32
|
+
const event = EVENTS[node ?? ''];
|
|
33
|
+
if (!event || context.nodeId !== node) throw new Error(`traffic node context mismatch: ${node ?? 'missing'}`);
|
|
34
|
+
const options = deps.options ?? trafficOptionsFromConfig(env, releaseLoopConfig());
|
|
35
|
+
if (!options.trafficFile) {
|
|
36
|
+
const summary = `traffic-file-unset: ${event} skipped because trafficFile is not configured`;
|
|
37
|
+
debug.log('release-loop.traffic', 'traffic-file-unset', { runId, version, event, node, reason: summary });
|
|
38
|
+
(deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version} ${runId || 'unknown'} ${node} ${summary}. Release traffic was not changed.`);
|
|
39
|
+
return finishNode(node!, version, { outcome: 'fail', verdict: 'fail', summary });
|
|
40
|
+
}
|
|
41
|
+
const result = await applyReleaseTrafficEvent(context, event, options);
|
|
42
|
+
if (!result.applied) {
|
|
43
|
+
const consequence = event === 'verify-complete'
|
|
44
|
+
? '릴리스 검증은 성공했으나 트래픽 복귀가 적용되지 않았습니다.'
|
|
45
|
+
: 'gate는 성공했으나 트래픽 감속이 적용되지 않았습니다.';
|
|
46
|
+
(deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version} ${runId || 'unknown'} ${node} 무시 — ${result.reason ?? 'traffic state unchanged'}. ${consequence}`);
|
|
47
|
+
}
|
|
48
|
+
return finishNode(node!, version, { outcome: 'ok', verdict: 'pass', summary: `${event} ${result.applied ? 'applied' : result.reason ?? 'ignored'}` });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
const detail = errorResult(error).summary;
|
|
51
|
+
const isLockTimeout = detail.startsWith('traffic lock timeout:');
|
|
52
|
+
const failure = isLockTimeout ? `traffic-lock-timeout · stale 의심 경로 ${detail.slice('traffic lock timeout:'.length).trim()}` : detail;
|
|
53
|
+
debug.log('release-loop.traffic', 'non-blocking-failure', { node: node ?? 'missing', version, reason: detail });
|
|
54
|
+
(deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version || 'unknown'} ${runId || 'unknown'} ${node ?? 'unknown'} 실패 — ${failure}. 릴리스 판정은 계속 진행한다.`);
|
|
55
|
+
return finishNode(node ?? 'traffic', version, { outcome: 'fail', verdict: 'fail', summary: `traffic transition failed; release graph continues with warning: ${failure}` });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (import.meta.main) process.exitCode = await runTrafficNode();
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { debug } from '../../src/debug/log.js';
|
|
4
|
+
import type { GraphContext } from './node-verdict.js';
|
|
5
|
+
import type { ReleaseLoopConfig } from './unattended-release.js';
|
|
6
|
+
|
|
7
|
+
export type ReleaseTrafficEvent = 'gate-passed' | 'verify-complete';
|
|
8
|
+
|
|
9
|
+
export interface TrafficFile {
|
|
10
|
+
global?: string;
|
|
11
|
+
seats: Record<string, Record<string, unknown>>;
|
|
12
|
+
releaseLifecycle?: { runId: string; version: string; stage: 1 | 2; event: ReleaseTrafficEvent; startedAt?: string; updatedAt: string };
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ReleaseTrafficConfig = ReleaseLoopConfig & {
|
|
17
|
+
trafficFile?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
|
|
21
|
+
const STAGE: Record<ReleaseTrafficEvent, 1 | 2> = { 'gate-passed': 1, 'verify-complete': 2 };
|
|
22
|
+
const TRAFFIC_PER_CYCLE = { half: 3, full: 6 } as const;
|
|
23
|
+
|
|
24
|
+
export interface TrafficTransitionOptions {
|
|
25
|
+
trafficFile?: string;
|
|
26
|
+
lockTimeoutMs?: number;
|
|
27
|
+
now?: () => Date;
|
|
28
|
+
log?: (event: string, fields: Record<string, unknown>) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function waitSync(ms: number): void {
|
|
32
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function acquireTrafficLock(path: string, timeoutMs = 10_000): () => void {
|
|
36
|
+
const lock = `${path}.lock.d`;
|
|
37
|
+
const deadline = Date.now() + timeoutMs;
|
|
38
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
39
|
+
try {
|
|
40
|
+
mkdirSync(lock);
|
|
41
|
+
try { writeFileSync(`${lock}/pid`, `${process.pid}\n`, { flag: 'wx', mode: 0o600 }); }
|
|
42
|
+
catch (error) { rmSync(lock, { recursive: true, force: true }); throw error; }
|
|
43
|
+
return () => rmSync(lock, { recursive: true, force: true });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
46
|
+
try {
|
|
47
|
+
// A directory cannot be conditionally renamed by identity. Reclaiming after
|
|
48
|
+
// checking its PID can therefore move a replacement owner's live lock.
|
|
49
|
+
// Fail closed; OP can inspect and remove a stale lock after the warning mail.
|
|
50
|
+
readFileSync(`${lock}/pid`, 'utf8');
|
|
51
|
+
statSync(lock);
|
|
52
|
+
} catch { /* Unknown or concurrently changing locks are left intact. */ }
|
|
53
|
+
if (Date.now() >= deadline) throw new Error(`traffic lock timeout: ${lock}`);
|
|
54
|
+
waitSync(100);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readTrafficFile(path: string): TrafficFile {
|
|
60
|
+
const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
|
|
61
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('traffic file must contain a JSON object');
|
|
62
|
+
const file = parsed as TrafficFile;
|
|
63
|
+
if (!file.seats || typeof file.seats !== 'object' || Array.isArray(file.seats)
|
|
64
|
+
|| !SEATS.every((seat) => file.seats[seat] && typeof file.seats[seat] === 'object' && !Array.isArray(file.seats[seat]))) {
|
|
65
|
+
throw new Error('traffic seats object missing');
|
|
66
|
+
}
|
|
67
|
+
return file;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function atomicWriteTrafficFile(path: string, file: TrafficFile): void {
|
|
71
|
+
const temp = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
72
|
+
const mode = statSync(path).mode & 0o777;
|
|
73
|
+
try {
|
|
74
|
+
writeFileSync(temp, `${JSON.stringify(file, null, 2)}\n`, { flag: 'wx', mode });
|
|
75
|
+
renameSync(temp, path);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
rmSync(temp, { force: true });
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function compareReleaseVersions(left: string, right: string): number {
|
|
83
|
+
const parse = (value: string) => /^(\d+)\.(\d+)\.(\d+)(?:-(alpha|beta|rc)\.(\d+))?$/.exec(value);
|
|
84
|
+
const a = parse(left), b = parse(right);
|
|
85
|
+
if (!a || !b) return left.localeCompare(right);
|
|
86
|
+
for (const index of [1, 2, 3]) {
|
|
87
|
+
const delta = Number(a[index]) - Number(b[index]);
|
|
88
|
+
if (delta) return Math.sign(delta);
|
|
89
|
+
}
|
|
90
|
+
if (!a[4] || !b[4]) return a[4] ? -1 : b[4] ? 1 : 0;
|
|
91
|
+
const rank = { alpha: 0, beta: 1, rc: 2 } as const;
|
|
92
|
+
const kind = rank[a[4] as keyof typeof rank] - rank[b[4] as keyof typeof rank];
|
|
93
|
+
return kind || Math.sign(Number(a[5]) - Number(b[5]));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isLaterRun(current: string | undefined, previous: string | undefined): boolean {
|
|
97
|
+
return Boolean(current && previous && current > previous);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Apply one monotonic release graph event while preserving unrelated traffic policy fields. */
|
|
101
|
+
export async function applyReleaseTrafficEvent(
|
|
102
|
+
context: GraphContext,
|
|
103
|
+
event: ReleaseTrafficEvent,
|
|
104
|
+
options: TrafficTransitionOptions,
|
|
105
|
+
): Promise<{ applied: boolean; stage: number; reason?: string }> {
|
|
106
|
+
if (!context.runId) throw new Error('release traffic event requires graph runId');
|
|
107
|
+
if (!options.trafficFile) throw new Error('traffic file path is not configured');
|
|
108
|
+
const runId = context.runId;
|
|
109
|
+
const version = context.input.version;
|
|
110
|
+
const path = resolve(options.trafficFile);
|
|
111
|
+
if (!existsSync(path)) throw new Error(`traffic file does not exist: ${path}`);
|
|
112
|
+
const log = options.log ?? ((name, fields) => debug.log('release-loop.traffic', name, fields));
|
|
113
|
+
const stage = STAGE[event];
|
|
114
|
+
const stamp = (options.now ?? (() => new Date()))().toISOString();
|
|
115
|
+
const rate = event === 'gate-passed' ? TRAFFIC_PER_CYCLE.half : TRAFFIC_PER_CYCLE.full;
|
|
116
|
+
const releaseLifecycle = { runId, version, stage, event, ...(context.startedAt ? { startedAt: context.startedAt } : {}), updatedAt: stamp };
|
|
117
|
+
const release = () => {
|
|
118
|
+
const unlock = acquireTrafficLock(path, options.lockTimeoutMs);
|
|
119
|
+
try {
|
|
120
|
+
const before = readTrafficFile(path);
|
|
121
|
+
const prior = before.releaseLifecycle;
|
|
122
|
+
const sameRun = prior?.runId === runId;
|
|
123
|
+
const newerVersion = !prior?.runId || compareReleaseVersions(version, prior.version) > 0;
|
|
124
|
+
const sameVersionLaterRun = prior?.version === version && isLaterRun(context.startedAt, prior.startedAt);
|
|
125
|
+
if (sameRun && prior.stage >= stage) return { file: before, applied: true, idempotent: true };
|
|
126
|
+
const permitted = sameRun
|
|
127
|
+
? prior.stage === stage - 1
|
|
128
|
+
: newerVersion || (event === 'gate-passed' && prior?.version === version && (prior.stage ?? 0) < 2 && sameVersionLaterRun)
|
|
129
|
+
|| (event === 'verify-complete' && prior?.version === version && sameVersionLaterRun);
|
|
130
|
+
if (!permitted) {
|
|
131
|
+
return { file: before, applied: false, idempotent: false, reason: prior?.runId && prior.runId !== runId ? 'different-run-is-current' : 'event-out-of-order' };
|
|
132
|
+
}
|
|
133
|
+
const after: TrafficFile = {
|
|
134
|
+
...before,
|
|
135
|
+
...(event === 'verify-complete' ? { global: 'green' } : {}),
|
|
136
|
+
seats: Object.fromEntries(Object.entries(before.seats).map(([seat, value]) => [
|
|
137
|
+
seat, SEATS.includes(seat as typeof SEATS[number]) ? { ...value, perCycle: rate } : value,
|
|
138
|
+
])),
|
|
139
|
+
releaseLifecycle,
|
|
140
|
+
};
|
|
141
|
+
atomicWriteTrafficFile(path, after);
|
|
142
|
+
return { file: after, applied: true, idempotent: false };
|
|
143
|
+
} finally { unlock(); }
|
|
144
|
+
};
|
|
145
|
+
const result = release();
|
|
146
|
+
const reason = result.applied && result.idempotent ? 'already-applied' : result.reason;
|
|
147
|
+
log(result.idempotent ? 'already-applied' : result.applied ? 'applied' : 'ignored', {
|
|
148
|
+
runId, version, event, stage, reason, perCycle: rate, ...(event === 'verify-complete' ? { global: 'green' } : {}),
|
|
149
|
+
});
|
|
150
|
+
return { applied: result.applied, stage: result.file.releaseLifecycle?.stage ?? 0, ...(reason ? { reason } : {}) };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function trafficOptionsFromConfig(env: NodeJS.ProcessEnv = process.env, rawConfig: ReleaseLoopConfig = {}): TrafficTransitionOptions {
|
|
154
|
+
const config = rawConfig as ReleaseTrafficConfig;
|
|
155
|
+
const envTrafficFile = env.ELANOUS_RELEASE_TRAFFIC_FILE?.trim();
|
|
156
|
+
const configTrafficFile = typeof config.trafficFile === 'string' ? config.trafficFile.trim() : undefined;
|
|
157
|
+
const trafficFile = envTrafficFile || configTrafficFile;
|
|
158
|
+
return trafficFile ? { trafficFile } : {};
|
|
159
|
+
}
|
package/scripts/rhythm/daily.ts
CHANGED
|
@@ -15,7 +15,10 @@ import { loopEvent } from '../../src/loops/observe.js';
|
|
|
15
15
|
import { kindRouteTarget, mainHomeTarget } from '../../src/domains/telegram-kind-route.js';
|
|
16
16
|
import { recordOutbound } from '../../src/domains/outbound-alert.js';
|
|
17
17
|
import { listSelfDevRuns, runSummaryLine, selfDevRunsDir } from '../../src/self-dev/run-store.js';
|
|
18
|
-
import { runDraftSweep } from '../../src/self-dev/draft-sweep.js';
|
|
18
|
+
import { runDraftSweep, sweptReasonLabel } from '../../src/self-dev/draft-sweep.js';
|
|
19
|
+
import { LogStore } from '../../src/mss/logging/log-store.js';
|
|
20
|
+
import { BriefItemsLedger } from '../../src/briefing/brief-items.js';
|
|
21
|
+
import { debug } from '../../src/debug/log.js';
|
|
19
22
|
import { retroBriefingLines } from '../../src/retro/daily.js';
|
|
20
23
|
import { dispatchCooAdmin } from '../../src/domains/coo-admin-tool.js';
|
|
21
24
|
|
|
@@ -76,6 +79,133 @@ export function collectRunSummaries(now: Date, root = effectiveInstanceRoot(), l
|
|
|
76
79
|
prNumbers }));
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
/**
|
|
83
|
+
* DRAFT-SWEEP-DAILY-SUMMARY — 닫힌 draft 사유를 «하루 한 번 브리핑 원장 1건» 으로 적는다.
|
|
84
|
+
* 사유의 정본은 DRAFT-SWEEP-QUIET 이 닫을 때 남기는 `closed` 로그(PR 댓글 0 계약은 그쪽이 지킨다)이고,
|
|
85
|
+
* 이 경로는 로그 원장 «읽기» ⊕ 브리핑 원장 «쓰기» 만 한다 — GitHub 에 아무것도 쓰지 않는다.
|
|
86
|
+
*/
|
|
87
|
+
export const DRAFT_CLOSE_BRIEF_SOURCE = 'rhythm.daily.draft-close';
|
|
88
|
+
/** DRAFT-SWEEP-QUIET 가 닫힌 draft마다 남기는 로그 — 사유의 정본 좌표. */
|
|
89
|
+
const SWEPT_DRAFT_LOG = { category: 'self-dev.draft-sweep', event: 'closed' } as const;
|
|
90
|
+
const SWEPT_LABEL_PREFIX = 'elanous:swept:';
|
|
91
|
+
|
|
92
|
+
export type ClosedDraftReason = { reason: string; count: number };
|
|
93
|
+
export type ClosedDraftCount = { closed: number; reasons: ClosedDraftReason[]; unreadable: number };
|
|
94
|
+
export type DraftCloseSummary = {
|
|
95
|
+
/** 요약이 다루는 날짜(어제, KST). */
|
|
96
|
+
day: string;
|
|
97
|
+
closed: number;
|
|
98
|
+
reasons: ClosedDraftReason[];
|
|
99
|
+
/** 브리핑 원장에 실린 한 줄 — 표본이 없으면 null. */
|
|
100
|
+
text: string | null;
|
|
101
|
+
state: 'recorded' | 'already-recorded' | 'unmeasured' | 'unreadable';
|
|
102
|
+
/** 미측정·실패 사유(한 줄). */
|
|
103
|
+
detail?: string;
|
|
104
|
+
itemId?: number;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** `closed` 로그 한 줄의 `{ reason }` — Pod 전달 래퍼(`originalData`)도 벗긴다. 못 읽으면 null. */
|
|
108
|
+
function closedLogReason(data: string | null): string | null {
|
|
109
|
+
if (typeof data !== 'string' || !data.trim()) return null;
|
|
110
|
+
let payload: unknown;
|
|
111
|
+
try { payload = JSON.parse(data); } catch { return null; }
|
|
112
|
+
let body = record(payload);
|
|
113
|
+
const forwarded = body.originalData;
|
|
114
|
+
if (forwarded !== undefined) {
|
|
115
|
+
if (typeof forwarded !== 'string') body = record(forwarded);
|
|
116
|
+
else { try { body = record(JSON.parse(forwarded)); } catch { return null; } }
|
|
117
|
+
}
|
|
118
|
+
const reason = string(body.reason).trim();
|
|
119
|
+
return reason ? shortReason(reason) : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** 사유를 DRAFT-SWEEP-QUIET 의 사유 종류로 접는다(`superseded-by #24123` → `superseded-by`). */
|
|
123
|
+
export function sweptReasonKind(reason: string): string {
|
|
124
|
+
const label = sweptReasonLabel(reason);
|
|
125
|
+
return label.startsWith(SWEPT_LABEL_PREFIX) ? label.slice(SWEPT_LABEL_PREFIX.length) : label;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 어제(KST 00:00–24:00) 닫힌 draft 사유를 로그 원장에서 센다.
|
|
130
|
+
* ⛔ 원장이 없거나 못 읽으면 null — «0건»이 아니라 «미측정»이다.
|
|
131
|
+
*/
|
|
132
|
+
export function collectClosedDrafts(root: string, now: Date): ClosedDraftCount | null {
|
|
133
|
+
const path = join(root, 'logs', 'logs.db');
|
|
134
|
+
if (!existsSync(path)) return null;
|
|
135
|
+
const from = new Date(`${kstDay(new Date(now.getTime() - 86_400_000))}T00:00:00+09:00`);
|
|
136
|
+
const store = LogStore.openReadOnly(path);
|
|
137
|
+
try {
|
|
138
|
+
const rows = store.queryAll({ exactCategories: [SWEPT_DRAFT_LOG.category], events: [SWEPT_DRAFT_LOG.event],
|
|
139
|
+
sinceMs: from.getTime(), untilMs: from.getTime() + 86_400_000 - 1 });
|
|
140
|
+
const counts = new Map<string, number>();
|
|
141
|
+
let unreadable = 0;
|
|
142
|
+
for (const row of rows) {
|
|
143
|
+
const reason = closedLogReason(row.data);
|
|
144
|
+
if (reason === null) { unreadable += 1; continue; }
|
|
145
|
+
const kind = sweptReasonKind(reason);
|
|
146
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
147
|
+
}
|
|
148
|
+
return { closed: [...counts.values()].reduce((sum, count) => sum + count, 0),
|
|
149
|
+
reasons: [...counts].map(([reason, count]) => ({ reason, count }))
|
|
150
|
+
.sort((a, b) => b.count - a.count || sortNames(a.reason, b.reason)),
|
|
151
|
+
unreadable };
|
|
152
|
+
} finally { store.close(); }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** 브리핑 한 줄 — 사유 종류별 건수, 상위 5종만 적고 나머지는 «외 N종» 으로 접는다. */
|
|
156
|
+
export function composeDraftCloseSummaryText(reasons: readonly ClosedDraftReason[]): string {
|
|
157
|
+
const total = reasons.reduce((sum, item) => sum + item.count, 0);
|
|
158
|
+
const shown = reasons.slice(0, 5).map(item => `${item.reason} ${item.count}`).join(' · ');
|
|
159
|
+
const omitted = Math.max(0, reasons.length - 5);
|
|
160
|
+
return `어제 닫힌 draft ${total}건${shown ? ` — ${shown}` : ''}${omitted > 0 ? ` · 외 ${omitted}종` : ''}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 어제 닫힌 draft 사유를 브리핑 원장(`<state>/briefing/items.sqlite`, `eln brief`)에 **하루 1건** 으로 적는다.
|
|
165
|
+
* 표본이 없거나 원장을 못 읽으면 아무것도 쓰지 않고 `unmeasured`/`unreadable` 로 남긴다(«0건»으로 적지 않는다).
|
|
166
|
+
* 관측은 `deps.log`(루프 tick 전용)가 아니라 로그에 직접 남긴다 — 데일리 tick 의 의미를 바꾸지 않는다.
|
|
167
|
+
*/
|
|
168
|
+
export function recordDraftCloseSummary(root: string, now: Date,
|
|
169
|
+
log: (category: string, event: string, data?: unknown) => void = (category, event, data) => debug.log(category, event, data),
|
|
170
|
+
): DraftCloseSummary {
|
|
171
|
+
const observe = (summary: DraftCloseSummary): DraftCloseSummary => {
|
|
172
|
+
try {
|
|
173
|
+
log('rhythm.daily', 'draft-close-summary', { day: summary.day, closed: summary.closed, state: summary.state,
|
|
174
|
+
reasons: summary.reasons.map(item => `${item.reason} ${item.count}`).join(' · '),
|
|
175
|
+
...(summary.detail ? { detail: summary.detail } : {}), ...(summary.itemId === undefined ? {} : { itemId: summary.itemId }) });
|
|
176
|
+
} catch { /* 관측 실패가 브리핑을 막지 않는다 */ }
|
|
177
|
+
return summary;
|
|
178
|
+
};
|
|
179
|
+
const day = kstDay(new Date(now.getTime() - 86_400_000));
|
|
180
|
+
let counted: ClosedDraftCount | null;
|
|
181
|
+
try { counted = collectClosedDrafts(root, now); }
|
|
182
|
+
catch (error) { return observe({ day, closed: 0, reasons: [], text: null, state: 'unreadable',
|
|
183
|
+
detail: shortReason(`로그 원장 못 읽음 · ${reasonOf(error)}`) }); }
|
|
184
|
+
if (counted === null) return observe({ day, closed: 0, reasons: [], text: null, state: 'unmeasured', detail: '로그 원장 없음' });
|
|
185
|
+
if (counted.closed === 0) return observe({ day, closed: 0, reasons: [], text: null, state: 'unmeasured',
|
|
186
|
+
detail: counted.unreadable > 0 ? `사유 못 읽은 closed 로그 ${counted.unreadable}건` : '닫힌 draft 없음' });
|
|
187
|
+
// 사유를 못 읽은 closed 로그가 섞이면 읽힌 건수만으로 닫힘 총수를 단정할 수 없다 — 원장에 확정 기록하지 않고 부분 미측정으로 남긴다.
|
|
188
|
+
if (counted.unreadable > 0) return observe({ day, closed: counted.closed, reasons: counted.reasons, text: null,
|
|
189
|
+
state: 'unmeasured',
|
|
190
|
+
detail: `사유 못 읽은 closed 로그 ${counted.unreadable}건 — 읽힌 ${counted.closed}건만 셈(부분 미측정)` });
|
|
191
|
+
const text = composeDraftCloseSummaryText(counted.reasons);
|
|
192
|
+
try {
|
|
193
|
+
const ledger = new BriefItemsLedger({ stateDir: root, now: () => now, log });
|
|
194
|
+
const today = kstDay(now);
|
|
195
|
+
const recorded = ledger.list().find(item => item.source === DRAFT_CLOSE_BRIEF_SOURCE
|
|
196
|
+
&& kstDay(new Date(item.created_at)) === today);
|
|
197
|
+
if (recorded) return observe({ day, closed: counted.closed, reasons: counted.reasons, text,
|
|
198
|
+
state: 'already-recorded', itemId: recorded.id });
|
|
199
|
+
const item = ledger.add({ text, domain: '운영', priority: 'P2', source: DRAFT_CLOSE_BRIEF_SOURCE,
|
|
200
|
+
evidence: `elanous logs --category ${SWEPT_DRAFT_LOG.category} --event ${SWEPT_DRAFT_LOG.event} --since 24h`,
|
|
201
|
+
createdAt: now.toISOString(), dedupeKey: `${DRAFT_CLOSE_BRIEF_SOURCE}:${today}`, dedupeWithinMs: 86_400_000 });
|
|
202
|
+
return observe({ day, closed: counted.closed, reasons: counted.reasons, text, state: 'recorded', itemId: item.id });
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return observe({ day, closed: counted.closed, reasons: counted.reasons, text, state: 'unreadable',
|
|
205
|
+
detail: shortReason(`브리핑 원장 못 씀 · ${reasonOf(error)}`) });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
79
209
|
/** 기본은 어제 KST 00:00–24:00, 창을 주면 그 안에 실제 병합된 PR만 센다. */
|
|
80
210
|
export async function collectLandings(now: Date, window?: { from: Date; to: Date }, source?: { repoRoot?: string; repoName?: string; stateRoot?: string }): Promise<Landing[]> {
|
|
81
211
|
const configuredTree = source?.repoRoot ?? getUserConfig().ops?.cronRepoRoot;
|
|
@@ -495,7 +625,8 @@ export async function runDaily(options: { dryRun?: boolean; noNews?: boolean; st
|
|
|
495
625
|
else loopEvent('rhythm-daily', 'tick', tick);
|
|
496
626
|
}
|
|
497
627
|
}
|
|
498
|
-
return { file, markdown, header, risks, sections, sent, ...vault, sendError, recordError, status, deliveryState, deliveryLine
|
|
628
|
+
return { file, markdown, header, risks, sections, sent, ...vault, sendError, recordError, status, deliveryState, deliveryLine,
|
|
629
|
+
draftCloseSummary: undefined };
|
|
499
630
|
}
|
|
500
631
|
const safe = async <T>(fetch: () => Promise<T>): Promise<Section<T>> => {
|
|
501
632
|
try { return { status: 'ok', value: await fetch() }; } catch (e) { return unreadable(reasonOf(e)); }
|
|
@@ -589,6 +720,9 @@ export async function runDaily(options: { dryRun?: boolean; noNews?: boolean; st
|
|
|
589
720
|
writeFileSync(file, review.markdown);
|
|
590
721
|
}
|
|
591
722
|
const { vaultError, vaultFatal } = options.dryRun ? { vaultError: null, vaultFatal: false } : copyToVault(review.markdown, now, deps);
|
|
723
|
+
// DRAFT-SWEEP-DAILY-SUMMARY — 어제 닫힌 draft 사유를 오늘 브리핑 원장 1건으로 적는다(PR별 댓글 0 유지).
|
|
724
|
+
// --dry-run 은 원장에 쓰지 않는다(다른 산출물과 같은 규칙).
|
|
725
|
+
const draftCloseSummary = options.dryRun ? undefined : recordDraftCloseSummary(root, now);
|
|
592
726
|
let enabled = deps.sendEnabled === true;
|
|
593
727
|
let sendError: string | null = null;
|
|
594
728
|
let deliveryState = options.stage === 'collect' ? 'pending' : options.dryRun ? 'dry-run' : 'send-disabled';
|
|
@@ -628,7 +762,7 @@ export async function runDaily(options: { dryRun?: boolean; noNews?: boolean; st
|
|
|
628
762
|
else loopEvent('rhythm-daily', 'tick', tick);
|
|
629
763
|
}
|
|
630
764
|
}
|
|
631
|
-
return { file, ...review, sent, vaultError, vaultFatal, sendError, recordError, status, deliveryState, deliveryLine };
|
|
765
|
+
return { file, ...review, sent, vaultError, vaultFatal, sendError, recordError, status, deliveryState, deliveryLine, draftCloseSummary };
|
|
632
766
|
}
|
|
633
767
|
|
|
634
768
|
export async function main(args: string[] = process.argv.slice(2), deps: DailyDeps = {}): Promise<Awaited<ReturnType<typeof runDaily>>> {
|