elanous 0.2.40 → 0.2.42
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/README.md +7 -7
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4769.9fd582071301f382.js → 1063.2fb8a9dc55c731f2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +36 -0
- package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{470.51a72a2348306455.js → 1262.e804feb8fd1acb8c.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7219-15bdd9a80661af8e.js → 1455-530ca16c7e71baff.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1281.d81877e35b9068f4.js → 1487.2f67b79444062da6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +32 -0
- package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +286 -0
- package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +23 -0
- package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{1350.59f40e39e02d91b1.js → 2456.b6de0e1c05f28843.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +1 -0
- package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +115 -0
- package/apps/pwa/out/_next/static/chunks/{5436.fdf6fa8ae3e9588d.js → 2974.595d1e29d040cb6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5383.bdfa5d886e39a826.js → 3207.a756c95e9ec8c3c8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +93 -0
- package/apps/pwa/out/_next/static/chunks/{2177.df8547f9d7215c4e.js → 3347.f846266b5e5f3ede.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
- package/apps/pwa/out/_next/static/chunks/{9184.45afbf44fec87574.js → 3482.a9ae9d2ad6a5bd97.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3507.97e1977bc4d5fb0a.js +2 -0
- package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{8099.1cccf31c1baa7e1c.js → 3741.9cf76acaa2ee5298.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4864a2ca.1c1c280a7772c5e5.js → 3d0154af.5331fec737ffe8c3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4043.6b91203436c9c86b.js +309 -0
- package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +77 -0
- package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3330.7443183b7e6fdbdf.js → 4276.336d76d858fcdf1b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +148 -0
- package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +74 -0
- package/apps/pwa/out/_next/static/chunks/{3b355b3d.6f73c98bf6f9c749.js → 50061469.0326382241ef3b2d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +149 -0
- package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +27 -0
- package/apps/pwa/out/_next/static/chunks/{8d173e38.18a2f7b4e1848bdb.js → 53c73f84.373b6160adfa89a4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1537-6bb97b5214928ea9.js → 5431-72a1dbde59400691.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +287 -0
- package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{261.0d471e8aded799f3.js → 6131.348b7d6bdcd267ce.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
- package/apps/pwa/out/_next/static/chunks/{9175.2de5209a5ce6f68a.js → 6377.bc7df239edbff244.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2226.2cf61ec781827219.js → 6632.0eac1c554f8f2ef3.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +104 -0
- package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +94 -0
- package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b84cad00.594027ebe0531fe2.js → 73d2eb0e.4966ad55293c801a.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/{6257.c0037b8949239f5e.js → 7411.9b4b8b747dc7b802.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +1 -0
- package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9189505a.980427ac048c8c60.js → 7a98bc80.fa935989e829f9c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8138.6c10b2a356d52fca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2566.beaffc3a5e1fcd2e.js → 8262.ddc03e06b733f184.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +8 -0
- package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +79 -0
- package/apps/pwa/out/_next/static/chunks/{8272-025653884f9645d2.js → 8546-6f03f8ea34dd61d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6051-0ae955eedb10adb9.js → 8673-bab689a55f7e13b9.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4351.0f37a6db2b34d50b.js → 9249.976f1e69a9d80754.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6749.0744ce6eb08d40e9.js → 9427.96aa912ddb5801e0.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7611.85f045bf232053ac.js → 9633.ce5bddc56784da72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3746-f80a78f220bf37b5.js → 9656-374232558fd528d0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83d27757e2b8060a.js → page-0e3d7b9421b79dac.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-139d722de1dcd6c1.js → page-bf067887b3307423.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{409c787b.e48c5bec8ec10456.js → b450776b.3db34d8d965f82d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{ca3702f8.2913182e73889fa2.js → e8543907.5b040a5f22a0b0e5.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +1 -0
- package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +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 +23 -24
- 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 +22 -22
- 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 +23 -24
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -24
- 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/bin/elanous.mjs +6 -1
- package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
- package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
- package/docs/goal-context/04-mock-scenarios.md +13 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
- package/graphs/demo/persona-teacher/recipes.yaml +17 -0
- package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
- package/graphs/heal/heal-loop.yaml +1 -0
- package/graphs/overlays/stop-heal-ladder.yaml +136 -2
- package/graphs/release/release-loop.yaml +3 -1
- package/graphs/units/gate/gate.yaml +20 -0
- package/graphs/units/git/git.yaml +26 -0
- package/graphs/units/implement/implement.yaml +19 -0
- package/graphs/units/landing/landing.yaml +18 -0
- package/graphs/units/review/review.yaml +20 -0
- package/graphs/units/verbs/recipes.yaml +13 -10
- package/graphs/units/verbs/research.yaml +20 -0
- package/graphs/work/research-report.yaml +21 -3
- package/package.json +1 -1
- package/scripts/disk-steward.ts +46 -0
- package/scripts/fleet/node-setup.sh +50 -0
- package/scripts/graph-units/verbs/research.ts +55 -0
- package/scripts/heal/run-role.ts +99 -5
- package/scripts/land-global-checks.ts +6 -1
- package/scripts/lib/decompose-ab.ts +1 -0
- package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
- package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
- package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
- package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
- package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
- package/scripts/lib/tui-regress-checks.ts +33 -13
- package/scripts/llm-cost-ledger.ts +179 -8
- package/scripts/measure-nl-routing-corpus.ts +10 -2
- package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
- package/scripts/public-copy-lint.ts +163 -19
- package/scripts/ratchets/train-side-invariants.sh +0 -1
- package/scripts/release-loop/gate-autoheal.ts +117 -0
- package/scripts/release-loop/gate-baseline-receipt.ts +55 -10
- package/scripts/release-loop/gate-diff.ts +22 -3
- package/scripts/release-loop/gate-image-pin.ts +81 -0
- package/scripts/release-loop/gate-node-sim.ts +207 -0
- package/scripts/release-loop/gate-node.ts +489 -62
- package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
- package/scripts/release-loop/gate-radius-live.ts +97 -13
- package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
- package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
- package/scripts/release-loop/gate-train-receipts.ts +122 -0
- package/scripts/release-loop/gate-triage-node.ts +70 -10
- package/scripts/release-loop/resume-release.ts +330 -23
- package/scripts/release-loop/traffic-node.ts +6 -3
- package/scripts/release-loop/unattended-release.ts +4 -2
- package/scripts/shadow-authoring-probe.ts +20 -0
- package/scripts/ux-sim.ts +37 -3
- package/scripts/warm-pool-provider-config.ts +263 -0
- package/skills/omni-crawl/SKILL.md +5 -2
- package/skills/omni-crawl/scripts/main.ts +84 -23
- package/src/acp/core-turn-bridge.ts +99 -4
- package/src/agent/registry.ts +2 -0
- package/src/agent/task-notification.ts +25 -5
- package/src/agent/types.ts +6 -0
- package/src/agent-mission/mission-cli.ts +24 -0
- package/src/agent-mission/mission-out.ts +595 -0
- package/src/agent-mission/tool-install.ts +88 -2
- package/src/agent-substrate/context-strategy.ts +4 -4
- package/src/agent-substrate/self-review-cli.ts +267 -40
- package/src/auto-research/research-turn.ts +133 -51
- package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
- package/src/autopilot/pr-manager.ts +38 -7
- package/src/autopilot/pty-control-loop.ts +2 -0
- package/src/boot/daemon-tools/self-implement.ts +46 -9
- package/src/budget/shadow-arms.ts +52 -0
- package/src/capture/tui-self-report.ts +22 -6
- package/src/chat/index.ts +1 -1
- package/src/chat/pickers/state.ts +7 -1
- package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
- package/src/cli/log-event-names.ts +5 -0
- package/src/cli/mcp-login.ts +8 -18
- package/src/cli/msg-cli.ts +57 -1
- package/src/cli/pr-land-heavy-remote.ts +47 -9
- package/src/cli/pr-land-overlap-evidence.ts +100 -1
- package/src/cli/pr-land-pod-shadow-job.ts +60 -0
- package/src/cli/release-checklist-read-cli.ts +20 -10
- package/src/cli/release-cli.ts +27 -1
- package/src/cli/research-cli.ts +4 -5
- package/src/cli/sim-cli.ts +7 -1
- package/src/cli/tasks-cli.ts +12 -4
- package/src/codex/models.ts +30 -15
- package/src/compact/auto.ts +8 -1
- package/src/control-plane/ledger.ts +52 -2
- package/src/control-plane/server.ts +16 -2
- package/src/core-turn/run-core-turn.ts +313 -89
- package/src/core-turn/run-goal-loop.ts +106 -2
- package/src/core-turn/types.ts +2 -0
- package/src/dashboard/acp-boot.ts +72 -3
- package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
- package/src/dashboard/classic-sidetab-mission.ts +185 -0
- package/src/dashboard/index.ts +155 -15
- package/src/dashboard/input/agent-wake.ts +23 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +9 -0
- package/src/dashboard/mission-cli-consumer.ts +153 -0
- package/src/dashboard/seat-boot-preamble.ts +171 -1
- package/src/dashboard/side-basic-runtime.ts +54 -1
- package/src/dashboard/sidetab-runtime.ts +90 -1
- package/src/dashboard/skill-catalog-preamble.ts +101 -0
- package/src/dashboard/skill-detection-runtime.ts +55 -15
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
- package/src/dashboard/turn-preamble.ts +65 -16
- package/src/dashboard/turn-prompt-runtime.ts +4 -2
- package/src/disk-steward/judge.ts +73 -0
- package/src/disk-steward/ledger.ts +122 -0
- package/src/disk-steward/steward.ts +373 -0
- package/src/domains/guardian-tick.ts +191 -2
- package/src/graph-runner/graph-cli.ts +44 -7
- package/src/graph-runner/graph-tick.ts +10 -2
- package/src/graph-runner/in-process-registry.ts +37 -0
- package/src/graph-runner/runner.ts +34 -4
- package/src/grok/agent-search.ts +14 -4
- package/src/grok/credential.ts +28 -11
- package/src/grounding/ref-index.ts +19 -0
- package/src/grounding/sources-cli.ts +9 -1
- package/src/harness/harness-cli-command.ts +267 -14
- package/src/harness/harness-incidents.ts +312 -7
- package/src/harness/harness-pod-dispatch.ts +81 -15
- package/src/harness/harness-space.ts +15 -1
- package/src/harness/harness-stop.ts +155 -11
- package/src/harness/progress-worker-entry.ts +59 -0
- package/src/harness/progress-worker-runtime.ts +11 -0
- package/src/index.ts +52 -7
- package/src/instance/dev-project.ts +25 -0
- package/src/instance/spawn-elanous.ts +6 -4
- package/src/intelligence-map/model-alias.ts +6 -5
- package/src/intelligence-map/model-catalog.ts +59 -2
- package/src/llm/model-defaults.ts +4 -4
- package/src/llm.ts +208 -19
- package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
- package/src/loops/orchestrator/tick.ts +61 -0
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/mcp/mcp-oauth.ts +3 -1
- package/src/model-tier/llm-tier-map.ts +9 -9
- package/src/model-tier/turn-tier.ts +1 -1
- package/src/msg/msg-mirror-github.ts +80 -0
- package/src/msg/msg-mirror-policy.ts +61 -0
- package/src/msg/msg-mirror-runner.ts +219 -0
- package/src/msg/msg-mirror-state.ts +33 -0
- package/src/msg/msg-store.ts +11 -0
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/harness-traversal.ts +11 -0
- package/src/output-truncation.ts +41 -12
- package/src/pr-tool/census.ts +3 -2
- package/src/project/project-store.ts +48 -1
- package/src/pty-shell/tui-run-state.ts +188 -2
- package/src/release-loop/checklist-target-path-hint.ts +13 -0
- package/src/release-loop/checklist.ts +9 -1
- package/src/release-loop/gate-host-reservation.ts +144 -0
- package/src/release-loop/release-schedule.ts +90 -1
- package/src/repl/index.ts +43 -2
- package/src/research/axis-fanout.ts +121 -16
- package/src/research/bloom-exemplars.ts +137 -0
- package/src/research/gap-followup.ts +13 -1
- package/src/research/plugin-discovery.ts +36 -14
- package/src/research-bridge/invoke.ts +22 -5
- package/src/research-bridge/types.ts +2 -0
- package/src/self-dev/ask-launch-flow.ts +39 -25
- package/src/self-dev/decompose.ts +119 -5
- package/src/self-dev/dev-cli.ts +17 -17
- package/src/self-dev/graph-front-nodes.ts +23 -0
- package/src/self-dev/launch-preflight-text.ts +5 -6
- package/src/self-dev/launch-preflight.ts +9 -4
- package/src/self-dev/orchestrate.ts +27 -2
- package/src/self-dev/review-provider-fallback.ts +9 -2
- package/src/self-dev/run-supervisor.ts +226 -6
- package/src/self-dev/run-types.ts +7 -0
- package/src/self-implement/doctor-visit.ts +88 -0
- package/src/self-implement/gate-artifacts.ts +13 -0
- package/src/self-implement/gate-cli.ts +54 -24
- package/src/self-implement/gate-remote.ts +23 -5
- package/src/self-implement/goal-author-clarification.ts +31 -12
- package/src/self-implement/goal-author.ts +403 -8
- package/src/self-implement/graph-overlay-yaml.ts +7 -0
- package/src/self-implement/graph-stage-units.ts +172 -0
- package/src/self-implement/graph-templates.ts +162 -1
- package/src/self-implement/graph-variant.ts +37 -4
- package/src/self-implement/headless-elanous-driver.ts +31 -2
- package/src/self-implement/heal-intake.ts +112 -4
- package/src/self-implement/heal-triage.ts +20 -0
- package/src/self-implement/host-regate.ts +31 -8
- package/src/self-implement/knowhow-pack.ts +38 -0
- package/src/self-implement/merge-train.ts +5 -0
- package/src/self-implement/orchestrator.ts +359 -30
- package/src/self-implement/regate-detail.ts +38 -1
- package/src/self-implement/rework-child-provider.ts +25 -0
- package/src/self-implement/run-chart.ts +119 -0
- package/src/self-implement/run-ledger.ts +176 -17
- package/src/self-implement/run-stop.ts +208 -5
- package/src/self-implement/running-runs.ts +35 -12
- package/src/self-implement/self-implement-cli.ts +36 -4
- package/src/self-implement/stop-autoheal-operational.ts +43 -0
- package/src/self-implement/stop-autoheal.ts +28 -6
- package/src/session/chat.ts +69 -58
- package/src/session/request-size-guard.ts +123 -0
- package/src/session-runtime/index.ts +98 -4
- package/src/session-runtime/tier-flip.ts +3 -3
- package/src/sidetab-core/model.ts +150 -0
- package/src/sidetab-core/visible-agent-mission.ts +186 -0
- package/src/skills/name-match.ts +20 -0
- package/src/skills/tools/agent.ts +30 -2
- package/src/skills/tools/omni-search.ts +67 -5
- package/src/task-agent/parent-host.ts +14 -1
- package/src/task-agent/pod-distributor-entry.ts +3 -0
- package/src/task-agent/task-hand.ts +31 -1
- package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
- package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +163 -13
- package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +209 -39
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +280 -60
- package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
- package/src/tokens.ts +35 -2
- package/src/tool-runtime/agent-runtime.ts +1 -0
- package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
- package/src/tool-runtime/skill-exec-runtime.ts +33 -13
- package/src/tool-runtime/types.ts +1 -0
- package/src/tui/entry.ts +24 -7
- package/src/tui/sidetab/attach.ts +47 -0
- package/src/tui/sidetab/list.ts +45 -0
- package/src/tui/sidetab/nl-tool.ts +2 -136
- package/src/tui/sidetab/peek.ts +52 -0
- package/src/tui-client/dashboard-session.ts +3 -0
- package/src/tui-client/local-chat-turn.ts +26 -13
- package/src/tui.ts +22 -0
- package/src/user-config.ts +11 -5
- package/src/ux-sim/fake-kubectl.ts +37 -3
- package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
- package/src/ux-sim/fault-pod-child-result.ts +42 -0
- package/src/ux-sim/fault-pool-cap.ts +11 -6
- package/src/ux-sim/mock-scenario-catalog.json +41 -0
- package/src/ux-sim/mock-scenario-catalog.ts +156 -0
- package/src/ux-sim/release-sim-rig.ts +262 -0
- package/src/ux-sim/scenario.ts +116 -23
- package/src/ux-sim/seminar-rehearsal.ts +109 -0
- package/src/ux-sim/subagent-tools-device.ts +2 -2
- package/src/version/packed-revision.json +1 -1
- package/src/voice/voice-tts-singleton.ts +18 -3
- package/src/web-search/grok.ts +5 -9
- package/apps/pwa/out/_next/static/chunks/1060.014f1f01a17aec26.js +0 -115
- package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +0 -286
- package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +0 -149
- package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +0 -90
- package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +0 -104
- package/apps/pwa/out/_next/static/chunks/2047.8f23e210995d4548.js +0 -2
- package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2066.a77257c8c350770a.js +0 -23
- package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2499.02fbfd18126c10c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2522.f2766b76d22c8bdf.js +0 -34
- package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +0 -27
- package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2774-719820e3f5b2a3eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2829-734fe8bf2eee7347.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +0 -24
- package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +0 -77
- package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +0 -36
- package/apps/pwa/out/_next/static/chunks/413-24c8284bda08241d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +0 -287
- package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +0 -1
- package/apps/pwa/out/_next/static/chunks/483.725e4a61be922788.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +0 -74
- package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +0 -8
- package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +0 -62
- package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +0 -94
- package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +0 -32
- package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6908.06008c67f6380ea9.js +0 -148
- package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6cc1d1d6.9a98dd4622f1afc6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +0 -93
- package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +0 -24
- package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8357.1aaa713b052a78b1.js +0 -309
- package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8639-92c11d069792429e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +0 -41
- package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +0 -79
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-ba957ff875a026a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-4edb8df90f4537ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-fca2e59a81744d9e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-ccde99f07aaf71f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +0 -1
- package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/d5d3cefc-373c4eb722ed848b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/d8de0de2-dd0f4ac8e5ab1832.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-002b472a66935d31.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +0 -1
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
- package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
- /package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
|
@@ -8,17 +8,21 @@ import { tmpdir } from 'node:os';
|
|
|
8
8
|
import { dirname, join, resolve } from 'node:path';
|
|
9
9
|
import { debug } from '../../src/debug/log.js';
|
|
10
10
|
import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
|
|
11
|
+
import { acquireLockAsync } from '../../src/storage/file-lock.js';
|
|
11
12
|
import { lastJsonObject, runGraph, verifyReleaseCommandRoot, verifyReleaseRecipeSnapshot, type GraphRunState } from '../../src/graph-runner/runner.js';
|
|
12
13
|
import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
|
|
13
14
|
import { releaseRunUniverse } from './release-universe.js';
|
|
14
15
|
import { isReleaseVersion } from './release-version.js';
|
|
15
|
-
import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planStalledShardRecheck, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
|
|
16
|
+
import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planSameCommitRecheck, planStalledShardRecheck, testFileOfId, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
|
|
17
|
+
import { gateAssignableTestsAtCommit, gateEnvFingerprint } from './gate-baseline-receipt.js';
|
|
18
|
+
import { readGateShardManifest, readGateShardResult } from './gate-shard-receipts.js';
|
|
16
19
|
import { gitClosureTree, testsReachingChanged, TRACEABLE_RELEASE_JSON, type ClosureTree } from './gate-closure.js';
|
|
17
20
|
import { assertReleaseResumeApprovalBoundary, releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeGateShardReroute, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
|
|
18
21
|
import { reserveReleaseResumeAttempt } from './resume-cap.js';
|
|
19
22
|
import { cutReleaseBranch } from './cut-branch.js';
|
|
20
23
|
import { releaseLoopConfig } from './unattended-release.js';
|
|
21
24
|
import { trafficOptionsFromConfig } from './traffic-transition.js';
|
|
25
|
+
import type { GateAutohealDecision } from './gate-autoheal.js';
|
|
22
26
|
|
|
23
27
|
const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
|
|
24
28
|
|
|
@@ -46,6 +50,8 @@ export interface ResumeReleaseDeps {
|
|
|
46
50
|
ledgerRoot?: string;
|
|
47
51
|
/** GATE-REUSE-BY-DIFF: the tip tree for the import closure — default read from git objects of the tip commit. */
|
|
48
52
|
closureTree?: (commit: string) => ClosureTree;
|
|
53
|
+
/** Test seam for the pinned Pod gate selection; production reads the selected test universe at the cut commit. */
|
|
54
|
+
gateTestsAtCommit?: (commit: string) => string[] | null;
|
|
49
55
|
/** Source observation before the first resumed node. */
|
|
50
56
|
onSource?: (line: string) => void;
|
|
51
57
|
/** Test seam; production resolves this from ELANOUS_RELEASE_TRAFFIC_FILE / release.loop.trafficFile. */
|
|
@@ -62,9 +68,36 @@ export interface ResumeReleaseOptions {
|
|
|
62
68
|
export interface ResumeReleasePlan {
|
|
63
69
|
runId: string; version: string; fromNode: string; commandRoot: string; tip: TipRefresh;
|
|
64
70
|
partial: PartialOutcome; gatePlan: { mode: 'not-run' | 'partial' | 'full'; reason?: string };
|
|
71
|
+
/** Most recent failed gate's autoheal recommendation, carried into the resume plan for the caller. */
|
|
72
|
+
autoheal?: GateAutohealDecision;
|
|
73
|
+
autohealReroute?: string;
|
|
65
74
|
nextAttempt: number; priorCountKnown: boolean; preflight: 'passed';
|
|
66
75
|
}
|
|
67
76
|
|
|
77
|
+
function priorGateAutoheal(run: GraphRunState): GateAutohealDecision | undefined {
|
|
78
|
+
const gate = lastJsonObject(run.nodes.find((node) => node.nodeId === 'gate')?.output);
|
|
79
|
+
const value = gate?.autoheal;
|
|
80
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
81
|
+
const candidate = value as Partial<GateAutohealDecision>;
|
|
82
|
+
if (!['reroute-shard', 'retry-shard', 'partial-resume', 'host-switch', 'hitl'].includes(String(candidate.action))) return undefined;
|
|
83
|
+
if (!['shard-pod-death', 'timeout', 'host-down', 'interference', 'true-regression', 'environment'].includes(String(candidate.cause))) return undefined;
|
|
84
|
+
if (typeof candidate.signature !== 'string' || typeof candidate.reason !== 'string'
|
|
85
|
+
|| typeof candidate.shardElapsedMs !== 'number' || typeof candidate.overallElapsedMs !== 'number') return undefined;
|
|
86
|
+
return candidate as GateAutohealDecision;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function automaticReroute(run: GraphRunState, autoheal: GateAutohealDecision | undefined, tipChanged: boolean, from: string): string | undefined {
|
|
90
|
+
if (!autoheal || tipChanged || from !== 'gate' || typeof (run.input as Record<string, unknown> | undefined)?.gatePodPool !== 'string'
|
|
91
|
+
|| (autoheal.action !== 'retry-shard' && autoheal.action !== 'reroute-shard' && autoheal.action !== 'host-switch')) return undefined;
|
|
92
|
+
const key = autoheal.shardKey;
|
|
93
|
+
if (autoheal?.action === 'host-switch') return 'auto';
|
|
94
|
+
if (typeof key === 'string' && /^pod-\d+$/.test(key)) return key;
|
|
95
|
+
if (typeof key === 'string' && /^\d+$/.test(key)) return `pod-${key}`;
|
|
96
|
+
const gate = lastJsonObject(run.nodes.find((node) => node.nodeId === 'gate')?.output);
|
|
97
|
+
const stalled = gate?.stalledShards;
|
|
98
|
+
return Array.isArray(stalled) && stalled.length > 0 ? 'auto' : undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
68
101
|
/** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
|
|
69
102
|
const CLI_ENTRIES = ['bin/elanous.mjs', 'src/index.ts'] as const;
|
|
70
103
|
/** The gate's own test-file set (gate-node sweep: `git ls-files '*.test.*'` filtered by this). */
|
|
@@ -293,14 +326,111 @@ function verifyResumePreflight(run: GraphRunState, opts: ResumeReleaseOptions, d
|
|
|
293
326
|
}
|
|
294
327
|
}
|
|
295
328
|
|
|
296
|
-
/** GATE-PARTIAL outcome of a resume — applied (
|
|
297
|
-
* default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
|
|
329
|
+
/** GATE-PARTIAL outcome of a resume — applied (with its exact remeasure selection) or the reason it fell back to a full gate.
|
|
330
|
+
* `auto` marks the default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
|
|
298
331
|
export type PartialOutcome = { requested: false }
|
|
299
|
-
| { requested: true; applied: true; files: number; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
|
|
332
|
+
| { requested: true; applied: true; files: number; plannedFiles: string[]; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
|
|
300
333
|
| { requested: true; applied: false; reason: string; auto?: true };
|
|
301
334
|
|
|
302
335
|
/** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
|
|
303
336
|
* GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
|
|
337
|
+
type StalledGateEvidence = {
|
|
338
|
+
stalledShards: Array<{ shard: number; files: string[]; reason: string }>;
|
|
339
|
+
partialSummary: { pass: number; fail: number; errors: number; ran: number; files: number };
|
|
340
|
+
partialFailures: { failures: string[]; errors: string[] };
|
|
341
|
+
preservedShardCount: number;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
/** Recover an interrupted Pod sweep from its atomic shard receipts. No receipt or identity doubt authorizes a partial gate. */
|
|
345
|
+
function stalledGateEvidence(input: { version: string; commit: string; repo: string; roots: string[];
|
|
346
|
+
gateOutput?: Record<string, unknown>; expectedBase40?: string; expectedTests?: (commit: string) => string[] | null }):
|
|
347
|
+
{ ok: true; evidence: StalledGateEvidence } | { ok: false; reason: string } {
|
|
348
|
+
const fail = (reason: string) => ({ ok: false as const, reason });
|
|
349
|
+
if (input.gateOutput && (input.gateOutput.outcome !== 'error' || input.gateOutput.commit !== input.commit))
|
|
350
|
+
return fail('saved gate result is not an error for the recorded cut');
|
|
351
|
+
if (!/^[a-f0-9]{40}$/i.test(input.expectedBase40 ?? ''))
|
|
352
|
+
return fail('saved cutoff baseline is absent or invalid');
|
|
353
|
+
const fingerprint = gateEnvFingerprint(process.env);
|
|
354
|
+
let receiptDir: string | undefined;
|
|
355
|
+
for (const root of input.roots) {
|
|
356
|
+
const candidate = join(root, 'release', input.version, 'gate-logs', 'cut', 'receipts');
|
|
357
|
+
if (existsSync(candidate)) { receiptDir = candidate; break; }
|
|
358
|
+
}
|
|
359
|
+
if (!receiptDir) return fail('stalled gate receipts are absent');
|
|
360
|
+
const names = readdirSync(receiptDir).filter((name) => /^pod-\d+\.receipt\.json$/.test(name)).sort((a, b) =>
|
|
361
|
+
Number(/^pod-(\d+)/.exec(a)![1]) - Number(/^pod-(\d+)/.exec(b)![1]));
|
|
362
|
+
if (!names.length || names.some((name, index) => Number(/^pod-(\d+)/.exec(name)![1]) !== index))
|
|
363
|
+
return fail('stalled gate receipt shard set is incomplete');
|
|
364
|
+
const allFiles: string[] = [];
|
|
365
|
+
const failures: string[] = [];
|
|
366
|
+
const errors: string[] = [];
|
|
367
|
+
const stalledShards: StalledGateEvidence['stalledShards'] = [];
|
|
368
|
+
const imageDigests = new Set<string>();
|
|
369
|
+
let pass = 0, failCount = 0, errorCount = 0, ran = 0, files = 0, preservedShardCount = 0;
|
|
370
|
+
let base40: string | undefined;
|
|
371
|
+
for (const name of names) {
|
|
372
|
+
const id = name.slice(0, -'.receipt.json'.length);
|
|
373
|
+
const receipt = readGateShardManifest(receiptDir, id);
|
|
374
|
+
if (!receipt || receipt.head40 !== input.commit || !/^[a-f0-9]{40}$/i.test(receipt.base40)
|
|
375
|
+
|| !receipt.files.length
|
|
376
|
+
|| receipt.files.some((file) => typeof file !== 'string'
|
|
377
|
+
|| !/^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file) || file.split('/').includes('..'))
|
|
378
|
+
|| typeof receipt.envKey !== 'string' || typeof receipt.host !== 'string')
|
|
379
|
+
return fail(`stalled gate receipt identity or file list is invalid: ${id}`);
|
|
380
|
+
if (receipt.base40 !== input.expectedBase40)
|
|
381
|
+
return fail(`stalled gate receipt baseline differs from saved cutoff baseline: ${id}`);
|
|
382
|
+
if (base40 && receipt.base40 !== base40) return fail(`stalled gate receipts disagree on baseline: ${id}`);
|
|
383
|
+
base40 = receipt.base40;
|
|
384
|
+
allFiles.push(...receipt.files);
|
|
385
|
+
if (receipt.status === 'unmeasured') {
|
|
386
|
+
stalledShards.push({ shard: Number(id.slice(4)), files: [...receipt.files], reason: 'incomplete' });
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (!receipt.envKey.startsWith(`${fingerprint}:`)) return fail(`stalled gate receipt environment mismatch: ${id}`);
|
|
390
|
+
const imageDigest = receipt.envKey.slice(fingerprint.length + 1);
|
|
391
|
+
if (!/^sha256:[a-f0-9]{64}$/i.test(imageDigest)) return fail(`stalled gate receipt image identity is missing: ${id}`);
|
|
392
|
+
imageDigests.add(imageDigest.toLowerCase());
|
|
393
|
+
const result = readGateShardResult(receiptDir, { shard: { id, files: receipt.files }, head40: input.commit,
|
|
394
|
+
base40: receipt.base40, envKey: receipt.envKey });
|
|
395
|
+
if (!result) return fail(`stalled gate receipt payload is incomplete or corrupt: ${id}`);
|
|
396
|
+
const totals = result.runs.reduce((sum, run) => ({ pass: sum.pass + run.pass, fail: sum.fail + run.fail,
|
|
397
|
+
errors: sum.errors + run.errors, ran: sum.ran + run.ran, files: sum.files + run.files }),
|
|
398
|
+
{ pass: 0, fail: 0, errors: 0, ran: 0, files: 0 });
|
|
399
|
+
if (!Object.values(totals).every(Number.isSafeInteger) || receipt.failureIds.length !== totals.fail
|
|
400
|
+
|| receipt.errorIds.length !== totals.errors)
|
|
401
|
+
return fail(`stalled gate receipt counts do not reconcile: ${id}`);
|
|
402
|
+
pass += totals.pass; failCount += totals.fail; errorCount += totals.errors; ran += totals.ran; files += totals.files;
|
|
403
|
+
failures.push(...receipt.failureIds); errors.push(...receipt.errorIds); preservedShardCount++;
|
|
404
|
+
}
|
|
405
|
+
if (!stalledShards.length) return fail('no incomplete shard receipt identifies the interrupted work');
|
|
406
|
+
if (new Set(allFiles).size !== allFiles.length) return fail('stalled gate receipt file assignments overlap');
|
|
407
|
+
if (imageDigests.size !== 1) return fail('stalled gate completed shards do not share one executed image');
|
|
408
|
+
const expected = (input.expectedTests ?? ((commit) => gateAssignableTestsAtCommit(input.repo, commit)))(input.commit);
|
|
409
|
+
if (!expected || JSON.stringify([...expected].sort()) !== JSON.stringify([...allFiles].sort()))
|
|
410
|
+
return fail('stalled gate receipts do not cover the pinned test selection');
|
|
411
|
+
const partialSummary = { pass, fail: failCount, errors: errorCount, ran, files };
|
|
412
|
+
const partialFailures = { failures: [...new Set(failures)].sort(), errors: [...new Set(errors)].sort() };
|
|
413
|
+
const gate = input.gateOutput;
|
|
414
|
+
if (gate) {
|
|
415
|
+
const savedSummary = gate.partialSummary as Partial<StalledGateEvidence['partialSummary']> | undefined;
|
|
416
|
+
const savedFailures = gate.partialFailures as Partial<StalledGateEvidence['partialFailures']> | undefined;
|
|
417
|
+
if (savedSummary && JSON.stringify(savedSummary) !== JSON.stringify(partialSummary))
|
|
418
|
+
return fail('saved gate summary disagrees with shard receipts');
|
|
419
|
+
if (savedFailures && (JSON.stringify([...((savedFailures.failures as string[] | undefined) ?? [])].sort()) !== JSON.stringify(partialFailures.failures)
|
|
420
|
+
|| JSON.stringify([...((savedFailures.errors as string[] | undefined) ?? [])].sort()) !== JSON.stringify(partialFailures.errors)))
|
|
421
|
+
return fail('saved gate failures disagree with shard receipts');
|
|
422
|
+
if (gate.preservedShardCount !== undefined && gate.preservedShardCount !== preservedShardCount)
|
|
423
|
+
return fail('saved preserved shard count disagrees with shard receipts');
|
|
424
|
+
if (Array.isArray(gate.stalledShards)) {
|
|
425
|
+
const savedStalled = gate.stalledShards.flatMap((item) => item && typeof item === 'object'
|
|
426
|
+
&& Array.isArray((item as { files?: unknown }).files) ? (item as { files: string[] }).files : []).sort();
|
|
427
|
+
if (JSON.stringify(savedStalled) !== JSON.stringify(stalledShards.flatMap((item) => item.files).sort()))
|
|
428
|
+
return fail('saved stalled shard list disagrees with shard receipts');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return { ok: true, evidence: { stalledShards, partialSummary, partialFailures, preservedShardCount } };
|
|
432
|
+
}
|
|
433
|
+
|
|
304
434
|
function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean; gateExecutor?: string }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
|
|
305
435
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
306
436
|
// Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
|
|
@@ -326,6 +456,42 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
326
456
|
const saved = JSON.parse(readFileSync(tip.statePath, 'utf8')) as GraphRunState;
|
|
327
457
|
const gateRecord = saved.nodes.find((node) => node.nodeId === 'gate');
|
|
328
458
|
const previousGate = gateRecord ? lastJsonObject(gateRecord.output) as Record<string, unknown> | undefined : undefined;
|
|
459
|
+
// `loadFlaky` is also a partial-resume cause, but it is emitted separately from
|
|
460
|
+
// shard-only interference. Carry both sets into the same affected-file recheck.
|
|
461
|
+
const interference = [...new Set([
|
|
462
|
+
...(Array.isArray(previousGate?.interferenceErrors) ? previousGate.interferenceErrors : []),
|
|
463
|
+
...(Array.isArray(previousGate?.loadFlaky) ? previousGate.loadFlaky : []),
|
|
464
|
+
])];
|
|
465
|
+
if (previousGate?.outcome === 'error' && previousGate.commit === tip.to && previousGate.autoheal
|
|
466
|
+
&& typeof previousGate.autoheal === 'object' && (previousGate.autoheal as { action?: unknown }).action === 'partial-resume') {
|
|
467
|
+
if (!Array.isArray(interference) || !interference.length || !interference.every((id) => typeof id === 'string'))
|
|
468
|
+
return fallback('partial-resume has no complete interference error record');
|
|
469
|
+
let closure: ClosureTree;
|
|
470
|
+
try { closure = (deps.closureTree ?? ((commit: string) => gitClosureTree(deps.repo ?? process.cwd(), commit)))(tip.to); }
|
|
471
|
+
catch (error) { return fallback(`tip tree unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`); }
|
|
472
|
+
const tracked = new Set(closure.files);
|
|
473
|
+
const planned = planSameCommitRecheck({ version: tip.version, measuredCommit: tip.to, forCommit: tip.to,
|
|
474
|
+
failures: [], errors: interference as string[], existsAtCommit: (file) => tracked.has(file) });
|
|
475
|
+
if (!planned.ok) return fallback(planned.reason);
|
|
476
|
+
// The gate consumer only executes same-cut partial work for the stalled-only recheck shape.
|
|
477
|
+
// Interference files are likewise unmeasured, so mark exactly this set as the recheck boundary.
|
|
478
|
+
const partialFailures = previousGate.partialFailures as { failures?: unknown; errors?: unknown } | undefined;
|
|
479
|
+
if (!Array.isArray(partialFailures?.failures) || !partialFailures.failures.every((id) => typeof id === 'string')
|
|
480
|
+
|| !Array.isArray(partialFailures.errors) || !partialFailures.errors.every((id) => typeof id === 'string'))
|
|
481
|
+
return fallback('partial-resume has no complete prior failure record');
|
|
482
|
+
let priorFailures: string[];
|
|
483
|
+
let priorErrors: string[];
|
|
484
|
+
try {
|
|
485
|
+
const affected = new Set(planned.plan.files);
|
|
486
|
+
priorFailures = (partialFailures.failures as string[]).filter((id) => !affected.has(testFileOfId(id)));
|
|
487
|
+
priorErrors = (partialFailures.errors as string[]).filter((id) => !affected.has(testFileOfId(id)));
|
|
488
|
+
} catch { return fallback('partial-resume prior failure record is malformed'); }
|
|
489
|
+
const recheckPlan = { ...planned.plan, priorFailures, priorErrors, stalledFiles: planned.plan.files };
|
|
490
|
+
const path = dryRun ? undefined : writeRecheckPlan(root, recheckPlan);
|
|
491
|
+
debug.log('release-loop.resume', 'interference-recheck-planned', { version: tip.version, commit: tip.to,
|
|
492
|
+
remeasureFileCount: recheckPlan.files.length, fallbackReason: null, ...(path ? { path } : {}), dryRun });
|
|
493
|
+
return { requested: true, applied: true, files: recheckPlan.files.length, priorCommit: tip.to, ...(auto ? { auto: true as const } : {}) };
|
|
494
|
+
}
|
|
329
495
|
const stalled = previousGate?.stalledShards;
|
|
330
496
|
const summary = previousGate?.partialSummary as { pass?: unknown; fail?: unknown; errors?: unknown; ran?: unknown; files?: unknown } | undefined;
|
|
331
497
|
const measured = previousGate?.partialFailures as { failures?: unknown; errors?: unknown } | undefined;
|
|
@@ -356,9 +522,11 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
356
522
|
debug.log('release-loop.resume', 'stalled-recheck-planned', { version: tip.version, commit: tip.to,
|
|
357
523
|
planKind: planned.plan.kind, remeasureFileCount: planned.plan.files.length,
|
|
358
524
|
preservedShardCount: planned.plan.preservedShardCount ?? 0, fallbackReason: null, path });
|
|
359
|
-
return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.to,
|
|
525
|
+
return { requested: true, applied: true, files: planned.plan.files.length, plannedFiles: [...planned.plan.files], priorCommit: tip.to,
|
|
526
|
+
...(auto ? { auto: true as const } : {}) };
|
|
360
527
|
}
|
|
361
528
|
let prior: { commit?: unknown; failures?: unknown; errors?: unknown } | undefined;
|
|
529
|
+
let movedStalledEvidence: StalledGateEvidence | undefined;
|
|
362
530
|
for (const location of [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])]) {
|
|
363
531
|
const path = join(location, 'release', tip.version, 'gate-failures.json');
|
|
364
532
|
if (!existsSync(path)) continue;
|
|
@@ -368,7 +536,19 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
368
536
|
if (record?.commit === tip.from) { prior = record; break; }
|
|
369
537
|
}
|
|
370
538
|
if (!prior || !Array.isArray(prior.failures) || !(prior.errors === undefined || Array.isArray(prior.errors))) {
|
|
371
|
-
|
|
539
|
+
const gateRecord = saved.nodes.find((node) => node.nodeId === 'gate');
|
|
540
|
+
const gateOutput = gateRecord ? lastJsonObject(gateRecord.output) as Record<string, unknown> | undefined : undefined;
|
|
541
|
+
const cutoffRecord = saved.nodes.find((node) => node.nodeId === 'cutoff');
|
|
542
|
+
const cutoffOutput = cutoffRecord ? lastJsonObject(cutoffRecord.output) as Record<string, unknown> | undefined : undefined;
|
|
543
|
+
const expectedBase40 = (cutoffOutput?.baseline as { sha?: unknown } | undefined)?.sha;
|
|
544
|
+
const evidence = stalledGateEvidence({ version: tip.version, commit: tip.from, repo: deps.repo ?? process.cwd(),
|
|
545
|
+
roots: [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])], gateOutput,
|
|
546
|
+
...(typeof expectedBase40 === 'string' ? { expectedBase40 } : {}),
|
|
547
|
+
expectedTests: deps.gateTestsAtCommit });
|
|
548
|
+
if (!evidence.ok) return fallback(`no complete gate record for ${tip.from.slice(0, 12)}; ${evidence.reason}`);
|
|
549
|
+
movedStalledEvidence = evidence.evidence;
|
|
550
|
+
prior = { commit: tip.from, failures: movedStalledEvidence.partialFailures.failures,
|
|
551
|
+
errors: movedStalledEvidence.partialFailures.errors };
|
|
372
552
|
}
|
|
373
553
|
// Both sides of a rename count (`--no-renames`): a source moved to a test/Markdown name is still a source change.
|
|
374
554
|
let changed: string[];
|
|
@@ -404,10 +584,11 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
404
584
|
const savedInput = saved.input as { gateExecutor?: unknown; gatePodPool?: unknown };
|
|
405
585
|
const podGate = (opts.gateExecutor ?? savedInput.gateExecutor ?? (savedInput.gatePodPool ? 'pod' : undefined)) === 'pod';
|
|
406
586
|
const priorShards = priorCutLogs ? collectExecutedPodShardImages(priorCutLogs, tip.from) : undefined;
|
|
407
|
-
const
|
|
587
|
+
const stalledFiles = movedStalledEvidence?.stalledShards.flatMap((shard) => shard.files) ?? [];
|
|
588
|
+
const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: [...changed, ...stalledFiles],
|
|
408
589
|
priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
|
|
409
590
|
existsAtTip: (file) => tracked.has(file),
|
|
410
|
-
...(podGate ? { priorShards: priorShards ?? [] } : {}),
|
|
591
|
+
...((podGate || movedStalledEvidence) ? { priorShards: priorShards ?? [] } : {}),
|
|
411
592
|
closure: (changedCode) => testsReachingChanged(tree, changedCode, tests, { cliEntries: CLI_ENTRIES }) });
|
|
412
593
|
if (!planned.ok) return fallback(planned.reason);
|
|
413
594
|
if (planned.plan.files.length >= tests.length) return fallback(`every test is re-run (${tests.length}) — full gate`);
|
|
@@ -427,7 +608,7 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
427
608
|
const path = dryRun ? undefined : writePartialPlan(root, planned.plan);
|
|
428
609
|
debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length,
|
|
429
610
|
tests: tests.length, auto, ...(path ? { path } : {}), dryRun, ...(planned.closure ? { reached: planned.closure.reached, opaque: planned.closure.opaque } : {}) });
|
|
430
|
-
return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.from,
|
|
611
|
+
return { requested: true, applied: true, files: planned.plan.files.length, plannedFiles: [...planned.plan.files], priorCommit: tip.from,
|
|
431
612
|
...(auto ? { auto: true as const } : {}), ...(planned.closure ? { closure: planned.closure } : {}) };
|
|
432
613
|
}
|
|
433
614
|
|
|
@@ -483,19 +664,25 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
|
|
|
483
664
|
waiverCommit = recorded;
|
|
484
665
|
}
|
|
485
666
|
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
|
|
667
|
+
const fromIndex = savedRun.path.indexOf(opts.from);
|
|
668
|
+
const gateIndex = savedRun.path.indexOf('gate');
|
|
669
|
+
const gateWillRun = fromIndex >= 0 && gateIndex >= fromIndex;
|
|
486
670
|
if (opts.rerouteShard !== undefined && tip.changed) throw new Error('shard reroute requires the original cut head; moved tip needs a full re-gate');
|
|
487
671
|
verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, tip.version);
|
|
488
672
|
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
|
|
489
673
|
verifyResumePreflight(savedRun, opts, deps, tip.version, tip.from, tip.to, commandRoot);
|
|
490
|
-
const
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
const
|
|
674
|
+
const autoheal = gateWillRun ? priorGateAutoheal(savedRun) : undefined;
|
|
675
|
+
const autoReroute = opts.rerouteShard === undefined ? automaticReroute(savedRun, autoheal, tip.changed, opts.from) : undefined;
|
|
676
|
+
const automaticShardReroute = autoReroute ? releaseResumeGateShardReroute(savedRun, { from: opts.from, shard: autoReroute }) : undefined;
|
|
677
|
+
const effectiveOpts = automaticShardReroute ? { ...opts, rerouteShard: automaticShardReroute.set.gateRerouteShard } : opts;
|
|
678
|
+
const partial = preparePartial(tip, effectiveOpts.rerouteShard !== undefined ? { ...effectiveOpts, partial: false } : effectiveOpts, deps, true);
|
|
494
679
|
const gatePlan = !gateWillRun ? { mode: 'not-run' as const }
|
|
495
680
|
: partial.requested && partial.applied ? { mode: 'partial' as const }
|
|
496
681
|
: { mode: 'full' as const, reason: partial.requested ? partial.reason : opts.partial === false ? 'partial recheck disabled' : 'no partial plan applies' };
|
|
682
|
+
if (autoheal) debug.log('release-loop.resume', 'gate-autoheal-plan', { runId: opts.runId, version: tip.version, appliedReroute: automaticShardReroute?.set.gateRerouteShard ?? null, ...autoheal });
|
|
497
683
|
return { runId: opts.runId, version: tip.version, fromNode: opts.from, commandRoot, tip, partial,
|
|
498
|
-
gatePlan,
|
|
684
|
+
gatePlan, ...(autoheal ? { autoheal } : {}), ...(automaticShardReroute ? { autohealReroute: automaticShardReroute.set.gateRerouteShard } : {}),
|
|
685
|
+
nextAttempt: attempt.attempt, priorCountKnown: attempt.priorCountKnown, preflight: 'passed' };
|
|
499
686
|
}
|
|
500
687
|
|
|
501
688
|
async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
|
|
@@ -550,7 +737,12 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
550
737
|
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), previewTip.to, deps.git);
|
|
551
738
|
verifyResumePreflight(savedRun, opts, deps, previewTip.version, previewTip.from, previewTip.to, commandRoot);
|
|
552
739
|
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, false, previewTip.to);
|
|
553
|
-
const
|
|
740
|
+
const autoheal = priorGateAutoheal(savedRun);
|
|
741
|
+
const autoReroute = !shardReroute ? automaticReroute(savedRun, autoheal, tip.changed, opts.from) : undefined;
|
|
742
|
+
const automaticShardReroute = autoReroute ? releaseResumeGateShardReroute(savedRun, { from: opts.from, shard: autoReroute }) : undefined;
|
|
743
|
+
const partial = preparePartial(tip, shardReroute || automaticShardReroute
|
|
744
|
+
? { ...opts, rerouteShard: (shardReroute ?? automaticShardReroute)!.set.gateRerouteShard, partial: false } : opts, deps);
|
|
745
|
+
if (autoheal) debug.log('release-loop.resume', 'gate-autoheal-plan', { runId: opts.runId, version: tip.version, appliedReroute: automaticShardReroute?.set.gateRerouteShard ?? null, ...autoheal });
|
|
554
746
|
// A failed preflight does not consume an observed release resume attempt.
|
|
555
747
|
const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now });
|
|
556
748
|
debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt,
|
|
@@ -565,8 +757,8 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
565
757
|
...(acceptedRegressions ? { releaseAcceptedRegressions: { requested: opts.acceptedRegressions!, acceptedRegressions } } : {}),
|
|
566
758
|
...(gateExecutor ? { releaseGateExecutor: { executor: gateExecutor.set.gateExecutor, ...(opts.gateHost !== undefined ? { host: opts.gateHost } : {}) } } : {}),
|
|
567
759
|
...(gatePodPool ? { releaseGatePodPool: gatePodPool.set.gatePodPool } : {}),
|
|
568
|
-
...(shardReroute ? { releaseGateShardReroute: { shard: shardReroute
|
|
569
|
-
...(shardReroute
|
|
760
|
+
...(shardReroute || automaticShardReroute ? { releaseGateShardReroute: { shard: (shardReroute ?? automaticShardReroute)!.set.gateRerouteShard,
|
|
761
|
+
...((shardReroute ?? automaticShardReroute)!.set.gateShardHost ? { host: (shardReroute ?? automaticShardReroute)!.set.gateShardHost } : {}),
|
|
570
762
|
...(opts.shardOs ? { os: opts.shardOs } : {}) } } : {}),
|
|
571
763
|
deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
|
|
572
764
|
return { tip, partial, state };
|
|
@@ -604,6 +796,23 @@ export interface RecoverReleaseResult {
|
|
|
604
796
|
resume?: { status: string; tip: TipRefresh; partial: PartialOutcome };
|
|
605
797
|
}
|
|
606
798
|
|
|
799
|
+
export interface RecoverReleaseLandingOptions {
|
|
800
|
+
runId: string;
|
|
801
|
+
/** The landed repair commit on main; this is the idempotency key for a landing notification. */
|
|
802
|
+
mergeSha: string;
|
|
803
|
+
from?: string;
|
|
804
|
+
commandRoot?: string;
|
|
805
|
+
partial?: boolean;
|
|
806
|
+
repoRoot?: string;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export type RecoverReleaseLandingResult = RecoverReleaseResult & { duplicate: boolean };
|
|
810
|
+
|
|
811
|
+
/** Commander represents `--no-partial` as false and its default (flag omitted) as true. */
|
|
812
|
+
export function recoverLandingPartialOption(optionValue?: boolean): boolean | undefined {
|
|
813
|
+
return optionValue === false ? false : undefined;
|
|
814
|
+
}
|
|
815
|
+
|
|
607
816
|
export interface RecoverReleaseDeps extends ResumeReleaseDeps {
|
|
608
817
|
/** `release cut-branch --append` — tests record the plan instead of pushing the branch. */
|
|
609
818
|
cutBranch?: typeof cutReleaseBranch;
|
|
@@ -619,11 +828,19 @@ export class ReleaseRecoverStopped extends Error {
|
|
|
619
828
|
}
|
|
620
829
|
}
|
|
621
830
|
|
|
831
|
+
function readRecoverJson(path: string, label: string): unknown {
|
|
832
|
+
try { return JSON.parse(readFileSync(path, 'utf8')) as unknown; }
|
|
833
|
+
catch (error) {
|
|
834
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
835
|
+
throw new Error(`release ${label} JSON을 읽을 수 없다: ${path} (${detail}) — 파일을 보존하고 원장을 올바른 JSON으로 복구한 뒤 재시도하라.`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
622
839
|
/** Append one stage result to the resume-attempts ledger. `attempts[]` keeps its shape — the stage results are added. */
|
|
623
840
|
function recordRecoverStage(entry: { root: string; runId: string; version: string; priorCountKnown: boolean; stage: RecoverStageRecord }): void {
|
|
624
841
|
const path = join(entry.root, 'release', entry.version, 'resume-attempts', `${entry.runId}.json`);
|
|
625
842
|
const saved = existsSync(path)
|
|
626
|
-
?
|
|
843
|
+
? readRecoverJson(path, 'resume attempts 원장') as { attempts?: unknown; recoveries?: unknown; priorCountKnown?: unknown }
|
|
627
844
|
: undefined;
|
|
628
845
|
if (saved !== undefined && (!saved || typeof saved !== 'object' || Array.isArray(saved) || !Array.isArray(saved.attempts)
|
|
629
846
|
|| (saved.recoveries !== undefined && !Array.isArray(saved.recoveries)))) throw new Error('release resume attempts ledger invalid');
|
|
@@ -637,16 +854,39 @@ function recordRecoverStage(entry: { root: string; runId: string; version: strin
|
|
|
637
854
|
renameSync(temporary, path);
|
|
638
855
|
}
|
|
639
856
|
|
|
857
|
+
async function acquireReleaseRecoveryLock(root: string, runId: string) {
|
|
858
|
+
const lockDir = join(root, 'release', 'recovery-locks');
|
|
859
|
+
mkdirSync(lockDir, { recursive: true });
|
|
860
|
+
// A full-gate subprocess can block this process's event loop, delaying the async lock heartbeat.
|
|
861
|
+
// The observed release gate reached 2h20m; leave a finite four-hour stale/wait window for one recovery.
|
|
862
|
+
const lock = await acquireLockAsync(join(lockDir, `${runId}.lock`), {
|
|
863
|
+
staleMs: 4 * 60 * 60_000, retryBusyMs: 1_000, maxTries: 14_400,
|
|
864
|
+
});
|
|
865
|
+
if (!lock.stillHeld()) {
|
|
866
|
+
lock.release();
|
|
867
|
+
throw new Error(`release recovery lock was lost for ${runId} — retry after inspecting the recovery ledger`);
|
|
868
|
+
}
|
|
869
|
+
return lock;
|
|
870
|
+
}
|
|
871
|
+
|
|
640
872
|
/**
|
|
641
873
|
* Repair a failed release run in one command: append dry-run (plan only) → append (fast-forward release/<v> onto the
|
|
642
874
|
* picked repairs) → resume --from export-check (preflight plan, then the graph). A failing stage stops the command:
|
|
643
875
|
* the later stages never run, and the stage results are already in the ledger for the human to read.
|
|
644
876
|
*/
|
|
645
877
|
export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: RecoverReleaseDeps = {}): Promise<RecoverReleaseResult> {
|
|
646
|
-
if (!Array.isArray(opts.pick) || !opts.pick.length) throw new Error('release recover requires at least one --pick commit');
|
|
647
878
|
const root = releaseRunUniverse({ root: deps.root });
|
|
879
|
+
const runId = safeRunId(opts.runId);
|
|
880
|
+
const lock = await acquireReleaseRecoveryLock(root, runId);
|
|
881
|
+
try {
|
|
882
|
+
return await recoverReleaseRunLocked(opts, deps, root);
|
|
883
|
+
} finally { lock.release(); }
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
async function recoverReleaseRunLocked(opts: RecoverReleaseOptions, deps: RecoverReleaseDeps, root: string): Promise<RecoverReleaseResult> {
|
|
887
|
+
if (!Array.isArray(opts.pick) || !opts.pick.length) throw new Error('release recover requires at least one --pick commit');
|
|
648
888
|
const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
|
|
649
|
-
const savedRun =
|
|
889
|
+
const savedRun = readRecoverJson(statePath, 'release run 상태') as GraphRunState;
|
|
650
890
|
if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
|
|
651
891
|
// The saved version-release record names what to repair: the release branch and the commit the run cut.
|
|
652
892
|
const versionRecord = savedRun.nodes.find((node) => node.nodeId === 'version-release');
|
|
@@ -707,8 +947,9 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
|
|
|
707
947
|
const now = (): string => (deps.now?.() ?? new Date()).toISOString();
|
|
708
948
|
const stages: RecoverStageRecord[] = [];
|
|
709
949
|
const record = (stage: RecoverStageRecord): void => {
|
|
710
|
-
|
|
711
|
-
|
|
950
|
+
const withPick = { ...stage, pick: stage.pick ?? [...opts.pick] };
|
|
951
|
+
stages.push(withPick);
|
|
952
|
+
recordRecoverStage({ root, runId: opts.runId, version, priorCountKnown: !savedRun.resume, stage: withPick });
|
|
712
953
|
};
|
|
713
954
|
const stop = (stage: RecoverStageName, error: unknown): never => {
|
|
714
955
|
throw new ReleaseRecoverStopped(stage, stages, RECOVER_STAGES.slice(RECOVER_STAGES.indexOf(stage) + 1),
|
|
@@ -733,7 +974,7 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
|
|
|
733
974
|
};
|
|
734
975
|
const ledgerPath = join(root, 'release', version, 'resume-attempts', `${opts.runId}.json`);
|
|
735
976
|
const recoveries: RecoverStageRecord[] = existsSync(ledgerPath)
|
|
736
|
-
? ((
|
|
977
|
+
? ((readRecoverJson(ledgerPath, 'resume attempts 원장') as { recoveries?: unknown[] }).recoveries ?? []) as RecoverStageRecord[]
|
|
737
978
|
: [];
|
|
738
979
|
let appendedCommit: string | undefined;
|
|
739
980
|
for (let index = recoveries.length - 1; index >= 0; index -= 1) {
|
|
@@ -843,3 +1084,69 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
|
|
|
843
1084
|
}
|
|
844
1085
|
}
|
|
845
1086
|
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Manual landing-event entrypoint. The event's merge SHA is the dedupe key; once that repair has completed its
|
|
1090
|
+
* resume stage, a redelivered event returns the recorded result without appending or running the graph again.
|
|
1091
|
+
* The event producer can call this after its own hook lands; this function does not subscribe to PR-land events.
|
|
1092
|
+
*/
|
|
1093
|
+
export async function recoverReleaseRunFromLanding(opts: RecoverReleaseLandingOptions,
|
|
1094
|
+
deps: RecoverReleaseDeps = {}): Promise<RecoverReleaseLandingResult> {
|
|
1095
|
+
if (!/^[0-9a-f]{40}$/i.test(opts.mergeSha)) throw new Error('release recover-landing requires a full 40-hex merge SHA');
|
|
1096
|
+
const root = releaseRunUniverse({ root: deps.root });
|
|
1097
|
+
const runId = safeRunId(opts.runId);
|
|
1098
|
+
// Share one run-scoped lock with manual `release recover`: the dedupe read, append, and resume must not race.
|
|
1099
|
+
const landingLock = await acquireReleaseRecoveryLock(root, runId);
|
|
1100
|
+
try {
|
|
1101
|
+
return await recoverReleaseRunFromLandingLocked(opts, deps, root, runId);
|
|
1102
|
+
} finally { landingLock.release(); }
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
async function recoverReleaseRunFromLandingLocked(opts: RecoverReleaseLandingOptions, deps: RecoverReleaseDeps,
|
|
1106
|
+
root: string, runId: string): Promise<RecoverReleaseLandingResult> {
|
|
1107
|
+
const pick = [opts.mergeSha.toLowerCase()];
|
|
1108
|
+
const statePath = join(root, 'graph-runs', 'release-loop', `${runId}.json`);
|
|
1109
|
+
if (existsSync(statePath)) {
|
|
1110
|
+
const savedRun = readRecoverJson(statePath, 'release run 상태') as GraphRunState;
|
|
1111
|
+
const versionNode = savedRun.nodes.find((node) => node.nodeId === 'version-release');
|
|
1112
|
+
const recorded = lastJsonObject(versionNode?.output);
|
|
1113
|
+
const version = typeof recorded?.version === 'string' ? recorded.version : '';
|
|
1114
|
+
if (savedRun.graphId === 'release-loop' && savedRun.runId === runId && isReleaseVersion(version)) {
|
|
1115
|
+
const ledgerPath = join(root, 'release', version, 'resume-attempts', `${runId}.json`);
|
|
1116
|
+
if (existsSync(ledgerPath)) {
|
|
1117
|
+
const ledger = readRecoverJson(ledgerPath, 'resume attempts 원장') as { recoveries?: unknown };
|
|
1118
|
+
if (ledger.recoveries !== undefined && !Array.isArray(ledger.recoveries)) throw new Error(`release resume attempts ledger invalid: ${ledgerPath}`);
|
|
1119
|
+
const records = (ledger.recoveries ?? []) as RecoverStageRecord[];
|
|
1120
|
+
const matchesPick = (entry: RecoverStageRecord): boolean => Array.isArray(entry.pick)
|
|
1121
|
+
&& entry.pick.some((sha) => typeof sha === 'string' && sha.toLowerCase() === pick[0]);
|
|
1122
|
+
let completedIndex = -1;
|
|
1123
|
+
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
1124
|
+
const entry = records[index]!;
|
|
1125
|
+
if (matchesPick(entry) && entry.stage === 'resume' && entry.outcome === 'ok') {
|
|
1126
|
+
completedIndex = index;
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
if (completedIndex >= 0) {
|
|
1131
|
+
let cycleStart = completedIndex;
|
|
1132
|
+
for (let index = completedIndex; index >= 0; index -= 1) {
|
|
1133
|
+
const entry = records[index]!;
|
|
1134
|
+
if (matchesPick(entry) && entry.stage === 'append-dry-run') {
|
|
1135
|
+
cycleStart = index;
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
const completed = records[completedIndex]!;
|
|
1140
|
+
return { runId, version, branch: `release/${version}`, fromNode: completed.from ?? opts.from ?? RECOVER_FIRST_NODE,
|
|
1141
|
+
stages: records.slice(cycleStart, completedIndex + 1).filter(matchesPick), duplicate: true };
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
const result = await recoverReleaseRunLocked({ runId, pick,
|
|
1147
|
+
...(opts.from === undefined ? {} : { from: opts.from }),
|
|
1148
|
+
...(opts.commandRoot === undefined ? {} : { commandRoot: opts.commandRoot }),
|
|
1149
|
+
...(opts.partial === undefined ? {} : { partial: opts.partial }),
|
|
1150
|
+
...(opts.repoRoot === undefined ? {} : { repoRoot: opts.repoRoot }) }, deps, root);
|
|
1151
|
+
return { ...result, duplicate: false };
|
|
1152
|
+
}
|
|
@@ -42,7 +42,10 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
|
|
|
42
42
|
return finishNode(node!, version, { outcome: 'fail', verdict: 'fail', summary });
|
|
43
43
|
}
|
|
44
44
|
const result = await applyReleaseTrafficEvent(context, event, options);
|
|
45
|
-
|
|
45
|
+
// Replaying the terminal restore after this run already restored its pre-cut traffic is a successful no-op.
|
|
46
|
+
const alreadyRestored = event === 'failed' && result.reason === 'already-restored';
|
|
47
|
+
const applied = result.applied || alreadyRestored;
|
|
48
|
+
if (!applied) {
|
|
46
49
|
const consequence = event === 'failed' && result.reason === 'published-keeps-full'
|
|
47
50
|
? 'npm 발행은 완료됐으므로 전체 트래픽을 유지합니다. 후속 발행 단계 실패를 확인하세요.'
|
|
48
51
|
: event === 'verify-complete' || event === 'npm-published'
|
|
@@ -52,8 +55,8 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
|
|
|
52
55
|
: 'gate는 성공했으나 트래픽 감속이 적용되지 않았습니다.';
|
|
53
56
|
(deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version} ${runId || 'unknown'} ${node} 무시 — ${result.reason ?? 'traffic state unchanged'}. ${consequence}`);
|
|
54
57
|
}
|
|
55
|
-
return finishNode(node!, version, { outcome:
|
|
56
|
-
summary: `${event} ${
|
|
58
|
+
return finishNode(node!, version, { outcome: applied ? 'ok' : 'fail', verdict: applied ? 'pass' : 'fail',
|
|
59
|
+
summary: `${event} ${alreadyRestored ? 'already-restored' : applied ? 'applied' : result.reason ?? 'ignored'}` });
|
|
57
60
|
} catch (error) {
|
|
58
61
|
const detail = errorResult(error).summary;
|
|
59
62
|
const isLockTimeout = detail.startsWith('traffic lock timeout:');
|
|
@@ -49,6 +49,7 @@ export interface ReleaseLoopConfig {
|
|
|
49
49
|
export interface ReleaseRunInput extends ReleaseLoopConfig {
|
|
50
50
|
version: string;
|
|
51
51
|
previousVersion: string;
|
|
52
|
+
triggerEvent?: { id: string; type: 'release.cut.due'; version: string; cutAt: string; at: string };
|
|
52
53
|
cutCommit?: string;
|
|
53
54
|
/** Required for the pod executor (the default); a host-executor run may have none. */
|
|
54
55
|
gatePodPool?: string;
|
|
@@ -143,7 +144,7 @@ export function buildReleaseRunInput(version: string, deps: UnattendedReleaseDep
|
|
|
143
144
|
|
|
144
145
|
/** Fail closed at the entry boundary, before any graph node can change a release. */
|
|
145
146
|
export async function runUnattendedRelease(
|
|
146
|
-
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
|
|
147
|
+
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string; triggerEvent?: ReleaseRunInput['triggerEvent'] },
|
|
147
148
|
deps: UnattendedReleaseDeps = {},
|
|
148
149
|
): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
|
|
149
150
|
// A dry run only builds the input and mutates nothing, so it neither creates nor waits on the OP-seat lock.
|
|
@@ -154,7 +155,7 @@ export async function runUnattendedRelease(
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
async function runUnattendedReleaseLocked(
|
|
157
|
-
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
|
|
158
|
+
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string; triggerEvent?: ReleaseRunInput['triggerEvent'] },
|
|
158
159
|
deps: UnattendedReleaseDeps,
|
|
159
160
|
): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
|
|
160
161
|
// RELEASE-BRANCH (10-06): the default cut is a release branch, so main keeps landing and the run no longer needs a
|
|
@@ -171,6 +172,7 @@ async function runUnattendedReleaseLocked(
|
|
|
171
172
|
}
|
|
172
173
|
const version = resolveRunVersion(opts.version, opts.prerelease, deps);
|
|
173
174
|
const input = buildReleaseRunInput(version, deps, opts.cutCommit, { executor: opts.gateExecutor, host: opts.gateHost });
|
|
175
|
+
if (opts.triggerEvent) input.triggerEvent = opts.triggerEvent;
|
|
174
176
|
if (opts.publishAt !== undefined) {
|
|
175
177
|
const invalid = publishAtError(opts.publishAt);
|
|
176
178
|
if (invalid) throw new Error(invalid);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHADOW-MODEL-ARMS(0.2.44 칸) · 저작 단계 그림자 «1팔» probe — 명시 실행 전용 스크립트.
|
|
3
|
+
*
|
|
4
|
+
* 저작(goal-author) 단계 그림자 1팔을 `planShadowArms` 일일 상한 안에서 «실제 호출»해
|
|
5
|
+
* 난이도·모델·예상/실제 비용·결과를 한 줄 원장 형식으로 stdout 에 출력한다.
|
|
6
|
+
* ⛔ 관측만 한다 — 라이브 LLM(Claude 구독) 호출 0 · 골/PR/착지 파일 쓰기 0.
|
|
7
|
+
* ⛔ 자동 런 훅과 다른 단계(implement·review·judge) 팔은 후속 조각이다.
|
|
8
|
+
*
|
|
9
|
+
* 사용(저장소 루트에서): bun scripts/shadow-authoring-probe.ts
|
|
10
|
+
*/
|
|
11
|
+
import { probeAuthoringShadowArm } from '../src/self-implement/goal-author.js';
|
|
12
|
+
|
|
13
|
+
/** 명시 실행 probe — 생산 저작 probe 경로(probeAuthoringShadowArm ⊕ probeShadowArm ⊕ planShadowArms)를 그대로 지나 한 줄을 낸다. */
|
|
14
|
+
export function runShadowAuthoringProbe(authorRunId = `shadow-authoring-probe-${new Date().toISOString().slice(0, 10)}`): string {
|
|
15
|
+
return probeAuthoringShadowArm(authorRunId).line;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (import.meta.main) {
|
|
19
|
+
console.log(runShadowAuthoringProbe());
|
|
20
|
+
}
|