elanous 0.2.42 → 0.2.43
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/{X1ucLw10Cds_h614NoEnh → Om5xS9jpjSXJCeTNGYEi4}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{7a98bc80.fa935989e829f9c2.js → 0966f8a1.745fcd44f64a87ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{73d2eb0e.4966ad55293c801a.js → 0ad6802e.ae7afb63fdcb4491.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/114.2b90424d1a3ce84c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1151-5c2bb8f002ca2dd6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1200.6a37c202f9f17e9a.js +226 -0
- package/apps/pwa/out/_next/static/chunks/1261-a616d5728e02e624.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1313-19f737e0222c4444.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1357.3bbef3976c37f94a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1362-20adddfaf662b0fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3507.97e1977bc4d5fb0a.js → 1406.a1dc2ab6f8449374.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1737-9491fc6ab29b9ec6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1803-4f91a8e7dcaf042c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1961.a81b12872c3e93a9.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{53c73f84.373b6160adfa89a4.js → 1970cf59.2d4d8ce5d6c81853.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1971-8890f3962ba7ee0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9211-330b6736a63d4cb3.js → 2073-ba274e00e4f9efb4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{7411.9b4b8b747dc7b802.js → 2144.25b292396712196d.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/2268-53861f339e0b65a2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2312.0c8b61e22d3136d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6632.0eac1c554f8f2ef3.js → 2573.387a707f6f9d720c.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/273.f156eee28f11af77.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2873.d2140e5a82546401.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1262.e804feb8fd1acb8c.js → 3072.01338851ca21a6f7.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3091.a5a4143dea33d197.js +90 -0
- package/apps/pwa/out/_next/static/chunks/{2456.b6de0e1c05f28843.js → 3095.dc44586a5f83b86d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3207.a756c95e9ec8c3c8.js → 3113.d7472bbc27d580c7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3228-9650b6447b6fa288.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3402.44b565b314f2263c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3557ac4a.c8931684475018ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8505.fd803c6c33b54cc3.js → 360.ae9f9f4c33c395e8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3638.80412ea63aae8f3f.js +286 -0
- package/apps/pwa/out/_next/static/chunks/{b450776b.3db34d8d965f82d3.js → 363bfab3.9623b9f89363541f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3694.e0099884345f0198.js +64 -0
- package/apps/pwa/out/_next/static/chunks/3744.54f87db52acfd1ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3903.3ef4656c67572764.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3935.16701e33f8982dc9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3955.6922286237f2ac2b.js +24 -0
- package/apps/pwa/out/_next/static/chunks/3969.272ee7f9fd6c62e5.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{8138.6c10b2a356d52fca.js → 4199.ce86b281fd0a60b7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4311-6dd3144002cc0c2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4426-2b230fad00c88930.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2690.0521c738b7749931.js → 4445.7990c76b3751a918.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4447-14c5161fa59166c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4856-b0b8075bba8c53f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4870-de370d65e73c0bb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4885-2c989165c4b29fad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1945.7b54990d578c119b.js → 4894.fec2ca9e99caefa2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5093.8f04b7f8cb899a95.js +94 -0
- package/apps/pwa/out/_next/static/chunks/5122-2bb177cebf89e19a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5154.5f56dc3902e3d275.js +36 -0
- package/apps/pwa/out/_next/static/chunks/529.64ff7257180b2ac9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5394.5dabeb0c62293a28.js +93 -0
- package/apps/pwa/out/_next/static/chunks/5461.08bf2404ff38ec85.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5474-22e08a85583209b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9427.96aa912ddb5801e0.js → 5564.9e72532188f57586.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/5567-265df39ed8fe9dcb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5570-a94ae19811cb2046.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5574-b47c9ef2dceff1fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4043.6b91203436c9c86b.js → 5628.6ddb2d5ffba4ff4c.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/{1319.b9b815ae0f181e06.js → 5730.43d7ccc09988e646.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3482.a9ae9d2ad6a5bd97.js → 5789.2da35c258292e1e5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5824.da9527b15a3221e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5866.366966af47fdde5d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1487.2f67b79444062da6.js → 5930.8b4df3b46c32b05a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5963-c4d8469c52a0828b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6001.ec4049ceb3c43418.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6120-471f03db1e299c0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6175-41479e643aca1878.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6291.a2de4f9787637075.js +104 -0
- package/apps/pwa/out/_next/static/chunks/6400-6699907c3537436a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6428.1f4327d6ae5fdf9a.js +287 -0
- package/apps/pwa/out/_next/static/chunks/644.02cb026fa44bcd0a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6466-6c87d60d3dd174fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3347.f846266b5e5f3ede.js → 6540.c7bef57ad8f90a87.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6773.d4264665ab31bec0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6822.3c789f1a8443e774.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8262.ddc03e06b733f184.js → 6944.309f4e3ea54d1537.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7261-8ff69d3b2ee671fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7266.084c6bbc70035140.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7271.5c0100e7320aa856.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7429-bf5731358c8f2f68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7464.01f7a0ae1215fbe7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7479.0fcad5f37eb64f0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4770.769c9cd9c7034eba.js → 7483.427a2fe2049e388d.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{1147-5196d19c222051b3.js → 7502-be74718178e13e56.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7530.0889722b19572065.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7645.5b2ee3ad45c55ea3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7788.7d1c79f1a8e18946.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7900.096f455c7c1efc53.js +77 -0
- package/apps/pwa/out/_next/static/chunks/7910-6f139bcafd238b9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7966.f71c8958d1637548.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8673-bab689a55f7e13b9.js → 8062-7cd0bcc7f01455ba.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/8073.f6cf2f2c71aafbd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8201.190b94088980f430.js +74 -0
- package/apps/pwa/out/_next/static/chunks/8205.282ca4bb62d54a0a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/8313-dfa1021bb58a46d6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/837-81336ebaca8198a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8447.7963352ef5f71b76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8574-868e8129525ef6d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8588.d1c6a2903727ce87.js +149 -0
- package/apps/pwa/out/_next/static/chunks/8637.90f92d5c8db4732c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/868.6e953be05bcf6dca.js +41 -0
- package/apps/pwa/out/_next/static/chunks/8706-e1fe0309343a2273.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8749-06384cdfc4520add.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9656-374232558fd528d0.js → 8861-cc1fa1c7b920fa8e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5440-4236de26c5ea5856.js → 8967-7dfebf710c9dec9d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1063.2fb8a9dc55c731f2.js → 8986.8e2e4efbfa655c59.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8997-3c564dbb3b58b055.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4320.583cbab64db6be02.js → 923.2034d10ae91e9c20.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9312.bc1b89c5d4368033.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1574.878c18ecc97b1bd4.js → 943.39ae5e82f4f1893f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9469.0500480de55fb205.js +32 -0
- package/apps/pwa/out/_next/static/chunks/9770.5f75bc6088b16109.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9810-2543267050e58b82.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9870.8419702475254745.js +8 -0
- package/apps/pwa/out/_next/static/chunks/9941-376d1b74d51b01a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9958.385434ab04241758.js +1 -0
- package/apps/pwa/out/_next/static/chunks/aa57532f.510551854ff239b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-deea1450b17cb8dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-70985b904f8650ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a2e15e33e56a4353.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-fc72ce2db12d6ca9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-be03e41f9436ebd9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-35a35999382da3f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-3ff5b1e6ca88b848.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-a65101300a0ee5c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3aca5b6d67dba511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-45b8ab0a71e61314.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-53046e282d4cda8e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-17809e60e8354c17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-fd2442d347bf5898.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-e9d720d6fc082e21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-6b8b4267bab0a677.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-deea1450b17cb8dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-8bcf6233ac7c17af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-7cc25591cfc2126f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-053e871f35c9beba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-eff819f0c2d5e084.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-4d7e9d8db5274a87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2b2d8f5aa5f69ad4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-0b8f92c5062e68b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1bd9d0fdac2ced3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-0f291d3c8fd55a3a.js → page-9ee28f7df07428ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-44b7a1ff468c6648.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-cbc6523711dc1b65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-fdad47899c2695be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-79a8b5fee4c20871.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-03e8ac9606aeaffb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-4cd55c55ec287e2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-db17b4305404161e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-30041d848350fd02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c540da022dbf86b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9399efc538da057f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ca2d09128c585ca4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0e2635aecd343038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ef7f4399b6b7726e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-deea1450b17cb8dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-caf530535b875468.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-eaa20c1543a5fd8e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c541eb1b75120f61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-ab5e213eda9479f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-0a815f34367b6db6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-e858ba0ed9393b68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-1638ee9dd6e0582f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4cf5a399109391f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b4396321238798ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-cb72bfd21006781a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6135d0242afbc434.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a1dccf907eddb856.js +1 -0
- package/apps/pwa/out/_next/static/chunks/c7945435-b44d4dbe90ba0dcb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3d0154af.5331fec737ffe8c3.js → cb0f8df8.09a4a6be97e3fc5f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{e8543907.5b040a5f22a0b0e5.js → d96660fb.cbc21a2a88539dbb.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/{50061469.0326382241ef3b2d.js → ea514caa.05a24f78d9dffe5f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/fb7d20d5-6892502b6ee39ca6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-e8a911e19b1be41f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-7022f418fabba021.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-e5f15df755756a1e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-4525f91e36940c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-140f0e519c085b0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-8f22ef63dcb865b1.js +1 -0
- package/apps/pwa/out/_next/static/css/d1f8bb401740a549.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/catalog/external-commands.yaml +5 -3
- package/catalog/meta/catalogVersion.yaml +2 -1
- package/catalog/signing/catalog-signing-public.pem +3 -0
- package/graphs/intake/intake-daily.yaml +16 -0
- package/graphs/intake/news-sources.yaml +1 -0
- package/graphs/intake/recipes.yaml +7 -0
- package/graphs/overlays/examples/research-runtime-empty-terminal.yaml +28 -0
- package/graphs/profiles/local.yaml +5 -0
- package/graphs/profiles/pod.yaml +4 -0
- package/package.json +6 -2
- package/plugins/demo-absorption-briefing/plugin.json +18 -0
- package/plugins/demo-absorption-briefing/skills/demo-absorption-briefing/SKILL.md +8 -0
- package/plugins/demo-browser-hand/plugin.json +13 -0
- package/plugins/demo-browser-hand/skills/demo-browser-hand/SKILL.md +8 -0
- package/plugins/demo-linear-secretary/plugin.json +15 -0
- package/plugins/demo-linear-secretary/skills/demo-linear-secretary/SKILL.md +8 -0
- package/plugins/demo-plugin-wizard/plugin.json +11 -0
- package/plugins/demo-plugin-wizard/skills/demo-plugin-wizard/SKILL.md +8 -0
- package/plugins/demo-research/plugin.json +16 -0
- package/plugins/demo-research/skills/demo-research/SKILL.md +8 -0
- package/plugins/demo-video-briefing/plugin.json +15 -0
- package/plugins/demo-video-briefing/skills/demo-video-briefing/SKILL.md +8 -0
- package/scripts/build-signed-catalog.ts +48 -0
- package/scripts/ci-typecheck-main-delta.sh +88 -0
- package/scripts/codex-fallback-live-probe.ts +1 -1
- package/scripts/demo/demo1104-parallel-scenes.ts +301 -0
- package/scripts/demo/demo1104-stage.config.ts +21 -0
- package/scripts/demo/demo1104-stage.ts +316 -0
- package/scripts/goal-method-audit.ts +17 -1
- package/scripts/graph-units/land-enqueue.ts +90 -0
- package/scripts/hooks/codex-notify.sh +32 -0
- package/scripts/install.sh +10 -38
- package/scripts/landing-heal/land-plan.ts +57 -0
- package/scripts/release-loop/auto-approve-node.ts +1 -1
- package/scripts/release-loop/gate-autoheal-sim-evidence.ts +60 -0
- package/scripts/release-loop/gate-method.ts +139 -0
- package/scripts/release-loop/gate-node.ts +180 -20
- package/scripts/release-loop/gate-traffic-mode.ts +151 -3
- package/scripts/release-loop/node-verdict.ts +38 -25
- package/scripts/release-loop/preflight.ts +362 -8
- package/scripts/release-loop/unattended-release.ts +20 -9
- package/scripts/rhythm/daily.ts +36 -4
- package/scripts/rhythm/linear-progress-sync.ts +298 -0
- package/scripts/video-pipeline.ts +9 -1
- package/scripts/warm-pool-provider-config.ts +29 -6
- package/src/acp/agent-manager.ts +1 -0
- package/src/acp/codex-app-server-agent.ts +33 -0
- package/src/acp/codex-app-server-client.ts +153 -16
- package/src/acp/core-turn-bridge.ts +17 -12
- package/src/acp/event-router.ts +20 -1
- package/src/acp/server.ts +6 -0
- package/src/agent-mission/browser-roundtrip-coordinator.ts +85 -0
- package/src/agent-mission/claude-headless.ts +4 -1
- package/src/agent-mission/codex-exec.ts +105 -0
- package/src/agent-mission/driver.ts +192 -11
- package/src/agent-mission/grok-auth-status.ts +47 -0
- package/src/agent-mission/hand-hooks.ts +101 -0
- package/src/agent-mission/mission-cli.ts +56 -21
- package/src/agent-mission/mission-hand-reply.ts +118 -0
- package/src/agent-mission/mission-line.ts +180 -0
- package/src/agent-mission/mission-out.ts +256 -18
- package/src/agent-mission/tool-install.ts +122 -11
- package/src/agents/general-purpose.md +1 -1
- package/src/auto-research/research-turn.ts +95 -10
- package/src/autopilot/pr-manager.ts +31 -3
- package/src/boot/daemon-tools/self-implement.ts +3 -1
- package/src/boot/daemon-tools/web-search.ts +1 -2
- package/src/briefing/brief-items.ts +55 -11
- package/src/cli/agent-cli.ts +2 -1
- package/src/cli/catalog-cli.ts +31 -0
- package/src/cli/connector-cli.ts +16 -0
- package/src/cli/doctor-fix.ts +2 -2
- package/src/cli/intake-source-cli.ts +44 -8
- package/src/cli/msg-cli.ts +32 -3
- package/src/cli/pr-cli.ts +21 -1
- package/src/cli/pr-land-heavy-remote.ts +44 -6
- package/src/cli/pr-land-overlap-evidence.ts +13 -1
- package/src/cli/pr-land-pod-shadow-job.ts +143 -5
- package/src/cli/pr-land-pod-shadow.ts +9 -2
- package/src/cli/release-checklist-read-cli.ts +44 -16
- package/src/cli/release-cli.ts +23 -3
- package/src/cli/research-cli.ts +9 -0
- package/src/cli/setup-cli.ts +166 -6
- package/src/cli/setup-keys.ts +187 -0
- package/src/cli/task-plugin-wizard-host.ts +114 -0
- package/src/cli/tasks-cli.ts +54 -9
- package/src/cli/tui-test-cli.ts +186 -0
- package/src/config.ts +4 -2
- package/src/connectors/linear-daily-progress.ts +228 -0
- package/src/connectors/linear-projector.ts +34 -12
- package/src/connectors/linear.ts +151 -7
- package/src/core-turn/deep-turn-bloom.ts +67 -0
- package/src/core-turn/research-policy.ts +37 -0
- package/src/core-turn/run-core-turn.ts +320 -30
- package/src/core-turn/types.ts +2 -0
- package/src/dashboard/chat/acp-chat.ts +10 -0
- package/src/dashboard/classic-sidetab-mission-job.ts +103 -0
- package/src/dashboard/classic-sidetab-mission-pair.ts +81 -0
- package/src/dashboard/classic-sidetab-mission-trio.ts +219 -0
- package/src/dashboard/classic-sidetab-mission.ts +167 -5
- package/src/dashboard/index.ts +378 -13
- package/src/dashboard/input/chat-main-acp-dispatch.ts +23 -0
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +8 -0
- package/src/dashboard/mission-cli-consumer.ts +20 -8
- package/src/dashboard/plugin-wizard-runtime.ts +50 -0
- package/src/dashboard/plugin-wizard-tool.ts +55 -0
- package/src/dashboard/sidetab-runtime.ts +1 -1
- package/src/dashboard/terminal-popup-runtime.ts +6 -3
- package/src/decisions/linear-progress-summary.ts +78 -0
- package/src/decisions/linear-progress-sync.ts +152 -0
- package/src/domains/outbound-alert.ts +49 -11
- package/src/domains/telegram-kind-route.ts +16 -9
- package/src/graph-runner/arg-allowance.ts +13 -0
- package/src/graph-runner/graph-run-api.ts +3 -1
- package/src/graph-runner/runner.ts +85 -17
- package/src/graph-wizard/mission-compose.ts +407 -12
- package/src/harness/dispatch-backpressure.ts +54 -1
- package/src/harness/dispatch-shadow-compare.ts +74 -0
- package/src/harness/harness-cli-command.ts +11 -1
- package/src/harness/skill-exec.ts +21 -9
- package/src/harness/skill-select.ts +1 -1
- package/src/hooks/error-report-cloud.ts +101 -0
- package/src/hooks/receiver.ts +29 -2
- package/src/index.ts +127 -10
- package/src/intake-plane/actions.ts +354 -0
- package/src/intake-plane/adapters/telegram-ambient.ts +34 -18
- package/src/intake-plane/intake-sources.ts +78 -8
- package/src/intake-plane/items.ts +14 -4
- package/src/intake-plane/linear-mission-sync.ts +80 -0
- package/src/intake-plane/linear-secretary-runtime.ts +178 -0
- package/src/intake-plane/linear-wish.ts +227 -1
- package/src/life/life-cli.ts +168 -0
- package/src/life/life-kinds.ts +160 -0
- package/src/live-supply/budget-consumer.ts +78 -0
- package/src/live-supply/read.ts +3 -0
- package/src/live-supply/snapshot.ts +45 -0
- package/src/llm/provider-credentials.ts +8 -2
- package/src/llm.ts +2 -1
- package/src/maturity/feature-maturity.ts +3 -3
- package/src/msg/msg-sse-server.ts +3 -3
- package/src/msg/msg-store.ts +177 -19
- package/src/native-tool-catalog.ts +36 -2
- package/src/nexus/api/http-server.ts +37 -1
- package/src/nexus/api/mcp-public-allowlist.ts +2 -0
- package/src/nexus/index.ts +7 -2
- package/src/nexus/outbound/format.ts +9 -0
- package/src/nexus/outbound/router.ts +98 -13
- package/src/nexus/setup-status.ts +9 -0
- package/src/nexus/state/state.ts +3 -0
- package/src/oauth/codex-account-store.ts +20 -7
- package/src/oauth/codex-owner-mirror.ts +44 -0
- package/src/oauth/codex.ts +17 -2
- package/src/oauth/fallback-chain.ts +47 -17
- package/src/onboarding/first-run-doctor.ts +8 -1
- package/src/onboarding/first-run.ts +85 -26
- package/src/onboarding/key-setup.ts +103 -0
- package/src/onboarding/menu-answer.ts +13 -0
- package/src/onboarding/secret-prompt.ts +37 -0
- package/src/plugins/install/plugin-install.ts +59 -0
- package/src/pr-tool/census.ts +130 -0
- package/src/pty-shell/pty-control-ipc.ts +4 -1
- package/src/registry/catalog-server.ts +232 -0
- package/src/registry/loader.ts +45 -1
- package/src/registry/types.ts +1 -2
- package/src/research/axis-fanout.ts +43 -28
- package/src/self-dev/ask-launch-flow.ts +72 -1
- package/src/self-dev/dev-cli.ts +4 -1
- package/src/self-dev/dev-pipeline.ts +5 -0
- package/src/self-dev/draft-residue.ts +64 -5
- package/src/self-dev/graph-front-nodes.ts +2 -1
- package/src/self-dev/graph-journey-route.ts +76 -21
- package/src/self-dev/launch-preflight.ts +58 -0
- package/src/self-dev/orchestrate.ts +1 -1
- package/src/self-dev/shard-aggregate.ts +4 -0
- package/src/self-dev/shard-run.ts +36 -15
- package/src/self-implement/abandoned-classification.ts +12 -3
- package/src/self-implement/budget-gate.ts +7 -0
- package/src/self-implement/child-llm-preference.ts +10 -1
- package/src/self-implement/doctor-visit.ts +30 -8
- package/src/self-implement/elanous-tui-spawn.ts +3 -0
- package/src/self-implement/fixtures/tdd-green-missing-passing-child-transcript.txt +2 -0
- package/src/self-implement/gate-artifacts.ts +2 -1
- package/src/self-implement/goal-author-cli.ts +4 -2
- package/src/self-implement/goal-author.ts +358 -4
- package/src/self-implement/graph-overlay-yaml.ts +30 -4
- package/src/self-implement/graph-overlay.ts +3 -1
- package/src/self-implement/graph-profile-yaml.ts +107 -0
- package/src/self-implement/graph-yaml.ts +26 -3
- package/src/self-implement/off-diff-evidence.ts +249 -5
- package/src/self-implement/orchestrator.ts +240 -15
- package/src/self-implement/run-chart.ts +6 -3
- package/src/self-implement/run-ledger.ts +51 -1
- package/src/self-implement/runtime-overlay-run-steps.ts +95 -0
- package/src/self-implement/tdd-evidence-tags.ts +9 -0
- package/src/self-implement/train-gate-receipt.ts +166 -0
- package/src/self-implement/train-gate-remote.ts +62 -18
- package/src/session/chat.ts +4 -2
- package/src/session/session-fanout.ts +2 -0
- package/src/session/streaming/acp-stream-sink.ts +2 -1
- package/src/session/streaming/chunk-producer.ts +6 -0
- package/src/session/streaming/stream-compositor.ts +3 -2
- package/src/session-runtime/index.ts +37 -1
- package/src/session-runtime/retry-policy.ts +1 -1
- package/src/setup/llm-env-detect.ts +3 -0
- package/src/setup/service-key-catalog.ts +46 -0
- package/src/setup/service-keys.ts +241 -0
- package/src/sidetab-core/visible-agent-mission.ts +98 -22
- package/src/skills/tools/agent.ts +23 -4
- package/src/skills/tools/omni-search.ts +123 -25
- package/src/task-agent/parent-host.ts +10 -0
- package/src/task-agent/repair-outcomes.ts +65 -0
- package/src/task-agent/task-hand.ts +20 -7
- package/src/task-orchestrator/dispatcher.ts +14 -1
- package/src/task-orchestrator/linear-capture-events.ts +55 -0
- package/src/task-orchestrator/store.ts +16 -5
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +92 -1
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +113 -14
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +23 -11
- package/src/task-orchestrator/surfaces/pod-warm-switch.ts +2 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +35 -9
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -6
- package/src/task-orchestrator/surfaces/self-implement.ts +27 -3
- package/src/task-orchestrator/surfaces/warm-reuse-evidence.ts +169 -0
- package/src/task-orchestrator/types.ts +14 -0
- package/src/telegram.ts +14 -16
- package/src/terminal/coding-agent.ts +14 -5
- package/src/terminal/session-registry.ts +1 -1
- package/src/tool-hints/gate.ts +1 -0
- package/src/tool-runtime/agent-runtime.ts +1 -0
- package/src/tool-runtime/browser-hand-runtime.ts +381 -0
- package/src/tool-runtime/browser-runtime.ts +2 -1
- package/src/tool-runtime/elanous-linear-issue-runtime.ts +76 -0
- package/src/tool-runtime/elanous-linear-secretary-runtime.ts +57 -0
- package/src/tool-runtime/graph-mcp-runtimes.ts +240 -0
- package/src/tool-runtime/index.ts +6 -0
- package/src/tool-runtime/types.ts +3 -0
- package/src/tui/entry.ts +24 -1
- package/src/tui/sidetab/attach.ts +140 -11
- package/src/tui/sidetab/herdr-portal.ts +225 -0
- package/src/user-config.ts +34 -9
- package/src/user-intent/types.ts +4 -3
- package/src/ux-sim/browser-hand.ts +165 -0
- package/src/ux-sim/draft-inventory-builder.ts +198 -0
- package/src/ux-sim/fake-browser-page.ts +81 -0
- package/src/ux-sim/fake-linear-device.ts +31 -0
- package/src/ux-sim/graph-engine-sim.ts +47 -0
- package/src/ux-sim/linear-secretary.ts +216 -0
- package/src/ux-sim/mock-scenario-catalog.json +75 -23
- package/src/ux-sim/mock-scenario-catalog.ts +120 -2
- package/src/ux-sim/onboarding-first-run-device.ts +31 -21
- package/src/ux-sim/scenario.ts +70 -26
- package/src/ux-sim/service-key-setup.ts +50 -0
- package/src/ux-sim/term-slash-device.ts +74 -0
- package/src/ux-sim/turn-throw-sim.ts +31 -0
- package/src/version/packed-revision.json +1 -1
- package/src/web-search/exa.ts +77 -0
- package/src/web-search/index.ts +30 -9
- package/src/web-search/provider.ts +4 -1
- package/src/web-search/x-intent.ts +5 -0
- package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +0 -36
- package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1455-530ca16c7e71baff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +0 -32
- package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +0 -286
- package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +0 -23
- package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +0 -34
- package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +0 -1
- package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2974.595d1e29d040cb6a.js +0 -64
- package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +0 -93
- package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +0 -41
- package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +0 -24
- package/apps/pwa/out/_next/static/chunks/3741.9cf76acaa2ee5298.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +0 -77
- package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4276.336d76d858fcdf1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +0 -74
- package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +0 -149
- package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +0 -27
- package/apps/pwa/out/_next/static/chunks/5431-72a1dbde59400691.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +0 -287
- package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6131.348b7d6bdcd267ce.js +0 -226
- package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +0 -90
- package/apps/pwa/out/_next/static/chunks/6377.bc7df239edbff244.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +0 -104
- package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +0 -94
- package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +0 -24
- package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +0 -1
- package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +0 -8
- package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8546-6f03f8ea34dd61d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9249.976f1e69a9d80754.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9633.ce5bddc56784da72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +0 -62
- package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-0e3d7b9421b79dac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-bf067887b3307423.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +0 -1
- package/apps/pwa/out/_next/static/css/be9019f696217521.css +0 -1
- /package/apps/pwa/out/_next/static/{X1ucLw10Cds_h614NoEnh → Om5xS9jpjSXJCeTNGYEi4}/_ssgManifest.js +0 -0
|
@@ -52,7 +52,7 @@ import { markGateShardUnmeasured, nextGateShardAttempt, readGateShardManifest, r
|
|
|
52
52
|
import { planGateShardDispatch, summarizeGateProgress, type GateProgressGroup, type GateShardReceipt } from './gate-shard-dispatch.js';
|
|
53
53
|
import { defaultKubectl } from '../../src/task-orchestrator/surfaces/self-implement-pod.js';
|
|
54
54
|
import { executeTuiNodeCheck, type ExecutedTuiCheck } from './tui-node-checks.js';
|
|
55
|
-
import { runWithReleaseGateTraffic } from './gate-traffic-mode.js';
|
|
55
|
+
import { runWithReleaseGateTraffic, runWithReleasePwaTraffic } from './gate-traffic-mode.js';
|
|
56
56
|
import { emitPodLifecycle, gatePodLifecycleEvent, gatePodOutcome } from '../../src/task-orchestrator/surfaces/pod-lifecycle-stages.js';
|
|
57
57
|
import { gateHostReservationRoot, reserveReleaseGateHosts } from '../../src/release-loop/gate-host-reservation.js';
|
|
58
58
|
import { normRoot } from '../../src/instance/resolve.js';
|
|
@@ -227,6 +227,10 @@ export function reconcileStaleGateShardBoard(input: {
|
|
|
227
227
|
}
|
|
228
228
|
export interface GateOptions {
|
|
229
229
|
commit: string;
|
|
230
|
+
/** Explicit pre-cut warm↔fresh comparison, persisted before the ordinary gate reads warmHistory. */
|
|
231
|
+
warmPrecutCanary?: { file: string };
|
|
232
|
+
/** RELEASE-POLICY-PACK — the gate Pod image is tied to this resolved cut commit. */
|
|
233
|
+
imagePin?: 'cut-commit';
|
|
230
234
|
/** The graph run that launched this gate; direct hard rehearsal instead pins source bytes explicitly. */
|
|
231
235
|
graphRunId?: string;
|
|
232
236
|
/** Only a direct hard rehearsal may pin graph bytes from its measured Git commit. */
|
|
@@ -250,6 +254,7 @@ export interface GateOptions {
|
|
|
250
254
|
recheckFileLimit?: number;
|
|
251
255
|
/** freshShards — GATE-SHARD-RESUME off: every shard starts a Pod even when this logDir already holds its result. */
|
|
252
256
|
pod?: { pool: string; shards?: number; shardTimeoutSeconds?: number; durationSource?: string; bunCache?: string; freshShards?: boolean;
|
|
257
|
+
imagePin?: 'cut-commit';
|
|
253
258
|
/** One immutable container image identity for this gate graph run and all its resumes. */
|
|
254
259
|
pinnedImageDigest?: string;
|
|
255
260
|
pinnedRunId?: string;
|
|
@@ -409,29 +414,151 @@ const WARM_REPLAY_BUDGET = 3;
|
|
|
409
414
|
interface WarmVerdictSample { version: string; commit: string; baselineCommit: string; warmShadow: WarmShadowObservation[] }
|
|
410
415
|
const recordValue = (value: unknown): Record<string, unknown> | null =>
|
|
411
416
|
value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
417
|
+
function validWarmShadowObservation(value: unknown): value is WarmShadowObservation {
|
|
418
|
+
const row = recordValue(value);
|
|
419
|
+
if (!row || !Array.isArray(row.files) || row.files.length !== 1 || typeof row.files[0] !== 'string'
|
|
420
|
+
|| !/^(?:[\w.-]+\/)*[\w.-]+\.test\.[cm]?[jt]sx?$/.test(row.files[0])
|
|
421
|
+
|| row.files[0].split('/').some((part) => part === '.' || part === '..')
|
|
422
|
+
|| typeof row.attemptId !== 'string' || !row.attemptId || typeof row.reason !== 'string'
|
|
423
|
+
|| !['observed', 'fallback', 'unmeasured'].includes(String(row.status))
|
|
424
|
+
|| !['yes', 'no', 'failed', 'unavailable'].includes(String(row.quarantined))) return false;
|
|
425
|
+
if (row.finalized !== undefined && typeof row.finalized !== 'boolean') return false;
|
|
426
|
+
if (row.cutSha !== undefined && typeof row.cutSha !== 'string') return false;
|
|
427
|
+
if (row.baselineSha !== undefined && typeof row.baselineSha !== 'string') return false;
|
|
428
|
+
if (row.workerKey !== undefined) {
|
|
429
|
+
const key = recordValue(row.workerKey);
|
|
430
|
+
if (!key || typeof key.context !== 'string' || !key.context || typeof key.imageDigest !== 'string' || !key.imageDigest
|
|
431
|
+
|| typeof key.lockHash !== 'string' || !key.lockHash || typeof key.architecture !== 'string' || !key.architecture) return false;
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
function validWarmVerdictSample(value: unknown): value is WarmVerdictSample {
|
|
436
|
+
const row = recordValue(value);
|
|
437
|
+
return !!row && typeof row.version === 'string' && typeof row.commit === 'string'
|
|
438
|
+
&& typeof row.baselineCommit === 'string' && Array.isArray(row.warmShadow)
|
|
439
|
+
&& row.warmShadow.every(validWarmShadowObservation);
|
|
440
|
+
}
|
|
412
441
|
/** The verdict is atomically replaced; carry a bounded prior-verdict window in that same SSOT. */
|
|
413
442
|
function readWarmVerdictHistory(path: string): WarmVerdictSample[] | undefined {
|
|
414
443
|
if (!existsSync(path)) return [];
|
|
415
444
|
try {
|
|
416
445
|
const current = recordValue(JSON.parse(readFileSync(path, 'utf8')));
|
|
417
446
|
if (!current || !Array.isArray(current.warmHistory)) return undefined;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
447
|
+
if (current.warmHistory.some((entry) => !validWarmVerdictSample(entry))) return undefined;
|
|
448
|
+
const hasCurrentSample = ['version', 'commit', 'baselineCommit', 'warmShadow'].some((key) => key in current);
|
|
449
|
+
const history = [...current.warmHistory];
|
|
450
|
+
if (current.warmHistoryOnly !== true || hasCurrentSample) {
|
|
451
|
+
const currentSample = { version: current.version, commit: current.commit,
|
|
452
|
+
baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
|
|
453
|
+
if (!validWarmVerdictSample(currentSample)) return undefined;
|
|
454
|
+
if (current.warmHistoryOnly !== true) history.push(currentSample);
|
|
455
|
+
}
|
|
456
|
+
return history.slice(-WARM_VERDICT_HISTORY_LIMIT) as WarmVerdictSample[];
|
|
424
457
|
} catch { return undefined; }
|
|
425
458
|
}
|
|
459
|
+
|
|
460
|
+
/** Append one actual pre-cut replay observation to the gate verdict's bounded history, preserving its gate result. */
|
|
461
|
+
function recordWarmPrecutCanarySample(path: string, sample: WarmVerdictSample): void {
|
|
462
|
+
if (!sample.version || !/^[0-9a-f]{7,40}$/i.test(sample.commit) || !/^[0-9a-f]{7,40}$/i.test(sample.baselineCommit)
|
|
463
|
+
|| !Array.isArray(sample.warmShadow) || sample.warmShadow.length !== 1 || !sample.warmShadow.every(validWarmShadowObservation))
|
|
464
|
+
throw new Error('invalid pre-cut canary sample');
|
|
465
|
+
let current: Record<string, unknown> = {};
|
|
466
|
+
if (existsSync(path)) {
|
|
467
|
+
let decoded: unknown;
|
|
468
|
+
try { decoded = JSON.parse(readFileSync(path, 'utf8')); }
|
|
469
|
+
catch (error) { throw new Error(`gate verdict JSON is malformed: ${String(error)}`); }
|
|
470
|
+
const parsed = recordValue(decoded);
|
|
471
|
+
if (!parsed) throw new Error('gate verdict is not an object');
|
|
472
|
+
current = parsed;
|
|
473
|
+
if (current.warmHistory !== undefined && !Array.isArray(current.warmHistory)) throw new Error('gate verdict warmHistory is invalid');
|
|
474
|
+
}
|
|
475
|
+
const storedHistory = Array.isArray(current.warmHistory) ? [...current.warmHistory] : [];
|
|
476
|
+
if (storedHistory.some((entry) => !validWarmVerdictSample(entry))) throw new Error('gate verdict warmHistory contains an invalid row');
|
|
477
|
+
const history = storedHistory;
|
|
478
|
+
const hasCurrentSample = ['version', 'commit', 'baselineCommit', 'warmShadow'].some((key) => key in current);
|
|
479
|
+
if (current.warmHistoryOnly === true && hasCurrentSample) {
|
|
480
|
+
const previous = { version: current.version, commit: current.commit, baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
|
|
481
|
+
if (!validWarmVerdictSample(previous)) throw new Error('gate verdict current warm sample is invalid');
|
|
482
|
+
} else if (current.warmHistoryOnly !== true) {
|
|
483
|
+
const previous = { version: current.version, commit: current.commit, baselineCommit: current.baselineCommit, warmShadow: current.warmShadow };
|
|
484
|
+
if (!validWarmVerdictSample(previous)) throw new Error('gate verdict current warm sample is invalid');
|
|
485
|
+
history.push(previous);
|
|
486
|
+
}
|
|
487
|
+
history.push(sample);
|
|
488
|
+
const body = JSON.stringify({ ...current, warmHistory: history.slice(-WARM_VERDICT_HISTORY_LIMIT), warmHistoryOnly: true }, null, 2) + '\n';
|
|
489
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
490
|
+
const temp = join(dirname(path), `.gate-verdict-${process.pid}-${randomUUID()}.tmp`);
|
|
491
|
+
try { writeFileSync(temp, body, { mode: 0o600, flag: 'wx' }); chmodSync(temp, 0o600); renameSync(temp, path); }
|
|
492
|
+
finally { if (existsSync(temp)) rmSync(temp); }
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Run a bounded set of real fresh-Job ↔ warm-worker comparisons before the release gate. */
|
|
496
|
+
export async function runWarmPrecutCanary(input: { opts: GateOptions; runner: GateRunner; file: string;
|
|
497
|
+
config: unknown; historyPath: string; timeoutMs?: number }): Promise<{ samples: WarmShadowObservation[]; decision: ReturnType<typeof decideWarmReuse> }> {
|
|
498
|
+
const { opts, runner } = input;
|
|
499
|
+
if (!opts.pod || !runner.recheckPod || !opts.baselineCommit || !opts.pod.warmReplay || !opts.pod.warmFinalize || !opts.pod.warmQuarantine)
|
|
500
|
+
throw new Error('pre-cut canary requires a Pod pool, baseline commit, and warm worker hooks');
|
|
501
|
+
if (!/^(?:[\w.-]+\/)*[\w.-]+\.test\.[cm]?[jt]sx?$/.test(input.file)
|
|
502
|
+
|| input.file.split('/').some((part) => part === '.' || part === '..')) throw new Error('pre-cut canary requires one repository test file');
|
|
503
|
+
if (readWarmVerdictHistory(input.historyPath) === undefined)
|
|
504
|
+
throw new Error('gate verdict contains malformed warm history or current sample; refusing canary without writing');
|
|
505
|
+
const samples = 2;
|
|
506
|
+
if (samples > WARM_REPLAY_BUDGET) throw new Error(`pre-cut canary sample count exceeds replay budget (${WARM_REPLAY_BUDGET})`);
|
|
507
|
+
const lock = await runner.localCommand('git', ['show', `${opts.commit}:bun.lock`], opts.repo ?? process.cwd());
|
|
508
|
+
if (lock.rc !== 0 || !lock.output) throw new Error('pre-cut canary cut lock is unmeasured');
|
|
509
|
+
if (!/^[0-9a-f]{40}$/i.test(opts.baselineCommit)) throw new Error('pre-cut canary baseline commit is invalid');
|
|
510
|
+
const baseline = await runner.localCommand('git', ['cat-file', '-e', `${opts.baselineCommit}^{commit}`], opts.repo ?? process.cwd());
|
|
511
|
+
if (baseline.rc !== 0) throw new Error(`pre-cut canary baseline commit is absent: ${opts.baselineCommit.slice(0, 12)}`);
|
|
512
|
+
const trackedFile = await runner.localCommand('git', ['cat-file', '-e', `${opts.commit}:${input.file}`], opts.repo ?? process.cwd());
|
|
513
|
+
if (trackedFile.rc !== 0) throw new Error(`pre-cut canary test file is absent from cut ${opts.commit.slice(0, 12)}`);
|
|
514
|
+
const lockHash = createHash('sha256').update(lock.output).digest('hex');
|
|
515
|
+
const pod = { ...opts.pod, warmProducerEnabled: false, warmCutSha: opts.commit, warmBaselineSha: opts.baselineCommit,
|
|
516
|
+
warmEligible: true, warmReplayOwner: true, warmShadowScope: { requested: false, attempts: 0 } };
|
|
517
|
+
const observations: WarmShadowObservation[] = [];
|
|
518
|
+
const attemptIds = new Set<string>();
|
|
519
|
+
for (let index = 0; index < samples; index++) {
|
|
520
|
+
let observation: WarmShadowObservation;
|
|
521
|
+
let failure: Error | undefined;
|
|
522
|
+
try {
|
|
523
|
+
const result = await runner.recheckPod(input.file, opts.commit, pod, input.timeoutMs ?? GATE_ISOLATED_TIMEOUT_MS);
|
|
524
|
+
observation = result.warmShadow ?? { files: [input.file], attemptId: `precut-unmeasured-${index + 1}-${randomUUID()}`,
|
|
525
|
+
status: 'unmeasured', reason: result.rc === 0 ? 'warm-shadow-missing' : `fresh-job-rc-${result.rc}`, quarantined: 'no' };
|
|
526
|
+
if (result.rc !== 0) { observation = { ...observation, status: 'unmeasured', reason: `fresh-job-rc-${result.rc}` };
|
|
527
|
+
failure = new Error(`pre-cut fresh Job failed on attempt ${index + 1}: rc=${result.rc}`); }
|
|
528
|
+
else if (observation.status !== 'observed' || !observation.workerKey
|
|
529
|
+
|| observation.files.length !== 1 || observation.files[0] !== input.file
|
|
530
|
+
|| observation.cutSha !== opts.commit || observation.baselineSha !== opts.baselineCommit
|
|
531
|
+
|| observation.workerKey.lockHash !== lockHash || !['match', 'result'].includes(observation.reason)
|
|
532
|
+
|| attemptIds.has(observation.attemptId)
|
|
533
|
+
|| (observation.reason === 'match' && (observation.finalized !== true || observation.quarantined !== 'no'))) {
|
|
534
|
+
failure = new Error(`pre-cut warm comparison was not measured on attempt ${index + 1}: ${observation.reason}`);
|
|
535
|
+
}
|
|
536
|
+
} catch (error) {
|
|
537
|
+
observation = { files: [input.file], attemptId: `precut-error-${index + 1}-${randomUUID()}`,
|
|
538
|
+
status: 'unmeasured', reason: String(error), quarantined: 'unavailable' };
|
|
539
|
+
failure = error instanceof Error ? error : new Error(String(error));
|
|
540
|
+
}
|
|
541
|
+
attemptIds.add(observation.attemptId);
|
|
542
|
+
observations.push(observation);
|
|
543
|
+
recordWarmPrecutCanarySample(input.historyPath, { version: opts.version, commit: opts.commit,
|
|
544
|
+
baselineCommit: opts.baselineCommit, warmShadow: [observation] });
|
|
545
|
+
if (failure) throw failure;
|
|
546
|
+
}
|
|
547
|
+
const history = readWarmVerdictHistory(input.historyPath);
|
|
548
|
+
const evidence = warmEvidenceFromVerdicts(history, opts.version, opts.commit, opts.baselineCommit, lockHash);
|
|
549
|
+
const warmReuseConfig = (input.config && typeof input.config === 'object' && 'pod' in input.config
|
|
550
|
+
&& recordValue((input.config as Record<string, unknown>).pod)?.warmReuse) as WarmReuseSwitchConfig | undefined;
|
|
551
|
+
return { samples: observations, decision: decideWarmReuse('gate-recheck', warmReuseConfig,
|
|
552
|
+
evidence ? { 'gate-recheck': evidence } : undefined) };
|
|
553
|
+
}
|
|
426
554
|
/** Every counted match has a measured, finalized worker with this exact cut, baseline and lock. */
|
|
427
555
|
export function warmEvidenceFromVerdicts(history: readonly WarmVerdictSample[] | undefined,
|
|
428
556
|
version: string, head: string, baseline: string, lock: string): WarmReuseEvidence | undefined {
|
|
429
557
|
if (!history?.length || !lock) return undefined;
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
});
|
|
558
|
+
const matchingHistory = history.filter((verdict) => verdict.version === version && verdict.commit === head
|
|
559
|
+
&& verdict.baselineCommit === baseline);
|
|
560
|
+
const rows = matchingHistory.flatMap((verdict) => verdict.warmShadow.length ? verdict.warmShadow : [null]);
|
|
561
|
+
if (!rows.length) return undefined;
|
|
435
562
|
const attempts = new Set<string>();
|
|
436
563
|
const key = rows.at(-1)?.workerKey;
|
|
437
564
|
if (rows.some((entry) => {
|
|
@@ -2227,7 +2354,8 @@ export function createGateRunner(repo: string, remote?: string, commandOverride?
|
|
|
2227
2354
|
pool: pod.pool, poolScheduler: trackedPool(shard, branch, jobName, resultFile, attemptId, (member) => {
|
|
2228
2355
|
stamp('poolPlaced'); jobContext = member.context; jobContextForCapture = member.context; placedOn = member.context; placed();
|
|
2229
2356
|
if (branch === '-tail') tailPlacedHost = member.context;
|
|
2230
|
-
}, skipMembers, depth === 0 && branch === '' && hostRetries === 0 && !retriedNoOutput && !batchIsolation && skipMembers.size === 0), clone: true, source: { kind: 'commit', sha: commit },
|
|
2357
|
+
}, skipMembers, depth === 0 && branch === '' && hostRetries === 0 && !retriedNoOutput && !batchIsolation && skipMembers.size === 0), clone: true, source: { kind: 'commit', sha: commit },
|
|
2358
|
+
...(pod.imagePin === 'cut-commit' ? { imageCommit: commit } : {}), deadlineSeconds: shardDeadline,
|
|
2231
2359
|
...gateHostMirror(pod),
|
|
2232
2360
|
...(multiHost ? { statusUnreadableMs: memberUnreadableSeconds * 1000 } : {}),
|
|
2233
2361
|
...(bunCache ? { bunCache } : {}),
|
|
@@ -3266,11 +3394,11 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3266
3394
|
let cutSweepRc: number | null = null;
|
|
3267
3395
|
let radiusShadow: GateRadiusShadowPlan | undefined;
|
|
3268
3396
|
const trees: string[] = [];
|
|
3397
|
+
const baselineVersion = opts.baselineVersion;
|
|
3269
3398
|
try {
|
|
3270
3399
|
if (!sha.test(opts.commit) || !versionPattern.test(opts.version)
|
|
3271
3400
|
|| (opts.baselineVersion && !versionPattern.test(opts.baselineVersion))
|
|
3272
3401
|
|| (opts.baselineCommit && !sha.test(opts.baselineCommit))) throw new Error('invalid commit or version');
|
|
3273
|
-
const baselineVersion = opts.baselineVersion;
|
|
3274
3402
|
if (!baselineVersion) throw new Error('previous release version required (--baseline-version)');
|
|
3275
3403
|
if (opts.rehearsalBaseline && (opts.graphRunId || !opts.pod || !/-rc\.\d+$/.test(opts.version)))
|
|
3276
3404
|
throw new Error('rehearsal baseline requires a direct Pod gate with an isolated rc version');
|
|
@@ -3506,7 +3634,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3506
3634
|
try {
|
|
3507
3635
|
const sweepResult = await runner.sweep(cutTree,
|
|
3508
3636
|
join(root, 'release', opts.version, 'gate-logs', selected ? 'cut-partial' : 'cut'),
|
|
3509
|
-
opts.pod ? { ...opts.pod, durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
|
|
3637
|
+
opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource, ...(knownFailures.length ? { knownFailures } : {}), onSelectionTimed: ({ ms }) => { result.selectionMs = ms; },
|
|
3510
3638
|
// GATE-RISK-LANE — the watch list is read from this gate's own ledgers (the instance root ⊕ the machine ledger);
|
|
3511
3639
|
// the cut sweep puts those files first, the baseline sweep keeps its plan.
|
|
3512
3640
|
riskWatch: { instanceRoots: roots, ledgerRoots: roots },
|
|
@@ -3608,7 +3736,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3608
3736
|
check(await runner.command('bun', ['install'], dir), `bun install ${dir}`);
|
|
3609
3737
|
const rechecked = await runner.sweep(recheckTree,
|
|
3610
3738
|
join(root, 'release', opts.version, 'gate-logs', 'baseline-attribution-recheck'),
|
|
3611
|
-
opts.pod ? { ...opts.pod, durationSource } : undefined,
|
|
3739
|
+
opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource } : undefined,
|
|
3612
3740
|
targetFiles);
|
|
3613
3741
|
if (rechecked.timedOut || rechecked.abortedShards || rechecked.stalledEnv?.length) {
|
|
3614
3742
|
attribution.baselineResult = 'timeout';
|
|
@@ -3723,7 +3851,7 @@ export async function judgeGate(opts: GateOptions, runner: GateRunner = createGa
|
|
|
3723
3851
|
// K9b — the baseline is swept on the same Pod pool as the cut, not on this host (0.2.6: an hour of local baseline
|
|
3724
3852
|
// after a 23-minute Pod cut).
|
|
3725
3853
|
const baseRun = await runner.sweep(baseTree!, join(root, 'release', opts.version, 'gate-logs', 'baseline'),
|
|
3726
|
-
opts.pod ? { ...opts.pod, durationSource } : undefined,
|
|
3854
|
+
opts.pod ? { ...opts.pod, ...(opts.imagePin ? { imagePin: opts.imagePin } : {}), durationSource } : undefined,
|
|
3727
3855
|
present);
|
|
3728
3856
|
baseline = await localStalled(baseRun, baseTree!, baseSha, 'baseline');
|
|
3729
3857
|
} else baseline = { failures: [], errors: [] };
|
|
@@ -4174,6 +4302,14 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
4174
4302
|
const fromGraph: Record<string, unknown> = context?.input ?? {};
|
|
4175
4303
|
const graphCommit = context?.outputs['version-release']?.commit;
|
|
4176
4304
|
if (typeof graphCommit === 'string') fromGraph.commit = graphCommit;
|
|
4305
|
+
if (fromGraph.gateImagePin !== undefined && fromGraph.gateImagePin !== 'cut-commit') {
|
|
4306
|
+
throw new Error(`unsupported gate image pin: ${String(fromGraph.gateImagePin)}`);
|
|
4307
|
+
}
|
|
4308
|
+
// Policy-selected Pod gates must pin their container to the resolved cut commit. Host sweeps do
|
|
4309
|
+
// not consume a container image pin, and the legacy no-executor path retains its old behavior.
|
|
4310
|
+
if (fromGraph.gateExecutor === 'pod' && fromGraph.gateImagePin !== 'cut-commit') {
|
|
4311
|
+
throw new Error('gateImagePin cut-commit is required for a policy-selected release gate');
|
|
4312
|
+
}
|
|
4177
4313
|
const baselineCommit = context?.outputs.cutoff?.baseline as { sha?: unknown } | undefined;
|
|
4178
4314
|
if (typeof baselineCommit?.sha === 'string') fromGraph.previousCommit = baselineCommit.sha;
|
|
4179
4315
|
if (fromGraph.gateExecutor !== undefined && !isGateExecutor(fromGraph.gateExecutor)) throw new Error(`gateExecutor must be pod or host: ${String(fromGraph.gateExecutor)}`);
|
|
@@ -4186,6 +4322,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
4186
4322
|
|| !['darwin', 'linux'].includes((value as { os?: string }).os ?? ''))))
|
|
4187
4323
|
throw new Error('gateShardCapabilities must map pod-N to {os: darwin|linux}');
|
|
4188
4324
|
const opts: GateOptions = {
|
|
4325
|
+
...(fromGraph.gateImagePin === 'cut-commit' ? { imagePin: 'cut-commit' as const } : {}),
|
|
4189
4326
|
rehearsalTui: fromGraph.rehearsalTui === true,
|
|
4190
4327
|
recheckFileLimit: fromGraph.gateRecheckFileLimit !== undefined ? Number(fromGraph.gateRecheckFileLimit) : undefined,
|
|
4191
4328
|
commit: typeof fromGraph.commit === 'string' ? fromGraph.commit : '',
|
|
@@ -4214,12 +4351,19 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
4214
4351
|
};
|
|
4215
4352
|
if (isGateExecutor(fromGraph.gateExecutor)) opts.executor = fromGraph.gateExecutor;
|
|
4216
4353
|
let freshShards = false;
|
|
4354
|
+
let warmPrecutCanaryFile: string | undefined;
|
|
4217
4355
|
let rerouteShard: string | undefined;
|
|
4218
4356
|
let shardHost: string | undefined;
|
|
4219
4357
|
for (let i = 0; i < args.length; i++) {
|
|
4220
4358
|
const arg = args[i];
|
|
4221
4359
|
if (arg === '--help') return 'help';
|
|
4222
4360
|
if (arg === '--json') continue;
|
|
4361
|
+
if (arg === '--precut-canary-file') {
|
|
4362
|
+
const value = args[++i];
|
|
4363
|
+
if (!value || value.startsWith('--')) throw new Error('--precut-canary-file requires one test file');
|
|
4364
|
+
warmPrecutCanaryFile = value;
|
|
4365
|
+
continue;
|
|
4366
|
+
}
|
|
4223
4367
|
if (arg === '--full') { opts.full = true; continue; }
|
|
4224
4368
|
if (arg === '--rehearsal-baseline') { opts.rehearsalBaseline = true; continue; }
|
|
4225
4369
|
// Applied after the loop and only to a Pod gate — on its own it must not turn a local gate into a Pod one (review r5).
|
|
@@ -4264,6 +4408,7 @@ export function parseOptions(args: string[], env: NodeJS.ProcessEnv): GateOption
|
|
|
4264
4408
|
else if (key === 'remoteMirror') opts.remoteMirror = value;
|
|
4265
4409
|
}
|
|
4266
4410
|
if (freshShards && opts.pod) opts.pod = { ...opts.pod, freshShards: true };
|
|
4411
|
+
if (warmPrecutCanaryFile) opts.warmPrecutCanary = { file: warmPrecutCanaryFile };
|
|
4267
4412
|
if ((rerouteShard || shardHost) && !opts.pod?.pool) throw new Error('shard reroute requires a Pod pool');
|
|
4268
4413
|
if (shardHost && !rerouteShard) throw new Error('--shard-host requires --reroute-shard');
|
|
4269
4414
|
if (opts.pod && rerouteShard) opts.pod = { ...opts.pod, rerouteShard, ...(shardHost ? { shardHost } : {}) };
|
|
@@ -4299,12 +4444,26 @@ export async function runGateNode(argv: string[], env: NodeJS.ProcessEnv, runner
|
|
|
4299
4444
|
catch (error) { debug.log('release-loop.gate', 'warm-profile-unavailable', { error: String(error) }); }
|
|
4300
4445
|
}
|
|
4301
4446
|
const opts = parsed === 'help' ? parsed : withWarmGateHooks(parsed, config, !!runner);
|
|
4447
|
+
if (opts !== 'help' && opts.warmPrecutCanary) {
|
|
4448
|
+
const canaryRunner = runner ?? createGateRunner(opts.repo ?? process.cwd(), opts.remote);
|
|
4449
|
+
const warmPrecutTestSeam = (runner as (GateRunner & { warmPrecutCanaryTestSeam?: {
|
|
4450
|
+
config: unknown; hooks: Pick<NonNullable<GateOptions['pod']>, 'warmReplay' | 'warmFinalize' | 'warmQuarantine' | 'warmReuseConfig'>;
|
|
4451
|
+
} }) | undefined)?.warmPrecutCanaryTestSeam;
|
|
4452
|
+
const canaryOpts = warmPrecutTestSeam && opts.pod ? { ...opts, pod: { ...opts.pod, ...warmPrecutTestSeam.hooks } } : opts;
|
|
4453
|
+
const canary = await runWarmPrecutCanary({ opts: canaryOpts, runner: canaryRunner, file: opts.warmPrecutCanary.file,
|
|
4454
|
+
config: warmPrecutTestSeam?.config ?? config,
|
|
4455
|
+
historyPath: verdictFile(opts.ledgerRoot ?? releaseLedgerRoot(), opts.version) });
|
|
4456
|
+
const output = { version: opts.version, commit: opts.commit, samples: canary.samples, decision: canary.decision };
|
|
4457
|
+
console.log(JSON.stringify(output));
|
|
4458
|
+
return 0;
|
|
4459
|
+
}
|
|
4302
4460
|
if (opts !== 'help') {
|
|
4303
4461
|
placement = gateExecutorOf(opts);
|
|
4304
|
-
debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null
|
|
4462
|
+
debug.log('release-loop.gate', 'executor', { version: opts.version, ...placement, requested: opts.executor ?? null,
|
|
4463
|
+
imagePin: opts.imagePin ?? null, imageCommit: opts.imagePin === 'cut-commit' ? opts.commit : null });
|
|
4305
4464
|
}
|
|
4306
4465
|
if (opts === 'help') {
|
|
4307
|
-
console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--recheck-file-limit <n>] [--fresh-shards] [--reroute-shard <pod-N|auto>] [--shard-host <pool-context>] [--executor <pod|host>] [--rehearsal-baseline] [--full] [--json]\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.\n--executor host (input.gateExecutor) ignores every pod option and sweeps on --remote (input.gateRemote, required).\n--rehearsal-baseline pins source graph bytes only for a direct Pod gate on an isolated rc version.\n--full disables receipt and partial reuse and runs the complete test inventory.');
|
|
4466
|
+
console.log('Usage: bun scripts/release-loop/gate-node.ts --commit <sha> --version <v> [--baseline-version <prev>] [--baseline-commit <sha>] [--remote <ssh-host>] [--remote-mirror <path>] [--pod-pool <pool>] [--precut-canary-file <test-file>] [--pod-shards <n>] [--pod-shard-timeout-seconds <n>] [--recheck-file-limit <n>] [--fresh-shards] [--reroute-shard <pod-N|auto>] [--shard-host <pool-context>] [--executor <pod|host>] [--rehearsal-baseline] [--full] [--json]\n--precut-canary-file runs exactly two one-file warm↔fresh comparisons and stores them in this version\'s gate-verdict warmHistory before the standard gate consumes it; decideWarmReuse applies minMatchSamples.\nWithout flags, input.commit, input.version and input.previousVersion come from the JSON file at ELANOUS_GRAPH_CONTEXT.\n--executor host (input.gateExecutor) ignores every pod option and sweeps on --remote (input.gateRemote, required).\n--rehearsal-baseline pins source graph bytes only for a direct Pod gate on an isolated rc version.\n--full disables receipt and partial reuse and runs the complete test inventory.');
|
|
4308
4467
|
return 0;
|
|
4309
4468
|
}
|
|
4310
4469
|
version = opts.version;
|
|
@@ -4363,5 +4522,6 @@ if (import.meta.main) {
|
|
|
4363
4522
|
// The graph spawns this file as its own process, which inherits no daemon sink — without this every
|
|
4364
4523
|
// `release-loop.gate`/`release.gate`/`pod.pool` event stayed in the file trail and `elanous logs` read 0 rows.
|
|
4365
4524
|
await registerStandaloneLogSink('release-loop');
|
|
4366
|
-
process.exitCode = await runWithReleaseGateTraffic(process.env,
|
|
4525
|
+
process.exitCode = await runWithReleaseGateTraffic(process.env,
|
|
4526
|
+
() => runWithReleasePwaTraffic(process.env, () => runGateNode(process.argv.slice(2), process.env)));
|
|
4367
4527
|
}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
5
|
import { debug } from '../../src/debug/log.js';
|
|
6
6
|
|
|
7
|
-
type TrafficBoard = Record<string, unknown> & { global: string };
|
|
7
|
+
type TrafficBoard = Record<string, unknown> & { global: string; seats?: Record<string, Record<string, unknown>> };
|
|
8
8
|
type PatchResult = { changed: true; previous: string; warning?: string } | { changed: false; current: string };
|
|
9
|
+
type PwaSnapshot = { reason: unknown; hadReason: boolean; updatedAt: unknown; hadUpdatedAt: boolean; activeUpdatedAt: string;
|
|
10
|
+
heavyLocalBan: unknown; hadHeavyLocalBan: boolean;
|
|
11
|
+
perCycle: Record<string, { value: unknown; hadValue: boolean }> };
|
|
12
|
+
type PwaPatchResult = { changed: true; previous: PwaSnapshot; warning?: string };
|
|
13
|
+
const RELEASE_SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
|
|
14
|
+
const PWA_PER_CYCLE = 3;
|
|
15
|
+
const MAX_PWA_TRAFFIC_BACKUPS_PER_KIND = 10;
|
|
9
16
|
|
|
10
17
|
const defaultTrafficPath = () => join(homedir(), 'elanous-hq', 'seat-state', 'OP', 'traffic.json');
|
|
11
18
|
const trafficPath = (env: NodeJS.ProcessEnv) => env.ELANOUS_TRAFFIC_SIGNAL || defaultTrafficPath();
|
|
@@ -21,6 +28,17 @@ function releaseGateContext(env: NodeJS.ProcessEnv): boolean {
|
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
|
|
31
|
+
function releasePwaContext(env: NodeJS.ProcessEnv): boolean {
|
|
32
|
+
const source = env.ELANOUS_GRAPH_CONTEXT;
|
|
33
|
+
if (!source) return false;
|
|
34
|
+
try {
|
|
35
|
+
const raw = JSON.parse(source.trimStart().startsWith('{') ? source : readFileSync(source, 'utf8')) as Record<string, unknown>;
|
|
36
|
+
return raw.graphId === 'release-loop' && raw.nodeId === 'pwa';
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
function withTrafficLock<T>(path: string, action: () => T): T {
|
|
25
43
|
const lock = `${path}.lock.d`;
|
|
26
44
|
const deadline = Date.now() + 10_000;
|
|
@@ -86,6 +104,136 @@ function patchGlobal(path: string, next: string, reason: string, onlyIf?: string
|
|
|
86
104
|
});
|
|
87
105
|
}
|
|
88
106
|
|
|
107
|
+
function writeTrafficBoard(path: string, after: TrafficBoard, suffix: string): void {
|
|
108
|
+
const at = new Date();
|
|
109
|
+
const stamp = [at.getMonth() + 1, at.getDate(), at.getHours(), at.getMinutes(), at.getSeconds()].map(value => String(value).padStart(2, '0')).join('');
|
|
110
|
+
writeFileSync(`${path}.bak-${stamp}-${randomUUID().slice(0, 8)}-${suffix}`, readFileSync(path));
|
|
111
|
+
const temp = `${path}.tmp.${process.pid}.${randomUUID()}`;
|
|
112
|
+
try {
|
|
113
|
+
writeFileSync(temp, `${JSON.stringify(after, null, 2)}\n`, { mode: statSync(path).mode & 0o777 });
|
|
114
|
+
renameSync(temp, path);
|
|
115
|
+
} finally { rmSync(temp, { force: true }); }
|
|
116
|
+
// Re-read after the atomic rename so a malformed write is visible to the caller.
|
|
117
|
+
JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
|
|
118
|
+
if (suffix.startsWith('release-pwa-')) {
|
|
119
|
+
try { pruneTrafficBackups(path, suffix); }
|
|
120
|
+
catch (error) { debug.log('release-loop.pwa', 'traffic backup pruning failed', { path, suffix, error: String(error) }, { level: 'warn' }); }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function pruneTrafficBackups(path: string, suffix: string): void {
|
|
125
|
+
const directory = dirname(path);
|
|
126
|
+
const prefix = `${basename(path)}.bak-`;
|
|
127
|
+
const backups = readdirSync(directory)
|
|
128
|
+
.filter(name => name.startsWith(prefix) && name.endsWith(`-${suffix}`))
|
|
129
|
+
.map(name => ({ path: join(directory, name), modifiedAt: statSync(join(directory, name)).mtimeMs }))
|
|
130
|
+
.sort((a, b) => a.modifiedAt - b.modifiedAt);
|
|
131
|
+
for (const backup of backups.slice(0, Math.max(0, backups.length - MAX_PWA_TRAFFIC_BACKUPS_PER_KIND))) {
|
|
132
|
+
rmSync(backup.path, { force: true });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function patchPwaTraffic(path: string): PwaPatchResult {
|
|
137
|
+
return withTrafficLock(path, () => {
|
|
138
|
+
if (!existsSync(path)) throw new Error(`traffic board missing: ${path}`);
|
|
139
|
+
const board = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
|
|
140
|
+
if (!board || typeof board !== 'object' || Array.isArray(board) || typeof board.global !== 'string'
|
|
141
|
+
|| !board.seats || typeof board.seats !== 'object' || Array.isArray(board.seats)
|
|
142
|
+
|| !RELEASE_SEATS.every(seat => board.seats![seat] && typeof board.seats![seat] === 'object' && !Array.isArray(board.seats![seat]))) {
|
|
143
|
+
throw new Error(`invalid traffic board for PWA policy: ${path}`);
|
|
144
|
+
}
|
|
145
|
+
const activeUpdatedAt = new Date().toISOString();
|
|
146
|
+
const previous: PwaSnapshot = {
|
|
147
|
+
hadReason: Object.hasOwn(board, 'reason'), reason: board.reason,
|
|
148
|
+
hadUpdatedAt: Object.hasOwn(board, 'updatedAt'), updatedAt: board.updatedAt, activeUpdatedAt,
|
|
149
|
+
hadHeavyLocalBan: Object.hasOwn(board, 'heavyLocalBan'), heavyLocalBan: board.heavyLocalBan,
|
|
150
|
+
perCycle: Object.fromEntries(RELEASE_SEATS.map(seat => [seat, {
|
|
151
|
+
hadValue: Object.hasOwn(board.seats![seat]!, 'perCycle'), value: board.seats![seat]!.perCycle,
|
|
152
|
+
}])),
|
|
153
|
+
};
|
|
154
|
+
const updated: TrafficBoard = {
|
|
155
|
+
...board,
|
|
156
|
+
...(board.heavyLocalBan === true ? {} : { reason: 'release PWA node running (heavy-local banned)' }),
|
|
157
|
+
heavyLocalBan: true,
|
|
158
|
+
seats: Object.fromEntries(Object.entries(board.seats).map(([seat, value]) => [seat,
|
|
159
|
+
RELEASE_SEATS.includes(seat as typeof RELEASE_SEATS[number]) ? { ...value, perCycle: PWA_PER_CYCLE } : value])),
|
|
160
|
+
updatedAt: activeUpdatedAt,
|
|
161
|
+
};
|
|
162
|
+
writeTrafficBoard(path, updated, 'release-pwa-policy');
|
|
163
|
+
return { changed: true, previous };
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function restorePwaTraffic(path: string, previous: PwaSnapshot): { restored: boolean; reason?: string } {
|
|
168
|
+
return withTrafficLock(path, () => {
|
|
169
|
+
const board = JSON.parse(readFileSync(path, 'utf8')) as TrafficBoard;
|
|
170
|
+
if (!board || typeof board !== 'object' || Array.isArray(board) || !board.seats || typeof board.seats !== 'object') {
|
|
171
|
+
throw new Error(`invalid traffic board while restoring PWA policy: ${path}`);
|
|
172
|
+
}
|
|
173
|
+
// If another writer removed/replaced our marker, that writer owns the current policy.
|
|
174
|
+
if (board.heavyLocalBan !== true) return { restored: false, reason: 'heavyLocalBan-changed' };
|
|
175
|
+
const seats = { ...board.seats };
|
|
176
|
+
for (const seat of RELEASE_SEATS) {
|
|
177
|
+
const current = seats[seat];
|
|
178
|
+
const snapshot = previous.perCycle[seat];
|
|
179
|
+
if (!current || current.perCycle !== PWA_PER_CYCLE) continue;
|
|
180
|
+
const restored = { ...current };
|
|
181
|
+
if (snapshot?.hadValue) restored.perCycle = snapshot.value;
|
|
182
|
+
else delete restored.perCycle;
|
|
183
|
+
seats[seat] = restored;
|
|
184
|
+
}
|
|
185
|
+
const updated: TrafficBoard = { ...board, seats };
|
|
186
|
+
if (previous.hadHeavyLocalBan) updated.heavyLocalBan = previous.heavyLocalBan;
|
|
187
|
+
else delete updated.heavyLocalBan;
|
|
188
|
+
if (updated.reason === 'release PWA node running (heavy-local banned)') {
|
|
189
|
+
if (previous.hadReason) updated.reason = previous.reason;
|
|
190
|
+
else delete updated.reason;
|
|
191
|
+
}
|
|
192
|
+
if (board.updatedAt === previous.activeUpdatedAt) {
|
|
193
|
+
if (previous.hadUpdatedAt) updated.updatedAt = previous.updatedAt;
|
|
194
|
+
else delete updated.updatedAt;
|
|
195
|
+
}
|
|
196
|
+
writeTrafficBoard(path, updated, 'release-pwa-restore');
|
|
197
|
+
return { restored: true };
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Apply the release PWA node's temporary half-traffic and heavy-local marker, restoring on every exit. */
|
|
202
|
+
export async function runWithReleasePwaTraffic<T>(env: NodeJS.ProcessEnv, run: () => Promise<T>): Promise<T> {
|
|
203
|
+
if (!releasePwaContext(env)) return run();
|
|
204
|
+
const path = trafficPath(env);
|
|
205
|
+
let started: PwaPatchResult;
|
|
206
|
+
try { started = patchPwaTraffic(path); }
|
|
207
|
+
catch (error) {
|
|
208
|
+
debug.log('release-loop.pwa', 'traffic policy set failed', { path, error: String(error) }, { level: 'warn' });
|
|
209
|
+
throw new Error(`release PWA traffic policy could not be applied: ${String(error)}`, { cause: error });
|
|
210
|
+
}
|
|
211
|
+
if (started.warning) debug.log('release-loop.pwa', 'traffic policy verification failed', { path, error: started.warning }, { level: 'warn' });
|
|
212
|
+
let restoreResult: Promise<void> | undefined;
|
|
213
|
+
const restoreOnce = () => restoreResult ??= Promise.resolve().then(() => {
|
|
214
|
+
try {
|
|
215
|
+
const restored = restorePwaTraffic(path, started.previous);
|
|
216
|
+
if (!restored.restored) debug.log('release-loop.pwa', 'traffic policy restore skipped', { path, reason: restored.reason }, { level: 'warn' });
|
|
217
|
+
} catch (error) { debug.log('release-loop.pwa', 'traffic policy restore failed', { path, error: String(error) }, { level: 'warn' }); }
|
|
218
|
+
});
|
|
219
|
+
const interrupt = (signal: NodeJS.Signals) => {
|
|
220
|
+
void restoreOnce().finally(() => {
|
|
221
|
+
process.removeListener(signal, signal === 'SIGINT' ? onSigint : onSigterm);
|
|
222
|
+
process.kill(process.pid, signal);
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
const onSigint = () => interrupt('SIGINT');
|
|
226
|
+
const onSigterm = () => interrupt('SIGTERM');
|
|
227
|
+
process.once('SIGINT', onSigint);
|
|
228
|
+
process.once('SIGTERM', onSigterm);
|
|
229
|
+
try { return await run(); }
|
|
230
|
+
finally {
|
|
231
|
+
process.removeListener('SIGINT', onSigint);
|
|
232
|
+
process.removeListener('SIGTERM', onSigterm);
|
|
233
|
+
await restoreOnce();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
89
237
|
/** Hold dispatcher traffic at gate only for the release-loop's gate node, then restore conditionally. */
|
|
90
238
|
export async function runWithReleaseGateTraffic<T>(env: NodeJS.ProcessEnv, run: () => Promise<T>): Promise<T> {
|
|
91
239
|
if (!releaseGateContext(env)) return run();
|
|
@@ -3,6 +3,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
3
3
|
import { isReleaseVersion } from './release-version.js';
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
5
|
import { debug } from '../../src/debug/log.js';
|
|
6
|
+
import { runWithReleasePwaTraffic } from './gate-traffic-mode.js';
|
|
6
7
|
|
|
7
8
|
export type NodeOutcome = 'ok' | 'fail' | 'error';
|
|
8
9
|
export type NodeVerdict = 'pass' | 'flaky' | 'fail';
|
|
@@ -86,30 +87,42 @@ export function pwaNodeArgs(commit: string, input: Record<string, unknown>): str
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
// Existing cutoff/PWA/TUI programs have different output conventions; normalize them at the graph boundary.
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
export async function runNodeVerdict(argv: string[], env: NodeJS.ProcessEnv = process.env,
|
|
91
|
+
runner: CommandRunner = runCommand): Promise<number> {
|
|
92
|
+
const node = argv[0];
|
|
91
93
|
let version = '';
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
: run.status === 0 &&
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
94
|
+
const execute = async (): Promise<number> => {
|
|
95
|
+
let result: Record<string, unknown> & { outcome: NodeOutcome; verdict: NodeVerdict; summary: string };
|
|
96
|
+
try {
|
|
97
|
+
const context = readGraphContext(env);
|
|
98
|
+
version = context.input.version;
|
|
99
|
+
const commit = nodeOutput(context, 'version-release', 'commit');
|
|
100
|
+
const predecessor = node === 'pwa' ? 'gate' : node === 'tui' ? 'upgrade' : 'version-release';
|
|
101
|
+
if (context.outputs[predecessor]?.outcome !== 'ok') throw new Error(`${predecessor} did not pass`);
|
|
102
|
+
const args = node === 'cutoff' ? ['scripts/release-loop/cutoff.ts', '--version', version, '--cutoff', commit, '--baseline', `v${context.input.previousVersion}`, '--json']
|
|
103
|
+
: node === 'pwa' ? pwaNodeArgs(commit, context.input)
|
|
104
|
+
: node === 'tui' ? ['scripts/release-loop/tui-sim-node.ts', '--commit', commit, '--json'] : (() => { throw new Error(`unknown node: ${node}`); })();
|
|
105
|
+
const run = runner('bun', args);
|
|
106
|
+
if (run.stderr) process.stderr.write(run.stderr);
|
|
107
|
+
const data = lastResult(run);
|
|
108
|
+
const verdict = data?.verdict;
|
|
109
|
+
const baseline = data?.baseline as { sha?: string } | undefined;
|
|
110
|
+
const outcome: NodeOutcome = node === 'cutoff' ? run.status === 0 && data?.version === version && (data?.cutoff as { sha?: string } | undefined)?.sha === commit && typeof baseline?.sha === 'string' ? 'ok' : 'error'
|
|
111
|
+
: run.status === 0 && (verdict === 'pass' || verdict === 'flaky') ? 'ok' : run.status === 1 ? 'fail' : 'error';
|
|
112
|
+
if (node === 'cutoff' && outcome === 'ok' && cutoffNotesEmpty(data)) {
|
|
113
|
+
result = { ...data, outcome: 'fail', verdict: 'fail', summary: 'cutoff notes-empty — 사용자에게 보일 변경 줄이 0이다(release/next.md 와 PR 노트 조각을 확인하라)' };
|
|
114
|
+
} else result = { ...data, outcome, verdict: outcome === 'ok' ? verdict === 'flaky' ? 'flaky' : 'pass' : 'fail',
|
|
115
|
+
summary: outcome === 'ok' ? `${node} ${verdict === 'flaky' ? 'flaky' : 'pass'}` : `${node} ${data?.error || run.stderr.trim() || data?.verdict || 'failed'} (rc=${run.status})` };
|
|
116
|
+
} catch (error) { result = errorResult(error); }
|
|
117
|
+
return finishNode(node ?? 'unknown', version, result);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (node !== 'pwa') return execute();
|
|
121
|
+
try { return await runWithReleasePwaTraffic(env, execute); }
|
|
122
|
+
catch (error) {
|
|
123
|
+
try { version = readGraphContext(env).input.version; } catch { /* preserve empty version when context itself is invalid */ }
|
|
124
|
+
return finishNode(node, version, errorResult(error));
|
|
125
|
+
}
|
|
115
126
|
}
|
|
127
|
+
|
|
128
|
+
if (import.meta.main) process.exitCode = await runNodeVerdict(process.argv.slice(2), process.env);
|