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
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
interface StickyGatePool { id?: string | null; available: boolean; imageDigest: string | null }
|
|
2
|
+
interface StickyGateShard { id?: string | null; files?: string[] | null }
|
|
3
|
+
interface StickyGateReceipt { shardId: string; status: 'complete' | 'stalled'; pool?: string | null; imageDigest: string }
|
|
4
|
+
interface StickyGateShardAction {
|
|
5
|
+
shardId: string; files: string[]; kind: 'reuse' | 'run' | 'unmeasured'; pool?: string; imageDigest?: string;
|
|
6
|
+
}
|
|
7
|
+
interface StickyGatePlan {
|
|
8
|
+
mode: 'full' | 'partial' | 'HOLD'; pinnedDigest: string | null; excludedPools: string[];
|
|
9
|
+
shards: StickyGateShardAction[]; reason?: string;
|
|
10
|
+
}
|
|
11
|
+
interface StickyGatePlanInput {
|
|
12
|
+
phase: 'start' | 'resume'; pools: readonly StickyGatePool[]; shards: readonly StickyGateShard[];
|
|
13
|
+
pinnedDigest?: string | null; receipts?: readonly StickyGateReceipt[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/i;
|
|
17
|
+
const validDigest = (value: string | null | undefined): value is string => typeof value === 'string' && DIGEST.test(value);
|
|
18
|
+
const normalizeDigest = (value: string | null | undefined): string | null => validDigest(value) ? value.toLowerCase() : null;
|
|
19
|
+
const validPoolIdentity = (value: string | null | undefined): value is string => typeof value === 'string' && value.trim().length > 0;
|
|
20
|
+
|
|
21
|
+
function hold(input: StickyGatePlanInput, pinnedDigest: string | null, reason: string, excludedPools: string[] = []): StickyGatePlan {
|
|
22
|
+
return { mode: 'HOLD', pinnedDigest, excludedPools, reason,
|
|
23
|
+
shards: input.shards.map((shard, index) => ({
|
|
24
|
+
shardId: validPoolIdentity(shard?.id) ? shard.id : `unmeasured-${index}`,
|
|
25
|
+
files: Array.isArray(shard?.files) ? [...shard.files] : [], kind: 'unmeasured',
|
|
26
|
+
})) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isValidPool(pool: StickyGatePool | null | undefined): pool is StickyGatePool & { id: string } {
|
|
30
|
+
return !!pool && validPoolIdentity(pool.id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isValidShard(shard: StickyGateShard | null | undefined): shard is StickyGateShard & { id: string; files: string[] } {
|
|
34
|
+
return !!shard && validPoolIdentity(shard.id) && Array.isArray(shard.files) && shard.files.length > 0
|
|
35
|
+
&& shard.files.every((file) => typeof file === 'string') && new Set(shard.files).size === shard.files.length;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Deterministic E4 planning simulation. A production caller must persist the chosen digest at run start
|
|
40
|
+
* and pass it unchanged on resume; this module itself never runs a host command or pulls an image.
|
|
41
|
+
*/
|
|
42
|
+
export function planStickyGateImages(input: StickyGatePlanInput): StickyGatePlan {
|
|
43
|
+
if (!input.shards.length || !input.pools.length) return hold(input, null, 'empty pool or shard inventory');
|
|
44
|
+
const pools = input.pools.filter(isValidPool);
|
|
45
|
+
const validShards = input.shards.filter(isValidShard);
|
|
46
|
+
if (pools.length !== input.pools.length || new Set(pools.map((pool) => pool.id)).size !== pools.length
|
|
47
|
+
|| validShards.length !== input.shards.length || new Set(validShards.map((shard) => shard.id)).size !== validShards.length)
|
|
48
|
+
return hold(input, null, 'invalid pool or shard identity');
|
|
49
|
+
|
|
50
|
+
let pinnedDigest: string | null = null;
|
|
51
|
+
if (input.phase === 'resume') {
|
|
52
|
+
pinnedDigest = normalizeDigest(input.pinnedDigest);
|
|
53
|
+
if (!pinnedDigest) return hold(input, null, 'run image digest missing');
|
|
54
|
+
} else if (input.pinnedDigest !== undefined && input.pinnedDigest !== null) {
|
|
55
|
+
pinnedDigest = normalizeDigest(input.pinnedDigest);
|
|
56
|
+
if (!pinnedDigest) return hold(input, null, 'invalid pinned image digest');
|
|
57
|
+
} else {
|
|
58
|
+
pinnedDigest = pools.map((pool) => pool.available ? normalizeDigest(pool.imageDigest) : null)
|
|
59
|
+
.find((digest): digest is string => digest !== null) ?? null;
|
|
60
|
+
if (!pinnedDigest) return hold(input, null, 'no available pool has a valid image digest');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const excludedPools = pools.filter((pool) => !pool.available || normalizeDigest(pool.imageDigest) !== pinnedDigest)
|
|
64
|
+
.map((pool) => pool.id);
|
|
65
|
+
const eligiblePools = pools.filter((pool) => pool.available && normalizeDigest(pool.imageDigest) === pinnedDigest);
|
|
66
|
+
if (!eligiblePools.length) return hold(input, pinnedDigest, 'no available pool has the pinned digest', excludedPools);
|
|
67
|
+
|
|
68
|
+
const receipts = input.receipts ?? [];
|
|
69
|
+
const shardIds = new Set(validShards.map((shard) => shard.id));
|
|
70
|
+
if (receipts.some((receipt) => !shardIds.has(receipt.shardId))
|
|
71
|
+
|| new Set(receipts.map((receipt) => receipt.shardId)).size !== receipts.length)
|
|
72
|
+
return hold(input, pinnedDigest, 'receipt inventory does not match the shard plan', excludedPools);
|
|
73
|
+
|
|
74
|
+
const receiptByShard = new Map(receipts.map((receipt) => [receipt.shardId, receipt]));
|
|
75
|
+
const completedReceipts = input.phase === 'resume' ? receipts.filter((receipt) => receipt.status === 'complete') : [];
|
|
76
|
+
const mismatchedCompleteDigest = completedReceipts.some((receipt) => normalizeDigest(receipt.imageDigest) !== pinnedDigest);
|
|
77
|
+
const missingCompletePool = completedReceipts.some((receipt) => !validPoolIdentity(receipt.pool));
|
|
78
|
+
const mixedCompleteReceipt = mismatchedCompleteDigest || missingCompletePool;
|
|
79
|
+
const fullSweep = input.phase === 'start' || mixedCompleteReceipt
|
|
80
|
+
|| !validShards.some((shard) => receiptByShard.get(shard.id)?.status === 'complete');
|
|
81
|
+
const mode: StickyGatePlan['mode'] = fullSweep ? 'full' : 'partial';
|
|
82
|
+
const shards: StickyGateShardAction[] = [];
|
|
83
|
+
let nextPool = 0;
|
|
84
|
+
for (const shard of validShards) {
|
|
85
|
+
const receipt = receiptByShard.get(shard.id);
|
|
86
|
+
const canReuse = input.phase === 'resume' && !fullSweep && receipt?.status === 'complete'
|
|
87
|
+
&& normalizeDigest(receipt.imageDigest) === pinnedDigest && validPoolIdentity(receipt.pool);
|
|
88
|
+
if (canReuse) {
|
|
89
|
+
shards.push({ shardId: shard.id, files: [...shard.files], kind: 'reuse', pool: receipt.pool ?? undefined, imageDigest: pinnedDigest });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const pool = eligiblePools[nextPool++ % eligiblePools.length]!;
|
|
93
|
+
shards.push({ shardId: shard.id, files: [...shard.files], kind: 'run', pool: pool.id, imageDigest: pinnedDigest });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { mode, pinnedDigest, excludedPools, shards,
|
|
97
|
+
...(mixedCompleteReceipt ? { reason: mismatchedCompleteDigest
|
|
98
|
+
? 'completed receipts contain a different image digest' : 'completed receipt is missing pool identity' } : {}) };
|
|
99
|
+
}
|
|
@@ -5,9 +5,10 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { changedFilesBetween, planPartialRegate, testFileOfId, type PartialPlan } from './gate-partial.js';
|
|
6
6
|
import { CODE_FILE, gitClosureTree, testsReachingChanged } from './gate-closure.js';
|
|
7
7
|
import { versionOnlyBumps } from './gate-radius-shadow.js';
|
|
8
|
-
import { gateAssignableTestsAtCommit, gateBaselineReceipt, type GateBaselineReceipt } from './gate-baseline-receipt.js';
|
|
9
|
-
import {
|
|
8
|
+
import { gateAssignableTestsAtCommit, gateBaselineReceipt, gateEnvFingerprint, type GateBaselineReceipt, type GatePodRuntimeIdentity } from './gate-baseline-receipt.js';
|
|
9
|
+
import { defaultRemoteRun, NODE_REGISTRY, parsePodPool } from '../../src/task-orchestrator/surfaces/pod-pool.js';
|
|
10
10
|
import { podCommandImageFor } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
11
|
+
import { gateTestBaseline } from '../lib/gate-test-env.js';
|
|
11
12
|
import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
|
|
12
13
|
|
|
13
14
|
const SHA = /^[0-9a-f]{40}$/i;
|
|
@@ -27,18 +28,91 @@ export function hardRehearsalGateArgs(version: string, runId: string, commit: st
|
|
|
27
28
|
|
|
28
29
|
export interface LiveRadiusDecision { plan: PartialPlan | null; reason: string; rehearsalRunId?: string }
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
export interface GatePodExecutionIdentity extends GatePodRuntimeIdentity { imageDigest: string }
|
|
32
|
+
|
|
33
|
+
const shellQuote = (value: string) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
34
|
+
|
|
35
|
+
/** Registry v2 manifest digest is the identity Kubernetes reports in containerStatuses.imageID. */
|
|
36
|
+
export function parseGateRepoDigestResponse(headers: string): string | null {
|
|
37
|
+
const match = /^docker-content-digest:\s*(sha256:[0-9a-f]{64})\s*$/im.exec(headers);
|
|
38
|
+
return match?.[1]?.toLowerCase() ?? null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface GateRepoDigestProbeRequest { url: string; acceptHeader: string; command: string }
|
|
42
|
+
|
|
43
|
+
export function gateRepoDigestProbeRequest(registry: string, image: string, commit: string): GateRepoDigestProbeRequest | null {
|
|
44
|
+
const port = /:(\d+)$/.exec(registry)?.[1];
|
|
45
|
+
if (!port || !SHA.test(commit)) return null;
|
|
46
|
+
const imageName = image.startsWith('elanous-harness-lite:') ? 'elanous-harness-lite' : 'elanous-harness';
|
|
47
|
+
const tag = commit.slice(0, 12);
|
|
48
|
+
const url = `http://localhost:${port}/v2/${imageName}/manifests/${tag}`;
|
|
49
|
+
const accept = 'application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json';
|
|
50
|
+
const acceptHeader = `Accept: ${accept}`;
|
|
51
|
+
return { url, acceptHeader, command: `curl -fsS --max-time 8 -D - -o /dev/null -H ${shellQuote(acceptHeader)} ${shellQuote(url)}` };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function probeGateRepoDigest(member: ReturnType<typeof parsePodPool>[number], image: string, commit: string): string | null {
|
|
55
|
+
const registry = member.registry ?? NODE_REGISTRY;
|
|
56
|
+
const request = gateRepoDigestProbeRequest(registry, image, commit);
|
|
57
|
+
if (!request) return null;
|
|
58
|
+
const result = member.sshHost
|
|
59
|
+
? defaultRemoteRun(member.sshHost, request.command)
|
|
60
|
+
: spawnSync('curl', ['-fsS', '--max-time', '8', '-D', '-', '-o', '/dev/null', '-H', request.acceptHeader, request.url],
|
|
61
|
+
{ encoding: 'utf8', timeout: 10_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
62
|
+
return result.status === 0 ? parseGateRepoDigestResponse(result.stdout) : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function gateRegistryHostImage(member: ReturnType<typeof parsePodPool>[number], image: string, commit: string): string | null {
|
|
66
|
+
const registry = member.registry ?? NODE_REGISTRY;
|
|
67
|
+
const port = /:(\d+)$/.exec(registry)?.[1];
|
|
68
|
+
if (!port) return null;
|
|
69
|
+
const imageName = image.startsWith('elanous-harness-lite:') ? 'elanous-harness-lite' : 'elanous-harness';
|
|
70
|
+
return `localhost:${port}/${imageName}:${commit.slice(0, 12)}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Read the immutable pushed image manifest on every candidate host. Local Docker Image IDs are diagnostic only. */
|
|
74
|
+
export function preflightGateImageDigest(pool: string, commit?: string): string | null {
|
|
75
|
+
try {
|
|
76
|
+
if (!commit || !SHA.test(commit)) return null;
|
|
77
|
+
const image = podCommandImageFor([], true);
|
|
78
|
+
const members = parsePodPool(pool);
|
|
79
|
+
if (!members.length) return null;
|
|
80
|
+
const digests = members.map((member) => probeGateRepoDigest(member, image, commit));
|
|
81
|
+
return digests.every((digest) => digest && DIGEST.test(digest) && digest === digests[0]) ? digests[0]! : null;
|
|
82
|
+
} catch { return null; }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Probe each candidate's exact gate image/runtime and deterministic environment without a Kubernetes Job. */
|
|
86
|
+
export function preflightGateExecutionIdentity(pool: string, env: NodeJS.ProcessEnv, commit?: string): GatePodExecutionIdentity | null {
|
|
32
87
|
try {
|
|
88
|
+
if (!commit || !SHA.test(commit)) return null;
|
|
33
89
|
const image = podCommandImageFor([], true);
|
|
34
90
|
const members = parsePodPool(pool);
|
|
35
91
|
if (!members.length) return null;
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
92
|
+
const baseline = gateTestBaseline(env);
|
|
93
|
+
const expectedEnvFingerprint = gateEnvFingerprint(env);
|
|
94
|
+
const script = "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}));";
|
|
95
|
+
const rows = members.map((member): GatePodExecutionIdentity | null => {
|
|
96
|
+
const imageDigest = probeGateRepoDigest(member, image, commit);
|
|
97
|
+
if (!imageDigest || !DIGEST.test(imageDigest)) return null;
|
|
98
|
+
const hostImage = gateRegistryHostImage(member, image, commit);
|
|
99
|
+
if (!hostImage) return null;
|
|
100
|
+
const args = ['run', '--rm', '--pull=never', '--network=none', ...Object.entries(baseline).flatMap(([key, value]) => ['-e', `${key}=${value}`]), '--entrypoint', 'bun', hostImage, '-e', script];
|
|
101
|
+
const result = member.sshHost
|
|
102
|
+
? defaultRemoteRun(member.sshHost, `docker ${args.map(shellQuote).join(' ')}`)
|
|
103
|
+
: spawnSync('docker', args, { encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
104
|
+
if (result.status !== 0) return null;
|
|
105
|
+
try {
|
|
106
|
+
const row = JSON.parse(result.stdout.trim()) as Partial<GatePodRuntimeIdentity>;
|
|
107
|
+
if (typeof row.bunVersion !== 'string' || typeof row.platform !== 'string' || typeof row.architecture !== 'string'
|
|
108
|
+
|| row.envFingerprint !== expectedEnvFingerprint) return null;
|
|
109
|
+
return { imageDigest, bunVersion: row.bunVersion, platform: row.platform, architecture: row.architecture,
|
|
110
|
+
envFingerprint: row.envFingerprint };
|
|
111
|
+
} catch { return null; }
|
|
40
112
|
});
|
|
41
|
-
|
|
113
|
+
if (rows.some((row) => row === null)) return null;
|
|
114
|
+
const first = rows[0]!;
|
|
115
|
+
return rows.every((row) => JSON.stringify(row) === JSON.stringify(first)) ? first : null;
|
|
42
116
|
} catch { return null; }
|
|
43
117
|
}
|
|
44
118
|
|
|
@@ -82,10 +156,15 @@ export function partialGateImagesMatch(root: string, version: string, digest: st
|
|
|
82
156
|
/** Every missing or mismatched proof returns a full gate. The caller uses this only when no explicit resume plan exists. */
|
|
83
157
|
export function planHardRehearsalRadius(input: { root: string; repo: string; version: string; forCommit: string;
|
|
84
158
|
graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string;
|
|
85
|
-
imageDigest: string | null }): LiveRadiusDecision {
|
|
159
|
+
imageDigest: string | null; executionIdentity: GatePodExecutionIdentity | null }): LiveRadiusDecision {
|
|
86
160
|
const full = (reason: string): LiveRadiusDecision => ({ plan: null, reason });
|
|
87
161
|
if (!/^\d+\.\d+\.\d+$/.test(input.version) || !SHA.test(input.forCommit)) return full('invalid release identity');
|
|
88
|
-
if (!input.graphPins || !/^[0-9a-f]{64}$/i.test(input.envFingerprint) || !input.imageDigest || !DIGEST.test(input.imageDigest)
|
|
162
|
+
if (!input.graphPins || !/^[0-9a-f]{64}$/i.test(input.envFingerprint) || !input.imageDigest || !DIGEST.test(input.imageDigest)
|
|
163
|
+
|| !input.executionIdentity || input.executionIdentity.imageDigest !== input.imageDigest.toLowerCase()
|
|
164
|
+
|| input.executionIdentity.envFingerprint !== input.envFingerprint
|
|
165
|
+
|| !/^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(input.executionIdentity.bunVersion)
|
|
166
|
+
|| !/^[a-z0-9_-]{1,32}$/.test(input.executionIdentity.platform)
|
|
167
|
+
|| !/^[a-z0-9_-]{1,32}$/.test(input.executionIdentity.architecture))
|
|
89
168
|
return full('current graph, environment or Pod image identity unmeasured');
|
|
90
169
|
try {
|
|
91
170
|
const stamps = ['hard-5h', 'hard-mid'].map((step) => {
|
|
@@ -118,14 +197,19 @@ export function planHardRehearsalRadius(input: { root: string; repo: string; ver
|
|
|
118
197
|
if (verdict.commit !== stamp.sha || verdict.outcome !== 'ok' || failures.commit !== stamp.sha
|
|
119
198
|
|| !Array.isArray(failures.failures) || failures.failures.length !== 0
|
|
120
199
|
|| !Array.isArray(failures.errors) || failures.errors.length !== 0
|
|
121
|
-
|| !receipt || receipt.schema !==
|
|
200
|
+
|| !receipt || receipt.schema !== 2 || !receipt.valid || receipt.version !== gateVersion || receipt.commit !== stamp.sha
|
|
122
201
|
|| receipt.graphSha !== input.graphPins.graphSha || receipt.recipeSha !== input.graphPins.recipeSha
|
|
123
202
|
|| receipt.envFingerprint !== input.envFingerprint || receipt.imageDigest !== input.imageDigest.toLowerCase()
|
|
203
|
+
|| !receipt.executionIdentity
|
|
204
|
+
|| JSON.stringify(receipt.executionIdentity) !== JSON.stringify({ bunVersion: input.executionIdentity.bunVersion,
|
|
205
|
+
platform: input.executionIdentity.platform, architecture: input.executionIdentity.architecture,
|
|
206
|
+
envFingerprint: input.executionIdentity.envFingerprint })
|
|
124
207
|
|| !Number.isSafeInteger(receipt.files) || receipt.files < 1 || !Number.isSafeInteger(receipt.shards) || receipt.shards < 1)
|
|
125
208
|
return full('hard rehearsal receipt or current execution identity differs');
|
|
126
209
|
const recomputed = gateBaselineReceipt({ root: input.root, version: gateVersion, commit: stamp.sha,
|
|
127
210
|
fullSweep: true, graphPins: input.graphPins, envFingerprint: input.envFingerprint });
|
|
128
|
-
if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest
|
|
211
|
+
if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest
|
|
212
|
+
|| JSON.stringify(recomputed.executionIdentity) !== JSON.stringify(receipt.executionIdentity) || recomputed.files !== receipt.files
|
|
129
213
|
|| recomputed.shards !== receipt.shards) return full(`hard rehearsal Pod proof differs: ${recomputed.reason}`);
|
|
130
214
|
const ancestor = spawnSync('git', ['merge-base', '--is-ancestor', stamp.sha, input.forCommit], { cwd: input.repo, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
131
215
|
if (ancestor.status !== 0 || ancestor.error) return full('hard rehearsal commit is not an ancestor');
|
|
@@ -29,6 +29,49 @@ export interface GateShardHost {
|
|
|
29
29
|
reserved: boolean;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export type GateProgressState = 'pending' | 'running' | 'done' | 'failed' | 'timeout' | 'unmeasured';
|
|
33
|
+
|
|
34
|
+
/** A host group or planned Pod shard as seen by the release gate's live progress observer. */
|
|
35
|
+
export interface GateProgressGroup {
|
|
36
|
+
id: string;
|
|
37
|
+
state: GateProgressState;
|
|
38
|
+
host?: string;
|
|
39
|
+
startedAtMs?: number;
|
|
40
|
+
endedAtMs?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface GateProgressSummary {
|
|
44
|
+
executor: 'host' | 'pod';
|
|
45
|
+
host: string | null;
|
|
46
|
+
completed: number;
|
|
47
|
+
total: number;
|
|
48
|
+
failed: number;
|
|
49
|
+
groups: Array<{ id: string; state: GateProgressState; host: string | null; elapsedMs: number | null }>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Keep the progress event shape identical for serial host groups and parallel Pod shards. */
|
|
53
|
+
export function summarizeGateProgress(input: {
|
|
54
|
+
executor: 'host' | 'pod';
|
|
55
|
+
groups: readonly GateProgressGroup[];
|
|
56
|
+
nowMs: number;
|
|
57
|
+
}): GateProgressSummary {
|
|
58
|
+
const terminal = new Set<GateProgressState>(['done', 'failed', 'timeout', 'unmeasured']);
|
|
59
|
+
const hosts = [...new Set(input.groups.flatMap((group) => group.host?.trim() ? [group.host.trim()] : []))].sort();
|
|
60
|
+
return {
|
|
61
|
+
executor: input.executor,
|
|
62
|
+
host: hosts.length ? hosts.join(',') : null,
|
|
63
|
+
completed: input.groups.filter((group) => terminal.has(group.state)).length,
|
|
64
|
+
total: input.groups.length,
|
|
65
|
+
failed: input.groups.filter((group) => group.state === 'failed' || group.state === 'timeout').length,
|
|
66
|
+
groups: input.groups.map((group) => {
|
|
67
|
+
const end = group.endedAtMs ?? input.nowMs;
|
|
68
|
+
const elapsedMs = Number.isFinite(group.startedAtMs) && Number.isFinite(end)
|
|
69
|
+
? Math.max(0, end - group.startedAtMs!) : null;
|
|
70
|
+
return { id: group.id, state: group.state, host: group.host?.trim() || null, elapsedMs };
|
|
71
|
+
}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
32
75
|
export interface GateShardDispatchInput {
|
|
33
76
|
head40: string;
|
|
34
77
|
base40: string;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/** Safe test carry from passed host-regate receipts into a release cut. */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { gateAssignableTestsAtCommit } from './gate-baseline-receipt.js';
|
|
5
|
+
import { gitClosureTree, testsReachingChanged } from './gate-closure.js';
|
|
6
|
+
import { changedFilesBetween } from './gate-partial.js';
|
|
7
|
+
import { gateReceiptSelfKey, loadGateReceipts, type GateReceipt } from '../../src/self-implement/gate-receipt-registry.js';
|
|
8
|
+
import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
|
|
9
|
+
|
|
10
|
+
const SHA = /^[0-9a-f]{40}$/i;
|
|
11
|
+
const TEST = /^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/;
|
|
12
|
+
const INERT = /\.(?:md|mdx|txt)$/i;
|
|
13
|
+
|
|
14
|
+
function git(repo: string, args: string[]): string | null {
|
|
15
|
+
const result = spawnSync('git', args, { cwd: repo, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
16
|
+
return result.status === 0 && !result.error ? result.stdout.trim() : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ancestor(repo: string, base: string, head: string): boolean {
|
|
20
|
+
const result = spawnSync('git', ['merge-base', '--is-ancestor', base, head], { cwd: repo, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
21
|
+
return result.status === 0 && !result.error;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function blob(repo: string, commit: string, file: string): string | null {
|
|
25
|
+
return git(repo, ['rev-parse', `${commit}:${file}`]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function candidateTests(tree: ReturnType<typeof gitClosureTree>, receipt: GateReceipt): string[] {
|
|
29
|
+
// The registry stores the changed-file radius, not the expanded importer/neighbor list that the
|
|
30
|
+
// host gate selected. Only changed test files are certainly in that measurement; never infer
|
|
31
|
+
// pass evidence for siblings or importers that the receipt did not persist explicitly.
|
|
32
|
+
return [...new Set(receipt.radiusFiles.filter((file) => TEST.test(file) && tree.files.includes(file)))].sort();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface GateTrainReceiptPlan {
|
|
36
|
+
priorCommit: string;
|
|
37
|
+
files: string[];
|
|
38
|
+
carriedFiles: string[];
|
|
39
|
+
totalFiles: number;
|
|
40
|
+
reasons: string[];
|
|
41
|
+
receipts: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Missing/invalid evidence only reduces carry; it never makes a gate pass. */
|
|
45
|
+
export function planGateTrainReceiptCarry(input: {
|
|
46
|
+
repo: string;
|
|
47
|
+
commit: string;
|
|
48
|
+
receiptPath: string;
|
|
49
|
+
bunVersion: string;
|
|
50
|
+
lockfileHash: string;
|
|
51
|
+
envFingerprint: string;
|
|
52
|
+
}): GateTrainReceiptPlan | null {
|
|
53
|
+
if (!SHA.test(input.commit) || !input.bunVersion || !input.lockfileHash || !/^[0-9a-f]{64}$/i.test(input.envFingerprint)) return null;
|
|
54
|
+
const tests = gateAssignableTestsAtCommit(input.repo, input.commit);
|
|
55
|
+
if (!tests?.length) return null;
|
|
56
|
+
const receipts = Object.values(loadGateReceipts(input.receiptPath));
|
|
57
|
+
const currentTree = gitClosureTree(input.repo, input.commit);
|
|
58
|
+
const carried = new Set<string>();
|
|
59
|
+
const accepted: GateReceipt[] = [];
|
|
60
|
+
const reasons = new Set<string>();
|
|
61
|
+
const changedByBase = new Map<string, string[]>();
|
|
62
|
+
|
|
63
|
+
for (const receipt of receipts) {
|
|
64
|
+
if (receipt.verdict !== 'passed' || gateReceiptSelfKey(receipt) !== receipt.key) { reasons.add('invalid-or-non-passing receipt'); continue; }
|
|
65
|
+
if (!receipt.checks.some((check) => check === 'test-interference:with-neighbours' || check === 'test-interference:changed-only')) {
|
|
66
|
+
reasons.add('receipt lacks passing test-interference check'); continue;
|
|
67
|
+
}
|
|
68
|
+
if (receipt.bunVersion !== input.bunVersion || receipt.lockfileHash !== input.lockfileHash
|
|
69
|
+
|| receipt.envFingerprint !== input.envFingerprint) { reasons.add('bun/lockfile/environment differs'); continue; }
|
|
70
|
+
if (!ancestor(input.repo, receipt.baseCommit, input.commit)) { reasons.add('receipt base is not an ancestor of cut'); continue; }
|
|
71
|
+
if (receipt.radiusFiles.some((file) => !currentTree.files.includes(file)
|
|
72
|
+
|| blob(input.repo, receipt.headCommit, file) !== blob(input.repo, input.commit, file))) {
|
|
73
|
+
reasons.add('receipt radius changed or missing at cut'); continue;
|
|
74
|
+
}
|
|
75
|
+
let measuredChanges: string[];
|
|
76
|
+
try { measuredChanges = changedFilesBetween(input.repo, receipt.baseCommit, receipt.headCommit); }
|
|
77
|
+
catch { reasons.add('receipt base-to-head diff unavailable'); continue; }
|
|
78
|
+
if (measuredChanges.some((file) => !receipt.radiusFiles.includes(file))) {
|
|
79
|
+
reasons.add('receipt radius does not cover base-to-head changes'); continue;
|
|
80
|
+
}
|
|
81
|
+
const candidates = candidateTests(currentTree, receipt);
|
|
82
|
+
if (!candidates?.length) { reasons.add('receipt test radius is empty or opaque'); continue; }
|
|
83
|
+
|
|
84
|
+
let changed: string[];
|
|
85
|
+
try {
|
|
86
|
+
changed = changedByBase.get(receipt.baseCommit) ?? changedFilesBetween(input.repo, receipt.baseCommit, input.commit);
|
|
87
|
+
changedByBase.set(receipt.baseCommit, changed);
|
|
88
|
+
} catch { reasons.add('cut diff from receipt base unavailable'); continue; }
|
|
89
|
+
const outside = changed.filter((file) => !receipt.radiusFiles.includes(file) && !INERT.test(file));
|
|
90
|
+
const nonCodeOutside = outside.find((file) => !/\.(?:[cm]?[jt]sx?)$/.test(file));
|
|
91
|
+
if (nonCodeOutside) {
|
|
92
|
+
reasons.add(`non-code release input changed outside receipt radius: ${nonCodeOutside}`); continue;
|
|
93
|
+
}
|
|
94
|
+
let safeCandidates = candidates;
|
|
95
|
+
if (outside.length) {
|
|
96
|
+
const affected = testsReachingChanged(currentTree, outside, candidates);
|
|
97
|
+
if (!affected.ok) { reasons.add('cut import closure opaque'); continue; }
|
|
98
|
+
const affectedSet = new Set(affected.tests);
|
|
99
|
+
safeCandidates = candidates.filter((test) => !affectedSet.has(test));
|
|
100
|
+
if (!safeCandidates.length) { reasons.add('merge-skew changes reach every receipt test'); continue; }
|
|
101
|
+
}
|
|
102
|
+
for (const test of safeCandidates) carried.add(test);
|
|
103
|
+
accepted.push(receipt);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const noCarry = (reason: string): GateTrainReceiptPlan => ({ priorCommit: accepted[0]?.baseCommit ?? input.commit,
|
|
107
|
+
files: [...tests], carriedFiles: [], totalFiles: tests.length,
|
|
108
|
+
reasons: [...new Set([...reasons, ...(reason === 'no safe receipt coverage' && reasons.size ? [] : [reason])])].sort(),
|
|
109
|
+
receipts: accepted.map((receipt) => receipt.key).sort() });
|
|
110
|
+
if (!carried.size || !accepted.length) return noCarry(receipts.length ? 'no safe receipt coverage' : 'gate receipt registry empty');
|
|
111
|
+
const selected = tests.filter((file) => !carried.has(file));
|
|
112
|
+
// The caller falls back to a full sweep rather than skipping all test Pods.
|
|
113
|
+
if (!selected.length) return noCarry('all tests carried; keep at least one measured sweep');
|
|
114
|
+
const latest = [...accepted].sort((a, b) => b.savedAt.localeCompare(a.savedAt))[0]!;
|
|
115
|
+
return { priorCommit: latest.baseCommit, files: selected, carriedFiles: [...carried].sort(), totalFiles: tests.length,
|
|
116
|
+
reasons: [...reasons].sort(), receipts: accepted.map((receipt) => receipt.key).sort() };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function gateReceiptRegistryPath(repo: string): string | null {
|
|
120
|
+
const common = git(repo, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
|
|
121
|
+
return common ? join(common, 'elanous-host-regate', 'gate-receipts.json') : null;
|
|
122
|
+
}
|
|
@@ -22,6 +22,7 @@ import { reserveReleaseResumeAttempt } from './resume-cap.js';
|
|
|
22
22
|
import { cutReleaseBranch } from './cut-branch.js';
|
|
23
23
|
import { releaseLoopConfig } from './unattended-release.js';
|
|
24
24
|
import { trafficOptionsFromConfig } from './traffic-transition.js';
|
|
25
|
+
import type { GateAutohealDecision } from './gate-autoheal.js';
|
|
25
26
|
|
|
26
27
|
const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
|
|
27
28
|
|
|
@@ -67,9 +68,36 @@ export interface ResumeReleaseOptions {
|
|
|
67
68
|
export interface ResumeReleasePlan {
|
|
68
69
|
runId: string; version: string; fromNode: string; commandRoot: string; tip: TipRefresh;
|
|
69
70
|
partial: PartialOutcome; gatePlan: { mode: 'not-run' | 'partial' | 'full'; reason?: string };
|
|
71
|
+
/** Most recent failed gate's autoheal recommendation, carried into the resume plan for the caller. */
|
|
72
|
+
autoheal?: GateAutohealDecision;
|
|
73
|
+
autohealReroute?: string;
|
|
70
74
|
nextAttempt: number; priorCountKnown: boolean; preflight: 'passed';
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
function priorGateAutoheal(run: GraphRunState): GateAutohealDecision | undefined {
|
|
78
|
+
const gate = lastJsonObject(run.nodes.find((node) => node.nodeId === 'gate')?.output);
|
|
79
|
+
const value = gate?.autoheal;
|
|
80
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
81
|
+
const candidate = value as Partial<GateAutohealDecision>;
|
|
82
|
+
if (!['reroute-shard', 'retry-shard', 'partial-resume', 'host-switch', 'hitl'].includes(String(candidate.action))) return undefined;
|
|
83
|
+
if (!['shard-pod-death', 'timeout', 'host-down', 'interference', 'true-regression', 'environment'].includes(String(candidate.cause))) return undefined;
|
|
84
|
+
if (typeof candidate.signature !== 'string' || typeof candidate.reason !== 'string'
|
|
85
|
+
|| typeof candidate.shardElapsedMs !== 'number' || typeof candidate.overallElapsedMs !== 'number') return undefined;
|
|
86
|
+
return candidate as GateAutohealDecision;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function automaticReroute(run: GraphRunState, autoheal: GateAutohealDecision | undefined, tipChanged: boolean, from: string): string | undefined {
|
|
90
|
+
if (!autoheal || tipChanged || from !== 'gate' || typeof (run.input as Record<string, unknown> | undefined)?.gatePodPool !== 'string'
|
|
91
|
+
|| (autoheal.action !== 'retry-shard' && autoheal.action !== 'reroute-shard' && autoheal.action !== 'host-switch')) return undefined;
|
|
92
|
+
const key = autoheal.shardKey;
|
|
93
|
+
if (autoheal?.action === 'host-switch') return 'auto';
|
|
94
|
+
if (typeof key === 'string' && /^pod-\d+$/.test(key)) return key;
|
|
95
|
+
if (typeof key === 'string' && /^\d+$/.test(key)) return `pod-${key}`;
|
|
96
|
+
const gate = lastJsonObject(run.nodes.find((node) => node.nodeId === 'gate')?.output);
|
|
97
|
+
const stalled = gate?.stalledShards;
|
|
98
|
+
return Array.isArray(stalled) && stalled.length > 0 ? 'auto' : undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
73
101
|
/** CLI entry files: a production file that only names one (help text, a goal's command line) does not run it. */
|
|
74
102
|
const CLI_ENTRIES = ['bin/elanous.mjs', 'src/index.ts'] as const;
|
|
75
103
|
/** The gate's own test-file set (gate-node sweep: `git ls-files '*.test.*'` filtered by this). */
|
|
@@ -428,6 +456,42 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
428
456
|
const saved = JSON.parse(readFileSync(tip.statePath, 'utf8')) as GraphRunState;
|
|
429
457
|
const gateRecord = saved.nodes.find((node) => node.nodeId === 'gate');
|
|
430
458
|
const previousGate = gateRecord ? lastJsonObject(gateRecord.output) as Record<string, unknown> | undefined : undefined;
|
|
459
|
+
// `loadFlaky` is also a partial-resume cause, but it is emitted separately from
|
|
460
|
+
// shard-only interference. Carry both sets into the same affected-file recheck.
|
|
461
|
+
const interference = [...new Set([
|
|
462
|
+
...(Array.isArray(previousGate?.interferenceErrors) ? previousGate.interferenceErrors : []),
|
|
463
|
+
...(Array.isArray(previousGate?.loadFlaky) ? previousGate.loadFlaky : []),
|
|
464
|
+
])];
|
|
465
|
+
if (previousGate?.outcome === 'error' && previousGate.commit === tip.to && previousGate.autoheal
|
|
466
|
+
&& typeof previousGate.autoheal === 'object' && (previousGate.autoheal as { action?: unknown }).action === 'partial-resume') {
|
|
467
|
+
if (!Array.isArray(interference) || !interference.length || !interference.every((id) => typeof id === 'string'))
|
|
468
|
+
return fallback('partial-resume has no complete interference error record');
|
|
469
|
+
let closure: ClosureTree;
|
|
470
|
+
try { closure = (deps.closureTree ?? ((commit: string) => gitClosureTree(deps.repo ?? process.cwd(), commit)))(tip.to); }
|
|
471
|
+
catch (error) { return fallback(`tip tree unreadable: ${String(error instanceof Error ? error.message : error).slice(0, 200)}`); }
|
|
472
|
+
const tracked = new Set(closure.files);
|
|
473
|
+
const planned = planSameCommitRecheck({ version: tip.version, measuredCommit: tip.to, forCommit: tip.to,
|
|
474
|
+
failures: [], errors: interference as string[], existsAtCommit: (file) => tracked.has(file) });
|
|
475
|
+
if (!planned.ok) return fallback(planned.reason);
|
|
476
|
+
// The gate consumer only executes same-cut partial work for the stalled-only recheck shape.
|
|
477
|
+
// Interference files are likewise unmeasured, so mark exactly this set as the recheck boundary.
|
|
478
|
+
const partialFailures = previousGate.partialFailures as { failures?: unknown; errors?: unknown } | undefined;
|
|
479
|
+
if (!Array.isArray(partialFailures?.failures) || !partialFailures.failures.every((id) => typeof id === 'string')
|
|
480
|
+
|| !Array.isArray(partialFailures.errors) || !partialFailures.errors.every((id) => typeof id === 'string'))
|
|
481
|
+
return fallback('partial-resume has no complete prior failure record');
|
|
482
|
+
let priorFailures: string[];
|
|
483
|
+
let priorErrors: string[];
|
|
484
|
+
try {
|
|
485
|
+
const affected = new Set(planned.plan.files);
|
|
486
|
+
priorFailures = (partialFailures.failures as string[]).filter((id) => !affected.has(testFileOfId(id)));
|
|
487
|
+
priorErrors = (partialFailures.errors as string[]).filter((id) => !affected.has(testFileOfId(id)));
|
|
488
|
+
} catch { return fallback('partial-resume prior failure record is malformed'); }
|
|
489
|
+
const recheckPlan = { ...planned.plan, priorFailures, priorErrors, stalledFiles: planned.plan.files };
|
|
490
|
+
const path = dryRun ? undefined : writeRecheckPlan(root, recheckPlan);
|
|
491
|
+
debug.log('release-loop.resume', 'interference-recheck-planned', { version: tip.version, commit: tip.to,
|
|
492
|
+
remeasureFileCount: recheckPlan.files.length, fallbackReason: null, ...(path ? { path } : {}), dryRun });
|
|
493
|
+
return { requested: true, applied: true, files: recheckPlan.files.length, priorCommit: tip.to, ...(auto ? { auto: true as const } : {}) };
|
|
494
|
+
}
|
|
431
495
|
const stalled = previousGate?.stalledShards;
|
|
432
496
|
const summary = previousGate?.partialSummary as { pass?: unknown; fail?: unknown; errors?: unknown; ran?: unknown; files?: unknown } | undefined;
|
|
433
497
|
const measured = previousGate?.partialFailures as { failures?: unknown; errors?: unknown } | undefined;
|
|
@@ -600,19 +664,25 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
|
|
|
600
664
|
waiverCommit = recorded;
|
|
601
665
|
}
|
|
602
666
|
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
|
|
667
|
+
const fromIndex = savedRun.path.indexOf(opts.from);
|
|
668
|
+
const gateIndex = savedRun.path.indexOf('gate');
|
|
669
|
+
const gateWillRun = fromIndex >= 0 && gateIndex >= fromIndex;
|
|
603
670
|
if (opts.rerouteShard !== undefined && tip.changed) throw new Error('shard reroute requires the original cut head; moved tip needs a full re-gate');
|
|
604
671
|
verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, tip.version);
|
|
605
672
|
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
|
|
606
673
|
verifyResumePreflight(savedRun, opts, deps, tip.version, tip.from, tip.to, commandRoot);
|
|
607
|
-
const
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
const
|
|
674
|
+
const autoheal = gateWillRun ? priorGateAutoheal(savedRun) : undefined;
|
|
675
|
+
const autoReroute = opts.rerouteShard === undefined ? automaticReroute(savedRun, autoheal, tip.changed, opts.from) : undefined;
|
|
676
|
+
const automaticShardReroute = autoReroute ? releaseResumeGateShardReroute(savedRun, { from: opts.from, shard: autoReroute }) : undefined;
|
|
677
|
+
const effectiveOpts = automaticShardReroute ? { ...opts, rerouteShard: automaticShardReroute.set.gateRerouteShard } : opts;
|
|
678
|
+
const partial = preparePartial(tip, effectiveOpts.rerouteShard !== undefined ? { ...effectiveOpts, partial: false } : effectiveOpts, deps, true);
|
|
611
679
|
const gatePlan = !gateWillRun ? { mode: 'not-run' as const }
|
|
612
680
|
: partial.requested && partial.applied ? { mode: 'partial' as const }
|
|
613
681
|
: { mode: 'full' as const, reason: partial.requested ? partial.reason : opts.partial === false ? 'partial recheck disabled' : 'no partial plan applies' };
|
|
682
|
+
if (autoheal) debug.log('release-loop.resume', 'gate-autoheal-plan', { runId: opts.runId, version: tip.version, appliedReroute: automaticShardReroute?.set.gateRerouteShard ?? null, ...autoheal });
|
|
614
683
|
return { runId: opts.runId, version: tip.version, fromNode: opts.from, commandRoot, tip, partial,
|
|
615
|
-
gatePlan,
|
|
684
|
+
gatePlan, ...(autoheal ? { autoheal } : {}), ...(automaticShardReroute ? { autohealReroute: automaticShardReroute.set.gateRerouteShard } : {}),
|
|
685
|
+
nextAttempt: attempt.attempt, priorCountKnown: attempt.priorCountKnown, preflight: 'passed' };
|
|
616
686
|
}
|
|
617
687
|
|
|
618
688
|
async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeReleaseDeps): Promise<{ tip: TipRefresh; partial: PartialOutcome; state: GraphRunState }> {
|
|
@@ -667,7 +737,12 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
667
737
|
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), previewTip.to, deps.git);
|
|
668
738
|
verifyResumePreflight(savedRun, opts, deps, previewTip.version, previewTip.from, previewTip.to, commandRoot);
|
|
669
739
|
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, false, previewTip.to);
|
|
670
|
-
const
|
|
740
|
+
const autoheal = priorGateAutoheal(savedRun);
|
|
741
|
+
const autoReroute = !shardReroute ? automaticReroute(savedRun, autoheal, tip.changed, opts.from) : undefined;
|
|
742
|
+
const automaticShardReroute = autoReroute ? releaseResumeGateShardReroute(savedRun, { from: opts.from, shard: autoReroute }) : undefined;
|
|
743
|
+
const partial = preparePartial(tip, shardReroute || automaticShardReroute
|
|
744
|
+
? { ...opts, rerouteShard: (shardReroute ?? automaticShardReroute)!.set.gateRerouteShard, partial: false } : opts, deps);
|
|
745
|
+
if (autoheal) debug.log('release-loop.resume', 'gate-autoheal-plan', { runId: opts.runId, version: tip.version, appliedReroute: automaticShardReroute?.set.gateRerouteShard ?? null, ...autoheal });
|
|
671
746
|
// A failed preflight does not consume an observed release resume attempt.
|
|
672
747
|
const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now });
|
|
673
748
|
debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt,
|
|
@@ -682,8 +757,8 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
682
757
|
...(acceptedRegressions ? { releaseAcceptedRegressions: { requested: opts.acceptedRegressions!, acceptedRegressions } } : {}),
|
|
683
758
|
...(gateExecutor ? { releaseGateExecutor: { executor: gateExecutor.set.gateExecutor, ...(opts.gateHost !== undefined ? { host: opts.gateHost } : {}) } } : {}),
|
|
684
759
|
...(gatePodPool ? { releaseGatePodPool: gatePodPool.set.gatePodPool } : {}),
|
|
685
|
-
...(shardReroute ? { releaseGateShardReroute: { shard: shardReroute
|
|
686
|
-
...(shardReroute
|
|
760
|
+
...(shardReroute || automaticShardReroute ? { releaseGateShardReroute: { shard: (shardReroute ?? automaticShardReroute)!.set.gateRerouteShard,
|
|
761
|
+
...((shardReroute ?? automaticShardReroute)!.set.gateShardHost ? { host: (shardReroute ?? automaticShardReroute)!.set.gateShardHost } : {}),
|
|
687
762
|
...(opts.shardOs ? { os: opts.shardOs } : {}) } } : {}),
|
|
688
763
|
deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
|
|
689
764
|
return { tip, partial, state };
|
package/scripts/ux-sim.ts
CHANGED
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
// bun scripts/ux-sim.ts --fault sqlite-busy # 결함 주입: 실제 SQLite 잠금 → 로그 배치 버퍼·복구
|
|
21
21
|
// bun scripts/ux-sim.ts emit child-error-long --json # 사건 주입(L0): 격리 우주 원장에 주입 줄 → 소비자 목록
|
|
22
22
|
//
|
|
23
|
-
//
|
|
23
|
+
// catalog list/show 는 재사용 장치의 참조를 보여 준다. 선언된 TUI 장면은 실제 위젯 render() 를 부른다 — 흉내 렌더러가 아니다.
|
|
24
24
|
import { simRenderLogFrame } from '../src/ux-sim/render-frame.js';
|
|
25
25
|
import { simTypeaheadState, type TurnPhase, type TurnSimSpec } from '../src/ux-sim/turn-states.js';
|
|
26
26
|
import { renderTurnTypeaheadQueueRow, renderTurnTypeaheadEcho } from '../src/chat/turn-typeahead.js';
|
|
27
|
+
import { loadMockScenarioCatalog, renderMockScenarioDetails, renderMockScenarioList } from '../src/ux-sim/mock-scenario-catalog.js';
|
|
27
28
|
|
|
28
29
|
const PHASES: TurnPhase[] = [
|
|
29
30
|
'idle', 'thinking', 'streaming', 'streaming-with-subagents', 'harness-child', 'interrupted',
|
|
@@ -31,6 +32,22 @@ const PHASES: TurnPhase[] = [
|
|
|
31
32
|
|
|
32
33
|
const argv = process.argv.slice(2);
|
|
33
34
|
|
|
35
|
+
if (argv[0] === 'list') {
|
|
36
|
+
if (argv.length !== 1) { console.error('usage: bun scripts/ux-sim.ts list'); process.exit(2); }
|
|
37
|
+
console.log(renderMockScenarioList(loadMockScenarioCatalog()).join('\n'));
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
if (argv[0] === 'show') {
|
|
41
|
+
if (argv.length !== 2) { console.error('usage: bun scripts/ux-sim.ts show <MS-id>'); process.exit(2); }
|
|
42
|
+
try {
|
|
43
|
+
console.log(renderMockScenarioDetails(loadMockScenarioCatalog(), argv[1]!).join('\n'));
|
|
44
|
+
process.exit(0);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
47
|
+
process.exit(2);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
34
51
|
// ── 사건 주입 «이벤트 몽키» (SIM-EVENT-MONKEY · L0) — 자기 플래그를 가진 하위 명령 ──
|
|
35
52
|
// bun scripts/ux-sim.ts emit <kind> [--run <id>] [--variant allowed|refused] [--state-dir <dir>] [--json]
|
|
36
53
|
// bun scripts/ux-sim.ts emit --list
|