elanous 0.2.37 → 0.2.38
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/{8kITumG9GAZ1LwT9POrAR → RrjXTwz-urRAbChP1bX02}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{2002-a43a3f380df873ec.js → 1026-44010f405704050c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7424.c0fe2a595d4232b5.js → 1632.7d87f998e714bede.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4937-28d437999b77c9dd.js → 1923-5c1695332240246a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2104.70e086d5174a4736.js +1 -0
- package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/214.186a430da28338f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +93 -0
- package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{3199.6a1a9b0e6cd01dc6.js → 2415.738b777642a02233.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1262.a89bb27a5b2f844e.js → 3086.fb14b60ce98b53d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3124-561f7f037943cde2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +24 -0
- package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2283.edd9627b7bdfb546.js → 3291.f6c1005387d514bd.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +36 -0
- package/apps/pwa/out/_next/static/chunks/{6877-84d47b7e85d0c41e.js → 3341-f09b9efbd7a82f70.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3389.012c70767a872f9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +287 -0
- package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6680.6a693c28aae4cd19.js → 3528.e4b8ab70d339f965.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +94 -0
- package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4199-f7c6eb40cd0322f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +32 -0
- package/apps/pwa/out/_next/static/chunks/{4036.d54e55e1ebf5acdc.js → 4804.6012b5cd33c22792.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +79 -0
- package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +74 -0
- package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6819.be5c9c4d67f22922.js → 5203.51e5d8b5f1dcfe53.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{757-4fbaf50942b8c424.js → 5255-1b60d50eabe9f057.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +286 -0
- package/apps/pwa/out/_next/static/chunks/5300.677c97e0393742e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7867.e8725745ddd1d940.js → 5307.d7328020227880c2.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +1 -0
- package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +8 -0
- package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5046.7fd5cbd859299448.js → 5638.107314dc479e2f37.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6142.913b44e260a3f925.js +64 -0
- package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6493.27ad77de7aac7966.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1224.2ceca6c180d5130b.js → 7000.28157393caaf5343.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6745-5a63492933959dd9.js → 7161-8f22db3ec5ed005e.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/{2479-fe7e188bd9402d14.js → 7311-60a7ea80d23c7521.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5952.294fc574a8625184.js → 7488.966b116789f3d41a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7524.ce973525d30b3d52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7573.2376e13c9c21f1f8.js +77 -0
- package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6252.240578f10952e091.js → 7628.6b16863ac3972333.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1746.3563617ea64023a3.js → 7954.4f7a8832663ee979.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4931.95cc6dbd230774ca.js → 8019.21ca554a821dad01.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2375.bf364c85dbb6f085.js → 8487.291666583bebe640.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4383.18e5c54db3a22c88.js → 8879.ac06755bcf3e44a1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{9151.a2c51e93f9156dae.js → 8911.bf5e712acb25fb6c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{58edce7d.f1b0c72acf13b123.js → 8994d93e.33ba491f95345fc2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5f6af561-f90b7a67f4f75d13.js → 89d6c04f-6b8ab341748e468a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9223.cbcc88e0e9950426.js +41 -0
- package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +226 -0
- package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +24 -0
- package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9581.da63ddf41acfad46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +2 -0
- package/apps/pwa/out/_next/static/chunks/{de8446d3.31b73eb239fecd7b.js → 9fe1b809.738da85c8b17e3fb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-866aac5ff226a7f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-5c112c6d7c33656e.js → page-c348a3959c5b5fc6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-88a1edbff62eaf1d.js → page-d1d1210f99f620ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-291ab19fe2acb173.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-b9af2ac1c4737ce8.js → page-7f569e533ddeecba.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-fb9e639c107f5f72.js → page-a9d9a3e66459c1e0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-6b140f77593576eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{709a918c.4fd3b4e2a32cb4e3.js → b7459770.6539b11860eb8a9c.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/{d9815f84.4918f682a45a8502.js → b7478fef.5917716286213fcc.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/{4337e36d.f42c3b391b3d8f20.js → bb6987f9.3647e1a23b0e3217.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/de8b930d.d94c0f42acf807fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8c58d09b.e8724a299658d86f.js → eaad0cba.771200940c529df7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.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/graphs/release/recipes.yaml +9 -0
- package/graphs/release/release-loop.yaml +26 -18
- package/package.json +1 -1
- package/scripts/ask-op-inbox-once.ts +17 -0
- package/scripts/brand/check.ts +13 -4
- package/scripts/gate-0226-accepted-remaining-90.txt +91 -0
- package/scripts/gate-0226-failure-census.ts +39 -0
- package/scripts/intake-collect-telegram-saved-stage.ts +48 -0
- package/scripts/provision-export-redactions.ts +15 -0
- package/scripts/public-copy-lint.ts +122 -0
- package/scripts/public-export.ts +69 -2
- package/scripts/ratchets/next-tui-entry-budget.ts +124 -0
- package/scripts/ratchets/train-side-invariants.sh +7 -1
- package/scripts/release-loop/cut-branch.ts +61 -12
- package/scripts/release-loop/gate-node.ts +161 -25
- package/scripts/release-loop/gate-partial.ts +82 -3
- package/scripts/release-loop/gate-prewarm-plan.ts +35 -0
- package/scripts/release-loop/gate-radius-live.ts +4 -2
- package/scripts/release-loop/resume-cap.ts +2 -1
- package/scripts/release-loop/resume-release.ts +297 -8
- package/scripts/release-loop/resume.ts +13 -5
- package/scripts/release-loop/traffic-node.ts +12 -4
- package/scripts/release-loop/traffic-transition.ts +53 -13
- package/scripts/release-loop/version-node.ts +29 -4
- package/scripts/ux-sim.ts +1 -1
- package/scripts/video-pipeline.ts +2 -2
- package/src/acp/core-turn-bridge.ts +6 -1
- package/src/agent/run-context.ts +1 -1
- package/src/agent/task-notification.ts +3 -1
- package/src/agent-substrate/review-observation.ts +10 -0
- package/src/agent-substrate/self-review-cli.ts +195 -27
- package/src/auto-research/question-ladder-contract.ts +137 -0
- package/src/auto-research/research-turn.ts +134 -0
- package/src/autopilot/discovery/doc-lint.ts +1 -1
- package/src/bots/screen-probe.ts +1 -1
- package/src/capture/tui-self-report.ts +10 -2
- package/src/chat/index.ts +1 -0
- package/src/chat/tool-render/dispatch.ts +8 -1
- package/src/cli/guardian-cli.ts +3 -1
- package/src/cli/intake-cli.ts +48 -12
- package/src/cli/lesson-cli.ts +69 -9
- package/src/cli/pr-land-overlap-evidence.ts +38 -5
- package/src/cli/pr-land-refusal.ts +1 -1
- package/src/cli/pty-takeover-cli.ts +58 -5
- package/src/cli/release-checklist-read-cli.ts +5 -2
- package/src/cli/release-cli.ts +47 -9
- package/src/cli/review-carry.ts +77 -0
- package/src/cli/self-update.ts +1 -1
- package/src/cli/tasks-card-show.ts +31 -1
- package/src/cli/tasks-cli.ts +62 -23
- package/src/cli/tasks-read-cli.ts +13 -5
- package/src/context-bus/context-now.ts +86 -2
- package/src/core-turn/run-core-turn.ts +9 -2
- package/src/core-turn/types.ts +3 -0
- package/src/dashboard/index.ts +18 -17
- package/src/dashboard/input/terminal-modal-router.ts +8 -159
- package/src/dashboard/side-basic-runtime.ts +61 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/dashboard/term-slash-runtime.ts +2 -0
- package/src/dashboard/turn-preamble.ts +28 -4
- package/src/domains/guardian-tick.ts +9 -0
- package/src/git-fs/watcher.ts +27 -22
- package/src/git-fs/worktree.ts +19 -3
- package/src/graph-runner/graph-grow.ts +37 -9
- package/src/graph-runner/runner.ts +17 -2
- package/src/grok/credential.ts +107 -36
- package/src/hq/fence-wrapper.ts +1 -1
- package/src/hq/transfer.ts +1 -1
- package/src/index.ts +44 -14
- package/src/intake-plane/collect-telegram-saved.ts +54 -5
- package/src/interactive-terminal-modal.ts +3 -0
- package/src/keybindings.ts +17 -22
- package/src/llm/provider-credentials.ts +1 -1
- package/src/llm-tool-schema.ts +20 -0
- package/src/llm.ts +81 -11
- package/src/loops/orchestrator/inbox-once.ts +28 -0
- package/src/loops/orchestrator/inbox.ts +130 -1
- package/src/loops/orchestrator/tick.ts +6 -3
- package/src/maturity/feature-maturity.ts +1 -0
- package/src/nexus/api/channel-ask-op.ts +96 -0
- package/src/nexus/api/discord-trigger-bot.ts +28 -1
- package/src/nexus/api/telegram-trigger-bot.ts +24 -0
- package/src/nexus/api/terminals.ts +28 -6
- package/src/patents/ledger.ts +18 -1
- package/src/preview/terminal.ts +4 -2
- package/src/prompt-cache/anthropic.ts +2 -0
- package/src/prompt-enhance/enhance.ts +5 -0
- package/src/pty-shell/pty-manifest.ts +16 -2
- package/src/release-loop/gate-shards.ts +61 -0
- package/src/release-loop/light-rc.ts +7 -4
- package/src/release-loop/rehearsal.ts +1 -1
- package/src/repl/index.ts +10 -0
- package/src/research/axis-fanout.ts +133 -0
- package/src/research/edge-decision.ts +88 -0
- package/src/research/output-router.ts +127 -0
- package/src/research-bridge/format.ts +16 -1
- package/src/research-bridge/index.ts +1 -0
- package/src/research-bridge/invoke.ts +41 -5
- package/src/self-dev/decompose.ts +45 -9
- package/src/self-dev/draft-residue.ts +221 -2
- package/src/self-dev/draft-sweep.ts +29 -16
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/run-supervisor.ts +22 -7
- package/src/self-dev/shard-run.ts +260 -3
- package/src/self-implement/comment-only-source.ts +54 -0
- package/src/self-implement/doctor-visit.ts +25 -0
- package/src/self-implement/gate-artifacts.ts +2 -2
- package/src/self-implement/gate-baseline.ts +44 -9
- package/src/self-implement/gate-cli.ts +151 -86
- package/src/self-implement/gate-failure-signature.ts +44 -0
- package/src/self-implement/gate-remote.ts +2 -0
- package/src/self-implement/goal-author.ts +74 -3
- package/src/self-implement/goal-doc/section.ts +22 -0
- package/src/self-implement/goal-supervisor-fill.ts +38 -4
- package/src/self-implement/knowhow-pack.ts +120 -7
- package/src/self-implement/l8-merge-queue.ts +198 -32
- package/src/self-implement/off-diff-evidence.ts +23 -0
- package/src/self-implement/orchestrator.ts +55 -12
- package/src/self-implement/pod-inplace-worktree.ts +82 -0
- package/src/self-implement/pod-warm-pool-profile.ts +2 -2
- package/src/self-implement/premise-review.ts +7 -5
- package/src/self-implement/run-ledger.ts +258 -0
- package/src/self-implement/seams.ts +33 -10
- package/src/self-implement/self-implement-cli.ts +26 -2
- package/src/session/channel-ask-op-envelope.ts +36 -0
- package/src/session/channel-op-reply-route.ts +200 -0
- package/src/session/chat.ts +3 -0
- package/src/session/index.ts +2 -0
- package/src/session-runtime/index.ts +9 -22
- package/src/skills/runner.ts +7 -3
- package/src/skills/tools/dev-harness.ts +2 -2
- package/src/skills/tools/schedule-wakeup.ts +1 -6
- package/src/status/bar.ts +3 -3
- package/src/storage/file-lock.ts +17 -4
- package/src/task-agent/adopt-pr.ts +63 -2
- package/src/task-agent/evidence-command-normalize.ts +42 -0
- package/src/task-agent/export-redactions-provision.ts +43 -0
- package/src/task-agent/parent-host.ts +59 -12
- package/src/task-agent/repair-cycle.ts +67 -8
- package/src/task-agent/repair-worker.ts +37 -1
- package/src/task-agent/task-hand.ts +98 -10
- package/src/task-agent/tier-split.ts +37 -3
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +31 -4
- package/src/task-orchestrator/surfaces/pod-pool.ts +27 -4
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +126 -48
- package/src/task-orchestrator/surfaces/self-implement.ts +33 -2
- package/src/terminal/session-registry.ts +1 -0
- package/src/terminal/terminal-modal-router.ts +156 -0
- package/src/tool-hints/gate.ts +6 -4
- package/src/tool-hints/signals.ts +33 -1
- package/src/tui/chat/background-job-status.ts +8 -2
- package/src/tui/entry.ts +469 -31
- package/src/tui/sidetab/attach.ts +304 -0
- package/src/tui/sidetab/list.ts +176 -0
- package/src/tui/sidetab/nl-tool.ts +136 -0
- package/src/tui/slash/resume-command.ts +23 -0
- package/src/tui/slash/resume.ts +3 -23
- package/src/tui/slash/term-next.ts +2 -1
- package/src/tui-client/local-chat-turn.ts +29 -8
- package/src/user-config.ts +11 -2
- package/src/ux-sim/fake-llm.ts +11 -5
- package/src/ux-sim/next-tui-seat-scenarios.ts +53 -1
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1041-5841aae848fb6403.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1087.3177e54f672dc526.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105.efe1e13503ac7a33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1417-1768233ac92f8a5d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1612.81bbc19af06af16c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1766.728d42053070bb2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1773.d38e945481e04125.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1974-c12f9600da3d3c26.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1ed5e8af.cf424815c7c0efad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2061.7232dc9dc058417f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/210.90b2cbd64926cf34.js +0 -32
- package/apps/pwa/out/_next/static/chunks/2318-450e75d07605b8fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/244-a810d282a20f1783.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2514.03b60f266531a015.js +0 -148
- package/apps/pwa/out/_next/static/chunks/2526-a657538869888604.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2533-6f0341ff075809e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2676.e7994c695ef0f30d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2734.3ffdf54254fb6f99.js +0 -27
- package/apps/pwa/out/_next/static/chunks/2770-23252bf3a37880b9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2969.66b8b545139d1544.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3128.a9210d08800ea6a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3212.12179fd3eee8cef1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3578.4e9f9948a9a6f2f4.js +0 -2
- package/apps/pwa/out/_next/static/chunks/3760-2909ddb9279281cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3825.1921b5901bf33dce.js +0 -36
- package/apps/pwa/out/_next/static/chunks/3994-63eb884ed0547011.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4115.5b438b99f0ae729f.js +0 -226
- package/apps/pwa/out/_next/static/chunks/4269.d8f9f34bb0fc6c43.js +0 -287
- package/apps/pwa/out/_next/static/chunks/431-4eed5bd2c5fe1719.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4313.76f232c304a15bde.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4324.c99ba3e83eb1b691.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4599-96eff3f2a9c242c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4725-34bed25720cc326b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4825-a9d9d4e9f5892309.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4833-7934c592c5569f00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4879-68518f547a207aff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4896.f9b70285363b27b2.js +0 -24
- package/apps/pwa/out/_next/static/chunks/49aa54ab.80f39398e0ed0f30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5056.482687319b82638e.js +0 -104
- package/apps/pwa/out/_next/static/chunks/506.16b83d56a437a9ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5260-20593b0a25d0afbc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5404.cc4e9fec26fd5925.js +0 -1
- package/apps/pwa/out/_next/static/chunks/576.485e83e3f3dec19a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5832-a6be14e663f65726.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5854-56648a9fe646b9a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5925.3b1f8b135710249b.js +0 -93
- package/apps/pwa/out/_next/static/chunks/6168.17749aa3905fa073.js +0 -74
- package/apps/pwa/out/_next/static/chunks/6416.5d50b8ec9986ca98.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6758.e7fbf532953c0783.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6795.0cb8a59730d99703.js +0 -149
- package/apps/pwa/out/_next/static/chunks/6971-fb5f9dc0a2cb5d2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/717.522b08685979b814.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7191.e41eb8f455c7f089.js +0 -41
- package/apps/pwa/out/_next/static/chunks/7233.8e744b7513119409.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7238-97c2226f80504bd8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7285.307963afcf7e7660.js +0 -24
- package/apps/pwa/out/_next/static/chunks/7299-fbe4b8c47a87614a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7314.df0d95930b0c3f50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7406.c0589fdf2ee66629.js +0 -1
- package/apps/pwa/out/_next/static/chunks/752-90e3e7daad3d8e39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7524-bcd3d9928c6842d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7549.9a4de3bf231cfc52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/765.76ae970ceb597b5b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7893-e444a1b0809cd1e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7956-33748cabb3a05562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7c5ed21f-f286708e57cf88f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8030-aa66b478b53dbfca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8051.c88c07dacbbc5f21.js +0 -8
- package/apps/pwa/out/_next/static/chunks/8080-40d781e7704d3671.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8149.80e402d4345e8dae.js +0 -77
- package/apps/pwa/out/_next/static/chunks/8205.50a5268f4e12ba0b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8231.a128feaa6f8b6079.js +0 -1
- package/apps/pwa/out/_next/static/chunks/85.4ddb1dda2ea3ffa2.js +0 -286
- package/apps/pwa/out/_next/static/chunks/8537.26517a09e8247beb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8595-e9b7081ab773a61f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8821-30cc65a61a60951f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8905.b3385aadb7b133e6.js +0 -79
- package/apps/pwa/out/_next/static/chunks/8983.246bb3a1ac38f50f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9072.18342f5941a1f3ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9123.9df04866b2d6e00f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9175-7d383df15a82e2be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9181-bf89c07ce5b8f599.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-429bbc4504b45f71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9207.625edf45d656151c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9378.d2575c6ffd6dd756.js +0 -94
- package/apps/pwa/out/_next/static/chunks/957.db5b5c4d32a35240.js +0 -1
- package/apps/pwa/out/_next/static/chunks/958.53a8dcf4729e6997.js +0 -64
- package/apps/pwa/out/_next/static/chunks/9647.8f0cd0898c592a29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9665-26abb02665704c2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9703.e7d81b6faef1050a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9882-7a9164b3c2dacbe5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9998.b74e033efadecd10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c604fb980a634d88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ec2d8a4979ca9075.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cb6add08ad3c7660.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-e2eaa321ec223fe6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5b8a09c475b5f844.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-91a77daedfbac191.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-f37fc5e974bda39b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-91429a0b78415a94.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-b95798b58263b910.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-763d53e175e17aa1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-d55023204055b12f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-89d2140d78abc810.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-82dc069abfe27f55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-25d9eee7beb552c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-770b4594c713ee97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c604fb980a634d88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ba8e0dcae4f83985.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-09788e1aa9a87db6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-3337c6a1328c2674.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-89acd5d68c0bc560.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5c13e2c76371d797.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-653a9f18642825c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-fe986fe79b107546.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-1e5da297557d2354.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-91ea9f5869a40943.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-7824e21b999962e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-53d00a60cc75a922.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4e931248b3f9574b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-443364c9e4a23b31.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-25ab9c614c14129b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-dbcdcfac0a395a8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-bf1de6645089ec58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c4201761a5e839c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-c304f469ea5d70e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8e5a238cf5e50c9c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c604fb980a634d88.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-32b526069ee50b3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-84220c776608cedc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-e475618cbfd5ca2b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-ac2c1e6169cfce57.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-fccd3020af2f8806.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-9c0d7198ff7b5a91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-d54b1ec9b9c6430d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8c4bef1791d732ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-46fcc0b0bbc6fbfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c1c80e345fa9d6f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7dfda00afd172f5b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f21fd540.63ac75328926ed30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-77fdcc873519a6cd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-d0d620d2ce235713.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-e0ef79c79337ba90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-84b1bc71e3f9ce6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-2acb61f1363128cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-462ff5727ceb7824.js +0 -1
- package/scripts/migrate-monad-to-elanous.ts +0 -195
- package/scripts/rebrand-elanous.ts +0 -242
- /package/apps/pwa/out/_next/static/{8kITumG9GAZ1LwT9POrAR → RrjXTwz-urRAbChP1bX02}/_ssgManifest.js +0 -0
|
@@ -3,23 +3,32 @@
|
|
|
3
3
|
// every later node reads the repaired commit without anyone hand-editing the run state.
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
import { createHash } from 'node:crypto';
|
|
6
|
-
import { accessSync, closeSync, constants, copyFileSync, existsSync, fstatSync, openSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
7
|
-
import {
|
|
6
|
+
import { accessSync, closeSync, constants, copyFileSync, existsSync, fstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, realpathSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { dirname, join, resolve } from 'node:path';
|
|
8
9
|
import { debug } from '../../src/debug/log.js';
|
|
9
10
|
import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
|
|
10
11
|
import { lastJsonObject, runGraph, verifyReleaseCommandRoot, verifyReleaseRecipeSnapshot, type GraphRunState } from '../../src/graph-runner/runner.js';
|
|
11
12
|
import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
|
|
12
13
|
import { releaseRunUniverse } from './release-universe.js';
|
|
13
14
|
import { isReleaseVersion } from './release-version.js';
|
|
14
|
-
import { changedFilesBetween, clearPartialPlan, planPartialRegate, planStalledShardRecheck, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
|
|
15
|
+
import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planStalledShardRecheck, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
|
|
15
16
|
import { gitClosureTree, testsReachingChanged, TRACEABLE_RELEASE_JSON, type ClosureTree } from './gate-closure.js';
|
|
16
17
|
import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
|
|
17
18
|
import { reserveReleaseResumeAttempt } from './resume-cap.js';
|
|
19
|
+
import { cutReleaseBranch } from './cut-branch.js';
|
|
18
20
|
import { releaseLoopConfig } from './unattended-release.js';
|
|
19
21
|
import { trafficOptionsFromConfig } from './traffic-transition.js';
|
|
20
22
|
|
|
21
23
|
const GRAPH = join(import.meta.dir, '../../graphs/release/release-loop.yaml');
|
|
22
24
|
|
|
25
|
+
/** Compare existing artifact paths by filesystem identity so macOS /var and /private/var aliases agree. */
|
|
26
|
+
function sameRealPath(left: unknown, right: unknown): boolean {
|
|
27
|
+
if (typeof left !== 'string' || typeof right !== 'string') return false;
|
|
28
|
+
try { return realpathSync(left) === realpathSync(right); }
|
|
29
|
+
catch { return false; }
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
export interface ResumeReleaseDeps {
|
|
24
33
|
root?: string;
|
|
25
34
|
repo?: string;
|
|
@@ -227,7 +236,8 @@ function verifyResumePreflight(run: GraphRunState, opts: ResumeReleaseOptions, d
|
|
|
227
236
|
let owner: { runId?: unknown; commit?: unknown } | undefined;
|
|
228
237
|
let ownerReadable = !existsSync(ownerPath);
|
|
229
238
|
try { owner = JSON.parse(readFileSync(ownerPath, 'utf8')) as typeof owner; ownerReadable = true; } catch { /* old artifacts can be owned by the run ledger */ }
|
|
230
|
-
const ledgerOwns = output
|
|
239
|
+
const ledgerOwns = output !== undefined && sameRealPath(output.out, prepared)
|
|
240
|
+
&& output.commit === currentTip && output.outcome === 'ok';
|
|
231
241
|
const receiptOwns = owner ? owner.runId === opts.runId && owner.commit === currentTip : ownerReadable && ledgerOwns;
|
|
232
242
|
if (!ledgerOwns || manifest?.version !== version || manifest.sourceCommit !== currentTip || !receiptOwns) {
|
|
233
243
|
throw new Error(`prepared artifact is outside the current release run ledger: ${prepared} — remedy: preserve it, compare release.json and .release-node-owner.json with run ${opts.runId}, then resolve ownership before resume`);
|
|
@@ -265,8 +275,8 @@ function verifyResumePreflight(run: GraphRunState, opts: ResumeReleaseOptions, d
|
|
|
265
275
|
const outputOwner = docsRecord?.artifactOwner as { runId?: unknown; attempt?: unknown } | undefined;
|
|
266
276
|
const receiptMatches = receipt?.branch === docsBranch && receipt.owner?.runId === opts.runId
|
|
267
277
|
&& receipt.owner.attempt === outputOwner?.attempt && outputOwner?.runId === opts.runId
|
|
268
|
-
&& typeof receipt.worktree === 'string' && worktree
|
|
269
|
-
&&
|
|
278
|
+
&& typeof receipt.worktree === 'string' && sameRealPath(worktree, receipt.worktree)
|
|
279
|
+
&& docsWorktrees.some((listedWorktree) => sameRealPath(listedWorktree, receipt.worktree));
|
|
270
280
|
if (hasDocsArtifact && !receiptMatches) {
|
|
271
281
|
throw new Error(`docs branch/worktree is outside the current release run ledger: ${docsBranch} — remedy: compare ${receiptPath} with the saved docs node output and resolve ownership before resume`);
|
|
272
282
|
}
|
|
@@ -290,7 +300,7 @@ export type PartialOutcome = { requested: false }
|
|
|
290
300
|
|
|
291
301
|
/** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
|
|
292
302
|
* GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
|
|
293
|
-
function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
|
|
303
|
+
function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean; gateExecutor?: string }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
|
|
294
304
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
295
305
|
// Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
|
|
296
306
|
if (!dryRun) clearPartialPlan(root, tip.version);
|
|
@@ -328,7 +338,7 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
328
338
|
for (const item of stalled) {
|
|
329
339
|
if (!item || typeof item !== 'object' || !Array.isArray((item as { files?: unknown }).files)
|
|
330
340
|
|| !(item as { files: unknown[] }).files.every((file) => typeof file === 'string')
|
|
331
|
-
|| !['incomplete', 'no-output', 'job-failed', 'exit-code', 'unattributed', 'timeout'].includes(String((item as { reason?: unknown }).reason)))
|
|
341
|
+
|| !['incomplete', 'no-output', 'job-failed', 'exit-code', 'unattributed', 'timeout', 'admission-timeout', 'admission-error'].includes(String((item as { reason?: unknown }).reason)))
|
|
332
342
|
return fallback('stalled-shard record is malformed');
|
|
333
343
|
stalledFiles.push(...(item as { files: string[] }).files);
|
|
334
344
|
}
|
|
@@ -387,9 +397,16 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
387
397
|
const jsonClosure = testsReachingChanged(tree, [TRACEABLE_RELEASE_JSON], tests, { cliEntries: CLI_ENTRIES });
|
|
388
398
|
if (!jsonClosure.ok || jsonClosure.reached === 0) return fallback('known-issues JSON reaches no verified test — full gate');
|
|
389
399
|
}
|
|
400
|
+
const priorCutLogs = [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])]
|
|
401
|
+
.map((location) => join(location, 'release', tip.version, 'gate-logs', 'cut'))
|
|
402
|
+
.find((dir) => existsSync(dir));
|
|
403
|
+
const savedInput = saved.input as { gateExecutor?: unknown; gatePodPool?: unknown };
|
|
404
|
+
const podGate = (opts.gateExecutor ?? savedInput.gateExecutor ?? (savedInput.gatePodPool ? 'pod' : undefined)) === 'pod';
|
|
405
|
+
const priorShards = priorCutLogs ? collectExecutedPodShardImages(priorCutLogs, tip.from) : undefined;
|
|
390
406
|
const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: changed,
|
|
391
407
|
priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
|
|
392
408
|
existsAtTip: (file) => tracked.has(file),
|
|
409
|
+
...(podGate ? { priorShards: priorShards ?? [] } : {}),
|
|
393
410
|
closure: (changedCode) => testsReachingChanged(tree, changedCode, tests, { cliEntries: CLI_ENTRIES }) });
|
|
394
411
|
if (!planned.ok) return fallback(planned.reason);
|
|
395
412
|
if (planned.plan.files.length >= tests.length) return fallback(`every test is re-run (${tests.length}) — full gate`);
|
|
@@ -539,3 +556,275 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
539
556
|
deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
|
|
540
557
|
return { tip, partial, state };
|
|
541
558
|
}
|
|
559
|
+
|
|
560
|
+
// RELEASE-RECOVER-ONE-CMD (0.2.36): a failed release run is repaired by three commands that used to be chained by
|
|
561
|
+
// hand (append dry-run → append → resume --from export-check). `release recover` runs them in that order, stops the
|
|
562
|
+
// moment one fails, and leaves every stage's result in the resume-attempts ledger.
|
|
563
|
+
export type RecoverStageName = 'append-dry-run' | 'append' | 'resume';
|
|
564
|
+
const RECOVER_STAGES: readonly RecoverStageName[] = ['append-dry-run', 'append', 'resume'];
|
|
565
|
+
/** The first node that consumes the cut — a moved tip must restart no later than here, so it is the default. */
|
|
566
|
+
const RECOVER_FIRST_NODE = 'export-check';
|
|
567
|
+
|
|
568
|
+
export interface RecoverReleaseOptions {
|
|
569
|
+
runId: string;
|
|
570
|
+
/** main commits that repair the cut — appended to release/<v> as a fast-forward only. */
|
|
571
|
+
pick: string[];
|
|
572
|
+
/** Node the resume restarts at — default `export-check`. */
|
|
573
|
+
from?: string;
|
|
574
|
+
commandRoot?: string;
|
|
575
|
+
partial?: boolean;
|
|
576
|
+
/** The checkout the append runs in (its `origin` remote); default the process cwd. */
|
|
577
|
+
repoRoot?: string;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export interface RecoverStageRecord {
|
|
581
|
+
stage: RecoverStageName; outcome: 'ok' | 'failed'; at: string; branch?: string; commit?: string | null;
|
|
582
|
+
from?: string; gatePlan?: 'not-run' | 'partial' | 'full'; status?: string; reason?: string;
|
|
583
|
+
/** The `--pick` list this recovery appended — a re-entry skips the append only for the same list. */
|
|
584
|
+
pick?: string[];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
export interface RecoverReleaseResult {
|
|
588
|
+
runId: string; version: string; branch: string; fromNode: string; stages: RecoverStageRecord[];
|
|
589
|
+
resume?: { status: string; tip: TipRefresh; partial: PartialOutcome };
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export interface RecoverReleaseDeps extends ResumeReleaseDeps {
|
|
593
|
+
/** `release cut-branch --append` — tests record the plan instead of pushing the branch. */
|
|
594
|
+
cutBranch?: typeof cutReleaseBranch;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/** A recovery that stopped at a stage: the stages after it never ran, and a human decides the next move. */
|
|
598
|
+
export class ReleaseRecoverStopped extends Error {
|
|
599
|
+
constructor(readonly stage: RecoverStageName, readonly stages: readonly RecoverStageRecord[],
|
|
600
|
+
readonly notRun: readonly RecoverStageName[], reason: string) {
|
|
601
|
+
super(`release recover ${stage} 단계에서 멈췄다: ${reason} — ${notRun.length
|
|
602
|
+
? `${notRun.join(' · ')} 단계는 실행하지 않았다` : '뒤에 실행할 단계가 없다'} · 사람이 판단한다(단계 원장: release/<v>/resume-attempts/<runId>.json)`);
|
|
603
|
+
this.name = 'ReleaseRecoverStopped';
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** Append one stage result to the resume-attempts ledger. `attempts[]` keeps its shape — the stage results are added. */
|
|
608
|
+
function recordRecoverStage(entry: { root: string; runId: string; version: string; priorCountKnown: boolean; stage: RecoverStageRecord }): void {
|
|
609
|
+
const path = join(entry.root, 'release', entry.version, 'resume-attempts', `${entry.runId}.json`);
|
|
610
|
+
const saved = existsSync(path)
|
|
611
|
+
? JSON.parse(readFileSync(path, 'utf8')) as { attempts?: unknown; recoveries?: unknown; priorCountKnown?: unknown }
|
|
612
|
+
: undefined;
|
|
613
|
+
if (saved !== undefined && (!saved || typeof saved !== 'object' || Array.isArray(saved) || !Array.isArray(saved.attempts)
|
|
614
|
+
|| (saved.recoveries !== undefined && !Array.isArray(saved.recoveries)))) throw new Error('release resume attempts ledger invalid');
|
|
615
|
+
const next = { ...(saved ?? {}), version: entry.version, runId: entry.runId,
|
|
616
|
+
priorCountKnown: saved?.priorCountKnown ?? entry.priorCountKnown,
|
|
617
|
+
attempts: (saved?.attempts ?? []) as string[],
|
|
618
|
+
recoveries: [...((saved?.recoveries ?? []) as RecoverStageRecord[]), entry.stage] };
|
|
619
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
620
|
+
const temporary = `${path}.${process.pid}.recover.tmp`;
|
|
621
|
+
writeFileSync(temporary, `${JSON.stringify(next)}\n`, { mode: 0o600 });
|
|
622
|
+
renameSync(temporary, path);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Repair a failed release run in one command: append dry-run (plan only) → append (fast-forward release/<v> onto the
|
|
627
|
+
* picked repairs) → resume --from export-check (preflight plan, then the graph). A failing stage stops the command:
|
|
628
|
+
* the later stages never run, and the stage results are already in the ledger for the human to read.
|
|
629
|
+
*/
|
|
630
|
+
export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: RecoverReleaseDeps = {}): Promise<RecoverReleaseResult> {
|
|
631
|
+
if (!Array.isArray(opts.pick) || !opts.pick.length) throw new Error('release recover requires at least one --pick commit');
|
|
632
|
+
const root = releaseRunUniverse({ root: deps.root });
|
|
633
|
+
const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
|
|
634
|
+
const savedRun = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
635
|
+
if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
|
|
636
|
+
// The saved version-release record names what to repair: the release branch and the commit the run cut.
|
|
637
|
+
const versionRecord = savedRun.nodes.find((node) => node.nodeId === 'version-release');
|
|
638
|
+
const recorded = lastJsonObject(versionRecord?.output);
|
|
639
|
+
const version = typeof recorded?.version === 'string' ? recorded.version : '';
|
|
640
|
+
// RELEASE-RECOVER-BASE: a first recover that reached the resume left refreshed-tip lines in the
|
|
641
|
+
// version-release output, and `lastJsonObject` names the CURRENT branch tip. The append builds on the
|
|
642
|
+
// commit the run CUT — an ancestor of the picked repairs on origin/main — so walk to the FIRST JSON
|
|
643
|
+
// object of the output for that original cut. Without this, a second recover with a different --pick
|
|
644
|
+
// fails append-dry-run with «base … is not an ancestor of origin/main» and strands the new repair
|
|
645
|
+
// unpushed while the run looks recovered.
|
|
646
|
+
const firstCutObject = (text: unknown): { version?: unknown; commit?: unknown } | undefined => {
|
|
647
|
+
if (typeof text !== 'string') return undefined;
|
|
648
|
+
for (const line of text.split('\n')) {
|
|
649
|
+
const trimmed = line.trim();
|
|
650
|
+
if (!trimmed.startsWith('{')) continue;
|
|
651
|
+
try {
|
|
652
|
+
const parsed: unknown = JSON.parse(trimmed);
|
|
653
|
+
if (parsed && typeof parsed === 'object' && typeof (parsed as { version?: unknown }).version === 'string') {
|
|
654
|
+
return parsed as { version?: unknown; commit?: unknown };
|
|
655
|
+
}
|
|
656
|
+
} catch { /* not a JSON line — keep walking */ }
|
|
657
|
+
}
|
|
658
|
+
return undefined;
|
|
659
|
+
};
|
|
660
|
+
const originalCut = firstCutObject(versionRecord?.output) ?? recorded;
|
|
661
|
+
const base = typeof originalCut?.commit === 'string' ? originalCut.commit : '';
|
|
662
|
+
if (!versionRecord?.ok || !isReleaseVersion(version) || !/^[0-9a-f]{40}$/i.test(base)) {
|
|
663
|
+
throw new Error(`release run ${opts.runId} has no successful version-release record — nothing to repair`);
|
|
664
|
+
}
|
|
665
|
+
const branch = `release/${version}`;
|
|
666
|
+
const fromNode = opts.from ?? RECOVER_FIRST_NODE;
|
|
667
|
+
// RELEASE-RECOVER-PREFLIGHT (MS-02 review): the append pushes release/<v> on origin, and the resume refuses a
|
|
668
|
+
// bad run only after that push. Every read-only resume rejection is checked here FIRST — a run the resume
|
|
669
|
+
// would refuse never moves the branch. command-root mismatch is gone by construction: recover prepares the
|
|
670
|
+
// tip checkout itself below, and a human-provided --command-root is verified by the plan before the graph.
|
|
671
|
+
if (savedRun.status !== 'failed') {
|
|
672
|
+
throw new Error(`release run ${opts.runId} is ${savedRun.status} — only a failed run is recovered; ${branch} was not moved`);
|
|
673
|
+
}
|
|
674
|
+
if (fromNode === 'version-release') {
|
|
675
|
+
throw new Error(`--from version-release would cut again — recover at a later node; ${branch} was not moved`);
|
|
676
|
+
}
|
|
677
|
+
const restartAt = savedRun.path.indexOf(fromNode);
|
|
678
|
+
if (restartAt < 0 || savedRun.path.lastIndexOf(fromNode) !== restartAt) {
|
|
679
|
+
throw new Error(`--from ${fromNode} is not a node on the saved path of ${opts.runId}; ${branch} was not moved`);
|
|
680
|
+
}
|
|
681
|
+
const consumers = ['export-check', 'gate'].map((node) => savedRun.path.indexOf(node)).filter((index) => index >= 0);
|
|
682
|
+
const firstConsumer = consumers.length ? Math.min(...consumers) : -1;
|
|
683
|
+
if (firstConsumer >= 0 && restartAt > firstConsumer) {
|
|
684
|
+
throw new Error(`--from ${fromNode} is later than ${savedRun.path[firstConsumer]!} — later nodes were built from the old tip; ${branch} was not moved`);
|
|
685
|
+
}
|
|
686
|
+
const savedInput = (savedRun.input ?? {}) as { branchCut?: unknown; version?: unknown };
|
|
687
|
+
if (savedInput.branchCut !== true || savedInput.version !== version || recorded?.branch !== branch) {
|
|
688
|
+
throw new Error(`release run ${opts.runId} was not cut as ${branch} (input.branchCut/version or version-release branch differ); ${branch} was not moved`);
|
|
689
|
+
}
|
|
690
|
+
// The attempt ledger is the resume's own read-only gate — a refused recover consumes no attempt.
|
|
691
|
+
reserveReleaseResumeAttempt(savedRun, root, { now: deps.now, checkOnly: true });
|
|
692
|
+
const now = (): string => (deps.now?.() ?? new Date()).toISOString();
|
|
693
|
+
const stages: RecoverStageRecord[] = [];
|
|
694
|
+
const record = (stage: RecoverStageRecord): void => {
|
|
695
|
+
stages.push(stage);
|
|
696
|
+
recordRecoverStage({ root, runId: opts.runId, version, priorCountKnown: !savedRun.resume, stage });
|
|
697
|
+
};
|
|
698
|
+
const stop = (stage: RecoverStageName, error: unknown): never => {
|
|
699
|
+
throw new ReleaseRecoverStopped(stage, stages, RECOVER_STAGES.slice(RECOVER_STAGES.indexOf(stage) + 1),
|
|
700
|
+
error instanceof Error ? error.message : String(error));
|
|
701
|
+
};
|
|
702
|
+
const reason = (error: unknown): string => error instanceof Error ? error.message : String(error);
|
|
703
|
+
const cutBranch = deps.cutBranch ?? cutReleaseBranch;
|
|
704
|
+
const cut = { version, base, pick: opts.pick, append: true, repoRoot: opts.repoRoot, log: deps.onSource };
|
|
705
|
+
const repoRoot = resolve(opts.repoRoot ?? deps.repo ?? process.cwd());
|
|
706
|
+
// RELEASE-RECOVER-REENTRY (MS-02 review): once the append pushed and the resume did not finish, re-running
|
|
707
|
+
// recover with the same --pick is refused by cut-branch with «pick … is already in release/<v>» and dies at
|
|
708
|
+
// stage 1 with no guidance. The ledger recoveries decide: when their append-ok commit is still inside the
|
|
709
|
+
// advertised branch tip AND its recorded pick list is this --pick (an entry with no pick record is «기록 없음»,
|
|
710
|
+
// not an empty list — it falls back to the safe side and appends again), the repair already landed and this
|
|
711
|
+
// recover continues from the resume — the append stages never re-run.
|
|
712
|
+
/** Same pick list, order-insensitive, full 40-hex object ids only — anything else is «no record». */
|
|
713
|
+
const samePickList = (recorded: unknown): boolean => {
|
|
714
|
+
if (!Array.isArray(recorded) || recorded.length !== opts.pick.length) return false;
|
|
715
|
+
const left = recorded.map((sha) => (typeof sha === 'string' ? sha.toLowerCase() : '')).sort();
|
|
716
|
+
const right = [...opts.pick].map((sha) => sha.toLowerCase()).sort();
|
|
717
|
+
return left.every((sha, index) => /^[0-9a-f]{40}$/.test(sha) && sha === right[index]);
|
|
718
|
+
};
|
|
719
|
+
const ledgerPath = join(root, 'release', version, 'resume-attempts', `${opts.runId}.json`);
|
|
720
|
+
const recoveries: RecoverStageRecord[] = existsSync(ledgerPath)
|
|
721
|
+
? ((JSON.parse(readFileSync(ledgerPath, 'utf8')) as { recoveries?: unknown[] }).recoveries ?? []) as RecoverStageRecord[]
|
|
722
|
+
: [];
|
|
723
|
+
let appendedCommit: string | undefined;
|
|
724
|
+
for (let index = recoveries.length - 1; index >= 0; index -= 1) {
|
|
725
|
+
const entry = recoveries[index]!;
|
|
726
|
+
if (entry.stage === 'append' && entry.outcome === 'ok' && typeof entry.commit === 'string' && samePickList(entry.pick)) {
|
|
727
|
+
appendedCommit = entry.commit;
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
let advertised = '';
|
|
732
|
+
try {
|
|
733
|
+
advertised = (deps.git ?? gitRunner(repoRoot))(['ls-remote', '--heads', 'origin', `refs/heads/${branch}`]).split(/\s+/)[0] ?? '';
|
|
734
|
+
} catch {
|
|
735
|
+
advertised = '';
|
|
736
|
+
}
|
|
737
|
+
const isAncestor = deps.isAncestor ?? ancestorRunner(repoRoot);
|
|
738
|
+
const humanResume = `release resume --run ${opts.runId} --from ${fromNode}`;
|
|
739
|
+
const alreadyInGuidance = (cause: string): string =>
|
|
740
|
+
`${cause} — ${branch}가 이미 고른 수선을 담고 있다: 이 recover는 append하지 않았다. 사람이 직접 이어 간다: ${humanResume}`;
|
|
741
|
+
let tip = '';
|
|
742
|
+
if (appendedCommit !== undefined && /^[0-9a-f]{40}$/i.test(advertised) && isAncestor(appendedCommit, advertised)) {
|
|
743
|
+
tip = advertised;
|
|
744
|
+
deps.onSource?.(`· recover 재진입 · 원장의 append-ok(${appendedCommit.slice(0, 12)})이 가지 끝에 그대로 있다 — append를 다시 하지 않고 ${fromNode} resume부터 이어 간다`);
|
|
745
|
+
} else {
|
|
746
|
+
if (/^[0-9a-f]{40}$/i.test(advertised) && opts.pick.every((sha) => isAncestor(sha, advertised))) {
|
|
747
|
+
// The repair is on the branch already (a hand-run append) and the ledger has no append-ok for it: there is
|
|
748
|
+
// nothing to append — the stop error and the ledger record both name the human's next step.
|
|
749
|
+
const guidance = alreadyInGuidance(`pick ${opts.pick.join(' + ')} is already in ${branch}`);
|
|
750
|
+
record({ stage: 'append-dry-run', outcome: 'failed', at: now(), branch, reason: guidance });
|
|
751
|
+
return stop('append-dry-run', new Error(guidance));
|
|
752
|
+
}
|
|
753
|
+
// 1/3 append dry-run — the fast-forward plan only: no worktree, no push, no run-state write.
|
|
754
|
+
try {
|
|
755
|
+
const planned = cutBranch({ ...cut, dryRun: true });
|
|
756
|
+
record({ stage: 'append-dry-run', outcome: 'ok', at: now(), branch: planned.branch, commit: planned.commit });
|
|
757
|
+
deps.onSource?.(`· recover 1/3 append dry-run · ${planned.branch} ← ${opts.pick.join(' + ')} · 가지·push·런 상태 변경 없음`);
|
|
758
|
+
} catch (error) {
|
|
759
|
+
const raw = reason(error);
|
|
760
|
+
if (!/is already in /.test(raw)) {
|
|
761
|
+
record({ stage: 'append-dry-run', outcome: 'failed', at: now(), branch, reason: raw });
|
|
762
|
+
return stop('append-dry-run', error);
|
|
763
|
+
}
|
|
764
|
+
const guided = alreadyInGuidance(raw);
|
|
765
|
+
record({ stage: 'append-dry-run', outcome: 'failed', at: now(), branch, reason: guided });
|
|
766
|
+
return stop('append-dry-run', new Error(guided));
|
|
767
|
+
}
|
|
768
|
+
// 2/3 append — cherry-pick the repairs onto release/<v> and push the fast-forward (never a force push).
|
|
769
|
+
try {
|
|
770
|
+
const appended = cutBranch(cut);
|
|
771
|
+
if (!appended.commit) throw new Error(`${appended.branch} append produced no commit`);
|
|
772
|
+
tip = appended.commit;
|
|
773
|
+
record({ stage: 'append', outcome: 'ok', at: now(), branch: appended.branch, commit: tip, pick: opts.pick });
|
|
774
|
+
deps.onSource?.(`↻ recover 2/3 append · ${appended.branch} ${base.slice(0, 12)} → ${tip.slice(0, 12)} (fast-forward)`);
|
|
775
|
+
} catch (error) {
|
|
776
|
+
const raw = reason(error);
|
|
777
|
+
if (!/is already in /.test(raw)) {
|
|
778
|
+
record({ stage: 'append', outcome: 'failed', at: now(), branch, reason: raw });
|
|
779
|
+
return stop('append', error);
|
|
780
|
+
}
|
|
781
|
+
const guided = alreadyInGuidance(raw);
|
|
782
|
+
record({ stage: 'append', outcome: 'failed', at: now(), branch, reason: guided });
|
|
783
|
+
return stop('append', new Error(guided));
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// 3/3 resume --from export-check — the read-only preflight first (its plan is the record), then the graph run.
|
|
788
|
+
// The append moved only the origin branch tip: the operator checkout is still wherever it was, and the resume
|
|
789
|
+
// preflight (verifyReleaseCommandRoot) demands a checkout whose HEAD is the new tip. So recover prepares that
|
|
790
|
+
// checkout itself — a temporary worktree at the tip. A human-provided --command-root stays a judgment
|
|
791
|
+
// boundary: the plan verifies it against the tip and stops there on a mismatch.
|
|
792
|
+
const resumeOptions: ResumeReleaseOptions = { runId: opts.runId, from: fromNode,
|
|
793
|
+
...(opts.partial === undefined ? {} : { partial: opts.partial }) };
|
|
794
|
+
let preparedRoot: { dir: string; git: (args: string[]) => string } | undefined;
|
|
795
|
+
if (opts.commandRoot !== undefined) resumeOptions.commandRoot = opts.commandRoot;
|
|
796
|
+
else {
|
|
797
|
+
const temp = mkdtempSync(join(tmpdir(), 'release-recover-'));
|
|
798
|
+
const dir = join(temp, 'command-root');
|
|
799
|
+
const worktreeGit = gitRunner(repoRoot);
|
|
800
|
+
try {
|
|
801
|
+
worktreeGit(['worktree', 'add', '--detach', dir, tip]);
|
|
802
|
+
} catch (error) {
|
|
803
|
+
rmSync(temp, { recursive: true, force: true });
|
|
804
|
+
throw error;
|
|
805
|
+
}
|
|
806
|
+
preparedRoot = { dir, git: worktreeGit };
|
|
807
|
+
resumeOptions.commandRoot = dir;
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
const plan = await planResumeReleaseRun(resumeOptions, deps);
|
|
811
|
+
const resumed = await resumeReleaseRun(resumeOptions, deps);
|
|
812
|
+
record({ stage: 'resume', outcome: 'ok', at: now(), from: fromNode, branch: resumed.tip.branch,
|
|
813
|
+
commit: resumed.tip.to, gatePlan: plan.gatePlan.mode, status: resumed.state.status });
|
|
814
|
+
deps.onSource?.(`▶ recover 3/3 resume ${fromNode}부터 · tip ${resumed.tip.to.slice(0, 12)} · gate ${plan.gatePlan.mode} → ${resumed.state.status}`);
|
|
815
|
+
return { runId: opts.runId, version, branch: resumed.tip.branch, fromNode, stages,
|
|
816
|
+
resume: { status: resumed.state.status, tip: resumed.tip, partial: resumed.partial } };
|
|
817
|
+
} catch (error) {
|
|
818
|
+
record({ stage: 'resume', outcome: 'failed', at: now(), from: fromNode, branch, reason: reason(error) });
|
|
819
|
+
return stop('resume', error);
|
|
820
|
+
} finally {
|
|
821
|
+
if (preparedRoot) {
|
|
822
|
+
try {
|
|
823
|
+
preparedRoot.git(['worktree', 'remove', '--force', preparedRoot.dir]);
|
|
824
|
+
} catch {
|
|
825
|
+
try { preparedRoot.git(['worktree', 'prune']); } catch { /* the temp dir below is still removed */ }
|
|
826
|
+
}
|
|
827
|
+
rmSync(dirname(preparedRoot.dir), { recursive: true, force: true });
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
@@ -53,12 +53,20 @@ function printedVerdict(output: unknown): boolean {
|
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
function
|
|
56
|
+
/** A failed release may execute its warning-only traffic restore before the terminal failure. */
|
|
57
|
+
function failedAfterTrafficRestore(state: GraphRunState, index: number): boolean {
|
|
58
58
|
if (state.nodes.length !== state.path.length || state.path.at(-1) !== 'failed' || state.nodes.at(-1)?.nodeId !== 'failed') return false;
|
|
59
59
|
if (index === state.path.length - 2) return true;
|
|
60
|
-
return index === state.path.length - 3 && state.path[index + 1] === '
|
|
61
|
-
&& state.nodes[index + 1]?.nodeId === '
|
|
60
|
+
return index === state.path.length - 3 && state.path[index + 1] === 'traffic-failed-restore'
|
|
61
|
+
&& state.nodes[index + 1]?.nodeId === 'traffic-failed-restore' && state.nodes[index + 1]?.executed === true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Gate may also pass through its executed triage shadow before the traffic restore. */
|
|
65
|
+
function failedAfterGate(state: GraphRunState, index: number): boolean {
|
|
66
|
+
if (failedAfterTrafficRestore(state, index)) return true;
|
|
67
|
+
return state.path[index + 1] === 'gate-triage'
|
|
68
|
+
&& state.nodes[index + 1]?.nodeId === 'gate-triage' && state.nodes[index + 1]?.executed === true
|
|
69
|
+
&& failedAfterTrafficRestore(state, index + 1);
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
export function gateUnmeasuredDetail(state: GraphRunState, index: number): string | undefined {
|
|
@@ -113,7 +121,7 @@ export function releaseResumeWaiver(
|
|
|
113
121
|
}
|
|
114
122
|
if (!unique || state.path.lastIndexOf(opts.waive) !== index || state.nodes[index]?.nodeId !== opts.waive || state.nodes[index]?.ok !== false ||
|
|
115
123
|
state.nodes[index]?.executed !== true || (opts.waive === 'gate' ? !failedAfterGate(state, index)
|
|
116
|
-
:
|
|
124
|
+
: !failedAfterTrafficRestore(state, index))) {
|
|
117
125
|
throw new Error(`--waive ${opts.waive} requires the failed executable node on the saved path`);
|
|
118
126
|
}
|
|
119
127
|
// Only user-visible, non-publication checks can become accepted regressions; build, safety and approval gates stay blocking.
|
|
@@ -7,8 +7,11 @@ import { errorResult, finishNode, readGraphContext } from './node-verdict.js';
|
|
|
7
7
|
import { applyReleaseTrafficEvent, trafficOptionsFromConfig, type ReleaseTrafficEvent, type TrafficTransitionOptions } from './traffic-transition.js';
|
|
8
8
|
|
|
9
9
|
const EVENTS: Record<string, ReleaseTrafficEvent> = {
|
|
10
|
+
'traffic-cut-start': 'cut-start',
|
|
10
11
|
'traffic-gate-passed': 'gate-passed',
|
|
12
|
+
'traffic-npm-published': 'npm-published',
|
|
11
13
|
'traffic-verify-complete': 'verify-complete',
|
|
14
|
+
'traffic-failed-restore': 'failed',
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
export interface TrafficNodeDeps {
|
|
@@ -40,12 +43,17 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
|
|
|
40
43
|
}
|
|
41
44
|
const result = await applyReleaseTrafficEvent(context, event, options);
|
|
42
45
|
if (!result.applied) {
|
|
43
|
-
const consequence = event === '
|
|
44
|
-
? '
|
|
45
|
-
: '
|
|
46
|
+
const consequence = event === 'failed' && result.reason === 'published-keeps-full'
|
|
47
|
+
? 'npm 발행은 완료됐으므로 전체 트래픽을 유지합니다. 후속 발행 단계 실패를 확인하세요.'
|
|
48
|
+
: event === 'verify-complete' || event === 'npm-published'
|
|
49
|
+
? '발행은 성공했으나 전체 트래픽 전환이 적용되지 않았습니다.'
|
|
50
|
+
: event === 'failed' ? '컷 전 트래픽 복귀가 적용되지 않았습니다.'
|
|
51
|
+
: event === 'cut-start' ? '컷은 시작했으나 gate 모드 전환이 적용되지 않았습니다.'
|
|
52
|
+
: 'gate는 성공했으나 트래픽 감속이 적용되지 않았습니다.';
|
|
46
53
|
(deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version} ${runId || 'unknown'} ${node} 무시 — ${result.reason ?? 'traffic state unchanged'}. ${consequence}`);
|
|
47
54
|
}
|
|
48
|
-
return finishNode(node!, version, { outcome: 'ok'
|
|
55
|
+
return finishNode(node!, version, { outcome: result.applied ? 'ok' : 'fail', verdict: result.applied ? 'pass' : 'fail',
|
|
56
|
+
summary: `${event} ${result.applied ? 'applied' : result.reason ?? 'ignored'}` });
|
|
49
57
|
} catch (error) {
|
|
50
58
|
const detail = errorResult(error).summary;
|
|
51
59
|
const isLockTimeout = detail.startsWith('traffic lock timeout:');
|
|
@@ -4,12 +4,17 @@ import { debug } from '../../src/debug/log.js';
|
|
|
4
4
|
import type { GraphContext } from './node-verdict.js';
|
|
5
5
|
import type { ReleaseLoopConfig } from './unattended-release.js';
|
|
6
6
|
|
|
7
|
-
export type ReleaseTrafficEvent = 'gate-passed' | 'verify-complete';
|
|
7
|
+
export type ReleaseTrafficEvent = 'cut-start' | 'gate-passed' | 'npm-published' | 'verify-complete' | 'failed';
|
|
8
|
+
|
|
9
|
+
export interface TrafficBeforeCut {
|
|
10
|
+
global?: string;
|
|
11
|
+
perCycle: Record<string, unknown>;
|
|
12
|
+
}
|
|
8
13
|
|
|
9
14
|
export interface TrafficFile {
|
|
10
15
|
global?: string;
|
|
11
16
|
seats: Record<string, Record<string, unknown>>;
|
|
12
|
-
releaseLifecycle?: { runId: string; version: string; stage: 1 | 2; event: ReleaseTrafficEvent; startedAt?: string; updatedAt: string };
|
|
17
|
+
releaseLifecycle?: { runId: string; version: string; stage: 0 | 1 | 2 | 3; event: ReleaseTrafficEvent; startedAt?: string; updatedAt: string; beforeCut?: TrafficBeforeCut };
|
|
13
18
|
[key: string]: unknown;
|
|
14
19
|
}
|
|
15
20
|
|
|
@@ -18,7 +23,7 @@ type ReleaseTrafficConfig = ReleaseLoopConfig & {
|
|
|
18
23
|
};
|
|
19
24
|
|
|
20
25
|
const SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
|
|
21
|
-
const STAGE: Record<ReleaseTrafficEvent, 1 | 2> = { 'gate-passed': 1, 'verify-complete': 2 };
|
|
26
|
+
const STAGE: Record<ReleaseTrafficEvent, 0 | 1 | 2 | 3> = { 'cut-start': 0, 'gate-passed': 1, 'npm-published': 2, 'verify-complete': 2, failed: 3 };
|
|
22
27
|
const TRAFFIC_PER_CYCLE = { half: 3, full: 6 } as const;
|
|
23
28
|
|
|
24
29
|
export interface TrafficTransitionOptions {
|
|
@@ -112,32 +117,65 @@ export async function applyReleaseTrafficEvent(
|
|
|
112
117
|
const log = options.log ?? ((name, fields) => debug.log('release-loop.traffic', name, fields));
|
|
113
118
|
const stage = STAGE[event];
|
|
114
119
|
const stamp = (options.now ?? (() => new Date()))().toISOString();
|
|
115
|
-
const rate = event === 'gate-passed' ? TRAFFIC_PER_CYCLE.half
|
|
116
|
-
|
|
120
|
+
const rate = event === 'gate-passed' ? TRAFFIC_PER_CYCLE.half
|
|
121
|
+
: event === 'npm-published' || event === 'verify-complete' ? TRAFFIC_PER_CYCLE.full : undefined;
|
|
117
122
|
const release = () => {
|
|
118
123
|
const unlock = acquireTrafficLock(path, options.lockTimeoutMs);
|
|
119
124
|
try {
|
|
120
125
|
const before = readTrafficFile(path);
|
|
121
126
|
const prior = before.releaseLifecycle;
|
|
127
|
+
if ((event === 'npm-published' || event === 'verify-complete') && context.outputs['npm-publish']?.outcome !== 'ok') {
|
|
128
|
+
return { file: before, applied: false, idempotent: false, reason: 'npm-publish-unconfirmed' };
|
|
129
|
+
}
|
|
122
130
|
const sameRun = prior?.runId === runId;
|
|
123
131
|
const newerVersion = !prior?.runId || compareReleaseVersions(version, prior.version) > 0;
|
|
124
132
|
const sameVersionLaterRun = prior?.version === version && isLaterRun(context.startedAt, prior.startedAt);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
||
|
|
133
|
+
const resumedTrafficNode = sameRun && prior.stage === 3
|
|
134
|
+
&& (event === 'cut-start' && context.nodeId === 'traffic-cut-start'
|
|
135
|
+
|| event === 'gate-passed' && context.nodeId === 'traffic-gate-passed'
|
|
136
|
+
|| event === 'npm-published' && context.nodeId === 'traffic-npm-published'
|
|
137
|
+
|| event === 'verify-complete' && context.nodeId === 'traffic-verify-complete' && context.outputs['npm-publish']?.outcome === 'ok');
|
|
138
|
+
if (sameRun && prior.stage === 3 && event !== 'failed' && !resumedTrafficNode) {
|
|
139
|
+
return { file: before, applied: false, idempotent: false, reason: 'run-already-failed' };
|
|
140
|
+
}
|
|
141
|
+
if (sameRun && prior.stage >= stage && event !== 'failed' && !resumedTrafficNode) return { file: before, applied: true, idempotent: true };
|
|
142
|
+
if (event === 'failed' && (!sameRun || prior?.stage === 2 || prior?.stage === 3 || !prior?.beforeCut)) {
|
|
143
|
+
return { file: before, applied: false, idempotent: false,
|
|
144
|
+
reason: !sameRun ? prior?.runId ? 'different-run-is-current' : 'before-cut-unmeasured'
|
|
145
|
+
: prior?.stage === 2 ? 'published-keeps-full' : prior?.stage === 3 ? 'already-restored' : 'before-cut-unmeasured' };
|
|
146
|
+
}
|
|
147
|
+
const permitted = event === 'failed' ? sameRun
|
|
148
|
+
: sameRun ? resumedTrafficNode || (stage === 0 ? prior.stage === 0 : stage > prior.stage)
|
|
149
|
+
: newerVersion || (prior?.version === version && sameVersionLaterRun && (prior.stage < 2 || prior.stage === 3 || event === 'verify-complete'));
|
|
130
150
|
if (!permitted) {
|
|
131
151
|
return { file: before, applied: false, idempotent: false, reason: prior?.runId && prior.runId !== runId ? 'different-run-is-current' : 'event-out-of-order' };
|
|
132
152
|
}
|
|
153
|
+
if (event === 'cut-start' && prior && prior.stage < 2 && !prior.beforeCut) {
|
|
154
|
+
return { file: before, applied: false, idempotent: false, reason: 'before-cut-unmeasured' };
|
|
155
|
+
}
|
|
156
|
+
const beforeCut = event === 'cut-start' ? {
|
|
157
|
+
...(prior?.stage !== undefined && prior.stage < 2 && prior.beforeCut
|
|
158
|
+
? prior.beforeCut
|
|
159
|
+
: { ...(before.global === undefined ? {} : { global: before.global }),
|
|
160
|
+
perCycle: Object.fromEntries(SEATS.map((seat) => [seat, before.seats[seat]?.perCycle])) }),
|
|
161
|
+
} : prior?.beforeCut;
|
|
162
|
+
const releaseLifecycle = { runId, version, stage, event, ...(context.startedAt ? { startedAt: context.startedAt } : {}),
|
|
163
|
+
updatedAt: stamp, ...(beforeCut ? { beforeCut } : {}) };
|
|
133
164
|
const after: TrafficFile = {
|
|
134
165
|
...before,
|
|
135
|
-
...(event === '
|
|
166
|
+
...(event === 'cut-start' ? { global: 'gate' }
|
|
167
|
+
: event === 'gate-passed' ? { global: 'yellow' }
|
|
168
|
+
: event === 'npm-published' || event === 'verify-complete' ? { global: 'green' }
|
|
169
|
+
: beforeCut?.global === undefined ? {} : { global: beforeCut.global }),
|
|
136
170
|
seats: Object.fromEntries(Object.entries(before.seats).map(([seat, value]) => [
|
|
137
|
-
seat, SEATS.includes(seat as typeof SEATS[number])
|
|
171
|
+
seat, SEATS.includes(seat as typeof SEATS[number])
|
|
172
|
+
? { ...value, ...(event === 'failed' ? { perCycle: beforeCut?.perCycle[seat] } : rate === undefined ? {} : { perCycle: rate }) }
|
|
173
|
+
: value,
|
|
138
174
|
])),
|
|
139
175
|
releaseLifecycle,
|
|
140
176
|
};
|
|
177
|
+
if (event === 'failed' && beforeCut?.global === undefined) delete after.global;
|
|
178
|
+
if (event === 'failed') for (const seat of SEATS) if (beforeCut?.perCycle[seat] === undefined) delete after.seats[seat]?.perCycle;
|
|
141
179
|
atomicWriteTrafficFile(path, after);
|
|
142
180
|
return { file: after, applied: true, idempotent: false };
|
|
143
181
|
} finally { unlock(); }
|
|
@@ -145,7 +183,9 @@ export async function applyReleaseTrafficEvent(
|
|
|
145
183
|
const result = release();
|
|
146
184
|
const reason = result.applied && result.idempotent ? 'already-applied' : result.reason;
|
|
147
185
|
log(result.idempotent ? 'already-applied' : result.applied ? 'applied' : 'ignored', {
|
|
148
|
-
runId, version, event, stage, reason,
|
|
186
|
+
runId, version, event, stage, reason, ...(rate === undefined ? {} : { perCycle: rate }),
|
|
187
|
+
...(event === 'cut-start' ? { global: 'gate' } : event === 'gate-passed' ? { global: 'yellow' }
|
|
188
|
+
: event === 'npm-published' || event === 'verify-complete' ? { global: 'green' } : {}),
|
|
149
189
|
});
|
|
150
190
|
return { applied: result.applied, stage: result.file.releaseLifecycle?.stage ?? 0, ...(reason ? { reason } : {}) };
|
|
151
191
|
}
|
|
@@ -8,6 +8,7 @@ import { debug } from '../../src/debug/log.js';
|
|
|
8
8
|
import { effectiveInstanceRoot, prodInstanceRoot } from '../../src/instance/resolve.js';
|
|
9
9
|
import { landingFreezeMessage, readLandingFreeze, type LandingFreeze } from '../../src/release-loop/landing-freeze.js';
|
|
10
10
|
import { baseVersion, isReleaseVersion, isStableVersion, prereleaseKind } from './release-version.js';
|
|
11
|
+
import { cutReleaseBranch } from './cut-branch.js';
|
|
11
12
|
|
|
12
13
|
type Kind = 'release' | 'dev-bump';
|
|
13
14
|
type Output = { outcome: 'ok' | 'error'; kind: Kind | null; version: string | null; commit: string | null; pr: number | null; files?: string[]; worktree?: string; error?: string;
|
|
@@ -441,19 +442,30 @@ export function main(args: string[] = process.argv.slice(2), repo = process.cwd(
|
|
|
441
442
|
let branchCut = false;
|
|
442
443
|
let runId: string | undefined;
|
|
443
444
|
try {
|
|
444
|
-
if (args[0] !== 'release' && args[0] !== 'dev-bump'
|
|
445
|
-
|
|
445
|
+
if (args[0] !== 'release' && args[0] !== 'dev-bump' && args[0] !== 'cut-branch')
|
|
446
|
+
throw new Error('usage: version-node.ts <release|dev-bump|cut-branch> [--version <v>] [--cut <sha>] [--cut-commit <sha>] [--pick <sha>] [--base <sha>] [--append] [--dry-run] --json');
|
|
447
|
+
kind = args[0] === 'cut-branch' ? 'release' : args[0];
|
|
448
|
+
const picks: string[] = [];
|
|
449
|
+
let base: string | undefined;
|
|
450
|
+
let append = false;
|
|
451
|
+
let dryRun = false;
|
|
452
|
+
let versionRelease: { commit?: unknown; base?: unknown; branch?: unknown } | undefined;
|
|
446
453
|
for (let i = 1; i < args.length; i++) {
|
|
447
454
|
if (args[i] === '--json') continue;
|
|
448
455
|
if (args[i] === '--version' && args[i + 1]) { version = args[++i]!; continue; }
|
|
449
456
|
if (args[i] === '--cut' && args[i + 1]) { cut = args[++i]!; continue; }
|
|
450
457
|
if (args[i] === '--cut-commit' && args[i + 1]) { cutCommit = args[++i]!; continue; }
|
|
458
|
+
if (args[i] === '--pick' && args[i + 1]) { picks.push(args[++i]!); continue; }
|
|
459
|
+
if (args[i] === '--base' && args[i + 1]) { base = args[++i]!; continue; }
|
|
460
|
+
if (args[i] === '--append') { append = true; continue; }
|
|
461
|
+
if (args[i] === '--dry-run') { dryRun = true; continue; }
|
|
451
462
|
throw new Error(`unknown or incomplete argument: ${args[i]}`);
|
|
452
463
|
}
|
|
453
464
|
if (process.env.ELANOUS_GRAPH_CONTEXT) {
|
|
454
465
|
const location = process.env.ELANOUS_GRAPH_CONTEXT;
|
|
455
466
|
const context = JSON.parse(location.trimStart().startsWith('{') ? location : readFileSync(location, 'utf8')) as {
|
|
456
|
-
runId?: unknown; input?: { version?: unknown; cutCommit?: unknown; forceFreeze?: unknown; branchCut?: unknown };
|
|
467
|
+
runId?: unknown; input?: { version?: unknown; cutCommit?: unknown; forceFreeze?: unknown; branchCut?: unknown };
|
|
468
|
+
outputs?: Record<string, { commit?: unknown; base?: unknown; branch?: unknown } | undefined> };
|
|
457
469
|
if (context.runId !== undefined) {
|
|
458
470
|
if (typeof context.runId !== 'string') throw new Error('runId must be a string');
|
|
459
471
|
runId = context.runId;
|
|
@@ -466,11 +478,24 @@ export function main(args: string[] = process.argv.slice(2), repo = process.cwd(
|
|
|
466
478
|
cutCommit = context.input.cutCommit;
|
|
467
479
|
}
|
|
468
480
|
// The cut = the commit version-release landed (what cutoff and the gate measured).
|
|
481
|
+
// RELEASE-APPEND-BASE-AUTO: cut-branch consumes that same record as its ledger seam
|
|
482
|
+
// (ledger: () => versionRelease), so it must not also see it injected as --cut.
|
|
469
483
|
const releasedCommit = context.outputs?.['version-release']?.commit;
|
|
470
|
-
if (!cut && typeof releasedCommit === 'string' && releasedCommit) cut = releasedCommit;
|
|
484
|
+
if (args[0] !== 'cut-branch' && !cut && typeof releasedCommit === 'string' && releasedCommit) cut = releasedCommit;
|
|
485
|
+
// RELEASE-APPEND-BASE-AUTO: the version-release node's record is the ledger seam cut-branch consults.
|
|
486
|
+
versionRelease = context.outputs?.['version-release'];
|
|
471
487
|
}
|
|
472
488
|
if (!version) throw new Error('version required (--version or ELANOUS_GRAPH_CONTEXT.input.version)');
|
|
473
489
|
nextDevVersion(version);
|
|
490
|
+
if (args[0] === 'cut-branch') {
|
|
491
|
+
if (cut !== undefined || cutCommit !== undefined) throw new Error('--cut/--cut-commit are not valid for cut-branch');
|
|
492
|
+
if (!picks.length) throw new Error('at least one --pick commit is required');
|
|
493
|
+
const result = cutReleaseBranch({
|
|
494
|
+
version, pick: picks, ...(base !== undefined ? { base } : {}), append, dryRun,
|
|
495
|
+
repoRoot: resolve(repo), ledger: () => versionRelease,
|
|
496
|
+
});
|
|
497
|
+
return { outcome: 'ok', kind: 'release', version, commit: result.commit, pr: null, branch: result.branch, ...(dryRun ? { skipped: 'dry-run' } : {}) };
|
|
498
|
+
}
|
|
474
499
|
if (kind === 'dev-bump' && cutCommit !== undefined) throw new Error('--cut-commit is only valid for release');
|
|
475
500
|
return landing(resolve(repo), kind, version, cut, cutCommit, forceFreeze, branchCut, undefined, runId);
|
|
476
501
|
} catch (error) {
|