elanous 0.2.41 → 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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{GiOLWmt4MqDZePubBjA4Q → 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/{5159.115f1c2ca471386c.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/{9048.212083de42d25be3.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/1455-530ca16c7e71baff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4527.a69f2894d1bc9f4a.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/2456.b6de0e1c05f28843.js +1 -0
- 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/{530.d26346e18089640a.js → 2690.0521c738b7749931.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4702.4a4adc1f16d7c93b.js → 2974.595d1e29d040cb6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2263.e09b6edea33bad2d.js → 3207.a756c95e9ec8c3c8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9541.eb8e2f8480c7feca.js → 3285.89ee2f298682f5e5.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{9507.7026657a2f47eb64.js → 3347.f846266b5e5f3ede.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
- package/apps/pwa/out/_next/static/chunks/{1178.1aa9cad0e7695427.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/3741.9cf76acaa2ee5298.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1661.0dd330f3d89f73f3.js → 3837.a210d7d0db8d8abd.js} +1 -1
- 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/{2fdf2931.b431eee4fb468bc3.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/4276.336d76d858fcdf1b.js +1 -0
- 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/50061469.0326382241ef3b2d.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{1115.434ae2ecb0ad986d.js → 5112.4b9f300124fab7b3.js} +3 -3
- 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/53c73f84.373b6160adfa89a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +1 -0
- 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/6131.348b7d6bdcd267ce.js +226 -0
- package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
- package/apps/pwa/out/_next/static/chunks/{2073.689dcb2680113d76.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/{1800.f7ff51dc4978a958.js → 6632.0eac1c554f8f2ef3.js} +1 -1
- 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/{2527-5c636a43c7cc5dde.js → 7279-c199ea83f34338fa.js} +1 -1
- 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/{385e4574.e9886ab32759eda3.js → 73d2eb0e.4966ad55293c801a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5471.1e611302ed3a7c53.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/{2fe2c302.41b517f88d6fe502.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/{5574.f35accf4f132b29d.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/{8050-9b364bdef8f71927.js → 8546-6f03f8ea34dd61d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5947-135e26537d80a6b9.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/{5937.c2df9e3447e1f667.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/{1971-7efb628c471dc5a0.js → 9353-12bfff0c130104cf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{7191.fdc955f7d96064a4.js → 9415.69e4a21c60c7d2db.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1219.7e17964e0e7cd8e5.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/9633.ce5bddc56784da72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5964-7e21164a8f2cf0ce.js → 9656-374232558fd528d0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6552.a3840aa7da35ad68.js → 9832.f4a98c878ecf2c0a.js} +1 -1
- 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-0e3d7b9421b79dac.js +1 -0
- 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-bf067887b3307423.js +1 -0
- 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/{a0a01613.6d1cca322238d7fe.js → b450776b.3db34d8d965f82d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{c0c1a699.0ba79f97005965d4.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/docs/goal-context/04-mock-scenarios.md +13 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/heal/heal-loop.yaml +1 -0
- package/graphs/overlays/stop-heal-ladder.yaml +113 -0
- package/graphs/units/verbs/recipes.yaml +11 -11
- package/graphs/work/research-report.yaml +10 -2
- package/package.json +1 -1
- 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/release-loop/gate-baseline-receipt.ts +47 -9
- 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 +285 -46
- 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-train-receipts.ts +122 -0
- package/scripts/release-loop/resume-release.ts +83 -8
- package/scripts/ux-sim.ts +18 -1
- 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 +13 -0
- 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 +178 -13
- package/src/auto-research/research-turn.ts +123 -50
- package/src/autopilot/pr-manager.ts +38 -7
- package/src/autopilot/pty-control-loop.ts +2 -0
- package/src/capture/tui-self-report.ts +22 -6
- package/src/chat/index.ts +1 -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/pr-land-heavy-remote.ts +32 -0
- 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/research-cli.ts +4 -5
- package/src/cli/tasks-cli.ts +7 -0
- 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 +311 -90
- package/src/core-turn/run-goal-loop.ts +18 -0
- package/src/core-turn/types.ts +2 -0
- package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
- package/src/dashboard/classic-sidetab-mission.ts +185 -0
- package/src/dashboard/index.ts +149 -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 +53 -15
- package/src/dashboard/turn-prompt-runtime.ts +4 -2
- package/src/graph-runner/graph-cli.ts +23 -7
- package/src/graph-runner/graph-tick.ts +10 -2
- package/src/graph-runner/runner.ts +1 -1
- package/src/grok/agent-search.ts +14 -4
- package/src/grok/credential.ts +28 -11
- package/src/harness/harness-pod-dispatch.ts +81 -15
- package/src/harness/harness-stop.ts +155 -11
- package/src/index.ts +34 -5
- package/src/instance/spawn-elanous.ts +6 -4
- package/src/llm.ts +166 -8
- package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
- package/src/loops/orchestrator/tick.ts +13 -0
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/harness-traversal.ts +11 -0
- package/src/pr-tool/census.ts +3 -2
- 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/repl/index.ts +43 -2
- package/src/research/axis-fanout.ts +121 -16
- package/src/research/bloom-exemplars.ts +3 -1
- 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/decompose.ts +119 -5
- package/src/self-dev/launch-preflight-text.ts +5 -6
- package/src/self-dev/orchestrate.ts +12 -2
- package/src/self-dev/review-provider-fallback.ts +9 -2
- package/src/self-dev/run-supervisor.ts +42 -4
- package/src/self-dev/run-types.ts +4 -0
- package/src/self-implement/doctor-visit.ts +88 -0
- package/src/self-implement/goal-author.ts +326 -3
- package/src/self-implement/graph-overlay-yaml.ts +7 -0
- 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 +27 -4
- package/src/self-implement/merge-train.ts +5 -0
- package/src/self-implement/orchestrator.ts +126 -11
- 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 +112 -11
- package/src/self-implement/run-stop.ts +188 -4
- package/src/self-implement/stop-autoheal.ts +13 -0
- package/src/session/chat.ts +38 -4
- package/src/session-runtime/index.ts +98 -4
- package/src/session-runtime/tier-flip.ts +3 -3
- package/src/sidetab-core/model.ts +19 -5
- 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/pod-distributor-entry.ts +3 -0
- package/src/task-agent/task-hand.ts +27 -0
- package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
- 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 +165 -45
- 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 +14 -4
- package/src/tui/sidetab/attach.ts +47 -0
- package/src/tui-client/local-chat-turn.ts +26 -13
- package/src/tui.ts +22 -0
- package/src/ux-sim/fake-kubectl.ts +37 -3
- 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/scenario.ts +116 -23
- 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/1121.667cec0bcdbb3fdb.js +0 -36
- package/apps/pwa/out/_next/static/chunks/1197-54da59d250a6e476.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1245.1cfd13b26dbd71d6.js +0 -287
- package/apps/pwa/out/_next/static/chunks/1494-f93046ff5c5a124d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1609.2b312797599b8e45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/167-0c121e4b47e0f9da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1700.cdc2bf8cc685e221.js +0 -90
- package/apps/pwa/out/_next/static/chunks/1703-78e46acad29f3cfc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1765.679b29625c8e88d4.js +0 -77
- package/apps/pwa/out/_next/static/chunks/1999-8de7b7bb36801913.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2198-557d49184104d165.js +0 -1
- package/apps/pwa/out/_next/static/chunks/221-440ce67fa9f5c839.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2427-8ed8e98624cbd664.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2457-725c5827bc003795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2491-2718b15a5ccb75a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2692-1027cb16fe4ccf49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2743.5d3f76948be32b34.js +0 -41
- package/apps/pwa/out/_next/static/chunks/2773.0e80b9d325fb5635.js +0 -32
- package/apps/pwa/out/_next/static/chunks/2873-0f3cb56c8e3528ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3097.864e92bdaa2f4d15.js +0 -79
- package/apps/pwa/out/_next/static/chunks/3136-d6870ba63300a93e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3173-0289113d434e83b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3230-49298c3b73ef2268.js +0 -1
- package/apps/pwa/out/_next/static/chunks/325-dd9208a1a0ab7ce4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3283.528f430dd043fb4e.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3295.cc8cb13b8f0dae1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3688-86e3b88abf18cb3b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3804-7220467a5473a8df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3808.3e1e247ce7bdf889.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3927.b5473ad109da44c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3934-98ea94832e194796.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4133.2056bb4d78f621af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4156.ba4de4a3664f04b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4297-239ca82f293faab0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4320-dcc5a7734126f4c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4434.94b4c552a2d91e69.js +0 -148
- package/apps/pwa/out/_next/static/chunks/4579.03e4092f73950965.js +0 -226
- package/apps/pwa/out/_next/static/chunks/4667.97e31aa38c95c475.js +0 -309
- package/apps/pwa/out/_next/static/chunks/4815.481501c57b3568d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/487-d16e698ef14662d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4888-7a38bf522c9cc6fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5034-30e429d6bb086881.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5040-35c494e60dadd97d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/50c6ab1e.81a96e419350909d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5154.4b6a836179356f30.js +0 -94
- package/apps/pwa/out/_next/static/chunks/5228-a8200df486278f2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5379.97af106d09566f69.js +0 -8
- package/apps/pwa/out/_next/static/chunks/53aeb4ee-51c92f8d683f5220.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5479-b6372e762719f92f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5534-699c5cbcbc96c5d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5701.9c4e8f250312eeee.js +0 -24
- package/apps/pwa/out/_next/static/chunks/5744.b9415e18ad623100.js +0 -23
- package/apps/pwa/out/_next/static/chunks/5892.2526d962339662d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5941.8210cc93c9202c0a.js +0 -286
- package/apps/pwa/out/_next/static/chunks/5975.4004f820ae3cecd5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6142-7ccd48795d7abd55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6160.bf9177b8c1bfb243.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6398.71ba0690c8027555.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6492.2bd5c10edc1a064e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6736.1e36d3ac5fa5e41d.js +0 -24
- package/apps/pwa/out/_next/static/chunks/6874.d5cf7eb8edb2cabe.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6900.3ccbe069bdd4556a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/71327eb4.add50b365f94bf3a.js +0 -122
- package/apps/pwa/out/_next/static/chunks/7136.deca3e3a2ddaa374.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7139-ee9f8ed2677a40b3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7423-e0aa5d9e2962120e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7453.afc1ea7e05d56a8c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7503-5e6a3d7fe67e4a0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7761-476fd7bf768abd34.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7975.eafb12097a0a9e62.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8152.64e253c35cccfaae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8190.620f0231b3924bcb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/83184dcd-0ee6247ff41eb3f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8448.45b6b1f1a99eeb35.js +0 -104
- package/apps/pwa/out/_next/static/chunks/8464-e24f10451b658aac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8477.e4c944bb3aa5d2f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8710.ca385257d5ca422e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8830.627ccf294ec2465c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8880.64c445592f04acaf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8981.c706964c8ba27aff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9053.bd0fffaff8d3f836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9161-c66e723d7ec9d682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9185.1ecdf9b65c0bb684.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9356.fccd5f58086b4ff5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9446.dc0406109371d329.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9506.dcf25d467a42627e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9577.ed7582186efa5482.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9579.4a8169600fff2058.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9642-ff2e039eb62e3b92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9659-8f2323348113ad80.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9724.4c5623af2db43153.js +0 -34
- package/apps/pwa/out/_next/static/chunks/9834.63ab2bbc3153172e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9869.81a1e24ca682d5a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/addc2b32.8c4ff8f0aefe987d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-8d9d76ea322d855c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed2c4e59f9f1fe21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-2dc99e03c5d76ca0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b5a3a788330e920f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-14949db884bfa672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ea76c85b36388a97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-01716d20af62d325.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-62baf6881a64b1e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f89ba590c2af8afa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-22f8b0f78d9637e5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-817f9d7e6820be80.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-ffb9bc0d2650249a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-f6d0e64873c0f629.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-8bfa0d4ba23a209d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-f5060462249e32c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-8d9d76ea322d855c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2049f8b1ef79117.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1446c5fa93907f11.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-b48a7a07c5204156.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-e974b8c4ca47614c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-8a919b869bd28594.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-d31b8de6bfcd72ed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-64765f817909db29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-d128c8569a36ee9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-b2679d357e9b9270.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-6fabec98a9e7b269.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-048f30149dcf1349.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-9113181d6b5a2725.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-40f9e19149024298.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-1e7a755a13227611.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3c8f8b67a203cf07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-18032f5437215062.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-da3b739192fd0ee3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d332545571bd6fd1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-5237aa5b3f3b8ec5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a142dfb0331a05f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d16e56c3989c147e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6b6593600869e2cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-8d9d76ea322d855c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-29e8b8a8396b55cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b3414d856a1a7e2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c3affa52b7ce961b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f21dddf9f95b6fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-f5a30a24745bf088.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-8c8569a09b4d4e34.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-2ce32722c0243483.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4767b0d9c0a98c6c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-116ee1339c387a75.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-35e8c50ca4ef7ffc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-52e17113356bb891.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7458ae05ed2ac1a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/b2271e54.c1de3347976e90b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ad6c9dd524ad6ef8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-2ddd1f2b44d6aa59.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-e3d82bdc358d20c5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-71d5aa3896e1655f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-03ffc54e00438546.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-56bb89af5a611d0b.js +0 -1
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
- /package/apps/pwa/out/_next/static/{GiOLWmt4MqDZePubBjA4Q → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
|
@@ -17,9 +17,13 @@ import { gateHistory } from './gate-history.js';
|
|
|
17
17
|
import { baselineFromCutLogs } from './baseline-from-logs';
|
|
18
18
|
import { mergePartialFailures, readPartialPlanResult, testFileOfId, verifyDocsOnlyCarry } from './gate-partial';
|
|
19
19
|
import { planGateRadiusShadow, shadowActual, writeGateRadiusShadow, type GateRadiusShadowPlan } from './gate-radius-shadow';
|
|
20
|
-
import { planHardRehearsalRadius, partialGateImagesMatch, preflightGateImageDigest } from './gate-radius-live';
|
|
21
|
-
import { actualGatePodImageDigest, gateBaselineReceipt, gateEnvFingerprint, gateGraphPins, gateSourcePins,
|
|
20
|
+
import { planHardRehearsalRadius, partialGateImagesMatch, preflightGateExecutionIdentity, preflightGateImageDigest } from './gate-radius-live';
|
|
21
|
+
import { actualGatePodImageDigest, gateBaselineReceipt, gateEnvFingerprint, gateGraphPins, gateSourcePins, parseGatePodRuntimeIdentity,
|
|
22
22
|
POD_SWEEP_INTEGRATION_ONLY } from './gate-baseline-receipt';
|
|
23
|
+
import { gateImagePinPath, readGateImagePin, resolveGateImagePin, writeGateImagePin } from './gate-image-pin.js';
|
|
24
|
+
import { gateReceiptRegistryPath, planGateTrainReceiptCarry } from './gate-train-receipts.js';
|
|
25
|
+
import { gateEnvFingerprint as gateReceiptEnvFingerprint, gateLockfileHash } from '../../src/self-implement/gate-receipt-registry.js';
|
|
26
|
+
import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
|
|
23
27
|
export { POD_SWEEP_INTEGRATION_ONLY } from './gate-baseline-receipt';
|
|
24
28
|
import { logPlanWalltime, planDurations, readFileMemory } from './shard-plan';
|
|
25
29
|
import { defaultGateHostMirrorReaders, resolveGateHostMirror, type GateHostMirror, type GateHostMirrorReaders } from './gate-host-mirror';
|
|
@@ -30,7 +34,7 @@ import { readJunitFileSeconds } from './gate-method';
|
|
|
30
34
|
import { deriveCdpTestPatterns } from '../test-deterministic';
|
|
31
35
|
import { emitNodeResult, readGraphContext } from './node-verdict.js';
|
|
32
36
|
import { isGateExecutor, type GateExecutor } from './gate-executor.js';
|
|
33
|
-
import { runPodCommand, parsePodCpu, podCpuMillis, resolvePodCpu, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
37
|
+
import { runPodCommand, parsePodCpu, podCpuMillis, resolvePodCpu, podCommandImageFor, type RunPodCommandOptions, type PodCommandResult, type PodCpu } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
34
38
|
import { POD_BUN_CACHE_HOST_PATH, parseInstallSeconds, podBunCacheVolume } from '../../src/task-orchestrator/surfaces/pod-bun-cache.js';
|
|
35
39
|
import { PodPoolScheduler, parsePodPool, checkPodPool, defaultRemoteRun, snapshotWarmPoolMembers, type PodPoolMember } from '../../src/task-orchestrator/surfaces/pod-pool.js';
|
|
36
40
|
import { POD_INSTALL_SLOTS_DEFAULT, POD_INSTALL_SLOTS_HOST_PATH, installSlotScript } from '../../src/task-orchestrator/surfaces/pod-install-slots.js';
|
|
@@ -45,7 +49,7 @@ import { getUserConfig } from '../../src/user-config.js';
|
|
|
45
49
|
import { beginGateShardAttempt, finalizeTerminalGateShards, gateShardsPath, recordGateShardsVerdict, writeGateShards, type GateJobTerminal, type GateShard } from '../../src/release-loop/gate-shards.js';
|
|
46
50
|
import { appendGateShardLedger, gateShardLedgerPath, type GateShardLedgerRow } from '../../src/release-loop/gate-shard-ledger.js';
|
|
47
51
|
import { markGateShardUnmeasured, nextGateShardAttempt, readGateShardManifest, readGateShardResult, saveGateShardResult } from './gate-shard-receipts.js';
|
|
48
|
-
import { planGateShardDispatch, type GateShardReceipt } from './gate-shard-dispatch.js';
|
|
52
|
+
import { planGateShardDispatch, summarizeGateProgress, type GateProgressGroup, type GateShardReceipt } from './gate-shard-dispatch.js';
|
|
49
53
|
import { defaultKubectl } from '../../src/task-orchestrator/surfaces/self-implement-pod.js';
|
|
50
54
|
import { executeTuiNodeCheck, type ExecutedTuiCheck } from './tui-node-checks.js';
|
|
51
55
|
import { runWithReleaseGateTraffic } from './gate-traffic-mode.js';
|
|
@@ -113,12 +117,28 @@ export interface GateTools {
|
|
|
113
117
|
podCommand?: (options: RunPodCommandOptions) => Promise<PodCommandResult>;
|
|
114
118
|
/** Kubernetes client (pool readiness · Pod lifecycle · Job log tail · failure evidence); absent = `defaultKubectl`. */
|
|
115
119
|
kubectl?: typeof defaultKubectl;
|
|
120
|
+
/** Mock-only imageID observation seam; production reads the completed Job's child imageID from Kubernetes. */
|
|
121
|
+
podImageDigest?: (input: { job: PodCommandResult; host: string }) => string | null;
|
|
116
122
|
/** SSH transport behind {@link GateRunner.command} on a remote host; absent = a real `ssh` child. */
|
|
117
123
|
ssh?: (host: string, script: string, limitMs?: number) => { rc: number; output: string; timedOut?: boolean };
|
|
118
124
|
/** SSH reachability probe of a multi-host roster; absent = `defaultRemoteRun`. */
|
|
119
125
|
remoteRun?: typeof defaultRemoteRun;
|
|
120
126
|
/** Pool admission a shard waits on (leases ⊕ a cluster reading); absent = the gate's own pool admission. */
|
|
121
127
|
admission?: GateAdmission;
|
|
128
|
+
/** The live progress heartbeat scheduler; tests inject a deterministic tick, production uses an unref'd interval. */
|
|
129
|
+
progressScheduler?: GateProgressScheduler;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type GateProgressScheduler = (tick: () => void, intervalMs: number) => () => void;
|
|
133
|
+
const scheduleGateProgress: GateProgressScheduler = (tick, intervalMs) => {
|
|
134
|
+
const timer = setInterval(tick, intervalMs);
|
|
135
|
+
timer.unref();
|
|
136
|
+
return () => clearInterval(timer);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export interface GateSweepProgress {
|
|
140
|
+
executor: 'host' | 'pod';
|
|
141
|
+
groups: readonly GateProgressGroup[];
|
|
122
142
|
}
|
|
123
143
|
export interface GateRunner {
|
|
124
144
|
command(cmd: string, args: string[], cwd: string, limitMs?: number): Promise<CommandResult>;
|
|
@@ -128,7 +148,7 @@ export interface GateRunner {
|
|
|
128
148
|
/** One checkout/install for a small independent file group; each file retains its own rc and log. */
|
|
129
149
|
recheckPodBatch?(files: readonly string[], commit: string, pod: NonNullable<GateOptions['pod']>, timeoutMs: number): Promise<Map<string, CommandResult>>;
|
|
130
150
|
/** `only` (GATE-PARTIAL) restricts the sweep to these test files; absent = every test file. */
|
|
131
|
-
sweep(tree: string, logDir?: string, pod?: GateOptions['pod'], only?: readonly string[]): Promise<CommandResult>;
|
|
151
|
+
sweep(tree: string, logDir?: string, pod?: GateOptions['pod'], only?: readonly string[], onProgress?: (progress: GateSweepProgress) => void): Promise<CommandResult>;
|
|
132
152
|
add(tree: string, commit: string): Promise<void>;
|
|
133
153
|
remove(tree: string): Promise<void>;
|
|
134
154
|
snapshot(tree: string, commit: string): Promise<void>;
|
|
@@ -137,6 +157,7 @@ export interface GateRunner {
|
|
|
137
157
|
remoteMirror?: string;
|
|
138
158
|
/** GATE-FLOW-SIM — the clock this runner measures with (the injected fake clock in a scenario); absent = `Date.now`. */
|
|
139
159
|
now?(): number;
|
|
160
|
+
progressScheduler?: GateProgressScheduler;
|
|
140
161
|
}
|
|
141
162
|
|
|
142
163
|
/** One watchdog tick. Batch Kubernetes reads per host and trust only the current attempt's result marker. */
|
|
@@ -211,6 +232,8 @@ export interface GateOptions {
|
|
|
211
232
|
/** Only a direct hard rehearsal may pin graph bytes from its measured Git commit. */
|
|
212
233
|
rehearsalBaseline?: boolean;
|
|
213
234
|
forceFreeze?: boolean;
|
|
235
|
+
/** Force all release tests to run, bypassing every reusable partial plan. */
|
|
236
|
+
full?: boolean;
|
|
214
237
|
version: string;
|
|
215
238
|
baselineVersion?: string;
|
|
216
239
|
baselineCommit?: string;
|
|
@@ -227,6 +250,9 @@ export interface GateOptions {
|
|
|
227
250
|
recheckFileLimit?: number;
|
|
228
251
|
/** freshShards — GATE-SHARD-RESUME off: every shard starts a Pod even when this logDir already holds its result. */
|
|
229
252
|
pod?: { pool: string; shards?: number; shardTimeoutSeconds?: number; durationSource?: string; bunCache?: string; freshShards?: boolean;
|
|
253
|
+
/** One immutable container image identity for this gate graph run and all its resumes. */
|
|
254
|
+
pinnedImageDigest?: string;
|
|
255
|
+
pinnedRunId?: string;
|
|
230
256
|
/** Remeasure one named shard, or all incomplete shards with auto. Complete siblings keep their receipts. */
|
|
231
257
|
rerouteShard?: string | 'auto';
|
|
232
258
|
/** Pool context for the rerouted shard; the planner otherwise distributes missing shards over pool contexts. */
|
|
@@ -346,7 +372,7 @@ export interface GateResult {
|
|
|
346
372
|
/** GATE-WARM-BOOT-SCAN — observation only: the cut sweep's test selection time (pod sweep · absent otherwise). */
|
|
347
373
|
selectionMs?: number;
|
|
348
374
|
error?: string;
|
|
349
|
-
stalledShards?: Array<{ shard: number; files: string[]; reason: 'incomplete' | 'no-output' | 'job-failed' | 'exit-code' | 'unattributed' | 'timeout' | 'admission-timeout' | 'admission-error'; lastFile?: string; summaryFailures?: number; namedFailures?: number; detail?: string;
|
|
375
|
+
stalledShards?: Array<{ shard: number; files: string[]; reason: 'incomplete' | 'no-output' | 'job-failed' | 'exit-code' | 'unattributed' | 'timeout' | 'admission-timeout' | 'admission-error' | 'image-pin-mismatch'; lastFile?: string; summaryFailures?: number; namedFailures?: number; detail?: string;
|
|
350
376
|
/** Pool member that ran the final unmeasured attempt; enables a bounded cross-host tail remeasure. */
|
|
351
377
|
lastHost?: string;
|
|
352
378
|
/** Failure of the optional cross-host tail attempt; original stall fields remain authoritative. */
|
|
@@ -360,6 +386,7 @@ export interface GateResult {
|
|
|
360
386
|
partialFailures?: SweepFailures;
|
|
361
387
|
/** GATE-PARTIAL — this verdict re-ran only these files and carried the prior gate's results for the rest. */
|
|
362
388
|
partial?: { priorCommit: string; files: number };
|
|
389
|
+
trainReceiptReuse?: { carried: number; total: number; receipts: number; reasons: string[] };
|
|
363
390
|
/** A failure that passed an evidence-checked isolated re-run: warning (flaky under load), not an introduced regression. */
|
|
364
391
|
loadFlaky?: string[];
|
|
365
392
|
/** File-level unhandled errors that reproduced or could not be measured in isolation. They keep the gate red. */
|
|
@@ -807,6 +834,7 @@ export function gateShardShell(o: { parts: readonly (readonly string[])[]; cdpPa
|
|
|
807
834
|
...(o.beatSeconds ? gateBeatScript(o.beatSeconds, o.installSlots > 0) : []),
|
|
808
835
|
...(o.cachePrefix ? [o.cachePrefix] : []),
|
|
809
836
|
gateEnvBaselineLine(),
|
|
837
|
+
`bun -e ${quote("const {createHash}=require('node:crypto');const keys=['ELANOUS_TEST_FAKE_LLM','LANG','LC_ALL','TZ'];const e=Object.fromEntries(keys.sort().map(k=>[k,process.env[k]??'']));const envFingerprint=createHash('sha256').update(JSON.stringify(e)).digest('hex');console.log(JSON.stringify({bunVersion:Bun.version,platform:process.platform,architecture:process.arch,envFingerprint}));")} > "$O/runtime-identity.json" 2> "$O/runtime-identity.err" || rm -f "$O/runtime-identity.json"`,
|
|
810
838
|
...(o.rulesBaseSha ? [`export ELANOUS_GATE_BASE_SHA=${quote(o.rulesBaseSha)}`] : []),
|
|
811
839
|
// Seconds since this container's PID 1 started (the clone ran before this shell) — 0 when /proc cannot tell.
|
|
812
840
|
`pod_age=$(awk -v t="$(cut -d' ' -f22 /proc/1/stat 2>/dev/null)" -v hz="$(getconf CLK_TCK 2>/dev/null)" '{ if (t > 0 && hz > 0) print int($1 - t / hz); else print 0 }' /proc/uptime 2>/dev/null)`,
|
|
@@ -1166,6 +1194,7 @@ export interface ShardArtifacts {
|
|
|
1166
1194
|
finished: string[];
|
|
1167
1195
|
/** Raw logs of the parts that did not finish (diagnostics · last started file). */
|
|
1168
1196
|
unfinishedLog?: string;
|
|
1197
|
+
runtimeIdentity?: ReturnType<typeof parseGatePodRuntimeIdentity>;
|
|
1169
1198
|
/** Original failure ids whose one same-Pod standalone retry completed cleanly. */
|
|
1170
1199
|
loadFlaky?: string[];
|
|
1171
1200
|
/** Files for which a same-Pod retry completed, even if it remained failing. */
|
|
@@ -1174,16 +1203,20 @@ export interface ShardArtifacts {
|
|
|
1174
1203
|
/** Reads a shard Pod's outbox: the part layout ({@link gateShardShell}) or the older single-run one (shard.log · shard.rc · junit.xml). */
|
|
1175
1204
|
export function readShardArtifacts(dir: string, parts: readonly (readonly string[])[]): ShardArtifacts {
|
|
1176
1205
|
const read = (name: string) => { const path = join(dir, name); return existsSync(path) ? readFileSync(path, 'utf8') : undefined; };
|
|
1206
|
+
const identityText = read('runtime-identity.json');
|
|
1207
|
+
const runtimeIdentity = identityText === undefined ? undefined : parseGatePodRuntimeIdentity(identityText) ?? undefined;
|
|
1177
1208
|
const rcOf = (text: string | undefined) => { const value = text?.trim() ?? ''; return /^\d+$/.test(value) && Number.isSafeInteger(Number(value)) ? Number(value) : undefined; };
|
|
1178
1209
|
const installRc = rcOf(read('install.rc'));
|
|
1179
1210
|
const install = read('install.log');
|
|
1180
1211
|
if (installRc === undefined && install === undefined) {
|
|
1181
1212
|
const junit = read('junit.xml');
|
|
1182
1213
|
const rc = rcOf(read('shard.rc'));
|
|
1183
|
-
return { output: read('shard.log'), ...(junit !== undefined ? { junit } : {}), ...(rc !== undefined ? { rc } : {}),
|
|
1214
|
+
return { output: read('shard.log'), ...(junit !== undefined ? { junit } : {}), ...(rc !== undefined ? { rc } : {}),
|
|
1215
|
+
...(runtimeIdentity ? { runtimeIdentity } : {}), timedOut: false, finished: [] };
|
|
1184
1216
|
}
|
|
1185
1217
|
const timedOut = read('shard.timeout') !== undefined;
|
|
1186
|
-
if (installRc !== 0) return { output: install, ...(installRc !== undefined ? { rc: installRc } : {}),
|
|
1218
|
+
if (installRc !== 0) return { output: install, ...(installRc !== undefined ? { rc: installRc } : {}),
|
|
1219
|
+
...(runtimeIdentity ? { runtimeIdentity } : {}), timedOut, finished: [] };
|
|
1187
1220
|
const good: Array<{ log: string; junit?: string; rc: 0 | 1; files: readonly string[]; loadFlaky?: string[]; loadFlakyAttemptedFiles?: string[] }> = [];
|
|
1188
1221
|
const bad: string[] = [];
|
|
1189
1222
|
const exitAnomaly: string[] = [];
|
|
@@ -1261,6 +1294,7 @@ export function readShardArtifacts(dir: string, parts: readonly (readonly string
|
|
|
1261
1294
|
...(attemptedFiles.size ? { loadFlakyAttemptedFiles: [...attemptedFiles].sort() } : {}),
|
|
1262
1295
|
...(bad.length ? { unfinishedLog: bad.join('\n') } : {}),
|
|
1263
1296
|
...(exitAnomaly.length && !otherBad ? { exitCodeOnly: exitAnomaly } : {}),
|
|
1297
|
+
...(runtimeIdentity ? { runtimeIdentity } : {}),
|
|
1264
1298
|
};
|
|
1265
1299
|
}
|
|
1266
1300
|
|
|
@@ -1447,6 +1481,7 @@ class StalledPodShards extends Error {
|
|
|
1447
1481
|
const exitCode = stalledShards.filter((item) => item.reason === 'exit-code');
|
|
1448
1482
|
super(`pod sweep incomplete: stalled shards ${stalledShards.map(({ shard }) => shard).join(', ')}`
|
|
1449
1483
|
+ (unmeasured.length ? ` (unmeasured: no test output within ${unmeasured[0]!.stall!.seconds}s of checkout — shards ${[...new Set(unmeasured.map(({ shard }) => shard))].join(', ')})` : '')
|
|
1484
|
+
+ (stalledShards.some((item) => item.reason === 'image-pin-mismatch') ? ` (image pin mismatch: ${stalledShards.filter((item) => item.reason === 'image-pin-mismatch').map((item) => item.detail ?? `shard ${item.shard}`).join('; ')})` : '')
|
|
1450
1485
|
+ (exitCode.length ? ` (exit-code anomaly: 0 fail but exit ≠ 0/1 — ${[...new Set(exitCode.flatMap(({ files }) => files))].join(', ')})` : '')
|
|
1451
1486
|
+ (abortedShards ? ` (중단 조각 ${abortedShards}: mid-run-stall)` : ''));
|
|
1452
1487
|
}
|
|
@@ -1676,7 +1711,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1676
1711
|
if (!/^[0-9a-f]{40}$/.test(base)) throw new Error(`rules gate base invalid for ${commit.slice(0, 12)}: ${base}`);
|
|
1677
1712
|
return base;
|
|
1678
1713
|
};
|
|
1679
|
-
const podSweep = async (tree: string, logDir: string | undefined, pod: NonNullable<GateOptions['pod']>, only?: readonly string[]): Promise<CommandResult> => {
|
|
1714
|
+
const podSweep = async (tree: string, logDir: string | undefined, pod: NonNullable<GateOptions['pod']>, only?: readonly string[], onProgress?: (progress: GateSweepProgress) => void): Promise<CommandResult> => {
|
|
1680
1715
|
const sweepId = randomUUID();
|
|
1681
1716
|
const shardCount = pod.shards ?? 24;
|
|
1682
1717
|
const isolationConcurrency = pod.isolationConcurrency ?? 4;
|
|
@@ -1936,6 +1971,13 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1936
1971
|
// GATE-LIVE-OBS — root rows stay stable; each recursive split/retry and tail gets its own row.
|
|
1937
1972
|
// Otherwise a finished root Job can hide a still-running child (or tail) behind 80/80 done.
|
|
1938
1973
|
const board: GateShard[] = shards.map((item, index) => ({ id: `pod-${index}`, state: 'pending', plannedMin: Math.round(item.plannedSeconds / 6) / 10 }));
|
|
1974
|
+
const reportProgress = () => onProgress?.({ executor: 'pod', groups: board.map((row) => ({
|
|
1975
|
+
id: row.id, state: row.state === 'retry' ? 'running' : row.state,
|
|
1976
|
+
...(row.host ? { host: row.host } : {}),
|
|
1977
|
+
...(row.startedAt ? { startedAtMs: Date.parse(row.startedAt) } : {}),
|
|
1978
|
+
...(row.endedAt ? { endedAtMs: Date.parse(row.endedAt) } : {}),
|
|
1979
|
+
})) });
|
|
1980
|
+
reportProgress();
|
|
1939
1981
|
const filesById = new Map(shards.map((item, index) => [`pod-${index}`, [...item.files]]));
|
|
1940
1982
|
const detailsById = new Map<string, Partial<GateShardLedgerRow>>();
|
|
1941
1983
|
const observeShard = (row: GateShard) => {
|
|
@@ -1972,6 +2014,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
1972
2014
|
for (const key of clear) delete next[key];
|
|
1973
2015
|
if (JSON.stringify(next) === JSON.stringify(row)) return;
|
|
1974
2016
|
board[index] = next;
|
|
2017
|
+
reportProgress();
|
|
1975
2018
|
observeShard(next);
|
|
1976
2019
|
flushBoard();
|
|
1977
2020
|
};
|
|
@@ -2075,6 +2118,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2075
2118
|
let podStages: Record<string, number> | undefined;
|
|
2076
2119
|
let podLifecycle: Record<string, string> | undefined;
|
|
2077
2120
|
let imageDigest: string | null = null;
|
|
2121
|
+
let runtimeIdentity: ReturnType<typeof parseGatePodRuntimeIdentity> | undefined;
|
|
2078
2122
|
let podJobName: string | undefined;
|
|
2079
2123
|
let batchArtifactsComplete = true;
|
|
2080
2124
|
let junit: string | undefined;
|
|
@@ -2086,6 +2130,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2086
2130
|
let lastFile: string | undefined;
|
|
2087
2131
|
let jobFailed = false;
|
|
2088
2132
|
let jobError = '';
|
|
2133
|
+
let imagePinMismatch = false;
|
|
2089
2134
|
let failureCapture: ReturnType<NonNullable<NonNullable<GateOptions['pod']>['failureEvidence']>> | undefined;
|
|
2090
2135
|
let jobNameForCapture: string | undefined;
|
|
2091
2136
|
let jobContextForCapture: string | undefined;
|
|
@@ -2198,7 +2243,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2198
2243
|
// poolOverride is the injected scheduler used by isolated tests; it has no authenticated kubectl
|
|
2199
2244
|
// context. Those sweeps intentionally cannot mint a valid baseline receipt (missing observed digest).
|
|
2200
2245
|
// Production pool sweeps always use this Kubernetes lookup; lookup failure is receipt-invalid only.
|
|
2201
|
-
if (
|
|
2246
|
+
if (tools.podImageDigest) {
|
|
2247
|
+
imageDigest = tools.podImageDigest({ job, host: placedOn ?? 'unobserved' });
|
|
2248
|
+
} else if (!poolOverride && placedOn) {
|
|
2202
2249
|
try {
|
|
2203
2250
|
const got = kubectl(['--context', placedOn, '-n', 'elanous-test', 'get', 'pods', '-l', `job-name=${job.job}`, '-o', 'json']);
|
|
2204
2251
|
if (got.status === 0) {
|
|
@@ -2207,10 +2254,18 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2207
2254
|
}
|
|
2208
2255
|
} catch { /* timings never change the verdict */ }
|
|
2209
2256
|
}
|
|
2257
|
+
if (pod.pinnedImageDigest && imageDigest !== pod.pinnedImageDigest) {
|
|
2258
|
+
imagePinMismatch = true;
|
|
2259
|
+
debug.log('release-loop.gate', 'image-pin-mismatch', { runId: pod.pinnedRunId ?? null,
|
|
2260
|
+
shard, branch, host: placedOn ?? null, pinnedDigest: pod.pinnedImageDigest, actualDigest: imageDigest,
|
|
2261
|
+
decision: 'reject-job' }, { level: 'error' });
|
|
2262
|
+
throw new Error(`gate image pin mismatch: expected ${pod.pinnedImageDigest}, got ${imageDigest ?? 'unmeasured'}`);
|
|
2263
|
+
}
|
|
2210
2264
|
jobExitCode = job.exitCode;
|
|
2211
2265
|
if (job.exitCode !== 0) captureFailure();
|
|
2212
2266
|
batchArtifactDir = job.artifactsDir;
|
|
2213
2267
|
const read = readShardArtifacts(job.artifactsDir, parts);
|
|
2268
|
+
runtimeIdentity = read.runtimeIdentity;
|
|
2214
2269
|
if (logDir) {
|
|
2215
2270
|
mkdirSync(logDir, { recursive: true });
|
|
2216
2271
|
let retryArtifactIndex = 0;
|
|
@@ -2319,7 +2374,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2319
2374
|
...(failureCapture.logTail !== undefined ? { logTail: redactedGateLogTail(failureCapture.logTail) } : {}),
|
|
2320
2375
|
...(failureCapture.error ? { error: redactSecretText(failureCapture.error) } : {}),
|
|
2321
2376
|
} } : {}),
|
|
2322
|
-
...(imageDigest ? { imageDigest } : {}),
|
|
2377
|
+
...(imageDigest ? { imageDigest } : {}), ...(runtimeIdentity ? { runtimeIdentity } : {}),
|
|
2323
2378
|
...(fileSeconds !== undefined ? { fileSeconds, testSeconds } : {}), ...(testWallSeconds !== undefined ? { testWallSeconds } : {}),
|
|
2324
2379
|
...(podOverheadSeconds !== undefined ? { podOverheadSeconds } : {}),
|
|
2325
2380
|
...(unfinishedLogBytes !== undefined ? { unfinishedLogBytes } : {}),
|
|
@@ -2353,6 +2408,7 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2353
2408
|
let namedFailures: number | undefined;
|
|
2354
2409
|
let unattributedDetail: string | undefined;
|
|
2355
2410
|
if (descent === 'split') reason = 'incomplete';
|
|
2411
|
+
else if (imagePinMismatch) reason = 'image-pin-mismatch';
|
|
2356
2412
|
// GATE-POD-STALL-FAILFAST: stopped with no test output — the existing «no output» reason (one fresh retry).
|
|
2357
2413
|
else if (stall) reason = 'no-output';
|
|
2358
2414
|
else if (timedOut) reason = 'timeout';
|
|
@@ -2471,6 +2527,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2471
2527
|
}
|
|
2472
2528
|
}
|
|
2473
2529
|
if (reason) {
|
|
2530
|
+
if (reason === 'image-pin-mismatch') return { runs: [], stalled: [{ shard, files: paths, reason,
|
|
2531
|
+
detail: jobError.slice(0, 300), ...(placedOn ? { lastHost: placedOn } : {}) }] };
|
|
2474
2532
|
// GATE-SKIP-KNOWN-RETRY — a shard whose summary is complete but whose failures could not be read back (unattributed)
|
|
2475
2533
|
// and whose every named failure is one the previous release already had is not re-run: re-running only re-measures
|
|
2476
2534
|
// known failures (0.2.18 cut: 109 retry Pods · 1,840 shard-minutes, 4x the first pass). Those failures go to the
|
|
@@ -2623,7 +2681,10 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2623
2681
|
// has no base/environment/image binding and cannot authorize this cross-host measurement.
|
|
2624
2682
|
// The sixth runShardImpl argument is `fresh`: a missing receipt must launch a new Job.
|
|
2625
2683
|
const freshForReceipt = !!receiptRoot || pod.freshShards === true;
|
|
2626
|
-
const done = await withRootSlot(() =>
|
|
2684
|
+
const done = await withRootSlot(() => {
|
|
2685
|
+
markShard(shard, { state: 'running', startedAt: new Date().toISOString() }, ['waitReason']);
|
|
2686
|
+
return runShard(item.files, shard, 0, '', false, freshForReceipt);
|
|
2687
|
+
});
|
|
2627
2688
|
shardDurations[shard] = clock() - shardStartedAt;
|
|
2628
2689
|
const state = !done.stalled.length ? 'done' as const : done.stalled.some((stall) => stall.reason === 'timeout') ? 'timeout' as const : 'failed' as const;
|
|
2629
2690
|
markShard(shard, { state, rc: done.stalled.length || done.runs.some((run) => run.fail || run.errors) ? 1 : 0, endedAt: new Date().toISOString() }, ['waitReason']);
|
|
@@ -2815,6 +2876,33 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2815
2876
|
await quarantine('gate-job-failed');
|
|
2816
2877
|
throw error;
|
|
2817
2878
|
}
|
|
2879
|
+
if (pod.pinnedImageDigest) {
|
|
2880
|
+
let actualDigest: string | null = null;
|
|
2881
|
+
if (tools.podImageDigest) {
|
|
2882
|
+
actualDigest = tools.podImageDigest({ job, host: producer?.context ?? 'unobserved' });
|
|
2883
|
+
} else if (!poolOverride) {
|
|
2884
|
+
// Rechecks launch through the pool scheduler too; find the Job in its configured
|
|
2885
|
+
// contexts so a recheck cannot silently escape the run-level image pin.
|
|
2886
|
+
const contexts = [...new Set(parsePodPool(pod.pool).map((member) => member.context))];
|
|
2887
|
+
const observed: string[] = [];
|
|
2888
|
+
for (const context of contexts) {
|
|
2889
|
+
try {
|
|
2890
|
+
const got = kubectl(['--context', context, '-n', 'elanous-test', 'get', 'pods', '-l', `job-name=${job.job}`, '-o', 'json']);
|
|
2891
|
+
if (got.status !== 0) continue;
|
|
2892
|
+
const digest = actualGatePodImageDigest(got.stdout);
|
|
2893
|
+
if (digest) observed.push(digest);
|
|
2894
|
+
} catch { /* inability to observe is rejected below */ }
|
|
2895
|
+
}
|
|
2896
|
+
if (new Set(observed).size === 1) actualDigest = observed[0]!;
|
|
2897
|
+
}
|
|
2898
|
+
if (actualDigest !== pod.pinnedImageDigest) {
|
|
2899
|
+
debug.log('release-loop.gate', 'image-pin-mismatch', { runId: pod.pinnedRunId ?? null,
|
|
2900
|
+
job: job.job, phase: 'recheck', host: producer?.context ?? null,
|
|
2901
|
+
pinnedDigest: pod.pinnedImageDigest, actualDigest, decision: 'reject-job' }, { level: 'error' });
|
|
2902
|
+
await quarantine('gate-image-pin-mismatch');
|
|
2903
|
+
throw new Error(`gate image pin mismatch: expected ${pod.pinnedImageDigest}, got ${actualDigest ?? 'unmeasured'} (recheck)`);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2818
2906
|
try {
|
|
2819
2907
|
const artifact = (name: string) => {
|
|
2820
2908
|
const path = join(job.artifactsDir, name);
|
|
@@ -2959,8 +3047,9 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2959
3047
|
recheckPodBatch: (files, commit, pod, timeoutMs) => runPodRecheckBatch(files, commit, pod, timeoutMs),
|
|
2960
3048
|
get remoteMirror() { return remoteMirror; },
|
|
2961
3049
|
set remoteMirror(path) { remoteMirror = path; },
|
|
2962
|
-
|
|
2963
|
-
|
|
3050
|
+
progressScheduler: tools.progressScheduler ?? scheduleGateProgress,
|
|
3051
|
+
async sweep(tree, logDir, pod, only, onProgress) {
|
|
3052
|
+
if (pod) return podSweep(tree, logDir, pod, only, onProgress);
|
|
2964
3053
|
const listed = await command('git', ['ls-files', '*.test.*'], tree);
|
|
2965
3054
|
check(listed, 'git ls-files tests');
|
|
2966
3055
|
const allFiles = listed.output.split(/\r?\n/).filter((file) => /\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file));
|
|
@@ -2982,13 +3071,26 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2982
3071
|
} else cdpPatterns = deriveCdpTestPatterns({ cwd: tree });
|
|
2983
3072
|
const outputs: string[] = [];
|
|
2984
3073
|
let total = { pass: 0, fail: 0, errors: 0, ran: 0, files: 0 };
|
|
2985
|
-
|
|
2986
|
-
|
|
3074
|
+
const selectedGroups = groups.filter((group) => group.paths.length && files.some((f) => group.paths.some((p) => f === p || f.startsWith(`${p}/`))));
|
|
3075
|
+
const progressGroups: GateProgressGroup[] = selectedGroups.map((group) => ({ id: group.name, state: 'pending', ...(remote ? { host: remote } : {}) }));
|
|
3076
|
+
onProgress?.({ executor: 'host', groups: progressGroups });
|
|
3077
|
+
for (const group of selectedGroups) {
|
|
3078
|
+
const progress = progressGroups.find((item) => item.id === group.name)!;
|
|
3079
|
+
progress.state = 'running'; progress.startedAtMs = clock();
|
|
3080
|
+
onProgress?.({ executor: 'host', groups: progressGroups });
|
|
2987
3081
|
const start = Date.now();
|
|
2988
3082
|
const groupIgnores = [...new Set(cdpPatterns)]
|
|
2989
3083
|
.filter((pattern) => files.includes(pattern) && group.paths.some((p) => pattern === p || pattern.startsWith(`${p}/`)))
|
|
2990
3084
|
.flatMap((pattern) => ['--path-ignore-patterns', pattern]);
|
|
2991
|
-
|
|
3085
|
+
let run: CommandResult;
|
|
3086
|
+
try { run = await command('bun', ['run', 'test:deterministic', ...groupIgnores, ...group.paths.map(asPath)], tree); }
|
|
3087
|
+
catch (error) {
|
|
3088
|
+
progress.state = 'failed'; progress.endedAtMs = clock();
|
|
3089
|
+
onProgress?.({ executor: 'host', groups: progressGroups });
|
|
3090
|
+
throw error;
|
|
3091
|
+
}
|
|
3092
|
+
progress.state = run.timedOut ? 'timeout' : run.rc === 0 ? 'done' : 'failed'; progress.endedAtMs = clock();
|
|
3093
|
+
onProgress?.({ executor: 'host', groups: progressGroups });
|
|
2992
3094
|
if (logDir) {
|
|
2993
3095
|
mkdirSync(logDir, { recursive: true });
|
|
2994
3096
|
writeFileSync(join(logDir, `${group.name}.log`), run.output, { mode: 0o600 });
|
|
@@ -3120,7 +3222,8 @@ export function saveBaselineFileCache(ledger: string, commit: string, rows: Base
|
|
|
3120
3222
|
}
|
|
3121
3223
|
|
|
3122
3224
|
export async function judgeGate(opts: GateOptions, runner: GateRunner = createGateRunner(opts.repo ?? process.cwd(), opts.remote),
|
|
3123
|
-
imageProbe: typeof preflightGateImageDigest = preflightGateImageDigest
|
|
3225
|
+
imageProbe: typeof preflightGateImageDigest = preflightGateImageDigest,
|
|
3226
|
+
identityProbe: typeof preflightGateExecutionIdentity = preflightGateExecutionIdentity): Promise<GateResult> {
|
|
3124
3227
|
if (opts.pod?.warmReplay) opts = { ...opts, pod: { ...opts.pod,
|
|
3125
3228
|
warmShadowScope: { requested: false, attempts: 0 } } };
|
|
3126
3229
|
const explicitConfig = getElanousConfigDirOverride();
|
|
@@ -3167,18 +3270,88 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3167
3270
|
if (!sha.test(opts.commit) || !versionPattern.test(opts.version)
|
|
3168
3271
|
|| (opts.baselineVersion && !versionPattern.test(opts.baselineVersion))
|
|
3169
3272
|
|| (opts.baselineCommit && !sha.test(opts.baselineCommit))) throw new Error('invalid commit or version');
|
|
3170
|
-
|
|
3273
|
+
const baselineVersion = opts.baselineVersion;
|
|
3274
|
+
if (!baselineVersion) throw new Error('previous release version required (--baseline-version)');
|
|
3171
3275
|
if (opts.rehearsalBaseline && (opts.graphRunId || !opts.pod || !/-rc\.\d+$/.test(opts.version)))
|
|
3172
3276
|
throw new Error('rehearsal baseline requires a direct Pod gate with an isolated rc version');
|
|
3173
3277
|
// Observation only. Failure to plan cannot change the gate selector or verdict.
|
|
3174
3278
|
try { radiusShadow = planGateRadiusShadow({ repo, ledgerRoot: ledger, version: opts.version,
|
|
3175
|
-
baselineVersion
|
|
3279
|
+
baselineVersion, forCommit: opts.commit, instanceRoot: root,
|
|
3176
3280
|
expectedBaselineCommit: opts.baselineCommit }); }
|
|
3177
3281
|
catch (error) { debug.log('release-loop.gate', 'radius-shadow-unavailable', { reason: String(error) }); }
|
|
3178
3282
|
if (!Number.isSafeInteger(opts.recheckFileLimit ?? GATE_HOST_RECHECK_FILE_LIMIT) || (opts.recheckFileLimit ?? GATE_HOST_RECHECK_FILE_LIMIT) < 1)
|
|
3179
3283
|
throw new Error('invalid gate recheck file limit');
|
|
3180
3284
|
if (opts.pod && (!opts.pod.pool || !Number.isSafeInteger(opts.pod.shards ?? 24) || (opts.pod.shards ?? 24) < 1
|
|
3181
3285
|
|| !Number.isSafeInteger(opts.pod.shardTimeoutSeconds ?? 1200) || (opts.pod.shardTimeoutSeconds ?? 1200) < 1)) throw new Error('invalid pod sweep options');
|
|
3286
|
+
if (opts.pod && opts.graphRunId) {
|
|
3287
|
+
const components = opts.pod.pool.split(',').map((part) => part.trim()).filter(Boolean);
|
|
3288
|
+
const members = parsePodPool(opts.pod.pool);
|
|
3289
|
+
const memberSpecs = members.map((member) => {
|
|
3290
|
+
const spec = components.find((part) => part.split(/[@:#]/, 1)[0] === member.context);
|
|
3291
|
+
if (!spec) throw new Error(`gate image pin cannot map pool member ${member.context}`);
|
|
3292
|
+
return { context: member.context, spec };
|
|
3293
|
+
});
|
|
3294
|
+
const poolImages = new Map<string, readonly string[]>();
|
|
3295
|
+
const currentImages: Record<string, string | null> = {};
|
|
3296
|
+
for (const member of memberSpecs) {
|
|
3297
|
+
const digest = imageProbe(member.spec, opts.commit);
|
|
3298
|
+
currentImages[member.context] = digest;
|
|
3299
|
+
poolImages.set(member.context, digest ? [digest] : []);
|
|
3300
|
+
}
|
|
3301
|
+
const pinRunId = opts.graphRunId;
|
|
3302
|
+
const pinPath = gateImagePinPath(root, opts.version, pinRunId);
|
|
3303
|
+
const gateImageTag = `${podCommandImageFor([], true).split(':')[0]}:${opts.commit.slice(0, 12)}`;
|
|
3304
|
+
let savedPin: ReturnType<typeof readGateImagePin>;
|
|
3305
|
+
try { savedPin = existsSync(pinPath) ? readGateImagePin(pinPath, pinRunId, gateImageTag, opts.commit) : null; }
|
|
3306
|
+
catch (error) {
|
|
3307
|
+
debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, currentImages, decision: 'fail-closed',
|
|
3308
|
+
reason: 'saved-image-pin-invalid', error: String(error).slice(0, 300) }, { level: 'error' });
|
|
3309
|
+
throw new Error('gate resume refused: saved image digest pin is invalid');
|
|
3310
|
+
}
|
|
3311
|
+
const savedDigest = savedPin?.digest ?? null;
|
|
3312
|
+
let previouslyExecuted = false;
|
|
3313
|
+
if (opts.graphRunId) {
|
|
3314
|
+
// A gate's immutable pin is written before any Pod work. The graph state is only a legacy-run detector:
|
|
3315
|
+
// if it already records an executed gate but this pin does not exist, resume cannot safely invent one.
|
|
3316
|
+
const runPath = join(root, 'graph-runs', 'release-loop', `${opts.graphRunId}.json`);
|
|
3317
|
+
if (existsSync(runPath)) {
|
|
3318
|
+
try {
|
|
3319
|
+
const prior = JSON.parse(readFileSync(runPath, 'utf8')) as { nodes?: Array<{ nodeId?: unknown; executed?: unknown }> };
|
|
3320
|
+
previouslyExecuted = prior.nodes?.some((node) => node.nodeId === 'gate' && node.executed === true) ?? false;
|
|
3321
|
+
} catch { previouslyExecuted = true; }
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
if (!savedDigest && previouslyExecuted) {
|
|
3325
|
+
debug.log('release-loop.gate', 'image-pin', { runId: opts.graphRunId, savedDigest: null, currentImages,
|
|
3326
|
+
decision: 'fail-closed', reason: 'resume-pin-missing' }, { level: 'error' });
|
|
3327
|
+
throw new Error('gate resume refused: saved image digest pin is missing');
|
|
3328
|
+
}
|
|
3329
|
+
const resolution = resolveGateImagePin({ ...(savedDigest ? { savedDigest } : {}),
|
|
3330
|
+
...(!savedDigest ? { initialDigest: [...poolImages.values()].flat().find(Boolean) ?? null } : {}), poolImages });
|
|
3331
|
+
if (!resolution.ok) {
|
|
3332
|
+
debug.log('release-loop.gate', 'image-pin', { runId: opts.graphRunId ?? null, savedDigest, currentImages,
|
|
3333
|
+
decision: 'fail-closed', reason: resolution.reason }, { level: 'error' });
|
|
3334
|
+
throw new Error(`gate image pin refused: ${resolution.reason}`);
|
|
3335
|
+
}
|
|
3336
|
+
const selected = memberSpecs.filter((member) => resolution.eligiblePools.includes(member.context));
|
|
3337
|
+
if (!selected.length) throw new Error('gate image pin refused: no eligible pool');
|
|
3338
|
+
if (!savedDigest) {
|
|
3339
|
+
try { writeGateImagePin(pinPath, pinRunId, resolution.digest, gateImageTag, opts.commit); }
|
|
3340
|
+
catch (error) {
|
|
3341
|
+
debug.log('release-loop.gate', 'image-pin', { runId: pinRunId, pinnedDigest: resolution.digest,
|
|
3342
|
+
currentImages, decision: 'fail-closed', reason: 'pin-write-failed', error: String(error).slice(0, 300) }, { level: 'error' });
|
|
3343
|
+
throw error;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
opts = { ...opts, pod: { ...opts.pod, pool: selected.map((member) => member.spec).join(','),
|
|
3347
|
+
pinnedImageDigest: resolution.digest, pinnedRunId: pinRunId } };
|
|
3348
|
+
debug.log('release-loop.gate', 'image-pin', { runId: opts.graphRunId ?? null, pinnedDigest: resolution.digest,
|
|
3349
|
+
currentImages, eligiblePools: resolution.eligiblePools, excludedPools: members.map((member) => member.context)
|
|
3350
|
+
.filter((context) => !resolution.eligiblePools.includes(context)), decision: savedDigest ? 'resume-pinned' : 'pin-created' });
|
|
3351
|
+
} else if (opts.pod) {
|
|
3352
|
+
debug.log('release-loop.gate', 'image-pin-unscoped', { runId: null, decision: 'not-applicable',
|
|
3353
|
+
reason: 'direct-pod-gate-without-graph-run-id' });
|
|
3354
|
+
}
|
|
3182
3355
|
if (opts.remote && !/^(?:[\w.-]+@)?[\w.-]+$/.test(opts.remote)) throw new Error('invalid ssh host');
|
|
3183
3356
|
if (opts.remote) {
|
|
3184
3357
|
runner.remoteMirror = undefined;
|
|
@@ -3189,8 +3362,8 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3189
3362
|
const homePath = home?.output.split(/\r?\n/).find((line) => line.startsWith('/'));
|
|
3190
3363
|
if (home && (!homePath || !/^\/[^\r\n]*$/.test(homePath))) throw new Error('invalid remote home');
|
|
3191
3364
|
const mirror = configured.startsWith('~/') ? join(homePath!, configured.slice(2)) : configured;
|
|
3192
|
-
const baseSha = opts.baselineCommit ?? baselineReleaseCommit(
|
|
3193
|
-
cachedBaseline(
|
|
3365
|
+
const baseSha = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
|
|
3366
|
+
cachedBaseline(baselineVersion, baseSha);
|
|
3194
3367
|
const present = await runner.command('test', ['-d', mirror], '/tmp');
|
|
3195
3368
|
if (present.rc === 1) {
|
|
3196
3369
|
check(await runner.command('mkdir', ['-p', dirname(mirror)], '/tmp'), 'remote mirror parent');
|
|
@@ -3219,26 +3392,57 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3219
3392
|
// GATE-PARTIAL: a plan made by `release resume --from gate --partial` for exactly this commit re-runs only its files.
|
|
3220
3393
|
// Its logs go to cut-partial/ — the full cut/ logs stay whole (the next release reads them as a baseline source).
|
|
3221
3394
|
const partialRead = readPartialPlanResult(roots, opts.version, opts.commit);
|
|
3222
|
-
const explicitPartial = partialRead.plan;
|
|
3395
|
+
const explicitPartial = opts.full ? null : partialRead.plan;
|
|
3223
3396
|
const rehearsalStampPresent = ['hard-5h', 'hard-mid'].some((step) =>
|
|
3224
3397
|
existsSync(join(root, 'release', opts.version, 'rehearsal', `${step}.json`)));
|
|
3225
|
-
const
|
|
3226
|
-
?
|
|
3398
|
+
const currentIdentity = !opts.full && !explicitPartial && opts.graphRunId && opts.pod && rehearsalStampPresent
|
|
3399
|
+
? identityProbe(opts.pod.pool, process.env, opts.commit) : null;
|
|
3400
|
+
const currentImage = currentIdentity?.imageDigest ?? (!opts.full && !explicitPartial
|
|
3401
|
+
&& opts.graphRunId && opts.pod && rehearsalStampPresent ? imageProbe(opts.pod.pool, opts.commit) : null);
|
|
3227
3402
|
const receiptImage = currentImage ?? (opts.graphRunId && opts.pod && process.env.ELANOUS_GRAPH_CONTEXT
|
|
3228
|
-
? imageProbe(opts.pod.pool) : null);
|
|
3229
|
-
const receiptBase = opts.baselineCommit ?? (
|
|
3230
|
-
existsSync(join(location, 'release',
|
|
3231
|
-
? baselineReleaseCommit(
|
|
3232
|
-
const radius = !explicitPartial && opts.graphRunId && opts.pod && rehearsalStampPresent
|
|
3403
|
+
? imageProbe(opts.pod.pool, opts.commit) : null);
|
|
3404
|
+
const receiptBase = opts.baselineCommit ?? (roots.some((location) =>
|
|
3405
|
+
existsSync(join(location, 'release', baselineVersion, 'release.json')))
|
|
3406
|
+
? baselineReleaseCommit(baselineVersion) : undefined);
|
|
3407
|
+
const radius = !opts.full && !explicitPartial && opts.graphRunId && opts.pod && rehearsalStampPresent
|
|
3233
3408
|
? planHardRehearsalRadius({ root, repo, version: opts.version, forCommit: opts.commit,
|
|
3234
|
-
graphPins: gateGraphPins(root, opts.graphRunId), envFingerprint: gateEnvFingerprint(process.env), imageDigest: currentImage
|
|
3409
|
+
graphPins: gateGraphPins(root, opts.graphRunId), envFingerprint: gateEnvFingerprint(process.env), imageDigest: currentImage,
|
|
3410
|
+
executionIdentity: currentIdentity })
|
|
3235
3411
|
: null;
|
|
3236
3412
|
if (radius) debug.log('release-loop.gate', 'radius-live-plan', { version: opts.version, reason: radius.reason,
|
|
3237
3413
|
files: radius.plan?.files.length ?? null, rehearsalRunId: radius.rehearsalRunId ?? null });
|
|
3238
|
-
let partial = explicitPartial ?? radius?.plan ?? null;
|
|
3414
|
+
let partial = opts.full ? null : explicitPartial ?? radius?.plan ?? null;
|
|
3415
|
+
let trainReceiptPlan: ReturnType<typeof planGateTrainReceiptCarry> = null;
|
|
3416
|
+
if (!opts.full && !partial && opts.graphRunId && opts.pod) {
|
|
3417
|
+
const receiptPath = gateReceiptRegistryPath(repo);
|
|
3418
|
+
const bun = spawnSync('bun', ['--version'], { cwd: repo, encoding: 'utf8', maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
3419
|
+
if (receiptPath && bun.status === 0 && !bun.error) {
|
|
3420
|
+
try {
|
|
3421
|
+
trainReceiptPlan = planGateTrainReceiptCarry({ repo, commit: opts.commit, receiptPath,
|
|
3422
|
+
bunVersion: bun.stdout.trim(), lockfileHash: gateLockfileHash(repo), envFingerprint: gateReceiptEnvFingerprint(process.env) });
|
|
3423
|
+
if (trainReceiptPlan?.carriedFiles.length) partial = { version: opts.version, priorCommit: trainReceiptPlan.priorCommit,
|
|
3424
|
+
forCommit: opts.commit, files: trainReceiptPlan.files, priorFailures: [], priorErrors: [] };
|
|
3425
|
+
} catch (error) {
|
|
3426
|
+
debug.log('release-loop.gate', 'train-receipt-plan-failed', { version: opts.version, reason: String(error).slice(0, 240) });
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
if (trainReceiptPlan) {
|
|
3430
|
+
result.trainReceiptReuse = { carried: trainReceiptPlan.carriedFiles.length, total: trainReceiptPlan.totalFiles,
|
|
3431
|
+
receipts: trainReceiptPlan.receipts.length, reasons: trainReceiptPlan.reasons };
|
|
3432
|
+
debug.log('release-loop.gate', 'train-receipt-plan', { version: opts.version, commit: opts.commit,
|
|
3433
|
+
carried: trainReceiptPlan.carriedFiles.length, total: trainReceiptPlan.totalFiles,
|
|
3434
|
+
ratio: trainReceiptPlan.carriedFiles.length / trainReceiptPlan.totalFiles,
|
|
3435
|
+
receipts: trainReceiptPlan.receipts, selectedFiles: trainReceiptPlan.files.length,
|
|
3436
|
+
reasons: trainReceiptPlan.reasons, action: trainReceiptPlan.carriedFiles.length ? 'run-remaining-tests' : 'full-sweep' });
|
|
3437
|
+
} else if (!opts.full && !explicitPartial && !radius?.plan) {
|
|
3438
|
+
debug.log('release-loop.gate', 'train-receipt-plan', { version: opts.version, commit: opts.commit,
|
|
3439
|
+
carried: 0, total: null, ratio: 0, receipts: [], selectedFiles: null,
|
|
3440
|
+
reasons: ['no safe receipt coverage'], action: 'full-sweep' });
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3239
3443
|
const explicitPodPlan = !!explicitPartial && explicitPartial.kind !== 'recheck' && !!opts.pod;
|
|
3240
3444
|
const explicitPodDigest = explicitPodPlan ? explicitPartial!.shardImageDigests?.[0] : undefined;
|
|
3241
|
-
const probedPodDigest = explicitPodDigest ? imageProbe(opts.pod!.pool) : null;
|
|
3445
|
+
const probedPodDigest = explicitPodDigest ? imageProbe(opts.pod!.pool, opts.commit) : null;
|
|
3242
3446
|
if (explicitPodPlan && (!explicitPodDigest || probedPodDigest !== explicitPodDigest)) {
|
|
3243
3447
|
debug.log('release-loop.gate', 'partial-image-preflight-fallback', { version: opts.version,
|
|
3244
3448
|
reason: !explicitPodDigest || !probedPodDigest ? 'executed-pod-image-digest-missing' : 'executed-pod-image-digest-mismatch' }, { level: 'warn' });
|
|
@@ -3274,15 +3478,33 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3274
3478
|
let knownFailures: string[] = [];
|
|
3275
3479
|
if (opts.pod) {
|
|
3276
3480
|
try {
|
|
3277
|
-
const knownSha = opts.baselineCommit ?? baselineReleaseCommit(
|
|
3278
|
-
const saved = cachedBaseline(
|
|
3279
|
-
const recorded = saved ? undefined : baselineFromCutLogs(join(ledger, 'release',
|
|
3481
|
+
const knownSha = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
|
|
3482
|
+
const saved = cachedBaseline(baselineVersion, knownSha)?.saved;
|
|
3483
|
+
const recorded = saved ? undefined : baselineFromCutLogs(join(ledger, 'release', baselineVersion, 'gate-logs', 'cut'), knownSha);
|
|
3280
3484
|
const trustedKnown = saved ?? (recorded?.complete ? recorded : undefined);
|
|
3281
3485
|
if (trustedKnown) knownFailures = [...trustedKnown.failures, ...(trustedKnown.errors ?? []), ...loadEnvKnownFailures(repo)];
|
|
3282
3486
|
} catch (error) { debug.log('release-loop.gate', 'known-failures-unavailable', { error: String(error) }); }
|
|
3283
3487
|
}
|
|
3284
|
-
const durationSource = join(ledger, 'release',
|
|
3285
|
-
const runCut = (selected: typeof partial) =>
|
|
3488
|
+
const durationSource = join(ledger, 'release', baselineVersion, 'gate-logs', 'cut');
|
|
3489
|
+
const runCut = async (selected: typeof partial) => {
|
|
3490
|
+
if (selected?.kind === 'carry') return undefined;
|
|
3491
|
+
const progressFile = opts.graphRunId && /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(opts.graphRunId)
|
|
3492
|
+
? join(ledger, 'graph-runs', 'release-loop', `${opts.graphRunId}.json.contexts`, 'gate-progress.jsonl') : undefined;
|
|
3493
|
+
let latest: GateSweepProgress | undefined;
|
|
3494
|
+
const emitProgress = () => {
|
|
3495
|
+
if (!latest) return;
|
|
3496
|
+
const nowMs = runner.now?.() ?? Date.now();
|
|
3497
|
+
const event = { version: opts.version, runId: opts.graphRunId ?? null, at: new Date(nowMs).toISOString(),
|
|
3498
|
+
...summarizeGateProgress({ executor: latest.executor, groups: latest.groups, nowMs }) };
|
|
3499
|
+
debug.log('release-loop.gate', 'progress', event);
|
|
3500
|
+
if (progressFile) {
|
|
3501
|
+
try { mkdirSync(dirname(progressFile), { recursive: true }); appendFileSync(progressFile, `${JSON.stringify(event)}\n`, { mode: 0o600 }); }
|
|
3502
|
+
catch (error) { debug.log('release-loop.gate', 'progress-log-write-failed', { path: progressFile, error: String(error) }, { level: 'warn' }); }
|
|
3503
|
+
}
|
|
3504
|
+
};
|
|
3505
|
+
const cancel = (runner.progressScheduler ?? scheduleGateProgress)(emitProgress, 60_000);
|
|
3506
|
+
try {
|
|
3507
|
+
const sweepResult = await runner.sweep(cutTree,
|
|
3286
3508
|
join(root, 'release', opts.version, 'gate-logs', selected ? 'cut-partial' : 'cut'),
|
|
3287
3509
|
opts.pod ? { ...opts.pod, durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
|
|
3288
3510
|
// GATE-RISK-LANE — the watch list is read from this gate's own ledgers (the instance root ⊕ the machine ledger);
|
|
@@ -3295,7 +3517,11 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3295
3517
|
receiptRoot: join(ledger, 'release', opts.version, 'gate-logs', 'cut', 'receipts'),
|
|
3296
3518
|
base40: receiptBase,
|
|
3297
3519
|
expectedImageDigest: receiptImage } } : undefined,
|
|
3298
|
-
selected?.files);
|
|
3520
|
+
selected?.files, (progress) => { latest = progress; emitProgress(); });
|
|
3521
|
+
emitProgress();
|
|
3522
|
+
return sweepResult;
|
|
3523
|
+
} finally { cancel?.(); }
|
|
3524
|
+
};
|
|
3299
3525
|
let cutRun = await runCut(partial);
|
|
3300
3526
|
const radiusSelectionMatched = partial?.files && cutRun?.podSweep?.files
|
|
3301
3527
|
&& JSON.stringify([...partial.files].sort()) === JSON.stringify([...cutRun.podSweep.files].sort());
|
|
@@ -3376,7 +3602,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3376
3602
|
if (!work) throw new Error('gate workspace missing before baseline attribution recheck');
|
|
3377
3603
|
const recheckTree = join(work, 'baseline-attribution-recheck');
|
|
3378
3604
|
trees.push(recheckTree);
|
|
3379
|
-
const baseShaForRecheck = opts.baselineCommit ?? baselineReleaseCommit(
|
|
3605
|
+
const baseShaForRecheck = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
|
|
3380
3606
|
await runner.add(recheckTree, baseShaForRecheck);
|
|
3381
3607
|
for (const dir of [recheckTree, join(recheckTree, 'apps/pwa')])
|
|
3382
3608
|
check(await runner.command('bun', ['install'], dir), `bun install ${dir}`);
|
|
@@ -3455,13 +3681,13 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3455
3681
|
}
|
|
3456
3682
|
if (partial) result.partial = { priorCommit: partial.priorCommit, files: partial.files.length };
|
|
3457
3683
|
cutFailures = cut;
|
|
3458
|
-
const baseSha = opts.baselineCommit ?? baselineReleaseCommit(
|
|
3684
|
+
const baseSha = opts.baselineCommit ?? baselineReleaseCommit(baselineVersion);
|
|
3459
3685
|
baselineCommitForVerdict = baseSha;
|
|
3460
|
-
const cached = cachedBaseline(
|
|
3461
|
-
const recorded = cached ? undefined : baselineFromCutLogs(join(ledger, 'release',
|
|
3686
|
+
const cached = cachedBaseline(baselineVersion, baseSha);
|
|
3687
|
+
const recorded = cached ? undefined : baselineFromCutLogs(join(ledger, 'release', baselineVersion, 'gate-logs', 'cut'), baseSha);
|
|
3462
3688
|
const trusted = cached?.saved ?? (recorded?.complete ? recorded : undefined);
|
|
3463
3689
|
result.baselineSource = cached ? cached.source : recorded?.complete ? 'cut-logs' : 'swept';
|
|
3464
|
-
debug.log('release-loop.gate', 'baseline', { version:
|
|
3690
|
+
debug.log('release-loop.gate', 'baseline', { version: baselineVersion, source: result.baselineSource, commit: baseSha });
|
|
3465
3691
|
let baseTree: string | undefined;
|
|
3466
3692
|
const getBaseTree = async () => {
|
|
3467
3693
|
if (!baseTree) {
|
|
@@ -3492,7 +3718,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3492
3718
|
present = cutFiles.filter((file) => known.has(file));
|
|
3493
3719
|
}
|
|
3494
3720
|
result.baselineDeferred = { cutFiles: cutFiles.length, swept: present.length };
|
|
3495
|
-
debug.log('release-loop.gate', 'baseline-deferred', { version:
|
|
3721
|
+
debug.log('release-loop.gate', 'baseline-deferred', { version: baselineVersion, commit: baseSha, cutFiles: cutFiles.length, swept: present.length });
|
|
3496
3722
|
if (present.length) {
|
|
3497
3723
|
// K9b — the baseline is swept on the same Pod pool as the cut, not on this host (0.2.6: an hour of local baseline
|
|
3498
3724
|
// after a 23-minute Pod cut).
|
|
@@ -3994,6 +4220,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
3994
4220
|
const arg = args[i];
|
|
3995
4221
|
if (arg === '--help') return 'help';
|
|
3996
4222
|
if (arg === '--json') continue;
|
|
4223
|
+
if (arg === '--full') { opts.full = true; continue; }
|
|
3997
4224
|
if (arg === '--rehearsal-baseline') { opts.rehearsalBaseline = true; continue; }
|
|
3998
4225
|
// Applied after the loop and only to a Pod gate — on its own it must not turn a local gate into a Pod one (review r5).
|
|
3999
4226
|
if (arg === '--fresh-shards') { freshShards = true; continue; }
|
|
@@ -4077,7 +4304,7 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
|
|
|
4077
4304
|
debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null });
|
|
4078
4305
|
}
|
|
4079
4306
|
if (opts === 'help') {
|
|
4080
|
-
console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--recheck-file-limit <n>] [--fresh-shards] [--reroute-shard <pod-N|auto>] [--shard-host <pool-context>] [--executor <pod|host>] [--rehearsal-baseline] [--json]\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.\n--executor host (input.gateExecutor) ignores every pod option and sweeps on --remote (input.gateRemote, required).\n--rehearsal-baseline pins source graph bytes only for a direct Pod gate on an isolated rc version.');
|
|
4307
|
+
console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--recheck-file-limit <n>] [--fresh-shards] [--reroute-shard <pod-N|auto>] [--shard-host <pool-context>] [--executor <pod|host>] [--rehearsal-baseline] [--full] [--json]\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.\n--executor host (input.gateExecutor) ignores every pod option and sweeps on --remote (input.gateRemote, required).\n--rehearsal-baseline pins source graph bytes only for a direct Pod gate on an isolated rc version.\n--full disables receipt and partial reuse and runs the complete test inventory.');
|
|
4081
4308
|
return 0;
|
|
4082
4309
|
}
|
|
4083
4310
|
version = opts.version;
|
|
@@ -4113,6 +4340,18 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
|
|
|
4113
4340
|
return decision;
|
|
4114
4341
|
});
|
|
4115
4342
|
result.autoheal = result.autoheals.find((decision) => decision.action === 'hitl') ?? result.autoheals[0];
|
|
4343
|
+
} else if (versionPattern.test(version)) {
|
|
4344
|
+
// Some failures (for example an unreachable gate host or a measured regression) have no
|
|
4345
|
+
// stalled shard. Keep them in the same durable policy ledger instead of silently skipping autoheal.
|
|
4346
|
+
const cause = gateAutohealCause(result);
|
|
4347
|
+
if (cause) {
|
|
4348
|
+
const signature = createHash('sha256').update(JSON.stringify({ cause,
|
|
4349
|
+
error: result.error ?? null, introduced: [...(result.introduced ?? [])].sort() })).digest('hex');
|
|
4350
|
+
result.autoheal = decideAndRecordGateAutoheal({ cause, signature, version, runId: graphRunId ?? 'direct',
|
|
4351
|
+
shardKey: 'gate', shardKeys: ['gate'], shardElapsedMs: result.durationMs, overallElapsedMs: result.durationMs,
|
|
4352
|
+
storeRoot: autohealStoreRoot || releaseLedgerRoot() });
|
|
4353
|
+
debug.log('release-loop.gate-autoheal', 'decision', { runId: graphRunId, version, ...result.autoheal });
|
|
4354
|
+
}
|
|
4116
4355
|
}
|
|
4117
4356
|
if (result.error) console.error(result.error);
|
|
4118
4357
|
debug.log('release-loop.gate', 'result', { version, outcome: result.outcome, ...(placement ?? {}) });
|