elanous 0.2.32 → 0.2.35
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/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{997ce8a5-2ad501927003b41b.js → 0368f25d-ad84af92fefee68f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +1 -0
- package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +32 -0
- package/apps/pwa/out/_next/static/chunks/{5525.86debf231e181813.js → 1530.0e612369b6fd7ea0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +41 -0
- package/apps/pwa/out/_next/static/chunks/{7391.ebae168025c91b6a.js → 1674.2a65fd5668e1cc5f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +74 -0
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7947-d0759430f8042240.js → 1993-c0007b7ad0c0ef41.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{60fb96c9.360b01d8d4927b7b.js → 1c735de4.f993a30bbd3c57c4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5307.60f2209e809bd627.js → 2018.a741297bce759490.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3902.e1e71aaac850a045.js → 2625.5d870aa999f33db1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +77 -0
- package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +62 -0
- package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +90 -0
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4174.92ed04922709ac9e.js → 3408.21d1570602f4b6c0.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/359-2250789a2d3499e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9398-6aed28f0a96d0d6f.js → 3677-4215f826f6aa68e5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +79 -0
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +122 -0
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7371.80c05c653b08e907.js → 4346.8601952e73bf5803.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4441-875b97f5821ed58a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +286 -0
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{2940-619bfd8ef337c611.js → 4705-194e64e47ca0fbdb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1335-6731f392c35216eb.js → 4718-17d2af636796a330.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7502.ccd5b8065545edd0.js → 5101.e32282718f99ed6f.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{2001.6c7e0fd02190886c.js → 5154.a118f3957ed14e36.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{5173-233188df2f42c626.js → 5483-dd5d7697f6bdb106.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +94 -0
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3267c0e6.d34854e7b3984a6e.js → 56d372f2.681ab4ec4c998fae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +287 -0
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +1 -0
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6433.fc573af22bbc9ab7.js → 6236.e18cced2d5ba6afb.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +8 -0
- package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +93 -0
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6544cb48.a87fe20ddf137be2.js → 6b89c8bd.24df687a820500b3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +149 -0
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{2871.0bca6137343b3f76.js → 7385.50a308a477bdfc21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2985.39ccdecd36848b12.js → 7644.17e37ba6f1d4226e.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8018.a32663f04a6fc5f9.js → 8219.baa715d1deee80e2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4111.2bfedbb233ad206b.js → 8256.e3ac8421a71f82f4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2213.2ccbada92c33ea0f.js → 8444.4c26b379ca2b9596.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +226 -0
- package/apps/pwa/out/_next/static/chunks/8557-e7898769499cd56f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +64 -0
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6092.a7f99e68b188ca66.js → 8637.47f5b35afcae5e6b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3278.2e5d15b53a4091c8.js → 9214.63a510d39178e997.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +115 -0
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9221.7d22eaf353a2d4e7.js → 938.66ad1ef0e5abae1c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6157.0c9af021de4fb31b.js → 9512.5a76207a730e0491.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{69-0a2c8209c6d4a158.js → 967-2aa39b2d04b4c690.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7862.30a8d6482bb238eb.js → 9952.5daf5f94063a2b3e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a27b1e5a0d1b308f.js → page-406b47745b53f269.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-32866485913d6a04.js → page-47c5d4db587266bd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-04d00ae9b0890349.js → page-dc0a532f5b9196ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7a1887c4.f36b88fba31da8fd.js → b2f6ba4d.fb56fdbbf798df33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/b6705a4f-b9a9047de9eecfd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b313b242.cb1265eea908db61.js → cbe38a15.5a032598e39970c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{409f1e57.d0649fc47691ebfe.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{0bb0ff36.b8d418980951e5ba.js → da5176c6.6773b6c5994e1d99.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.mjs +10 -2
- package/catalog/providers/opengateway.yaml +25 -0
- package/docs/goal-context/01-goal-authoring-checks.md +8 -0
- package/docs/goal-context/03-verification-menu.md +24 -0
- package/docs/goal-context/04-mock-scenarios.md +100 -0
- package/docs/goal-context/README.md +5 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/default-loop.yaml +76 -5
- package/graphs/document-loop.yaml +71 -4
- package/graphs/implement-loop.yaml +59 -4
- package/graphs/journey/SCENARIOS.md +33 -0
- package/graphs/journey/recipes.yaml +1 -0
- package/graphs/journey/ta-journey.yaml +94 -0
- package/graphs/journey/units/author/author.yaml +20 -0
- package/graphs/journey/units/author/recipes.yaml +2 -0
- package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
- package/graphs/journey/units/author-dialogue/observe.ts +96 -0
- package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
- package/graphs/journey/units/green/green.yaml +20 -0
- package/graphs/journey/units/green/recipes.yaml +2 -0
- package/graphs/journey/units/pr/pr.yaml +20 -0
- package/graphs/journey/units/pr/recipes.yaml +2 -0
- package/graphs/journey/units/remember/recipes.yaml +2 -0
- package/graphs/journey/units/remember/remember.yaml +20 -0
- package/graphs/operate-loop.yaml +71 -4
- package/graphs/overlays/stop-heal-ladder.yaml +1 -1
- package/graphs/release-rehearsal/recipes.yaml +1 -1
- package/graphs/release-rehearsal/release-rehearsal.yaml +2 -2
- package/graphs/research-loop.yaml +159 -4
- package/graphs/test-diet/test-diet.yaml +3 -0
- package/graphs/units/dispatch-pod/dispatch-pod.yaml +50 -0
- package/graphs/units/dispatch-pod/recipes.yaml +14 -0
- package/graphs/units/entry/entry.yaml +50 -0
- package/graphs/units/entry/recipes.yaml +14 -0
- package/graphs/units/harvest/harvest.yaml +25 -0
- package/graphs/units/harvest/recipes.yaml +4 -0
- package/graphs/units/land/land.yaml +45 -0
- package/graphs/units/land/recipes.yaml +3 -0
- package/graphs/units/recipes.yaml +7 -0
- package/graphs/units/recover.yaml +39 -0
- package/graphs/units/reflect/recipes.yaml +3 -0
- package/graphs/units/reflect/reflect.yaml +15 -0
- package/graphs/units/task-agent-recover-review.yaml +17 -0
- package/graphs/units/task-agent-recover-wait.yaml +17 -0
- package/graphs/units/verbs/make-doc.yaml +34 -0
- package/graphs/units/verbs/recipes.yaml +33 -0
- package/graphs/units/verbs/verify.yaml +55 -0
- package/graphs/work/recipes.yaml +2 -0
- package/graphs/work/research-report.yaml +28 -0
- package/package.json +1 -1
- package/scripts/ci-typecheck-changed.ts +69 -16
- package/scripts/coord-role-pack.ts +19 -6
- package/scripts/docs/generate-graph-anatomy.ts +19 -2
- package/scripts/graph-harvest-unit.ts +22 -0
- package/scripts/graph-units/journey-stub.ts +6 -0
- package/scripts/graph-units/land-enqueue.ts +78 -0
- package/scripts/graph-units/recover-plan.ts +15 -0
- package/scripts/graph-units/reflect-node.ts +88 -0
- package/scripts/graph-units/ta-journey-observe.ts +109 -0
- package/scripts/graph-units/verbs/headless-goal-loop.ts +152 -0
- package/scripts/graph-units/verbs/verify-stage.ts +75 -0
- package/scripts/graph-units/verbs-envelope.ts +139 -0
- package/scripts/hq/standby-snapshot.ts +30 -0
- package/scripts/public-export.ts +43 -1
- package/scripts/ratchets/train-side-invariants.sh +45 -0
- package/scripts/release-loop/auto-approve-node.ts +30 -16
- package/scripts/release-loop/docs-land-node.ts +21 -5
- package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
- package/scripts/release-loop/gate-closure.ts +5 -1
- package/scripts/release-loop/gate-diff.ts +1 -1
- package/scripts/release-loop/gate-node.ts +675 -49
- package/scripts/release-loop/gate-partial.ts +68 -14
- package/scripts/release-loop/gate-radius-live.ts +177 -0
- package/scripts/release-loop/gate-radius-shadow.ts +138 -0
- package/scripts/release-loop/gate-traffic-mode.ts +128 -0
- package/scripts/release-loop/gate-triage-node.ts +5 -2
- package/scripts/release-loop/known-issues-0.2.33-approved.json +194 -0
- package/scripts/release-loop/known-issues-node.ts +77 -8
- package/scripts/release-loop/node-verdict.ts +12 -1
- package/scripts/release-loop/npm-publish-node.ts +66 -12
- package/scripts/release-loop/preflight.ts +93 -7
- package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
- package/scripts/release-loop/publish-node.ts +60 -9
- package/scripts/release-loop/pwa-remote.ts +166 -0
- package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
- package/scripts/release-loop/resume-cap.ts +56 -0
- package/scripts/release-loop/resume-release.ts +155 -19
- package/scripts/release-loop/resume.ts +24 -1
- package/scripts/release-loop/tui-sim-node.ts +8 -2
- package/scripts/release-loop/unattended-release.ts +10 -2
- package/scripts/test-diet/lib.ts +6 -1
- package/scripts/test-diet/node.ts +194 -13
- package/scripts/ux-sim.ts +39 -0
- package/src/acp/core-turn-bridge.ts +20 -5
- package/src/acp/reasoning-thought-buffer.ts +45 -0
- package/src/agent/self-ambient-cache.ts +8 -0
- package/src/agent-substrate/acp-reviewer.ts +90 -15
- package/src/agent-substrate/pr-reviewer.ts +17 -0
- package/src/agent-substrate/review-finding-key.ts +53 -0
- package/src/agent-substrate/self-review-cli.ts +188 -18
- package/src/ask-user-question/pending-questions.ts +1 -1
- package/src/autopilot/pr-manager.ts +393 -65
- package/src/budget/run-usage-rollup.ts +1 -0
- package/src/chat/index.ts +0 -2
- package/src/chat/pickers/modal-runtime.ts +10 -27
- package/src/chat/tool-render/dispatch.ts +62 -10
- package/src/cli/agent-cli.ts +13 -2
- package/src/cli/control-cli.ts +24 -7
- package/src/cli/doctor-cli.ts +4 -1
- package/src/cli/guardian-cli.ts +10 -0
- package/src/cli/light-entry-route.ts +75 -0
- package/src/cli/llm-detect-cli.ts +23 -1
- package/src/cli/pod-cli.ts +77 -3
- package/src/cli/pr-cli.ts +81 -14
- package/src/cli/pr-land-base-read.ts +2 -0
- package/src/cli/pr-land-overlap-evidence.ts +154 -16
- package/src/cli/pr-land-pod-shadow-job.ts +3 -0
- package/src/cli/pr-land-recovery.ts +64 -0
- package/src/cli/pty-takeover-cli.ts +6 -5
- package/src/cli/release-checklist-read-cli.ts +79 -0
- package/src/cli/release-cli.ts +189 -132
- package/src/cli/release-run-if-ready.ts +29 -28
- package/src/cli/release-single-flight.ts +178 -0
- package/src/cli/remote-resolve.ts +1 -1
- package/src/cli/root-tui-entry.ts +48 -0
- package/src/cli/seat-cli.ts +37 -0
- package/src/cli/setup-cli.ts +40 -1
- package/src/cli/sim-cli.ts +22 -0
- package/src/cli/tasks-cli.ts +667 -14
- package/src/cli/usage-cli.ts +13 -11
- package/src/clipboard/history.ts +179 -13
- package/src/config.ts +8 -1
- package/src/control/control-status.ts +8 -3
- package/src/control-plane/ledger.ts +5 -3
- package/src/control-plane/server.ts +5 -3
- package/src/core-turn/short-question.ts +5 -1
- package/src/dashboard/chat/acp-chat.ts +8 -0
- package/src/dashboard/index.ts +114 -77
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +3 -0
- package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
- package/src/dashboard/input/seat-autowake.ts +56 -3
- package/src/dashboard/render/theme-resolver.ts +33 -4
- package/src/dashboard/seat-boot-preamble.ts +1 -1
- package/src/dashboard/slash-runtime/ask-clarification.ts +48 -0
- package/src/dashboard/slash-runtime/dashboard-delegated-slashes.ts +68 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +62 -322
- package/src/dashboard/slash-runtime/dashboard-log-slash.ts +208 -0
- package/src/dashboard/turn-preamble.ts +47 -1
- package/src/dashboard/turn-stream-formatter.ts +34 -1
- package/src/dashboard/turn-stream-runtime.ts +2 -0
- package/src/dashboard/turn-typeahead-prompt-zone.ts +67 -0
- package/src/domains/guardian-tick.ts +54 -2
- package/src/domains/outbound-alert.ts +3 -2
- package/src/domains/schedule-registry.ts +16 -2
- package/src/extractors.ts +3 -1
- package/src/git-fs/idle-refresh.ts +76 -0
- package/src/git-fs/index.ts +18 -7
- package/src/git-fs/retry.ts +4 -0
- package/src/git-fs/runner.ts +2 -0
- package/src/git-fs/worktree.ts +42 -4
- package/src/graph-kinds/registry.ts +2 -2
- package/src/graph-runner/runner.ts +267 -54
- package/src/grok/credential.ts +15 -3
- package/src/harness/harness-cli-command.ts +151 -38
- package/src/harness/harness-incidents.ts +37 -0
- package/src/harness/harness-pod-dispatch.ts +38 -1
- package/src/harness/harness-salvage-cli.ts +14 -2
- package/src/harness/launch-utterance-ledger.ts +208 -0
- package/src/impl-discipline/detector.ts +16 -4
- package/src/impl-discipline/system-prompt.ts +11 -0
- package/src/impl-discipline/types.ts +2 -2
- package/src/index.ts +683 -146
- package/src/intake-plane/already-built-hint.ts +91 -0
- package/src/intake-plane/requirement-funnel.ts +29 -1
- package/src/knowhow/pack.ts +199 -0
- package/src/light-cli.ts +111 -0
- package/src/llm/opengateway-models.ts +11 -0
- package/src/llm/provider-credentials.ts +14 -0
- package/src/llm/provider-detect.ts +8 -1
- package/src/llm/runtime-model-family.ts +2 -1
- package/src/llm.ts +268 -65
- package/src/loops/budget.ts +95 -0
- package/src/loops/msg-wake.ts +8 -2
- package/src/loops/orchestrator/traffic.ts +132 -1
- package/src/model-tier/llm-tier-map.ts +15 -1
- package/src/mss/logging/log-store.ts +4 -5
- package/src/native-tool-catalog.ts +15 -1
- package/src/nexus/api/graphs-api.ts +22 -3
- package/src/nexus/api/http-server.ts +1 -1
- package/src/nexus/api/loop-resources.ts +13 -1
- package/src/nexus/api/ops-api.ts +12 -0
- package/src/oauth/claude-pty-maturity.ts +66 -0
- package/src/oauth/codex-access-mirror.ts +55 -0
- package/src/oauth/codex.ts +7 -0
- package/src/oauth/fallback-chain.ts +1 -1
- package/src/onboarding/first-run.ts +5 -3
- package/src/onboarding/non-interactive.ts +64 -13
- package/src/onboarding.ts +10 -2
- package/src/ops/overview-collect.ts +54 -4
- package/src/ops/overview.ts +31 -7
- package/src/pr-tool/census.ts +282 -30
- package/src/pr-tool/inflight-registry.ts +160 -7
- package/src/prompt-library/gpt6-family-addendum.ts +14 -1
- package/src/pty-shell/herdr-agent-report.ts +442 -0
- package/src/pty-shell/pty-control-ipc.ts +5 -5
- package/src/pty-shell/pty-manifest.ts +7 -0
- package/src/pty-shell/pty-write-outcome.ts +24 -6
- package/src/pty-shell/tui-run-state.ts +117 -3
- package/src/release-loop/checklist.ts +103 -5
- package/src/release-loop/feature-store.ts +19 -2
- package/src/release-loop/landed-but-yellow.ts +129 -5
- package/src/release-loop/light-rc.ts +1 -0
- package/src/release-loop/placement.ts +10 -3
- package/src/release-loop/rehearsal.ts +205 -25
- package/src/release-loop/rubric.ts +16 -2
- package/src/repl/index.ts +174 -17
- package/src/repl/ops-msg-bridge.ts +32 -0
- package/src/self-dev/ask-launch-flow.ts +44 -5
- package/src/self-dev/cli-help-text.ts +17 -0
- package/src/self-dev/dev-cli.ts +11 -9
- package/src/self-dev/draft-residue.ts +37 -1
- package/src/self-dev/draft-sweep.ts +188 -13
- package/src/self-dev/draft-triage-rules.ts +107 -0
- package/src/self-dev/fleet-reduce.ts +3 -2
- package/src/self-dev/graph-front-nodes.ts +112 -0
- package/src/self-dev/graph-journey-nodes.ts +11 -6
- package/src/self-dev/graph-journey-route.ts +60 -6
- package/src/self-dev/harness-run-cli.ts +3 -15
- package/src/self-dev/orchestrate-cli-text.ts +10 -0
- package/src/self-dev/orchestrate.ts +79 -3
- package/src/self-dev/review-provider-fallback.ts +35 -1
- package/src/self-dev/run-supervisor.ts +107 -12
- package/src/self-dev/run-types.ts +5 -1
- package/src/self-dev/self-orchestrate-runtime.ts +1 -11
- package/src/self-dev/shard-aggregate.ts +42 -5
- package/src/self-dev/shard-run.ts +8 -1
- package/src/self-implement/blocked-draft-policy.ts +20 -0
- package/src/self-implement/classify-shadow.ts +3 -7
- package/src/self-implement/doctor-visit.ts +18 -1
- package/src/self-implement/gate-baseline.ts +95 -12
- package/src/self-implement/gate-cli.ts +1 -0
- package/src/self-implement/gate-remote.ts +40 -25
- package/src/self-implement/goal-author-decompose.ts +275 -47
- package/src/self-implement/goal-author-identity.ts +66 -0
- package/src/self-implement/goal-author-preservation-obligation.ts +35 -0
- package/src/self-implement/goal-author.ts +254 -120
- package/src/self-implement/goal-elaborate.ts +295 -0
- package/src/self-implement/goal-refine.ts +270 -0
- package/src/self-implement/graph-overlay-yaml.ts +28 -2
- package/src/self-implement/graph-unit-flatten.ts +103 -0
- package/src/self-implement/graph-unit-resolver.ts +107 -0
- package/src/self-implement/graph-yaml.ts +151 -1
- package/src/self-implement/headless-elanous-driver.ts +1 -0
- package/src/self-implement/knowhow-pack.ts +103 -0
- package/src/self-implement/orchestrator.ts +160 -45
- package/src/self-implement/pod-warm-pool-profile.ts +45 -0
- package/src/self-implement/review-mustfix-class.ts +203 -0
- package/src/self-implement/rework-budget-judgment.ts +5 -8
- package/src/self-implement/run-stop.ts +17 -2
- package/src/self-implement/running-runs.ts +29 -1
- package/src/self-implement/scope-adjacency.ts +113 -0
- package/src/self-implement/seams.ts +138 -4
- package/src/self-implement/stop-autoheal.ts +112 -2
- package/src/session/chat.ts +27 -0
- package/src/session-runtime/index.ts +1 -1
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/setup/llm-env-detect.ts +5 -0
- package/src/skills/tools/agent.ts +140 -14
- package/src/skills/tools/msg-ask.ts +100 -0
- package/src/skills/tools/ops-ledger.ts +2 -1
- package/src/skills/tools/schedule-wakeup.ts +66 -17
- package/src/steward/hold-hygiene.ts +24 -6
- package/src/steward/release-day.ts +7 -3
- package/src/steward/triage-budget.ts +194 -0
- package/src/steward/triage.ts +57 -7
- package/src/task-agent/actions.ts +69 -1
- package/src/task-agent/green-sweep.ts +4 -0
- package/src/task-agent/harvest-unit.ts +141 -0
- package/src/task-agent/live-moves.ts +257 -15
- package/src/task-agent/parent-host.ts +216 -6
- package/src/task-agent/postland-card.ts +80 -1
- package/src/task-agent/rejudge-on-head.ts +112 -26
- package/src/task-agent/repair-branch-cas.ts +4 -1
- package/src/task-agent/repair-cycle.ts +16 -3
- package/src/task-agent/run-identity.ts +4 -3
- package/src/task-agent/shadow.ts +19 -3
- package/src/task-agent/task-hand.ts +96 -36
- package/src/task-agent/tier-split.ts +127 -0
- package/src/task-agent/work-order-build.ts +24 -0
- package/src/task-agent/work-order.ts +3 -2
- package/src/task-orchestrator/generator-prompt.ts +60 -11
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +72 -3
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +47 -8
- package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
- package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +86 -8
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +73 -17
- package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +72 -4
- package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +32 -5
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +421 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +2 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +588 -71
- package/src/task-orchestrator/surfaces/self-implement.ts +106 -2
- package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
- package/src/tool-runtime/truncation-store.ts +4 -0
- package/src/tui/README.md +2 -0
- package/src/tui/chat/background-job-status.ts +54 -0
- package/src/tui/chat/log-view.ts +80 -0
- package/src/tui/entry.ts +406 -0
- package/src/tui/input/composer.ts +144 -0
- package/src/tui/seat-mail-boot.ts +59 -0
- package/src/tui/slash/compact.ts +100 -0
- package/src/tui/slash/new-session.ts +35 -0
- package/src/tui-client/dashboard-session.ts +10 -1
- package/src/tui-client/local-chat-turn.ts +239 -0
- package/src/tui-client/local-harness-command.ts +63 -0
- package/src/tui-client/local-provider-command.ts +184 -0
- package/src/user-config/coercion.ts +36 -0
- package/src/user-config.ts +22 -43
- package/src/ux-sim/event-monkey.ts +378 -0
- package/src/ux-sim/fake-bin.ts +42 -7
- package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
- package/src/ux-sim/fault-pod-codex-503.ts +211 -0
- package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
- package/src/ux-sim/scenario.ts +72 -13
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +0 -27
- package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +0 -287
- package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +0 -62
- package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +0 -23
- package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +0 -149
- package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +0 -41
- package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2898.fd781bc7cf01a7b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +0 -115
- package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +0 -64
- package/apps/pwa/out/_next/static/chunks/3541-ce3a5424ed5ad7f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +0 -36
- package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +0 -77
- package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +0 -226
- package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +0 -79
- package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +0 -148
- package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5458-16cd7a2b51b74c8c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5543.a438b8ab5a13ea47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +0 -74
- package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +0 -94
- package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +0 -90
- package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +0 -32
- package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +0 -104
- package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +0 -24
- package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +0 -93
- package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +0 -8
- package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +0 -1
- package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +0 -24
- package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9279-d28c5b7754d010ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989.3694300e5f000b14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-8c66a3b24a745630.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c219f2ed171131be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +0 -122
- package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +0 -1
- package/src/cli/g6a-registration-probe.ts +0 -16
- /package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
|
@@ -9,7 +9,7 @@ import { existsSync, linkSync, mkdirSync, readFileSync, rmSync, writeFileSync }
|
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
10
|
import { spawnSync } from 'node:child_process';
|
|
11
11
|
import { dirname, join } from 'node:path';
|
|
12
|
-
import { CODE_FILE, type ClosureVerdict } from './gate-closure';
|
|
12
|
+
import { CODE_FILE, TRACEABLE_RELEASE_JSON, type ClosureVerdict } from './gate-closure';
|
|
13
13
|
|
|
14
14
|
export interface PartialPlan {
|
|
15
15
|
/** A verified docs-only append carries the completed, clean cut without starting a test Pod. */
|
|
@@ -27,7 +27,13 @@ export interface PartialPlan {
|
|
|
27
27
|
|
|
28
28
|
/** G1: a second measurement of only the failed files on the very same release commit.
|
|
29
29
|
* This is deliberately separate from PartialPlan, which compares two different commits. */
|
|
30
|
-
export interface RecheckPlan extends PartialPlan {
|
|
30
|
+
export interface RecheckPlan extends PartialPlan {
|
|
31
|
+
kind: 'recheck';
|
|
32
|
+
/** Files from shards that could not produce a measurement; completed shard results remain in priorFailures/errors. */
|
|
33
|
+
stalledFiles?: string[];
|
|
34
|
+
/** Count of complete shards whose measurements are preserved in this recheck. */
|
|
35
|
+
preservedShardCount?: number;
|
|
36
|
+
}
|
|
31
37
|
|
|
32
38
|
const FULL_SHA = /^[0-9a-f]{40}$/i;
|
|
33
39
|
const RELEASE_VERSION = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
@@ -92,13 +98,49 @@ function validateRecheckPlan(raw: Partial<RecheckPlan>, version: string, path: s
|
|
|
92
98
|
typeof raw.forCommit !== 'string' || !FULL_SHA.test(raw.forCommit) || raw.priorCommit !== raw.forCommit ||
|
|
93
99
|
!Array.isArray(raw.priorFailures) || !raw.priorFailures.every((id) => typeof id === 'string') ||
|
|
94
100
|
!Array.isArray(raw.priorErrors) || !raw.priorErrors.every((id) => typeof id === 'string') ||
|
|
95
|
-
!Array.isArray(raw.files) || !raw.files.length || !raw.files.every((file) => typeof file === 'string')
|
|
101
|
+
!Array.isArray(raw.files) || !raw.files.length || !raw.files.every((file) => typeof file === 'string') ||
|
|
102
|
+
(raw.preservedShardCount !== undefined && (!Number.isSafeInteger(raw.preservedShardCount) || raw.preservedShardCount < 0))) invalid();
|
|
96
103
|
let expected: string[] = [];
|
|
97
|
-
try {
|
|
104
|
+
try {
|
|
105
|
+
const measuredFiles = [...raw.priorFailures!, ...raw.priorErrors!].map(testFileOfId);
|
|
106
|
+
const stalledFiles = raw.stalledFiles === undefined ? [] : raw.stalledFiles;
|
|
107
|
+
if (!Array.isArray(stalledFiles) || !stalledFiles.every((file) => typeof file === 'string' && TEST_FILE.test(file) && !file.split('/').includes('..'))) invalid();
|
|
108
|
+
if (raw.stalledFiles !== undefined && stalledFiles.some((file) => measuredFiles.includes(file))) invalid();
|
|
109
|
+
expected = [...new Set(stalledFiles)].sort();
|
|
110
|
+
// Legacy plans recheck every prior failed file. Stalled plans re-run only files from incomplete shards; all
|
|
111
|
+
// completed shard outcomes, including runtime errors, are preserved for the combined verdict.
|
|
112
|
+
if (raw.stalledFiles === undefined) expected = [...new Set(measuredFiles)].sort();
|
|
113
|
+
}
|
|
98
114
|
catch { invalid(); }
|
|
99
115
|
if (JSON.stringify(raw.files) !== JSON.stringify(expected)) invalid();
|
|
100
116
|
}
|
|
101
117
|
|
|
118
|
+
/** Plan a same-commit retry after a Pod sweep reports stalled shards. Reuse completed outcomes and rerun stalled files. */
|
|
119
|
+
export function planStalledShardRecheck(input: { version: string; measuredCommit: string; forCommit: string;
|
|
120
|
+
failures: readonly string[]; errors: readonly string[]; stalledFiles: readonly string[]; preservedShardCount?: number;
|
|
121
|
+
existsAtCommit: (file: string) => boolean }):
|
|
122
|
+
{ ok: true; plan: RecheckPlan } | { ok: false; reason: string } {
|
|
123
|
+
if (!RELEASE_VERSION.test(input.version)) return { ok: false, reason: 'invalid release version' };
|
|
124
|
+
if (!FULL_SHA.test(input.measuredCommit) || !FULL_SHA.test(input.forCommit)) return { ok: false, reason: 'commit is not a full sha' };
|
|
125
|
+
if (input.measuredCommit !== input.forCommit) return { ok: false, reason: 'recheck requires the same commit' };
|
|
126
|
+
try { [...input.failures, ...input.errors].forEach(testFileOfId); }
|
|
127
|
+
catch (error) { return { ok: false, reason: String(error instanceof Error ? error.message : error) }; }
|
|
128
|
+
const stalledFiles = [...new Set(input.stalledFiles)].sort();
|
|
129
|
+
if (!stalledFiles.length) return { ok: false, reason: 'no stalled test files to recheck' };
|
|
130
|
+
if (stalledFiles.some((file) => !TEST_FILE.test(file) || file.split('/').includes('..'))) return { ok: false, reason: 'unsafe stalled test path' };
|
|
131
|
+
const measuredFiles = [...new Set([...input.failures, ...input.errors].map(testFileOfId))];
|
|
132
|
+
if (stalledFiles.some((file) => measuredFiles.includes(file)))
|
|
133
|
+
return { ok: false, reason: 'stalled file overlaps a completed shard result' };
|
|
134
|
+
const files = stalledFiles;
|
|
135
|
+
try {
|
|
136
|
+
const missing = files.find((file) => !input.existsAtCommit(file));
|
|
137
|
+
if (missing) return { ok: false, reason: `failed test file missing at commit: ${missing}` };
|
|
138
|
+
} catch (error) { return { ok: false, reason: `test file existence unreadable: ${String(error)}` }; }
|
|
139
|
+
return { ok: true, plan: { kind: 'recheck', version: input.version, priorCommit: input.measuredCommit,
|
|
140
|
+
forCommit: input.forCommit, files, stalledFiles, ...(input.preservedShardCount === undefined ? {} : { preservedShardCount: input.preservedShardCount }),
|
|
141
|
+
priorFailures: [...input.failures], priorErrors: [...input.errors] } };
|
|
142
|
+
}
|
|
143
|
+
|
|
102
144
|
export const partialPlanPath = (root: string, version: string) => join(root, 'release', version, 'gate-partial.json');
|
|
103
145
|
/** The same test-file set the gate sweep runs (`git ls-files` filtered by this suffix). */
|
|
104
146
|
const TEST_FILE = /^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/;
|
|
@@ -137,13 +179,13 @@ export function planPartialRegate(input: { version: string; priorCommit: string;
|
|
|
137
179
|
const source = input.changedFiles.filter((file) => !TEST_FILE.test(file) && !isInert(file));
|
|
138
180
|
const list = (files: string[]) => `${files.slice(0, 5).join(', ')}${files.length > 5 ? ` (+${files.length - 5})` : ''}`;
|
|
139
181
|
// Only code is followed through the closure: a changed fixture, JSON, lockfile, config or script is a full gate.
|
|
140
|
-
const notCode = source.filter((file) => !CODE_FILE.test(file));
|
|
182
|
+
const notCode = source.filter((file) => !CODE_FILE.test(file) && file !== TRACEABLE_RELEASE_JSON);
|
|
141
183
|
if (source.length && (!input.closure || notCode.length)) return { ok: false, reason: `non-test change → full gate: ${list(notCode.length ? notCode : source)}` };
|
|
142
184
|
let reaching: string[] = [];
|
|
143
185
|
let closure: { reached: number; opaque: number } | undefined;
|
|
144
186
|
if (source.length) {
|
|
145
187
|
let verdict: ClosureVerdict;
|
|
146
|
-
try { verdict = input.closure!(input.changedFiles.filter((file) => CODE_FILE.test(file))); }
|
|
188
|
+
try { verdict = input.closure!(input.changedFiles.filter((file) => CODE_FILE.test(file) || file === TRACEABLE_RELEASE_JSON)); }
|
|
147
189
|
catch (error) { return { ok: false, reason: `import closure unreadable → full gate: ${String(error instanceof Error ? error.message : error).slice(0, 200)}` }; }
|
|
148
190
|
if (!verdict.ok) return { ok: false, reason: verdict.reason };
|
|
149
191
|
const unsafe = verdict.tests.filter((file) => !TEST_FILE.test(file) || file.split('/').includes('..'));
|
|
@@ -182,24 +224,32 @@ export function clearPartialPlan(root: string, version: string): void {
|
|
|
182
224
|
}
|
|
183
225
|
|
|
184
226
|
/** The plan for exactly this commit, or null. A malformed plan is an error (never silently a full or partial gate). */
|
|
185
|
-
export function
|
|
227
|
+
export function readPartialPlanResult(roots: readonly string[], version: string, commit: string):
|
|
228
|
+
{ plan: PartialPlan | null; fallbackReason: string | null } {
|
|
229
|
+
let fallbackReason: string | null = 'no-applicable-plan';
|
|
186
230
|
for (const root of roots) {
|
|
187
231
|
const path = partialPlanPath(root, version);
|
|
188
232
|
if (!existsSync(path)) continue;
|
|
189
233
|
const raw = JSON.parse(readFileSync(path, 'utf8')) as Partial<PartialPlan>;
|
|
234
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error(`invalid gate partial plan: ${path}`);
|
|
190
235
|
if ((raw as Partial<RecheckPlan>).kind !== undefined && (raw as Partial<RecheckPlan>).kind !== 'recheck' && raw.kind !== 'carry')
|
|
191
236
|
throw new Error(`invalid gate partial plan: ${path}`);
|
|
192
|
-
//
|
|
193
|
-
// A legacy full gate may run, but it must never interpret a same-commit recheck as a changed-tip partial gate.
|
|
237
|
+
// Only a well-formed stalled-shard recheck for this exact release commit enters the same-commit consumer path.
|
|
194
238
|
if ((raw as Partial<RecheckPlan>).kind === 'recheck') {
|
|
195
|
-
|
|
239
|
+
const recheck = raw as Partial<RecheckPlan>;
|
|
240
|
+
validateRecheckPlan(recheck, version, path);
|
|
241
|
+
if (recheck.forCommit === commit && recheck.stalledFiles !== undefined)
|
|
242
|
+
return { plan: recheck, fallbackReason: null };
|
|
243
|
+
if (recheck.forCommit === commit) { fallbackReason = 'recheck-not-stalled-only'; continue; }
|
|
244
|
+
fallbackReason = 'plan-commit-mismatch';
|
|
196
245
|
continue;
|
|
197
246
|
}
|
|
198
247
|
if (raw.version !== version || typeof raw.priorCommit !== 'string' || !/^[0-9a-f]{40}$/i.test(raw.priorCommit)
|
|
199
248
|
|| typeof raw.forCommit !== 'string' || !/^[0-9a-f]{40}$/i.test(raw.forCommit) || raw.priorCommit === raw.forCommit
|
|
200
249
|
|| !Array.isArray(raw.files) || (raw.files.length === 0) !== (raw.kind === 'carry')
|
|
201
250
|
|| !raw.files.every((file) => typeof file === 'string' && TEST_FILE.test(file) && !file.split('/').includes('..'))
|
|
202
|
-
|| !Array.isArray(raw.priorFailures) || !Array.isArray(raw.priorErrors))
|
|
251
|
+
|| !Array.isArray(raw.priorFailures) || !Array.isArray(raw.priorErrors))
|
|
252
|
+
throw new Error(`invalid gate partial plan: ${path}`);
|
|
203
253
|
if (raw.kind === 'carry' && (raw.priorFailures.length || raw.priorErrors.length)) throw new Error(`invalid gate partial plan: ${path}`);
|
|
204
254
|
// Every prior failure's file is re-run (planPartialRegate guarantees it) — a plan that would carry a failure it did
|
|
205
255
|
// not re-run is malformed, not «partial».
|
|
@@ -208,10 +258,14 @@ export function readPartialPlan(roots: readonly string[], version: string, commi
|
|
|
208
258
|
try { failureFiles = [...raw.priorFailures, ...raw.priorErrors].map((id) => testFileOfId(String(id))); }
|
|
209
259
|
catch { throw new Error(`invalid gate partial plan: ${path}`); }
|
|
210
260
|
if (failureFiles.some((file) => !rerun.has(file))) throw new Error(`invalid gate partial plan (prior failure not re-run): ${path}`);
|
|
211
|
-
if (raw.forCommit !== commit) continue;
|
|
212
|
-
return raw as PartialPlan;
|
|
261
|
+
if (raw.forCommit !== commit) { fallbackReason = 'plan-commit-mismatch'; continue; }
|
|
262
|
+
return { plan: raw as PartialPlan, fallbackReason: null };
|
|
213
263
|
}
|
|
214
|
-
return null;
|
|
264
|
+
return { plan: null, fallbackReason };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function readPartialPlan(roots: readonly string[], version: string, commit: string): PartialPlan | null {
|
|
268
|
+
return readPartialPlanResult(roots, version, commit).plan;
|
|
215
269
|
}
|
|
216
270
|
|
|
217
271
|
/** Cut failures after a partial re-run: prior results for files not re-run ⊕ this run's results for the re-run files. */
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/** A hard rehearsal's immutable gate evidence may select the next release's cut tests, but never authorizes a carry. */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { changedFilesBetween, planPartialRegate, testFileOfId, type PartialPlan } from './gate-partial.js';
|
|
6
|
+
import { CODE_FILE, gitClosureTree, testsReachingChanged } from './gate-closure.js';
|
|
7
|
+
import { versionOnlyBumps } from './gate-radius-shadow.js';
|
|
8
|
+
import { gateAssignableTestsAtCommit, gateBaselineReceipt, type GateBaselineReceipt } from './gate-baseline-receipt.js';
|
|
9
|
+
import { parsePodPool, remoteImageId } from '../../src/task-orchestrator/surfaces/pod-pool.js';
|
|
10
|
+
import { podCommandImageFor } from '../../src/task-orchestrator/surfaces/pod-command-job.js';
|
|
11
|
+
import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
|
|
12
|
+
|
|
13
|
+
const SHA = /^[0-9a-f]{40}$/i;
|
|
14
|
+
const DIGEST = /^sha256:[0-9a-f]{64}$/i;
|
|
15
|
+
const TEST = /\.test\.(?:tsx?|jsx?|mts|cts)$/;
|
|
16
|
+
|
|
17
|
+
export function hardRehearsalGateVersion(version: string, runId: string): string {
|
|
18
|
+
const id = /^hard-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i.exec(runId)?.[1];
|
|
19
|
+
if (!/^\d+\.\d+\.\d+$/.test(version) || !id) throw new Error('invalid hard rehearsal gate identity');
|
|
20
|
+
return `${version}-rc.${BigInt(`0x${id.replaceAll('-', '')}`).toString(10)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function hardRehearsalGateArgs(version: string, runId: string, commit: string, previous: string, pool: string): string[] {
|
|
24
|
+
return ['scripts/release-loop/gate-node.ts', '--commit', commit, '--version', hardRehearsalGateVersion(version, runId),
|
|
25
|
+
'--baseline-version', previous, '--executor', 'pod', '--pod-pool', pool, '--pod-shards', '12', '--rehearsal-baseline', '--json'];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface LiveRadiusDecision { plan: PartialPlan | null; reason: string; rehearsalRunId?: string }
|
|
29
|
+
|
|
30
|
+
/** Read the image ID on every candidate host before deciding. A later Job must still prove its actual image. */
|
|
31
|
+
export function preflightGateImageDigest(pool: string): string | null {
|
|
32
|
+
try {
|
|
33
|
+
const image = podCommandImageFor([], true);
|
|
34
|
+
const members = parsePodPool(pool);
|
|
35
|
+
if (!members.length) return null;
|
|
36
|
+
const ids = members.map((member) => {
|
|
37
|
+
if (member.sshHost) return remoteImageId(member, image);
|
|
38
|
+
const result = spawnSync('docker', ['image', 'inspect', image, '--format', '{{.Id}}'], { encoding: 'utf8', timeout: 30_000, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
39
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
40
|
+
});
|
|
41
|
+
return ids.every((id) => id && DIGEST.test(id) && id === ids[0]) ? ids[0]! : null;
|
|
42
|
+
} catch { return null; }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The current sweep ID and exact root inventory prevent stale Pod logs from proving a new partial run's image. */
|
|
46
|
+
export function partialGateImagesMatch(root: string, version: string, digest: string, commit: string,
|
|
47
|
+
sweep: { id: string; shards: number; files: readonly string[] } | undefined): boolean {
|
|
48
|
+
if (!SHA.test(commit) || !sweep || !/^[0-9a-f-]{36}$/i.test(sweep.id)
|
|
49
|
+
|| !Number.isSafeInteger(sweep.shards) || sweep.shards < 1) return false;
|
|
50
|
+
try {
|
|
51
|
+
const dir = join(root, 'release', version, 'gate-logs', 'cut-partial');
|
|
52
|
+
const names = readdirSync(dir).filter((name) => /^pod-\d+(?:-[\w-]+)?\.json$/.test(name));
|
|
53
|
+
const allRows = names.map((name) => ({ name, ...JSON.parse(readFileSync(join(dir, name), 'utf8')) as {
|
|
54
|
+
commit?: string; sweepId?: string; shardCount?: number; files?: unknown; imageDigest?: string;
|
|
55
|
+
attemptId?: string; parentAttempt?: string } }));
|
|
56
|
+
const rows = allRows.filter((row) => row.sweepId === sweep.id);
|
|
57
|
+
const roots = rows.filter((row) => /^pod-\d+\.json$/.test(row.name));
|
|
58
|
+
if (roots.length !== sweep.shards || roots.some((row) => row.shardCount !== sweep.shards
|
|
59
|
+
|| row.commit !== commit || !Array.isArray(row.files) || !row.attemptId || row.imageDigest !== digest)) return false;
|
|
60
|
+
if (allRows.some((row) => /^pod-\d+\.json$/.test(row.name)
|
|
61
|
+
&& row.commit === commit && row.sweepId !== sweep.id)) return false;
|
|
62
|
+
if (allRows.some((row) => row.commit === commit && !row.sweepId)) return false;
|
|
63
|
+
const indices = roots.map((row) => Number(/^pod-(\d+)\.json$/.exec(row.name)![1]));
|
|
64
|
+
if (new Set(indices).size !== sweep.shards || indices.some((index) => index < 0 || index >= sweep.shards)) return false;
|
|
65
|
+
const files = roots.flatMap((row) => row.files as string[]);
|
|
66
|
+
if (files.some((file) => typeof file !== 'string')
|
|
67
|
+
|| JSON.stringify(files.sort()) !== JSON.stringify([...sweep.files].sort())) return false;
|
|
68
|
+
const attempts = new Set(roots.map((row) => row.attemptId));
|
|
69
|
+
let pending = allRows.filter((row) => !/^pod-\d+\.json$/.test(row.name));
|
|
70
|
+
for (let count = 0; pending.length && count < allRows.length; count++) {
|
|
71
|
+
const connected = pending.filter((row) => row.parentAttempt && attempts.has(row.parentAttempt));
|
|
72
|
+
if (!connected.length) break;
|
|
73
|
+
if (connected.some((row) => row.sweepId !== sweep.id || row.commit !== commit
|
|
74
|
+
|| !row.attemptId || row.imageDigest !== digest)) return false;
|
|
75
|
+
for (const row of connected) attempts.add(row.attemptId);
|
|
76
|
+
pending = pending.filter((row) => !connected.includes(row));
|
|
77
|
+
}
|
|
78
|
+
return !pending.some((row) => row.sweepId === sweep.id || (row.commit === commit && !row.sweepId));
|
|
79
|
+
} catch { return false; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Every missing or mismatched proof returns a full gate. The caller uses this only when no explicit resume plan exists. */
|
|
83
|
+
export function planHardRehearsalRadius(input: { root: string; repo: string; version: string; forCommit: string;
|
|
84
|
+
graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string;
|
|
85
|
+
imageDigest: string | null }): LiveRadiusDecision {
|
|
86
|
+
const full = (reason: string): LiveRadiusDecision => ({ plan: null, reason });
|
|
87
|
+
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))
|
|
89
|
+
return full('current graph, environment or Pod image identity unmeasured');
|
|
90
|
+
try {
|
|
91
|
+
const stamps = ['hard-5h', 'hard-mid'].map((step) => {
|
|
92
|
+
const path = join(input.root, 'release', input.version, 'rehearsal', `${step}.json`);
|
|
93
|
+
if (!existsSync(path)) return null;
|
|
94
|
+
const stamp = JSON.parse(readFileSync(path, 'utf8')) as {
|
|
95
|
+
version?: string; step?: string; outcome?: string; sha?: string; endedAt?: string; hardGateRunId?: string;
|
|
96
|
+
hardGate?: { commit?: string; executor?: string; outcome?: string; introduced?: unknown[] };
|
|
97
|
+
};
|
|
98
|
+
return { step, stamp, ended: Date.parse(stamp.endedAt ?? '') };
|
|
99
|
+
}).filter((row): row is NonNullable<typeof row> => row !== null);
|
|
100
|
+
if (!stamps.length) return full('no clean hard rehearsal receipt');
|
|
101
|
+
if (stamps.some((row) => !Number.isFinite(row.ended))) return full('hard rehearsal completion time unmeasured');
|
|
102
|
+
stamps.sort((a, b) => b.ended - a.ended);
|
|
103
|
+
if (stamps.length > 1 && stamps[0]!.ended === stamps[1]!.ended) return full('hard rehearsal order ambiguous');
|
|
104
|
+
for (const { step, stamp } of stamps.slice(0, 1)) {
|
|
105
|
+
if (stamp.version !== input.version || stamp.step !== step || stamp.outcome !== 'ok' || !stamp.sha || !SHA.test(stamp.sha)
|
|
106
|
+
|| stamp.hardGate?.outcome !== 'ok' || stamp.hardGate.commit !== stamp.sha || stamp.hardGate.executor !== 'pod'
|
|
107
|
+
|| !Array.isArray(stamp.hardGate.introduced) || stamp.hardGate.introduced.length)
|
|
108
|
+
return full('latest hard rehearsal is not clean');
|
|
109
|
+
let gateVersion: string;
|
|
110
|
+
try { gateVersion = hardRehearsalGateVersion(input.version, stamp.hardGateRunId ?? ''); }
|
|
111
|
+
catch { return full('hard rehearsal run identity invalid'); }
|
|
112
|
+
const dir = join(input.root, 'release', gateVersion);
|
|
113
|
+
const verdict = JSON.parse(readFileSync(join(dir, 'gate-verdict.json'), 'utf8')) as {
|
|
114
|
+
commit?: string; outcome?: string; baselineReceipt?: GateBaselineReceipt };
|
|
115
|
+
const failures = JSON.parse(readFileSync(join(dir, 'gate-failures.json'), 'utf8')) as {
|
|
116
|
+
commit?: string; failures?: string[]; errors?: string[] };
|
|
117
|
+
const receipt = verdict.baselineReceipt;
|
|
118
|
+
if (verdict.commit !== stamp.sha || verdict.outcome !== 'ok' || failures.commit !== stamp.sha
|
|
119
|
+
|| !Array.isArray(failures.failures) || failures.failures.length !== 0
|
|
120
|
+
|| !Array.isArray(failures.errors) || failures.errors.length !== 0
|
|
121
|
+
|| !receipt || receipt.schema !== 1 || !receipt.valid || receipt.version !== gateVersion || receipt.commit !== stamp.sha
|
|
122
|
+
|| receipt.graphSha !== input.graphPins.graphSha || receipt.recipeSha !== input.graphPins.recipeSha
|
|
123
|
+
|| receipt.envFingerprint !== input.envFingerprint || receipt.imageDigest !== input.imageDigest.toLowerCase()
|
|
124
|
+
|| !Number.isSafeInteger(receipt.files) || receipt.files < 1 || !Number.isSafeInteger(receipt.shards) || receipt.shards < 1)
|
|
125
|
+
return full('hard rehearsal receipt or current execution identity differs');
|
|
126
|
+
const recomputed = gateBaselineReceipt({ root: input.root, version: gateVersion, commit: stamp.sha,
|
|
127
|
+
fullSweep: true, graphPins: input.graphPins, envFingerprint: input.envFingerprint });
|
|
128
|
+
if (!recomputed.valid || recomputed.imageDigest !== receipt.imageDigest || recomputed.files !== receipt.files
|
|
129
|
+
|| recomputed.shards !== receipt.shards) return full(`hard rehearsal Pod proof differs: ${recomputed.reason}`);
|
|
130
|
+
const ancestor = spawnSync('git', ['merge-base', '--is-ancestor', stamp.sha, input.forCommit], { cwd: input.repo, maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
131
|
+
if (ancestor.status !== 0 || ancestor.error) return full('hard rehearsal commit is not an ancestor');
|
|
132
|
+
const expectedFiles = gateAssignableTestsAtCommit(input.repo, stamp.sha);
|
|
133
|
+
const measuredFiles = readdirSync(join(dir, 'gate-logs', 'cut')).filter((name) => /^pod-\d+\.json$/.test(name))
|
|
134
|
+
.flatMap((name) => {
|
|
135
|
+
const row = JSON.parse(readFileSync(join(dir, 'gate-logs', 'cut', name), 'utf8')) as { files?: unknown };
|
|
136
|
+
return Array.isArray(row.files) ? row.files : [];
|
|
137
|
+
});
|
|
138
|
+
if (!expectedFiles || JSON.stringify(measuredFiles.sort()) !== JSON.stringify(expectedFiles)
|
|
139
|
+
|| measuredFiles.length !== receipt.files)
|
|
140
|
+
return full('hard rehearsal file inventory differs from receipt');
|
|
141
|
+
const tree = gitClosureTree(input.repo, input.forCommit);
|
|
142
|
+
const tests = tree.files.filter((file) => TEST.test(file));
|
|
143
|
+
const changed = changedFilesBetween(input.repo, stamp.sha, input.forCommit);
|
|
144
|
+
const bumps = versionOnlyBumps(input.repo, stamp.sha, input.forCommit, input.version, changed);
|
|
145
|
+
const effective = changed.filter((file) => !bumps.includes(file));
|
|
146
|
+
// A version-only JSON/lock bump is still observable to tests that read it. Follow those literal readers and
|
|
147
|
+
// opaque tests before removing the bump from the ordinary code-only partial planner.
|
|
148
|
+
const versionEnvReaders = bumps.includes('package.json')
|
|
149
|
+
? tree.files.filter((file) => CODE_FILE.test(file) && tree.read(file)?.includes('npm_package_version')) : [];
|
|
150
|
+
const bumpRadius = bumps.length ? testsReachingChanged(tree, [...bumps, ...versionEnvReaders], tests) : null;
|
|
151
|
+
if (bumpRadius && !bumpRadius.ok) return full(bumpRadius.reason);
|
|
152
|
+
const planned = planPartialRegate({ version: input.version, priorCommit: stamp.sha, forCommit: input.forCommit,
|
|
153
|
+
changedFiles: effective, priorFailures: failures.failures, priorErrors: failures.errors ?? [],
|
|
154
|
+
existsAtTip: (file) => tree.files.includes(file),
|
|
155
|
+
closure: (code) => testsReachingChanged(tree, code, tests) });
|
|
156
|
+
if (!planned.ok && effective.length) return full(planned.reason);
|
|
157
|
+
let fileSet: string[];
|
|
158
|
+
let base: PartialPlan;
|
|
159
|
+
if (planned.ok) {
|
|
160
|
+
if (planned.plan.kind === 'carry' && !bumpRadius?.ok) return full('docs-only carry requires full gate across releases');
|
|
161
|
+
base = planned.plan.kind === 'carry' ? { ...planned.plan, kind: undefined } : planned.plan;
|
|
162
|
+
fileSet = planned.plan.files;
|
|
163
|
+
} else {
|
|
164
|
+
let priorFiles: string[];
|
|
165
|
+
try { priorFiles = [...failures.failures, ...(failures.errors ?? [])].map(testFileOfId); }
|
|
166
|
+
catch { return full('hard rehearsal failure ids unreadable'); }
|
|
167
|
+
base = { version: input.version, priorCommit: stamp.sha, forCommit: input.forCommit,
|
|
168
|
+
files: [], priorFailures: failures.failures, priorErrors: failures.errors ?? [] };
|
|
169
|
+
fileSet = priorFiles;
|
|
170
|
+
}
|
|
171
|
+
const files = [...new Set([...fileSet, ...(bumpRadius?.ok ? bumpRadius.tests : [])])].sort();
|
|
172
|
+
if (!files.length || files.some((file) => !tests.includes(file))) return full('empty or missing rehearsal radius requires full gate');
|
|
173
|
+
return { plan: { ...base, files }, reason: 'hard rehearsal receipt and import radius verified', rehearsalRunId: stamp.hardGateRunId };
|
|
174
|
+
}
|
|
175
|
+
return full('no clean hard rehearsal receipt');
|
|
176
|
+
} catch (error) { return full(`hard rehearsal evidence unreadable: ${String(error)}`); }
|
|
177
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** Observation only: compare a proposed import radius with the full gate that actually ran. */
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { existsSync, linkSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { changedFilesBetween, planPartialRegate, testFileOfId } from './gate-partial';
|
|
7
|
+
import { gitClosureTree, testsReachingChanged } from './gate-closure';
|
|
8
|
+
import { SPAWN_SYNC_MAX_BUFFER } from '../../src/util/spawn-sync-output.js';
|
|
9
|
+
|
|
10
|
+
const SHA = /^[0-9a-f]{40}$/i;
|
|
11
|
+
const VERSION = /^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/;
|
|
12
|
+
const TEST = /\.test\.(?:tsx?|jsx?|mts|cts)$/;
|
|
13
|
+
const BUMP_FILES = new Set(['package.json', 'server.json', 'integrations/acp-registry/elanous/agent.json', 'bun.lock']);
|
|
14
|
+
|
|
15
|
+
export interface GateRadiusShadowPlan {
|
|
16
|
+
schema: 1;
|
|
17
|
+
version: string;
|
|
18
|
+
priorCommit: string | null;
|
|
19
|
+
forCommit: string;
|
|
20
|
+
proposed: 'partial' | 'full';
|
|
21
|
+
files: string[];
|
|
22
|
+
totalTests: number | null;
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface GateRadiusShadowRecord {
|
|
27
|
+
plan: GateRadiusShadowPlan;
|
|
28
|
+
actual: { commit: string; outcome: 'ok' | 'regression' | 'error'; cutRc: number | null; introduced: string[];
|
|
29
|
+
cutFailures: string[]; cutErrors: string[]; missedIntroduced: string[]; fullSweep: boolean;
|
|
30
|
+
comparison: 'complete' | 'unmeasured' };
|
|
31
|
+
recordedAt: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function blob(repo: string, commit: string, file: string): unknown {
|
|
35
|
+
const run = spawnSync('git', ['show', `${commit}:${file}`], { cwd: repo, encoding: 'utf8', maxBuffer: SPAWN_SYNC_MAX_BUFFER });
|
|
36
|
+
if (run.status !== 0 || run.error) throw new Error(`unreadable release blob ${file}`);
|
|
37
|
+
return JSON.parse(run.stdout);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Remove only the fields version-node itself changes; any other JSON difference leaves a full shadow plan. */
|
|
41
|
+
export function versionOnlyBumps(repo: string, from: string, to: string, version: string, changed: readonly string[]): string[] {
|
|
42
|
+
if (!changed.includes('package.json')) return [];
|
|
43
|
+
const prior = blob(repo, from, 'package.json') as Record<string, unknown>;
|
|
44
|
+
const next = blob(repo, to, 'package.json') as Record<string, unknown>;
|
|
45
|
+
if (typeof prior.version !== 'string' || prior.version === version || next.version !== version) return [];
|
|
46
|
+
const inert: string[] = [];
|
|
47
|
+
for (const file of changed.filter((name) => BUMP_FILES.has(name))) {
|
|
48
|
+
const left = blob(repo, from, file) as Record<string, any>;
|
|
49
|
+
const right = blob(repo, to, file) as Record<string, any>;
|
|
50
|
+
const normalize = (row: Record<string, any>, expected: string): boolean => {
|
|
51
|
+
if (file === 'package.json') { if (row.version !== expected) return false; row.version = '<version>'; }
|
|
52
|
+
else if (file === 'server.json') {
|
|
53
|
+
if (row.version !== expected || row.packages?.[0]?.version !== expected) return false;
|
|
54
|
+
row.version = '<version>'; row.packages[0].version = '<version>';
|
|
55
|
+
} else if (file.endsWith('/agent.json')) {
|
|
56
|
+
if (row.version !== expected || row.distribution?.npx?.package !== `elanous@${expected}`) return false;
|
|
57
|
+
row.version = '<version>'; row.distribution.npx.package = 'elanous@<version>';
|
|
58
|
+
} else {
|
|
59
|
+
if (row.workspaces?.['']?.version !== expected) return false;
|
|
60
|
+
row.workspaces[''].version = '<version>';
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
};
|
|
64
|
+
if (normalize(left, prior.version) && normalize(right, version) && JSON.stringify(left) === JSON.stringify(right)) inert.push(file);
|
|
65
|
+
}
|
|
66
|
+
return inert;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function planGateRadiusShadow(input: { repo: string; ledgerRoot: string; version: string;
|
|
70
|
+
baselineVersion: string; forCommit: string; instanceRoot?: string; expectedBaselineCommit?: string }): GateRadiusShadowPlan {
|
|
71
|
+
const { repo, ledgerRoot, version, baselineVersion, forCommit } = input;
|
|
72
|
+
const full = (reason: string, priorCommit: string | null = null, totalTests: number | null = null): GateRadiusShadowPlan =>
|
|
73
|
+
({ schema: 1, version, priorCommit, forCommit, proposed: 'full', files: [], totalTests, reason });
|
|
74
|
+
if (!VERSION.test(version) || !VERSION.test(baselineVersion) || !SHA.test(forCommit)) return full('invalid identity');
|
|
75
|
+
try {
|
|
76
|
+
const priorDir = join(ledgerRoot, 'release', baselineVersion);
|
|
77
|
+
const verdict = JSON.parse(readFileSync(join(priorDir, 'gate-verdict.json'), 'utf8')) as { commit?: string; outcome?: string };
|
|
78
|
+
const failures = JSON.parse(readFileSync(join(priorDir, 'gate-failures.json'), 'utf8')) as
|
|
79
|
+
{ commit?: string; failures?: string[]; errors?: string[] };
|
|
80
|
+
const priorCommit = verdict.commit;
|
|
81
|
+
const manifestPaths = [...new Set([ledgerRoot, input.instanceRoot].filter((root): root is string => !!root))]
|
|
82
|
+
.map((root) => join(root, 'release', baselineVersion, 'release.json')).filter(existsSync);
|
|
83
|
+
if (!manifestPaths.length) return full('prior release manifest absent', priorCommit ?? null);
|
|
84
|
+
const manifestCommits = manifestPaths.map((path) =>
|
|
85
|
+
(JSON.parse(readFileSync(path, 'utf8')) as { sourceCommit?: unknown }).sourceCommit);
|
|
86
|
+
if (manifestCommits.some((commit) => typeof commit !== 'string' || !SHA.test(commit) || commit !== priorCommit)
|
|
87
|
+
|| (input.expectedBaselineCommit && input.expectedBaselineCommit !== priorCommit))
|
|
88
|
+
return full('prior gate commit differs from release source', priorCommit ?? null);
|
|
89
|
+
if (!priorCommit || !SHA.test(priorCommit) || failures.commit !== priorCommit || verdict.outcome !== 'ok'
|
|
90
|
+
|| !Array.isArray(failures.failures) || !Array.isArray(failures.errors ?? []))
|
|
91
|
+
return full('prior gate evidence absent or not clean', priorCommit ?? null);
|
|
92
|
+
const tree = gitClosureTree(repo, forCommit);
|
|
93
|
+
const tests = tree.files.filter((file) => TEST.test(file));
|
|
94
|
+
const changed = changedFilesBetween(repo, priorCommit, forCommit);
|
|
95
|
+
const bump = versionOnlyBumps(repo, priorCommit, forCommit, version, changed);
|
|
96
|
+
const effective = changed.filter((file) => !bump.includes(file));
|
|
97
|
+
if (!effective.length && bump.length) {
|
|
98
|
+
const rerun = [...new Set([...failures.failures, ...(failures.errors ?? [])].map(testFileOfId))].sort();
|
|
99
|
+
if (rerun.some((file) => !tests.includes(file))) return full('prior failure file absent at cut', priorCommit, tests.length);
|
|
100
|
+
return { schema: 1, version, priorCommit, forCommit, proposed: 'partial', files: rerun,
|
|
101
|
+
totalTests: tests.length, reason: 'version-only candidate' };
|
|
102
|
+
}
|
|
103
|
+
const result = planPartialRegate({ version, priorCommit, forCommit, changedFiles: effective,
|
|
104
|
+
priorFailures: failures.failures, priorErrors: failures.errors ?? [],
|
|
105
|
+
existsAtTip: (file) => tree.files.includes(file),
|
|
106
|
+
closure: (code) => testsReachingChanged(tree, code, tests) });
|
|
107
|
+
if (!result.ok) return full(result.reason, priorCommit, tests.length);
|
|
108
|
+
// Shadow only: this proposal is never passed to writePartialPlan/readPartialPlan or the sweep selector.
|
|
109
|
+
return { schema: 1, version, priorCommit, forCommit, proposed: 'partial', files: result.plan.files,
|
|
110
|
+
totalTests: tests.length, reason: result.plan.kind === 'carry' ? 'zero-file candidate' : 'import radius candidate' };
|
|
111
|
+
} catch (error) { return full(`shadow evidence unreadable: ${String(error)}`); }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Append beside the gate verdict; a shadow write never changes a release decision. */
|
|
115
|
+
export function writeGateRadiusShadow(root: string, record: GateRadiusShadowRecord): string {
|
|
116
|
+
const { plan, actual } = record;
|
|
117
|
+
if (!VERSION.test(plan.version) || !SHA.test(plan.forCommit) || actual.commit !== plan.forCommit
|
|
118
|
+
|| !['ok', 'regression', 'error'].includes(actual.outcome)) throw new Error('invalid gate radius shadow');
|
|
119
|
+
const path = join(root, 'release', plan.version, `gate-radius-shadow-${plan.forCommit}-${randomUUID()}.json`);
|
|
120
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
121
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
122
|
+
try {
|
|
123
|
+
writeFileSync(temp, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
|
|
124
|
+
linkSync(temp, path);
|
|
125
|
+
} finally { rmSync(temp, { force: true }); }
|
|
126
|
+
return path;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function shadowActual(input: { plan: GateRadiusShadowPlan; outcome: 'ok' | 'regression' | 'error';
|
|
130
|
+
introduced: readonly string[]; cutFailures: readonly string[]; cutErrors: readonly string[]; cutRc: number | null;
|
|
131
|
+
fullSweep: boolean }): GateRadiusShadowRecord['actual'] {
|
|
132
|
+
const selected = new Set(input.plan.files);
|
|
133
|
+
const missedIntroduced = input.plan.proposed === 'partial' && input.fullSweep
|
|
134
|
+
? input.introduced.filter((id) => { try { return !selected.has(testFileOfId(id)); } catch { return true; } }) : [];
|
|
135
|
+
return { commit: input.plan.forCommit, outcome: input.outcome, cutRc: input.cutRc, introduced: [...input.introduced],
|
|
136
|
+
cutFailures: [...input.cutFailures], cutErrors: [...input.cutErrors], missedIntroduced, fullSweep: input.fullSweep,
|
|
137
|
+
comparison: input.fullSweep ? 'complete' : 'unmeasured' };
|
|
138
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { debug } from '../../src/debug/log.js';
|
|
6
|
+
|
|
7
|
+
type TrafficBoard = Record<string, unknown> & { global: string };
|
|
8
|
+
type PatchResult = { changed: true; previous: string; warning?: string } | { changed: false; current: string };
|
|
9
|
+
|
|
10
|
+
const defaultTrafficPath = () => join(homedir(), 'elanous-hq', 'seat-state', 'OP', 'traffic.json');
|
|
11
|
+
const trafficPath = (env: NodeJS.ProcessEnv) => env.ELANOUS_TRAFFIC_SIGNAL || defaultTrafficPath();
|
|
12
|
+
|
|
13
|
+
function releaseGateContext(env: NodeJS.ProcessEnv): boolean {
|
|
14
|
+
const source = env.ELANOUS_GRAPH_CONTEXT;
|
|
15
|
+
if (!source) return false;
|
|
16
|
+
try {
|
|
17
|
+
const raw = JSON.parse(source.trimStart().startsWith('{') ? source : readFileSync(source, 'utf8')) as Record<string, unknown>;
|
|
18
|
+
return raw.graphId === 'release-loop' && raw.nodeId === 'gate';
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function withTrafficLock<T>(path: string, action: () => T): T {
|
|
25
|
+
const lock = `${path}.lock.d`;
|
|
26
|
+
const deadline = Date.now() + 10_000;
|
|
27
|
+
for (let attempt = 1; attempt <= 100; attempt++) {
|
|
28
|
+
try { mkdirSync(lock); }
|
|
29
|
+
catch (error) {
|
|
30
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
31
|
+
try {
|
|
32
|
+
const stat = statSync(lock).mtimeMs;
|
|
33
|
+
const owner = Number(readFileSync(join(lock, 'pid'), 'utf8').trim());
|
|
34
|
+
if (Date.now() - stat > 60_000 && Number.isInteger(owner) && owner > 0) {
|
|
35
|
+
try { process.kill(owner, 0); }
|
|
36
|
+
catch (probeError) {
|
|
37
|
+
if ((probeError as NodeJS.ErrnoException).code === 'ESRCH'
|
|
38
|
+
&& Number(readFileSync(join(lock, 'pid'), 'utf8').trim()) === owner) {
|
|
39
|
+
try { renameSync(lock, `${lock}.stale.${process.pid}.${attempt}`); } catch { /* owner may have released it */ }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch { /* unknown owner and unreadable locks are never reaped */ }
|
|
44
|
+
if (attempt === 100 || Date.now() >= deadline) throw new Error(`traffic lock timeout: ${lock}`);
|
|
45
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
writeFileSync(join(lock, 'pid'), `${process.pid}\n`);
|
|
50
|
+
return action();
|
|
51
|
+
} finally {
|
|
52
|
+
rmSync(join(lock, 'pid'), { force: true });
|
|
53
|
+
try { rmdirSync(lock); } catch { /* another writer replaced the lock directory */ }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`traffic lock timeout: ${lock}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function patchGlobal(path: string, next: string, reason: string, onlyIf?: string): PatchResult {
|
|
60
|
+
return withTrafficLock(path, () => {
|
|
61
|
+
if (!existsSync(path)) throw new Error(`traffic board missing: ${path}`);
|
|
62
|
+
const board = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
|
|
63
|
+
if (!board || typeof board !== 'object' || Array.isArray(board) || typeof board.global !== 'string') {
|
|
64
|
+
throw new Error(`invalid traffic board: ${path}`);
|
|
65
|
+
}
|
|
66
|
+
if (onlyIf !== undefined && board.global !== onlyIf) return { changed: false, current: board.global };
|
|
67
|
+
const updated: TrafficBoard = { ...board, global: next, reason, updatedAt: new Date().toISOString() };
|
|
68
|
+
const at = new Date();
|
|
69
|
+
const stamp = [at.getMonth() + 1, at.getDate(), at.getHours(), at.getMinutes(), at.getSeconds()].map(value => String(value).padStart(2, '0')).join('');
|
|
70
|
+
const suffix = `bak-${stamp}-${randomUUID().slice(0, 8)}-global-${next}`;
|
|
71
|
+
const backup = `${path}.${suffix}`;
|
|
72
|
+
const temp = `${path}.tmp.${process.pid}.${randomUUID()}`;
|
|
73
|
+
const payload = `${JSON.stringify(updated)}\n`;
|
|
74
|
+
writeFileSync(backup, readFileSync(path));
|
|
75
|
+
writeFileSync(temp, payload, { mode: statSync(path).mode & 0o777 });
|
|
76
|
+
try { renameSync(temp, path); }
|
|
77
|
+
finally { rmSync(temp, { force: true }); }
|
|
78
|
+
try {
|
|
79
|
+
const verify = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
|
|
80
|
+
if (verify.global !== next) return { changed: true, previous: board.global, warning: `traffic verification mismatch: ${path}` };
|
|
81
|
+
} catch (error) {
|
|
82
|
+
// rename already committed the requested mode; keep the lease so the caller still attempts cleanup.
|
|
83
|
+
return { changed: true, previous: board.global, warning: `traffic verification failed: ${String(error)}` };
|
|
84
|
+
}
|
|
85
|
+
return { changed: true, previous: board.global };
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Hold dispatcher traffic at gate only for the release-loop's gate node, then restore conditionally. */
|
|
90
|
+
export async function runWithReleaseGateTraffic<T>(env: NodeJS.ProcessEnv, run: () => Promise<T>): Promise<T> {
|
|
91
|
+
if (!releaseGateContext(env)) return run();
|
|
92
|
+
const path = trafficPath(env);
|
|
93
|
+
let started: PatchResult;
|
|
94
|
+
try { started = patchGlobal(path, 'gate', 'release gate running', 'green'); }
|
|
95
|
+
catch (error) {
|
|
96
|
+
debug.log('release-loop.gate', 'traffic gate set failed', { path, error: String(error) }, { level: 'warn' });
|
|
97
|
+
return run(); // The traffic board is advisory; it must not block publication.
|
|
98
|
+
}
|
|
99
|
+
if (!started.changed) return run(); // Existing gate/yellow (or any operator change) belongs to its owner.
|
|
100
|
+
if (started.warning) debug.log('release-loop.gate', 'traffic gate verification failed', { path, error: started.warning }, { level: 'warn' });
|
|
101
|
+
let restoreResult: Promise<void> | undefined;
|
|
102
|
+
const restoreOnce = () => restoreResult ??= Promise.resolve().then(() => {
|
|
103
|
+
try {
|
|
104
|
+
const restored = patchGlobal(path, started.previous, 'release gate finished', 'gate');
|
|
105
|
+
if (!restored.changed) debug.log('release-loop.gate', 'traffic restore skipped', { path, current: restored.current }, { level: 'warn' });
|
|
106
|
+
else if (restored.warning) debug.log('release-loop.gate', 'traffic restore verification failed', { path, error: restored.warning }, { level: 'warn' });
|
|
107
|
+
} catch (error) {
|
|
108
|
+
debug.log('release-loop.gate', 'traffic restore failed', { path, error: String(error) }, { level: 'warn' });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const interrupt = (signal: NodeJS.Signals) => {
|
|
112
|
+
void restoreOnce().finally(() => {
|
|
113
|
+
process.removeListener(signal, signal === 'SIGINT' ? onSigint : onSigterm);
|
|
114
|
+
process.kill(process.pid, signal);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
const onSigint = () => interrupt('SIGINT');
|
|
118
|
+
const onSigterm = () => interrupt('SIGTERM');
|
|
119
|
+
process.once('SIGINT', onSigint);
|
|
120
|
+
process.once('SIGTERM', onSigterm);
|
|
121
|
+
try {
|
|
122
|
+
return await run();
|
|
123
|
+
} finally {
|
|
124
|
+
process.removeListener('SIGINT', onSigint);
|
|
125
|
+
process.removeListener('SIGTERM', onSigterm);
|
|
126
|
+
await restoreOnce();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -120,7 +120,7 @@ export function gateTriageShadowFromContext(context: GraphContext, evidence?: Ga
|
|
|
120
120
|
&& item.files.every((file: unknown) => typeof file === 'string') && typeof item.reason === 'string')))
|
|
121
121
|
|| (value.loadFlaky !== undefined && (!Array.isArray(value.loadFlaky)
|
|
122
122
|
|| !value.loadFlaky.every((item) => typeof item === 'string')))
|
|
123
|
-
|| !['ok', 'regression', 'error'].includes(String(value.outcome))) {
|
|
123
|
+
|| !['ok', 'regression', 'fail', 'error'].includes(String(value.outcome))) {
|
|
124
124
|
throw new Error('gate output is missing or malformed');
|
|
125
125
|
}
|
|
126
126
|
const input = context.input.gateTriageEvidence;
|
|
@@ -140,7 +140,10 @@ export function gateTriageShadowFromContext(context: GraphContext, evidence?: Ga
|
|
|
140
140
|
&& item.failureIds.every((id: unknown) => typeof id === 'string'))
|
|
141
141
|
? { priorDecisions: supplied.priorDecisions as GateTriageEvidence['priorDecisions'] } : {}),
|
|
142
142
|
};
|
|
143
|
-
|
|
143
|
+
// The graph adapter maps a measured gate regression to `fail` for edge routing.
|
|
144
|
+
// Restore the gate verdict before classifying the shadow; keep the context untouched.
|
|
145
|
+
const gate = value.outcome === 'fail' ? { ...value, outcome: 'regression' } : value;
|
|
146
|
+
return classifyGateShadow(gate as unknown as GateResult, observed);
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
export function logGateTriageShadow(result: GateTriageShadow, version: string): void {
|