elanous 0.2.38 → 0.2.41
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/README.md +7 -7
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/1115.434ae2ecb0ad986d.js +149 -0
- package/apps/pwa/out/_next/static/chunks/1121.667cec0bcdbb3fdb.js +36 -0
- package/apps/pwa/out/_next/static/chunks/1178.1aa9cad0e7695427.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1197-54da59d250a6e476.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5203.51e5d8b5f1dcfe53.js → 1219.7e17964e0e7cd8e5.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/1245.1cfd13b26dbd71d6.js +287 -0
- package/apps/pwa/out/_next/static/chunks/1494-f93046ff5c5a124d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1609.2b312797599b8e45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1661.0dd330f3d89f73f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/167-0c121e4b47e0f9da.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1700.cdc2bf8cc685e221.js +90 -0
- package/apps/pwa/out/_next/static/chunks/1703-78e46acad29f3cfc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7573.2376e13c9c21f1f8.js → 1765.679b29625c8e88d4.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{3528.e4b8ab70d339f965.js → 1800.f7ff51dc4978a958.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1971-7efb628c471dc5a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1999-8de7b7bb36801913.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2073.689dcb2680113d76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2198-557d49184104d165.js +1 -0
- package/apps/pwa/out/_next/static/chunks/221-440ce67fa9f5c839.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8879.ac06755bcf3e44a1.js → 2263.e09b6edea33bad2d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2427-8ed8e98624cbd664.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2457-725c5827bc003795.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2491-2718b15a5ccb75a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2527-5c636a43c7cc5dde.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2692-1027cb16fe4ccf49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2743.5d3f76948be32b34.js +41 -0
- package/apps/pwa/out/_next/static/chunks/2773.0e80b9d325fb5635.js +32 -0
- package/apps/pwa/out/_next/static/chunks/2873-0f3cb56c8e3528ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9fe1b809.738da85c8b17e3fb.js → 2fdf2931.b431eee4fb468bc3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{eaad0cba.771200940c529df7.js → 2fe2c302.41b517f88d6fe502.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3097.864e92bdaa2f4d15.js +79 -0
- package/apps/pwa/out/_next/static/chunks/3136-d6870ba63300a93e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3173-0289113d434e83b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3230-49298c3b73ef2268.js +1 -0
- package/apps/pwa/out/_next/static/chunks/325-dd9208a1a0ab7ce4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3283.528f430dd043fb4e.js +74 -0
- package/apps/pwa/out/_next/static/chunks/3295.cc8cb13b8f0dae1a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3688-86e3b88abf18cb3b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3804-7220467a5473a8df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3808.3e1e247ce7bdf889.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b7478fef.5917716286213fcc.js → 385e4574.e9886ab32759eda3.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/3927.b5473ad109da44c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3934-98ea94832e194796.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4133.2056bb4d78f621af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4156.ba4de4a3664f04b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4297-239ca82f293faab0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4320-dcc5a7734126f4c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4434.94b4c552a2d91e69.js +148 -0
- package/apps/pwa/out/_next/static/chunks/{8911.bf5e712acb25fb6c.js → 4527.a69f2894d1bc9f4a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4579.03e4092f73950965.js +226 -0
- package/apps/pwa/out/_next/static/chunks/{5307.d7328020227880c2.js → 4667.97e31aa38c95c475.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/{6142.913b44e260a3f925.js → 4702.4a4adc1f16d7c93b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4815.481501c57b3568d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/487-d16e698ef14662d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4888-7a38bf522c9cc6fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5034-30e429d6bb086881.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5040-35c494e60dadd97d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/50c6ab1e.81a96e419350909d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5154.4b6a836179356f30.js +94 -0
- package/apps/pwa/out/_next/static/chunks/{8487.291666583bebe640.js → 5159.115f1c2ca471386c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5228-a8200df486278f2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/530.d26346e18089640a.js +115 -0
- package/apps/pwa/out/_next/static/chunks/5379.97af106d09566f69.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{89d6c04f-6b8ab341748e468a.js → 53aeb4ee-51c92f8d683f5220.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3291.f6c1005387d514bd.js → 5471.1e611302ed3a7c53.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5479-b6372e762719f92f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5534-699c5cbcbc96c5d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3086.fb14b60ce98b53d3.js → 5574.f35accf4f132b29d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5701.9c4e8f250312eeee.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{1632.7d87f998e714bede.js → 5744.b9415e18ad623100.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5300.677c97e0393742e6.js → 5892.2526d962339662d8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5937.c2df9e3447e1f667.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5941.8210cc93c9202c0a.js +286 -0
- package/apps/pwa/out/_next/static/chunks/{7161-8f22db3ec5ed005e.js → 5947-135e26537d80a6b9.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/{3124-561f7f037943cde2.js → 5964-7e21164a8f2cf0ce.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5975.4004f820ae3cecd5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6142-7ccd48795d7abd55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6160.bf9177b8c1bfb243.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6398.71ba0690c8027555.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6492.2bd5c10edc1a064e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6552.a3840aa7da35ad68.js +62 -0
- package/apps/pwa/out/_next/static/chunks/6736.1e36d3ac5fa5e41d.js +24 -0
- package/apps/pwa/out/_next/static/chunks/6874.d5cf7eb8edb2cabe.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6900.3ccbe069bdd4556a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/71327eb4.add50b365f94bf3a.js +122 -0
- package/apps/pwa/out/_next/static/chunks/7136.deca3e3a2ddaa374.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7139-ee9f8ed2677a40b3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.738b777642a02233.js → 7191.fdc955f7d96064a4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7423-e0aa5d9e2962120e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7453.afc1ea7e05d56a8c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7503-5e6a3d7fe67e4a0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7761-476fd7bf768abd34.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7975.eafb12097a0a9e62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1026-44010f405704050c.js → 8050-9b364bdef8f71927.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8152.64e253c35cccfaae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8190.620f0231b3924bcb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/83184dcd-0ee6247ff41eb3f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8448.45b6b1f1a99eeb35.js +104 -0
- package/apps/pwa/out/_next/static/chunks/8464-e24f10451b658aac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8477.e4c944bb3aa5d2f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8710.ca385257d5ca422e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8830.627ccf294ec2465c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8880.64c445592f04acaf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8981.c706964c8ba27aff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5638.107314dc479e2f37.js → 9048.212083de42d25be3.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9053.bd0fffaff8d3f836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9161-c66e723d7ec9d682.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9185.1ecdf9b65c0bb684.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9356.fccd5f58086b4ff5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9446.dc0406109371d329.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9506.dcf25d467a42627e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8019.21ca554a821dad01.js → 9507.7026657a2f47eb64.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/9541.eb8e2f8480c7feca.js +93 -0
- package/apps/pwa/out/_next/static/chunks/9577.ed7582186efa5482.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9579.4a8169600fff2058.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9642-ff2e039eb62e3b92.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9659-8f2323348113ad80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9724.4c5623af2db43153.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{6493.27ad77de7aac7966.js → 9834.63ab2bbc3153172e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3389.012c70767a872f9b.js → 9869.81a1e24ca682d5a0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8994d93e.33ba491f95345fc2.js → a0a01613.6d1cca322238d7fe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{de8b930d.d94c0f42acf807fc.js → addc2b32.8c4ff8f0aefe987d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-8d9d76ea322d855c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed2c4e59f9f1fe21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-2dc99e03c5d76ca0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b5a3a788330e920f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-866aac5ff226a7f3.js → page-14949db884bfa672.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ea76c85b36388a97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-01716d20af62d325.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-62baf6881a64b1e9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f89ba590c2af8afa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-22f8b0f78d9637e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-817f9d7e6820be80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-ffb9bc0d2650249a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-f6d0e64873c0f629.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-8bfa0d4ba23a209d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-f5060462249e32c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-8d9d76ea322d855c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-b2049f8b1ef79117.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-1446c5fa93907f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-b48a7a07c5204156.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-e974b8c4ca47614c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-8a919b869bd28594.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-d31b8de6bfcd72ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-64765f817909db29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-d128c8569a36ee9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-b2679d357e9b9270.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-6fabec98a9e7b269.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-048f30149dcf1349.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-9113181d6b5a2725.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-40f9e19149024298.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-1e7a755a13227611.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3c8f8b67a203cf07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-18032f5437215062.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-da3b739192fd0ee3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d332545571bd6fd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-5237aa5b3f3b8ec5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a142dfb0331a05f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d16e56c3989c147e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6b6593600869e2cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-8d9d76ea322d855c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-29e8b8a8396b55cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-b3414d856a1a7e2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c3affa52b7ce961b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f21dddf9f95b6fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-f5a30a24745bf088.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-8c8569a09b4d4e34.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-2ce32722c0243483.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4767b0d9c0a98c6c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-116ee1339c387a75.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-35e8c50ca4ef7ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-52e17113356bb891.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-7458ae05ed2ac1a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/b2271e54.c1de3347976e90b0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b7459770.6539b11860eb8a9c.js → c0c1a699.0ba79f97005965d4.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/framework-ad6c9dd524ad6ef8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-2ddd1f2b44d6aa59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-e3d82bdc358d20c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-71d5aa3896e1655f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-03ffc54e00438546.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-56bb89af5a611d0b.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.mjs +6 -1
- package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
- package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
- package/graphs/demo/persona-marketer/fixtures/marketer-scenario.yaml +10 -0
- package/graphs/demo/persona-marketer/persona-marketer.yaml +25 -0
- package/graphs/demo/persona-marketer/recipes.yaml +19 -0
- package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
- package/graphs/demo/persona-teacher/recipes.yaml +17 -0
- package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
- package/graphs/overlays/stop-heal-ladder.yaml +23 -2
- package/graphs/release/release-loop.yaml +3 -1
- package/graphs/units/gate/gate.yaml +20 -0
- package/graphs/units/git/git.yaml +26 -0
- package/graphs/units/implement/implement.yaml +19 -0
- package/graphs/units/landing/landing.yaml +18 -0
- package/graphs/units/review/review.yaml +20 -0
- package/graphs/units/verbs/recipes.yaml +3 -0
- package/graphs/units/verbs/research.yaml +20 -0
- package/graphs/video/explainer-line.yaml +4 -1
- package/graphs/work/research-report.yaml +11 -1
- package/package.json +1 -1
- package/scripts/dead-code/report.ts +169 -0
- package/scripts/disk-steward.ts +46 -0
- package/scripts/fleet/node-setup.sh +50 -0
- package/scripts/graph-units/verbs/research.ts +55 -0
- package/scripts/lib/morning-report-options.ts +3 -4
- package/scripts/lib/tui-regress-evidence.ts +56 -0
- package/scripts/llm-cost-ledger.ts +179 -8
- package/scripts/measure-nl-routing-corpus.ts +10 -2
- package/scripts/morning-report.ts +5 -1
- package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
- package/scripts/public-copy-lint.ts +163 -19
- package/scripts/public-export.ts +15 -7
- package/scripts/ratchets/train-side-invariants.sh +0 -1
- package/scripts/release-loop/export-check-node.ts +21 -3
- package/scripts/release-loop/gate-autoheal.ts +117 -0
- package/scripts/release-loop/gate-baseline-receipt.ts +8 -1
- package/scripts/release-loop/gate-diff.ts +22 -3
- package/scripts/release-loop/gate-node.ts +583 -64
- package/scripts/release-loop/gate-shard-dispatch.ts +128 -0
- package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
- package/scripts/release-loop/gate-shard-receipts.ts +89 -0
- package/scripts/release-loop/gate-triage-node.ts +70 -10
- package/scripts/release-loop/gate-wall.ts +5 -5
- package/scripts/release-loop/preflight.ts +6 -2
- package/scripts/release-loop/resume-release.ts +267 -20
- package/scripts/release-loop/resume.ts +48 -0
- package/scripts/release-loop/traffic-node.ts +6 -3
- package/scripts/release-loop/unattended-release.ts +4 -2
- package/scripts/release-loop/version-node.ts +7 -2
- package/scripts/release-story/draft.ts +1 -1
- package/scripts/research-bench/README.md +53 -0
- package/scripts/research-bench/questions.json +66 -0
- package/scripts/research-bench/rubric.md +33 -0
- package/scripts/research-bench/run.ts +523 -0
- package/scripts/shadow-authoring-probe.ts +20 -0
- package/scripts/tui-regress.ts +85 -10
- package/scripts/ux-sim.ts +19 -2
- package/src/acp/core-turn-bridge.ts +94 -4
- package/src/agent/autonomous-tools.ts +10 -3
- package/src/agent/monad-agent-turn.ts +4 -1
- package/src/agent/registry.ts +26 -2
- package/src/agent/shared-app-tools.ts +1 -1
- package/src/agent/surface-ux/build.ts +12 -3
- package/src/agent/surface-ux/types.ts +1 -1
- package/src/agent/task-notification.ts +75 -6
- package/src/agent/types.ts +3 -0
- package/src/agent-mission/driver.ts +29 -3
- package/src/agent-mission/mission-cli.ts +2 -1
- package/src/agent-substrate/review-context-pack.ts +365 -0
- package/src/agent-substrate/self-review-cli.ts +291 -20
- package/src/ask-user-question/index.ts +6 -0
- package/src/ask-user-question/inline-resolver.ts +49 -0
- package/src/auto-research/question-ladder-contract.ts +22 -3
- package/src/auto-research/research-turn.ts +359 -17
- package/src/autopilot/agent-loop.ts +1 -1
- package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
- package/src/autopilot/pty-control-loop.ts +8 -1
- package/src/boot/daemon-tools/index.ts +4 -3
- package/src/boot/daemon-tools/self-implement.ts +312 -7
- package/src/boot/daemon-tools/types.ts +2 -0
- package/src/budget/llm-cost.ts +44 -5
- package/src/budget/shadow-arms.ts +52 -0
- package/src/budget/unified-usage.ts +17 -1
- package/src/capture/tui-self-report.ts +14 -0
- package/src/chat/index.ts +33 -7
- package/src/chat/pickers/state.ts +7 -1
- package/src/cli/agent-cli.ts +43 -8
- package/src/cli/intake-cli.ts +22 -1
- package/src/cli/lesson-cli.ts +19 -3
- package/src/cli/log-event-names.ts +6 -0
- package/src/cli/mcp-login.ts +8 -18
- package/src/cli/msg-cli.ts +57 -1
- package/src/cli/pod-cli.ts +17 -2
- package/src/cli/pr-land-heavy-remote.ts +15 -9
- package/src/cli/pty-takeover-cli.ts +67 -7
- package/src/cli/release-cli.ts +79 -7
- package/src/cli/repo-cli.ts +2 -1
- package/src/cli/sim-cli.ts +7 -1
- package/src/cli/tasks-cli.ts +41 -13
- package/src/codex/models.ts +30 -15
- package/src/control-plane/ledger.ts +21 -4
- package/src/core-turn/run-core-turn.ts +47 -3
- package/src/core-turn/run-goal-loop.ts +240 -10
- package/src/core-turn/types.ts +9 -0
- package/src/dashboard/acp-boot.ts +72 -3
- package/src/dashboard/chat/acp-chat.ts +2 -2
- package/src/dashboard/classic-sigquit.ts +16 -0
- package/src/dashboard/index.ts +327 -27
- package/src/dashboard/input/chat-main-interaction-opts.ts +3 -0
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +7 -2
- package/src/dashboard/input/chat-main-slash-command.ts +7 -0
- package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
- package/src/dashboard/input/dashboard-priority-key-route.ts +21 -5
- package/src/dashboard/input/external-input-host.ts +2 -0
- package/src/dashboard/sidetab-runtime.ts +228 -0
- package/src/dashboard/slash-runtime/ask-clarification.ts +7 -1
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +20 -8
- package/src/dashboard/slash-runtime/help-from-registry.ts +2 -2
- package/src/dashboard/term-slash-runtime.ts +16 -18
- package/src/dashboard/turn-preamble.ts +14 -1
- package/src/disk-steward/judge.ts +73 -0
- package/src/disk-steward/ledger.ts +122 -0
- package/src/disk-steward/steward.ts +373 -0
- package/src/domains/guardian-tick.ts +191 -2
- package/src/domains/knowledge.ts +1 -1
- package/src/esc-abort-gate.ts +33 -1
- package/src/execution-loop/launch-gate.ts +206 -1
- package/src/graph-runner/graph-cli.ts +27 -4
- package/src/graph-runner/in-process-registry.ts +37 -0
- package/src/graph-runner/runner.ts +63 -11
- package/src/graph-wizard/generate.ts +16 -7
- package/src/graph-wizard/mission-compose.ts +201 -0
- package/src/grok/agent-search.ts +8 -2
- package/src/grok/credential.ts +5 -0
- package/src/grounding/ref-index.ts +30 -0
- package/src/grounding/ref-vector.ts +61 -0
- package/src/grounding/sources-cli.ts +11 -3
- package/src/grounding/sources.ts +15 -1
- package/src/harness/harness-cli-command.ts +305 -23
- package/src/harness/harness-incidents.ts +312 -7
- package/src/harness/harness-pod-dispatch.ts +7 -1
- package/src/harness/harness-space.ts +15 -1
- package/src/harness/progress-worker-entry.ts +59 -0
- package/src/harness/progress-worker-runtime.ts +11 -0
- package/src/index.ts +35 -5
- package/src/input/external-input-queue.ts +10 -0
- package/src/instance/dev-project.ts +25 -0
- package/src/intake-plane/collect-telegram-saved.ts +36 -1
- package/src/intake-plane/intake-to-tasks.ts +246 -3
- package/src/intelligence-map/model-alias.ts +6 -5
- package/src/intelligence-map/model-catalog.ts +59 -2
- package/src/lessons/friction-ledger.ts +154 -0
- package/src/live-supply/collector.ts +149 -0
- package/src/live-supply/probes.ts +52 -0
- package/src/live-supply/snapshot.ts +40 -0
- package/src/llm/model-defaults.ts +4 -4
- package/src/llm.ts +473 -87
- package/src/loops/orchestrator/tick.ts +56 -0
- package/src/mcp/mcp-oauth.ts +3 -1
- package/src/model-tier/llm-tier-map.ts +9 -9
- package/src/model-tier/turn-tier.ts +1 -1
- package/src/msg/msg-mirror-github.ts +80 -0
- package/src/msg/msg-mirror-policy.ts +61 -0
- package/src/msg/msg-mirror-runner.ts +219 -0
- package/src/msg/msg-mirror-state.ts +33 -0
- package/src/msg/msg-store.ts +11 -0
- package/src/nexus/api/harness-api.ts +2 -1
- package/src/output-truncation.ts +41 -12
- package/src/project/project-store.ts +48 -1
- package/src/pty/snapshot.py +132 -0
- package/src/pty-shell/pty-control-ipc.ts +29 -2
- package/src/pty-shell/pty-manifest.ts +59 -3
- package/src/pty-shell/registry.ts +2 -0
- package/src/pty-shell/tui-run-state.ts +80 -3
- package/src/release-loop/gate-host-reservation.ts +157 -4
- package/src/release-loop/gate-shard-ledger.ts +68 -0
- package/src/release-loop/merged-pr-checklist.ts +25 -2
- package/src/release-loop/rehearsal-shard-plan.ts +6 -1
- package/src/release-loop/rehearsal.ts +27 -0
- package/src/release-loop/release-schedule.ts +90 -1
- package/src/research/bloom-exemplars.ts +135 -0
- package/src/research/edge-decision.ts +124 -15
- package/src/research/gap-followup.ts +249 -0
- package/src/research/lenses/counter-evidence.ts +100 -0
- package/src/research/lenses/working-backwards.ts +89 -0
- package/src/research/plugin-discovery.ts +242 -0
- package/src/research-bridge/invoke.ts +4 -2
- package/src/research-bridge/types.ts +2 -0
- package/src/self-dev/ask-launch-flow.ts +73 -131
- package/src/self-dev/dev-cli.ts +19 -17
- package/src/self-dev/dev-pipeline.ts +8 -4
- package/src/self-dev/graph-front-nodes.ts +23 -0
- package/src/self-dev/graph-journey-route.ts +10 -2
- package/src/self-dev/launch-preflight-text.ts +2 -2
- package/src/self-dev/launch-preflight.ts +47 -11
- package/src/self-dev/orchestrate-cli.ts +1 -0
- package/src/self-dev/orchestrate.ts +15 -0
- package/src/self-dev/run-supervisor.ts +204 -3
- package/src/self-dev/run-types.ts +3 -0
- package/src/self-implement/device-first.ts +8 -2
- package/src/self-implement/doctor-visit.ts +76 -58
- package/src/self-implement/gate-artifacts.ts +16 -0
- package/src/self-implement/gate-baseline.ts +75 -4
- package/src/self-implement/gate-cli.ts +368 -30
- package/src/self-implement/gate-receipt-registry.ts +180 -0
- package/src/self-implement/gate-remote.ts +165 -24
- package/src/self-implement/goal-author-clarification.ts +205 -32
- package/src/self-implement/goal-author-decompose.ts +24 -15
- package/src/self-implement/goal-author.ts +86 -10
- package/src/self-implement/graph-stage-units.ts +172 -0
- package/src/self-implement/graph-templates.ts +162 -1
- package/src/self-implement/graph-variant.ts +37 -4
- package/src/self-implement/host-regate.ts +124 -49
- package/src/self-implement/impl-findings-feedback.ts +188 -0
- package/src/self-implement/knowhow-pack.ts +227 -10
- package/src/self-implement/off-diff-evidence.ts +124 -0
- package/src/self-implement/orchestrator.ts +320 -28
- package/src/self-implement/run-ledger.ts +110 -14
- package/src/self-implement/run-stop.ts +33 -2
- package/src/self-implement/running-runs.ts +35 -12
- package/src/self-implement/seams.ts +6 -3
- package/src/self-implement/self-implement-cli.ts +40 -6
- package/src/self-implement/stop-autoheal-operational.ts +43 -0
- package/src/self-implement/stop-autoheal.ts +15 -6
- package/src/session/chat.ts +66 -15
- package/src/session/request-size-guard.ts +123 -0
- package/src/session-runtime/provider-circuit.ts +140 -0
- package/src/shell-runner/file-engine.ts +1 -0
- package/src/shell-runner/types.ts +4 -0
- package/src/sidetab-core/model.ts +312 -0
- package/src/skills/tools/schedule-wakeup.ts +23 -1
- package/src/skills/tools/shell.ts +51 -5
- package/src/steward/directive-audit.ts +354 -0
- package/src/surface/child-process.ts +31 -0
- package/src/surface/tui-stderr-owner.ts +48 -0
- package/src/task-agent/cover.ts +30 -12
- package/src/task-agent/judge.ts +56 -0
- package/src/task-agent/parent-host.ts +14 -1
- package/src/task-agent/postland-card.ts +35 -2
- package/src/task-agent/rejudge-on-head.ts +46 -2
- package/src/task-agent/repair-cycle.ts +1 -1
- package/src/task-agent/task-hand.ts +133 -25
- package/src/task-agent/tier-split.ts +105 -21
- package/src/task-orchestrator/surfaces/pod-command-job.ts +330 -2
- package/src/task-orchestrator/surfaces/pod-config-snapshot.ts +123 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +70 -2
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
- package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +11 -8
- package/src/task-orchestrator/surfaces/pod-si-warm-protocol.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +74 -6
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +2 -1
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +56 -15
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +298 -45
- package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
- package/src/tool-runtime/skill-exec-runtime.ts +95 -5
- package/src/tui/entry.ts +25 -5
- package/src/tui/inline-choice.ts +188 -0
- package/src/tui/sidetab/list.ts +36 -165
- package/src/tui/sidetab/nl-tool.ts +2 -136
- package/src/tui/sidetab/peek.ts +52 -0
- package/src/tui/slash/term.ts +12 -1
- package/src/tui-client/dashboard-session.ts +10 -1
- package/src/user-config.ts +45 -9
- package/src/ux-sim/classic-tui-seat-scenarios.ts +1 -7
- package/src/ux-sim/fake-land-queue.ts +88 -0
- package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
- package/src/ux-sim/fault-pod-child-result.ts +42 -0
- package/src/ux-sim/gate-shard-host-reroute.ts +82 -0
- package/src/ux-sim/goal-interim-trace.ts +24 -0
- package/src/ux-sim/next-tui-seat-scenarios.ts +8 -13
- package/src/ux-sim/release-sim-rig.ts +262 -0
- package/src/ux-sim/seminar-rehearsal.ts +109 -0
- package/src/ux-sim/subagent-tools-device.ts +2 -2
- package/src/ux-sim/task-orchestrator-surfaces-self-implement-pod-device.ts +4 -1
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/explainer.ts +54 -2
- package/src/web-search/grok.ts +32 -6
- package/src/web-search/index.ts +8 -7
- package/src/workflow-runtime/node-catalog.ts +53 -4
- package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +0 -27
- package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +0 -148
- package/apps/pwa/out/_next/static/chunks/2104.70e086d5174a4736.js +0 -1
- package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/214.186a430da28338f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +0 -93
- package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +0 -104
- package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +0 -24
- package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +0 -1
- package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +0 -36
- package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +0 -287
- package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +0 -94
- package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4199-f7c6eb40cd0322f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +0 -32
- package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +0 -90
- package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +0 -79
- package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +0 -74
- package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +0 -286
- package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +0 -1
- package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +0 -8
- package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +0 -62
- package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7488.966b116789f3d41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7524.ce973525d30b3d52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7628.6b16863ac3972333.js +0 -34
- package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7954.4f7a8832663ee979.js +0 -115
- package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +0 -1
- package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +0 -149
- package/apps/pwa/out/_next/static/chunks/9223.cbcc88e0e9950426.js +0 -41
- package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +0 -226
- package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +0 -24
- package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9581.da63ddf41acfad46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +0 -2
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-291ab19fe2acb173.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-6b140f77593576eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +0 -1
- package/apps/pwa/out/_next/static/chunks/bb6987f9.3647e1a23b0e3217.js +0 -122
- package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +0 -1
- package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
- /package/apps/pwa/out/_next/static/{RrjXTwz-urRAbChP1bX02 → GiOLWmt4MqDZePubBjA4Q}/_ssgManifest.js +0 -0
|
@@ -106,10 +106,14 @@ export async function scanOriginMainExport(): Promise<{ mainSha: string; files:
|
|
|
106
106
|
try { code = runPublicExport(['--leak-check', '--json'], snapshot); }
|
|
107
107
|
finally { console.log = originalLog; console.error = originalError; }
|
|
108
108
|
const report = JSON.parse(output.trim()) as {
|
|
109
|
-
files?: number; leaks?: number; hits?: Array<{ marker: string }>; codeLiterals?: { hits?: number };
|
|
109
|
+
files?: number; leaks?: number; hits?: Array<{ marker: string; file?: string; line?: number }>; codeLiterals?: { hits?: number };
|
|
110
110
|
privateRedactions?: { loaded?: boolean };
|
|
111
111
|
};
|
|
112
|
-
if (report.privateRedactions?.loaded !== true)
|
|
112
|
+
if (report.privateRedactions?.loaded !== true) {
|
|
113
|
+
const failure = report.hits?.find((hit) => hit.marker.startsWith('private-redaction-'));
|
|
114
|
+
if (failure) throw new Error(`공개 export 개인 치환 실패: ${failure.marker} ${failure.file ?? ''}:${failure.line ?? 0}`);
|
|
115
|
+
throw new Error('개인 치환표를 읽지 못해 export 누수 검사를 완결할 수 없다');
|
|
116
|
+
}
|
|
113
117
|
if (typeof report.files !== 'number' || typeof report.leaks !== 'number') throw new Error('public-export 검사 결과가 완전하지 않다');
|
|
114
118
|
if (code !== (report.leaks > 0 ? 1 : 0)) throw new Error(`public-export 종료 코드와 누수 결과 불일치: rc=${code} leaks=${report.leaks}`);
|
|
115
119
|
return { mainSha, files: report.files, leaks: report.leaks, codeLiterals: report.codeLiterals?.hits ?? 0 };
|
|
@@ -8,13 +8,16 @@ import { tmpdir } from 'node:os';
|
|
|
8
8
|
import { dirname, join, resolve } from 'node:path';
|
|
9
9
|
import { debug } from '../../src/debug/log.js';
|
|
10
10
|
import { effectiveInstanceRoot, releaseLedgerRoot } from '../../src/instance/resolve.js';
|
|
11
|
+
import { acquireLockAsync } from '../../src/storage/file-lock.js';
|
|
11
12
|
import { lastJsonObject, runGraph, verifyReleaseCommandRoot, verifyReleaseRecipeSnapshot, type GraphRunState } from '../../src/graph-runner/runner.js';
|
|
12
13
|
import { withOpSeatFlock } from '../../src/steward/resync-tick.js';
|
|
13
14
|
import { releaseRunUniverse } from './release-universe.js';
|
|
14
15
|
import { isReleaseVersion } from './release-version.js';
|
|
15
|
-
import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planStalledShardRecheck, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
|
|
16
|
+
import { changedFilesBetween, clearPartialPlan, collectExecutedPodShardImages, planPartialRegate, planSameCommitRecheck, planStalledShardRecheck, testFileOfId, writePartialPlan, writeRecheckPlan } from './gate-partial.js';
|
|
17
|
+
import { gateAssignableTestsAtCommit, gateEnvFingerprint } from './gate-baseline-receipt.js';
|
|
18
|
+
import { readGateShardManifest, readGateShardResult } from './gate-shard-receipts.js';
|
|
16
19
|
import { gitClosureTree, testsReachingChanged, TRACEABLE_RELEASE_JSON, type ClosureTree } from './gate-closure.js';
|
|
17
|
-
import { releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
|
|
20
|
+
import { assertReleaseResumeApprovalBoundary, releaseResumeAcceptedRegressions, releaseResumeGateExecutor, releaseResumeGatePodPool, releaseResumeGateShardReroute, releaseResumeWaiver, type AcceptedRegression } from './resume.js';
|
|
18
21
|
import { reserveReleaseResumeAttempt } from './resume-cap.js';
|
|
19
22
|
import { cutReleaseBranch } from './cut-branch.js';
|
|
20
23
|
import { releaseLoopConfig } from './unattended-release.js';
|
|
@@ -46,6 +49,8 @@ export interface ResumeReleaseDeps {
|
|
|
46
49
|
ledgerRoot?: string;
|
|
47
50
|
/** GATE-REUSE-BY-DIFF: the tip tree for the import closure — default read from git objects of the tip commit. */
|
|
48
51
|
closureTree?: (commit: string) => ClosureTree;
|
|
52
|
+
/** Test seam for the pinned Pod gate selection; production reads the selected test universe at the cut commit. */
|
|
53
|
+
gateTestsAtCommit?: (commit: string) => string[] | null;
|
|
49
54
|
/** Source observation before the first resumed node. */
|
|
50
55
|
onSource?: (line: string) => void;
|
|
51
56
|
/** Test seam; production resolves this from ELANOUS_RELEASE_TRAFFIC_FILE / release.loop.trafficFile. */
|
|
@@ -55,6 +60,7 @@ export interface ResumeReleaseDeps {
|
|
|
55
60
|
export interface ResumeReleaseOptions {
|
|
56
61
|
runId: string; from: string; partial?: boolean; waive?: string; reason?: string;
|
|
57
62
|
acceptedRegressions?: AcceptedRegression[]; gateExecutor?: string; gateHost?: string; gatePodPool?: string;
|
|
63
|
+
rerouteShard?: string; shardHost?: string; shardOs?: 'darwin' | 'linux';
|
|
58
64
|
commandRoot?: string;
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -292,14 +298,111 @@ function verifyResumePreflight(run: GraphRunState, opts: ResumeReleaseOptions, d
|
|
|
292
298
|
}
|
|
293
299
|
}
|
|
294
300
|
|
|
295
|
-
/** GATE-PARTIAL outcome of a resume — applied (
|
|
296
|
-
* default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
|
|
301
|
+
/** GATE-PARTIAL outcome of a resume — applied (with its exact remeasure selection) or the reason it fell back to a full gate.
|
|
302
|
+
* `auto` marks the default plan of `--from gate` (GATE-REUSE-BY-DIFF); `closure` is set when code changes were followed. */
|
|
297
303
|
export type PartialOutcome = { requested: false }
|
|
298
|
-
| { requested: true; applied: true; files: number; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
|
|
304
|
+
| { requested: true; applied: true; files: number; plannedFiles: string[]; priorCommit: string; auto?: true; closure?: { reached: number; opaque: number } }
|
|
299
305
|
| { requested: true; applied: false; reason: string; auto?: true };
|
|
300
306
|
|
|
301
307
|
/** Build (or clear) the partial re-gate plan for the new tip. Fail-closed: any doubt leaves a full gate.
|
|
302
308
|
* GATE-REUSE-BY-DIFF: `--from gate` on a moved tip plans by default (`partial: undefined`); `partial: false` opts out. */
|
|
309
|
+
type StalledGateEvidence = {
|
|
310
|
+
stalledShards: Array<{ shard: number; files: string[]; reason: string }>;
|
|
311
|
+
partialSummary: { pass: number; fail: number; errors: number; ran: number; files: number };
|
|
312
|
+
partialFailures: { failures: string[]; errors: string[] };
|
|
313
|
+
preservedShardCount: number;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/** Recover an interrupted Pod sweep from its atomic shard receipts. No receipt or identity doubt authorizes a partial gate. */
|
|
317
|
+
function stalledGateEvidence(input: { version: string; commit: string; repo: string; roots: string[];
|
|
318
|
+
gateOutput?: Record<string, unknown>; expectedBase40?: string; expectedTests?: (commit: string) => string[] | null }):
|
|
319
|
+
{ ok: true; evidence: StalledGateEvidence } | { ok: false; reason: string } {
|
|
320
|
+
const fail = (reason: string) => ({ ok: false as const, reason });
|
|
321
|
+
if (input.gateOutput && (input.gateOutput.outcome !== 'error' || input.gateOutput.commit !== input.commit))
|
|
322
|
+
return fail('saved gate result is not an error for the recorded cut');
|
|
323
|
+
if (!/^[a-f0-9]{40}$/i.test(input.expectedBase40 ?? ''))
|
|
324
|
+
return fail('saved cutoff baseline is absent or invalid');
|
|
325
|
+
const fingerprint = gateEnvFingerprint(process.env);
|
|
326
|
+
let receiptDir: string | undefined;
|
|
327
|
+
for (const root of input.roots) {
|
|
328
|
+
const candidate = join(root, 'release', input.version, 'gate-logs', 'cut', 'receipts');
|
|
329
|
+
if (existsSync(candidate)) { receiptDir = candidate; break; }
|
|
330
|
+
}
|
|
331
|
+
if (!receiptDir) return fail('stalled gate receipts are absent');
|
|
332
|
+
const names = readdirSync(receiptDir).filter((name) => /^pod-\d+\.receipt\.json$/.test(name)).sort((a, b) =>
|
|
333
|
+
Number(/^pod-(\d+)/.exec(a)![1]) - Number(/^pod-(\d+)/.exec(b)![1]));
|
|
334
|
+
if (!names.length || names.some((name, index) => Number(/^pod-(\d+)/.exec(name)![1]) !== index))
|
|
335
|
+
return fail('stalled gate receipt shard set is incomplete');
|
|
336
|
+
const allFiles: string[] = [];
|
|
337
|
+
const failures: string[] = [];
|
|
338
|
+
const errors: string[] = [];
|
|
339
|
+
const stalledShards: StalledGateEvidence['stalledShards'] = [];
|
|
340
|
+
const imageDigests = new Set<string>();
|
|
341
|
+
let pass = 0, failCount = 0, errorCount = 0, ran = 0, files = 0, preservedShardCount = 0;
|
|
342
|
+
let base40: string | undefined;
|
|
343
|
+
for (const name of names) {
|
|
344
|
+
const id = name.slice(0, -'.receipt.json'.length);
|
|
345
|
+
const receipt = readGateShardManifest(receiptDir, id);
|
|
346
|
+
if (!receipt || receipt.head40 !== input.commit || !/^[a-f0-9]{40}$/i.test(receipt.base40)
|
|
347
|
+
|| !receipt.files.length
|
|
348
|
+
|| receipt.files.some((file) => typeof file !== 'string'
|
|
349
|
+
|| !/^(?:[\w.-]+\/)*[\w.-]+\.test\.(?:tsx?|jsx?|mts|cts)$/.test(file) || file.split('/').includes('..'))
|
|
350
|
+
|| typeof receipt.envKey !== 'string' || typeof receipt.host !== 'string')
|
|
351
|
+
return fail(`stalled gate receipt identity or file list is invalid: ${id}`);
|
|
352
|
+
if (receipt.base40 !== input.expectedBase40)
|
|
353
|
+
return fail(`stalled gate receipt baseline differs from saved cutoff baseline: ${id}`);
|
|
354
|
+
if (base40 && receipt.base40 !== base40) return fail(`stalled gate receipts disagree on baseline: ${id}`);
|
|
355
|
+
base40 = receipt.base40;
|
|
356
|
+
allFiles.push(...receipt.files);
|
|
357
|
+
if (receipt.status === 'unmeasured') {
|
|
358
|
+
stalledShards.push({ shard: Number(id.slice(4)), files: [...receipt.files], reason: 'incomplete' });
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (!receipt.envKey.startsWith(`${fingerprint}:`)) return fail(`stalled gate receipt environment mismatch: ${id}`);
|
|
362
|
+
const imageDigest = receipt.envKey.slice(fingerprint.length + 1);
|
|
363
|
+
if (!/^sha256:[a-f0-9]{64}$/i.test(imageDigest)) return fail(`stalled gate receipt image identity is missing: ${id}`);
|
|
364
|
+
imageDigests.add(imageDigest.toLowerCase());
|
|
365
|
+
const result = readGateShardResult(receiptDir, { shard: { id, files: receipt.files }, head40: input.commit,
|
|
366
|
+
base40: receipt.base40, envKey: receipt.envKey });
|
|
367
|
+
if (!result) return fail(`stalled gate receipt payload is incomplete or corrupt: ${id}`);
|
|
368
|
+
const totals = result.runs.reduce((sum, run) => ({ pass: sum.pass + run.pass, fail: sum.fail + run.fail,
|
|
369
|
+
errors: sum.errors + run.errors, ran: sum.ran + run.ran, files: sum.files + run.files }),
|
|
370
|
+
{ pass: 0, fail: 0, errors: 0, ran: 0, files: 0 });
|
|
371
|
+
if (!Object.values(totals).every(Number.isSafeInteger) || receipt.failureIds.length !== totals.fail
|
|
372
|
+
|| receipt.errorIds.length !== totals.errors)
|
|
373
|
+
return fail(`stalled gate receipt counts do not reconcile: ${id}`);
|
|
374
|
+
pass += totals.pass; failCount += totals.fail; errorCount += totals.errors; ran += totals.ran; files += totals.files;
|
|
375
|
+
failures.push(...receipt.failureIds); errors.push(...receipt.errorIds); preservedShardCount++;
|
|
376
|
+
}
|
|
377
|
+
if (!stalledShards.length) return fail('no incomplete shard receipt identifies the interrupted work');
|
|
378
|
+
if (new Set(allFiles).size !== allFiles.length) return fail('stalled gate receipt file assignments overlap');
|
|
379
|
+
if (imageDigests.size !== 1) return fail('stalled gate completed shards do not share one executed image');
|
|
380
|
+
const expected = (input.expectedTests ?? ((commit) => gateAssignableTestsAtCommit(input.repo, commit)))(input.commit);
|
|
381
|
+
if (!expected || JSON.stringify([...expected].sort()) !== JSON.stringify([...allFiles].sort()))
|
|
382
|
+
return fail('stalled gate receipts do not cover the pinned test selection');
|
|
383
|
+
const partialSummary = { pass, fail: failCount, errors: errorCount, ran, files };
|
|
384
|
+
const partialFailures = { failures: [...new Set(failures)].sort(), errors: [...new Set(errors)].sort() };
|
|
385
|
+
const gate = input.gateOutput;
|
|
386
|
+
if (gate) {
|
|
387
|
+
const savedSummary = gate.partialSummary as Partial<StalledGateEvidence['partialSummary']> | undefined;
|
|
388
|
+
const savedFailures = gate.partialFailures as Partial<StalledGateEvidence['partialFailures']> | undefined;
|
|
389
|
+
if (savedSummary && JSON.stringify(savedSummary) !== JSON.stringify(partialSummary))
|
|
390
|
+
return fail('saved gate summary disagrees with shard receipts');
|
|
391
|
+
if (savedFailures && (JSON.stringify([...((savedFailures.failures as string[] | undefined) ?? [])].sort()) !== JSON.stringify(partialFailures.failures)
|
|
392
|
+
|| JSON.stringify([...((savedFailures.errors as string[] | undefined) ?? [])].sort()) !== JSON.stringify(partialFailures.errors)))
|
|
393
|
+
return fail('saved gate failures disagree with shard receipts');
|
|
394
|
+
if (gate.preservedShardCount !== undefined && gate.preservedShardCount !== preservedShardCount)
|
|
395
|
+
return fail('saved preserved shard count disagrees with shard receipts');
|
|
396
|
+
if (Array.isArray(gate.stalledShards)) {
|
|
397
|
+
const savedStalled = gate.stalledShards.flatMap((item) => item && typeof item === 'object'
|
|
398
|
+
&& Array.isArray((item as { files?: unknown }).files) ? (item as { files: string[] }).files : []).sort();
|
|
399
|
+
if (JSON.stringify(savedStalled) !== JSON.stringify(stalledShards.flatMap((item) => item.files).sort()))
|
|
400
|
+
return fail('saved stalled shard list disagrees with shard receipts');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return { ok: true, evidence: { stalledShards, partialSummary, partialFailures, preservedShardCount } };
|
|
404
|
+
}
|
|
405
|
+
|
|
303
406
|
function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean; gateExecutor?: string }, deps: ResumeReleaseDeps, dryRun = false): PartialOutcome {
|
|
304
407
|
const root = deps.root ?? effectiveInstanceRoot();
|
|
305
408
|
// Any resume starts from no plan: one without a partial plan (from whatever node) never inherits an older plan.
|
|
@@ -355,9 +458,11 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
355
458
|
debug.log('release-loop.resume', 'stalled-recheck-planned', { version: tip.version, commit: tip.to,
|
|
356
459
|
planKind: planned.plan.kind, remeasureFileCount: planned.plan.files.length,
|
|
357
460
|
preservedShardCount: planned.plan.preservedShardCount ?? 0, fallbackReason: null, path });
|
|
358
|
-
return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.to,
|
|
461
|
+
return { requested: true, applied: true, files: planned.plan.files.length, plannedFiles: [...planned.plan.files], priorCommit: tip.to,
|
|
462
|
+
...(auto ? { auto: true as const } : {}) };
|
|
359
463
|
}
|
|
360
464
|
let prior: { commit?: unknown; failures?: unknown; errors?: unknown } | undefined;
|
|
465
|
+
let movedStalledEvidence: StalledGateEvidence | undefined;
|
|
361
466
|
for (const location of [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])]) {
|
|
362
467
|
const path = join(location, 'release', tip.version, 'gate-failures.json');
|
|
363
468
|
if (!existsSync(path)) continue;
|
|
@@ -367,7 +472,19 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
367
472
|
if (record?.commit === tip.from) { prior = record; break; }
|
|
368
473
|
}
|
|
369
474
|
if (!prior || !Array.isArray(prior.failures) || !(prior.errors === undefined || Array.isArray(prior.errors))) {
|
|
370
|
-
|
|
475
|
+
const gateRecord = saved.nodes.find((node) => node.nodeId === 'gate');
|
|
476
|
+
const gateOutput = gateRecord ? lastJsonObject(gateRecord.output) as Record<string, unknown> | undefined : undefined;
|
|
477
|
+
const cutoffRecord = saved.nodes.find((node) => node.nodeId === 'cutoff');
|
|
478
|
+
const cutoffOutput = cutoffRecord ? lastJsonObject(cutoffRecord.output) as Record<string, unknown> | undefined : undefined;
|
|
479
|
+
const expectedBase40 = (cutoffOutput?.baseline as { sha?: unknown } | undefined)?.sha;
|
|
480
|
+
const evidence = stalledGateEvidence({ version: tip.version, commit: tip.from, repo: deps.repo ?? process.cwd(),
|
|
481
|
+
roots: [...new Set([deps.ledgerRoot ?? releaseLedgerRoot(), root])], gateOutput,
|
|
482
|
+
...(typeof expectedBase40 === 'string' ? { expectedBase40 } : {}),
|
|
483
|
+
expectedTests: deps.gateTestsAtCommit });
|
|
484
|
+
if (!evidence.ok) return fallback(`no complete gate record for ${tip.from.slice(0, 12)}; ${evidence.reason}`);
|
|
485
|
+
movedStalledEvidence = evidence.evidence;
|
|
486
|
+
prior = { commit: tip.from, failures: movedStalledEvidence.partialFailures.failures,
|
|
487
|
+
errors: movedStalledEvidence.partialFailures.errors };
|
|
371
488
|
}
|
|
372
489
|
// Both sides of a rename count (`--no-renames`): a source moved to a test/Markdown name is still a source change.
|
|
373
490
|
let changed: string[];
|
|
@@ -403,10 +520,11 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
403
520
|
const savedInput = saved.input as { gateExecutor?: unknown; gatePodPool?: unknown };
|
|
404
521
|
const podGate = (opts.gateExecutor ?? savedInput.gateExecutor ?? (savedInput.gatePodPool ? 'pod' : undefined)) === 'pod';
|
|
405
522
|
const priorShards = priorCutLogs ? collectExecutedPodShardImages(priorCutLogs, tip.from) : undefined;
|
|
406
|
-
const
|
|
523
|
+
const stalledFiles = movedStalledEvidence?.stalledShards.flatMap((shard) => shard.files) ?? [];
|
|
524
|
+
const planned = planPartialRegate({ version: tip.version, priorCommit: tip.from, forCommit: tip.to, changedFiles: [...changed, ...stalledFiles],
|
|
407
525
|
priorFailures: prior.failures as string[], priorErrors: (prior.errors ?? []) as string[],
|
|
408
526
|
existsAtTip: (file) => tracked.has(file),
|
|
409
|
-
...(podGate ? { priorShards: priorShards ?? [] } : {}),
|
|
527
|
+
...((podGate || movedStalledEvidence) ? { priorShards: priorShards ?? [] } : {}),
|
|
410
528
|
closure: (changedCode) => testsReachingChanged(tree, changedCode, tests, { cliEntries: CLI_ENTRIES }) });
|
|
411
529
|
if (!planned.ok) return fallback(planned.reason);
|
|
412
530
|
if (planned.plan.files.length >= tests.length) return fallback(`every test is re-run (${tests.length}) — full gate`);
|
|
@@ -426,7 +544,7 @@ function preparePartial(tip: TipRefresh, opts: { from: string; partial?: boolean
|
|
|
426
544
|
const path = dryRun ? undefined : writePartialPlan(root, planned.plan);
|
|
427
545
|
debug.log('release-loop.resume', 'partial-planned', { version: tip.version, priorCommit: tip.from, commit: tip.to, files: planned.plan.files.length,
|
|
428
546
|
tests: tests.length, auto, ...(path ? { path } : {}), dryRun, ...(planned.closure ? { reached: planned.closure.reached, opaque: planned.closure.opaque } : {}) });
|
|
429
|
-
return { requested: true, applied: true, files: planned.plan.files.length, priorCommit: tip.from,
|
|
547
|
+
return { requested: true, applied: true, files: planned.plan.files.length, plannedFiles: [...planned.plan.files], priorCommit: tip.from,
|
|
430
548
|
...(auto ? { auto: true as const } : {}), ...(planned.closure ? { closure: planned.closure } : {}) };
|
|
431
549
|
}
|
|
432
550
|
|
|
@@ -456,12 +574,16 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
|
|
|
456
574
|
const statePath = join(deps.root!, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
|
|
457
575
|
const savedRun = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
458
576
|
if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
|
|
577
|
+
assertReleaseResumeApprovalBoundary({ ...savedRun, statePath }, opts.from);
|
|
459
578
|
if (opts.gateHost !== undefined && opts.gateExecutor === undefined) throw new Error('--gate-host requires --gate-executor host');
|
|
579
|
+
if ((opts.shardHost !== undefined || opts.shardOs !== undefined) && opts.rerouteShard === undefined)
|
|
580
|
+
throw new Error('--shard-host/--shard-os require --reroute-shard');
|
|
460
581
|
const hasOverrides = opts.waive !== undefined || opts.reason !== undefined || opts.acceptedRegressions !== undefined
|
|
461
|
-
|| opts.gateExecutor !== undefined || opts.gatePodPool !== undefined;
|
|
582
|
+
|| opts.gateExecutor !== undefined || opts.gatePodPool !== undefined || opts.rerouteShard !== undefined;
|
|
462
583
|
const original = hasOverrides ? savedRun : undefined;
|
|
463
584
|
if (original && opts.gateExecutor !== undefined) releaseResumeGateExecutor(original, { from: opts.from, executor: opts.gateExecutor, host: opts.gateHost });
|
|
464
585
|
if (original && opts.gatePodPool !== undefined) releaseResumeGatePodPool(original, { from: opts.from, pool: opts.gatePodPool });
|
|
586
|
+
if (original && opts.rerouteShard !== undefined) releaseResumeGateShardReroute(original, { from: opts.from, shard: opts.rerouteShard, host: opts.shardHost, os: opts.shardOs });
|
|
465
587
|
const waiver = original && (opts.waive !== undefined || opts.reason !== undefined) ? releaseResumeWaiver(original, opts) : undefined;
|
|
466
588
|
if (original && opts.acceptedRegressions !== undefined && !waiver)
|
|
467
589
|
releaseResumeAcceptedRegressions(original, opts.acceptedRegressions, opts.from);
|
|
@@ -478,10 +600,11 @@ async function planResumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: Resu
|
|
|
478
600
|
waiverCommit = recorded;
|
|
479
601
|
}
|
|
480
602
|
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
|
|
603
|
+
if (opts.rerouteShard !== undefined && tip.changed) throw new Error('shard reroute requires the original cut head; moved tip needs a full re-gate');
|
|
481
604
|
verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, tip.version);
|
|
482
605
|
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), tip.to, deps.git);
|
|
483
606
|
verifyResumePreflight(savedRun, opts, deps, tip.version, tip.from, tip.to, commandRoot);
|
|
484
|
-
const partial = preparePartial(tip, opts, deps, true);
|
|
607
|
+
const partial = preparePartial(tip, opts.rerouteShard !== undefined ? { ...opts, partial: false } : opts, deps, true);
|
|
485
608
|
const fromIndex = savedRun.path.indexOf(opts.from);
|
|
486
609
|
const gateIndex = savedRun.path.indexOf('gate');
|
|
487
610
|
const gateWillRun = fromIndex >= 0 && gateIndex >= fromIndex;
|
|
@@ -498,15 +621,20 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
498
621
|
deps = { ...deps, root: releaseRunUniverse({ root: deps.root }) };
|
|
499
622
|
const statePath = join(deps.root!, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
|
|
500
623
|
const savedRun = JSON.parse(readFileSync(statePath, 'utf8')) as GraphRunState;
|
|
624
|
+
assertReleaseResumeApprovalBoundary({ ...savedRun, statePath }, opts.from);
|
|
501
625
|
let waiverCommit: string | undefined;
|
|
502
626
|
if (opts.gateHost !== undefined && opts.gateExecutor === undefined) throw new Error('--gate-host requires --gate-executor host');
|
|
503
|
-
|
|
627
|
+
if ((opts.shardHost !== undefined || opts.shardOs !== undefined) && opts.rerouteShard === undefined)
|
|
628
|
+
throw new Error('--shard-host/--shard-os require --reroute-shard');
|
|
629
|
+
const original = opts.waive !== undefined || opts.reason !== undefined || opts.acceptedRegressions !== undefined || opts.gateExecutor !== undefined || opts.gatePodPool !== undefined || opts.rerouteShard !== undefined
|
|
504
630
|
? savedRun : undefined;
|
|
505
631
|
// GATE-HOST-EXECUTOR: checked before the tip refresh writes anything; the runner checks it again under the resume lock.
|
|
506
632
|
const gateExecutor = original && opts.gateExecutor !== undefined
|
|
507
633
|
? releaseResumeGateExecutor(original, { from: opts.from, executor: opts.gateExecutor, host: opts.gateHost }) : undefined;
|
|
508
634
|
const gatePodPool = original && opts.gatePodPool !== undefined
|
|
509
635
|
? releaseResumeGatePodPool(original, { from: opts.from, pool: opts.gatePodPool }) : undefined;
|
|
636
|
+
const shardReroute = original && opts.rerouteShard !== undefined
|
|
637
|
+
? releaseResumeGateShardReroute(original, { from: opts.from, shard: opts.rerouteShard, host: opts.shardHost, os: opts.shardOs }) : undefined;
|
|
510
638
|
if (gateExecutor) debug.log('release-loop.resume', 'gate-executor', { runId: opts.runId, from: opts.from, executor: gateExecutor.set.gateExecutor, host: gateExecutor.set.gateRemote ?? null, previous: gateExecutor.previous });
|
|
511
639
|
if (gatePodPool) debug.log('release-loop.resume', 'gate-pod-pool', {
|
|
512
640
|
runId: opts.runId, from: opts.from, pool: gatePodPool.set.gatePodPool, previous: gatePodPool.previous,
|
|
@@ -534,11 +662,12 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
534
662
|
}
|
|
535
663
|
// The read-only tip and recipe pin preflight must pass before fetch or a saved-run rewrite.
|
|
536
664
|
const previewTip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, true);
|
|
665
|
+
if (shardReroute && previewTip.changed) throw new Error('shard reroute requires the original cut head; moved tip needs a full re-gate');
|
|
537
666
|
verifyResumeRecipePin(opts.commandRoot ?? process.cwd(), savedRun.graphSnapshot, previewTip.version);
|
|
538
667
|
const commandRoot = verifyReleaseCommandRoot(opts.commandRoot ?? process.cwd(), previewTip.to, deps.git);
|
|
539
668
|
verifyResumePreflight(savedRun, opts, deps, previewTip.version, previewTip.from, previewTip.to, commandRoot);
|
|
540
669
|
const tip = refreshReleaseBranchTipInternal(opts.runId, deps, opts.from, waiverCommit, false, previewTip.to);
|
|
541
|
-
const partial = preparePartial(tip, opts, deps);
|
|
670
|
+
const partial = preparePartial(tip, shardReroute ? { ...opts, partial: false } : opts, deps);
|
|
542
671
|
// A failed preflight does not consume an observed release resume attempt.
|
|
543
672
|
const attempt = reserveReleaseResumeAttempt(savedRun, deps.root!, { now: deps.now });
|
|
544
673
|
debug.log('release-loop.resume', 'attempt', { runId: opts.runId, attempt: attempt.attempt,
|
|
@@ -553,6 +682,9 @@ async function resumeReleaseRunLocked(opts: ResumeReleaseOptions, deps: ResumeRe
|
|
|
553
682
|
...(acceptedRegressions ? { releaseAcceptedRegressions: { requested: opts.acceptedRegressions!, acceptedRegressions } } : {}),
|
|
554
683
|
...(gateExecutor ? { releaseGateExecutor: { executor: gateExecutor.set.gateExecutor, ...(opts.gateHost !== undefined ? { host: opts.gateHost } : {}) } } : {}),
|
|
555
684
|
...(gatePodPool ? { releaseGatePodPool: gatePodPool.set.gatePodPool } : {}),
|
|
685
|
+
...(shardReroute ? { releaseGateShardReroute: { shard: shardReroute.set.gateRerouteShard,
|
|
686
|
+
...(shardReroute.set.gateShardHost ? { host: shardReroute.set.gateShardHost } : {}),
|
|
687
|
+
...(opts.shardOs ? { os: opts.shardOs } : {}) } } : {}),
|
|
556
688
|
deps: { root: deps.root, ...(deps.runBash ? { runBash: deps.runBash } : {}) } });
|
|
557
689
|
return { tip, partial, state };
|
|
558
690
|
}
|
|
@@ -589,6 +721,23 @@ export interface RecoverReleaseResult {
|
|
|
589
721
|
resume?: { status: string; tip: TipRefresh; partial: PartialOutcome };
|
|
590
722
|
}
|
|
591
723
|
|
|
724
|
+
export interface RecoverReleaseLandingOptions {
|
|
725
|
+
runId: string;
|
|
726
|
+
/** The landed repair commit on main; this is the idempotency key for a landing notification. */
|
|
727
|
+
mergeSha: string;
|
|
728
|
+
from?: string;
|
|
729
|
+
commandRoot?: string;
|
|
730
|
+
partial?: boolean;
|
|
731
|
+
repoRoot?: string;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
export type RecoverReleaseLandingResult = RecoverReleaseResult & { duplicate: boolean };
|
|
735
|
+
|
|
736
|
+
/** Commander represents `--no-partial` as false and its default (flag omitted) as true. */
|
|
737
|
+
export function recoverLandingPartialOption(optionValue?: boolean): boolean | undefined {
|
|
738
|
+
return optionValue === false ? false : undefined;
|
|
739
|
+
}
|
|
740
|
+
|
|
592
741
|
export interface RecoverReleaseDeps extends ResumeReleaseDeps {
|
|
593
742
|
/** `release cut-branch --append` — tests record the plan instead of pushing the branch. */
|
|
594
743
|
cutBranch?: typeof cutReleaseBranch;
|
|
@@ -604,11 +753,19 @@ export class ReleaseRecoverStopped extends Error {
|
|
|
604
753
|
}
|
|
605
754
|
}
|
|
606
755
|
|
|
756
|
+
function readRecoverJson(path: string, label: string): unknown {
|
|
757
|
+
try { return JSON.parse(readFileSync(path, 'utf8')) as unknown; }
|
|
758
|
+
catch (error) {
|
|
759
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
760
|
+
throw new Error(`release ${label} JSON을 읽을 수 없다: ${path} (${detail}) — 파일을 보존하고 원장을 올바른 JSON으로 복구한 뒤 재시도하라.`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
607
764
|
/** Append one stage result to the resume-attempts ledger. `attempts[]` keeps its shape — the stage results are added. */
|
|
608
765
|
function recordRecoverStage(entry: { root: string; runId: string; version: string; priorCountKnown: boolean; stage: RecoverStageRecord }): void {
|
|
609
766
|
const path = join(entry.root, 'release', entry.version, 'resume-attempts', `${entry.runId}.json`);
|
|
610
767
|
const saved = existsSync(path)
|
|
611
|
-
?
|
|
768
|
+
? readRecoverJson(path, 'resume attempts 원장') as { attempts?: unknown; recoveries?: unknown; priorCountKnown?: unknown }
|
|
612
769
|
: undefined;
|
|
613
770
|
if (saved !== undefined && (!saved || typeof saved !== 'object' || Array.isArray(saved) || !Array.isArray(saved.attempts)
|
|
614
771
|
|| (saved.recoveries !== undefined && !Array.isArray(saved.recoveries)))) throw new Error('release resume attempts ledger invalid');
|
|
@@ -622,16 +779,39 @@ function recordRecoverStage(entry: { root: string; runId: string; version: strin
|
|
|
622
779
|
renameSync(temporary, path);
|
|
623
780
|
}
|
|
624
781
|
|
|
782
|
+
async function acquireReleaseRecoveryLock(root: string, runId: string) {
|
|
783
|
+
const lockDir = join(root, 'release', 'recovery-locks');
|
|
784
|
+
mkdirSync(lockDir, { recursive: true });
|
|
785
|
+
// A full-gate subprocess can block this process's event loop, delaying the async lock heartbeat.
|
|
786
|
+
// The observed release gate reached 2h20m; leave a finite four-hour stale/wait window for one recovery.
|
|
787
|
+
const lock = await acquireLockAsync(join(lockDir, `${runId}.lock`), {
|
|
788
|
+
staleMs: 4 * 60 * 60_000, retryBusyMs: 1_000, maxTries: 14_400,
|
|
789
|
+
});
|
|
790
|
+
if (!lock.stillHeld()) {
|
|
791
|
+
lock.release();
|
|
792
|
+
throw new Error(`release recovery lock was lost for ${runId} — retry after inspecting the recovery ledger`);
|
|
793
|
+
}
|
|
794
|
+
return lock;
|
|
795
|
+
}
|
|
796
|
+
|
|
625
797
|
/**
|
|
626
798
|
* Repair a failed release run in one command: append dry-run (plan only) → append (fast-forward release/<v> onto the
|
|
627
799
|
* picked repairs) → resume --from export-check (preflight plan, then the graph). A failing stage stops the command:
|
|
628
800
|
* the later stages never run, and the stage results are already in the ledger for the human to read.
|
|
629
801
|
*/
|
|
630
802
|
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
803
|
const root = releaseRunUniverse({ root: deps.root });
|
|
804
|
+
const runId = safeRunId(opts.runId);
|
|
805
|
+
const lock = await acquireReleaseRecoveryLock(root, runId);
|
|
806
|
+
try {
|
|
807
|
+
return await recoverReleaseRunLocked(opts, deps, root);
|
|
808
|
+
} finally { lock.release(); }
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
async function recoverReleaseRunLocked(opts: RecoverReleaseOptions, deps: RecoverReleaseDeps, root: string): Promise<RecoverReleaseResult> {
|
|
812
|
+
if (!Array.isArray(opts.pick) || !opts.pick.length) throw new Error('release recover requires at least one --pick commit');
|
|
633
813
|
const statePath = join(root, 'graph-runs', 'release-loop', `${safeRunId(opts.runId)}.json`);
|
|
634
|
-
const savedRun =
|
|
814
|
+
const savedRun = readRecoverJson(statePath, 'release run 상태') as GraphRunState;
|
|
635
815
|
if (savedRun.graphId !== 'release-loop' || savedRun.runId !== opts.runId) throw new Error(`run identity mismatch: ${statePath}`);
|
|
636
816
|
// The saved version-release record names what to repair: the release branch and the commit the run cut.
|
|
637
817
|
const versionRecord = savedRun.nodes.find((node) => node.nodeId === 'version-release');
|
|
@@ -692,8 +872,9 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
|
|
|
692
872
|
const now = (): string => (deps.now?.() ?? new Date()).toISOString();
|
|
693
873
|
const stages: RecoverStageRecord[] = [];
|
|
694
874
|
const record = (stage: RecoverStageRecord): void => {
|
|
695
|
-
|
|
696
|
-
|
|
875
|
+
const withPick = { ...stage, pick: stage.pick ?? [...opts.pick] };
|
|
876
|
+
stages.push(withPick);
|
|
877
|
+
recordRecoverStage({ root, runId: opts.runId, version, priorCountKnown: !savedRun.resume, stage: withPick });
|
|
697
878
|
};
|
|
698
879
|
const stop = (stage: RecoverStageName, error: unknown): never => {
|
|
699
880
|
throw new ReleaseRecoverStopped(stage, stages, RECOVER_STAGES.slice(RECOVER_STAGES.indexOf(stage) + 1),
|
|
@@ -718,7 +899,7 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
|
|
|
718
899
|
};
|
|
719
900
|
const ledgerPath = join(root, 'release', version, 'resume-attempts', `${opts.runId}.json`);
|
|
720
901
|
const recoveries: RecoverStageRecord[] = existsSync(ledgerPath)
|
|
721
|
-
? ((
|
|
902
|
+
? ((readRecoverJson(ledgerPath, 'resume attempts 원장') as { recoveries?: unknown[] }).recoveries ?? []) as RecoverStageRecord[]
|
|
722
903
|
: [];
|
|
723
904
|
let appendedCommit: string | undefined;
|
|
724
905
|
for (let index = recoveries.length - 1; index >= 0; index -= 1) {
|
|
@@ -828,3 +1009,69 @@ export async function recoverReleaseRun(opts: RecoverReleaseOptions, deps: Recov
|
|
|
828
1009
|
}
|
|
829
1010
|
}
|
|
830
1011
|
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Manual landing-event entrypoint. The event's merge SHA is the dedupe key; once that repair has completed its
|
|
1015
|
+
* resume stage, a redelivered event returns the recorded result without appending or running the graph again.
|
|
1016
|
+
* The event producer can call this after its own hook lands; this function does not subscribe to PR-land events.
|
|
1017
|
+
*/
|
|
1018
|
+
export async function recoverReleaseRunFromLanding(opts: RecoverReleaseLandingOptions,
|
|
1019
|
+
deps: RecoverReleaseDeps = {}): Promise<RecoverReleaseLandingResult> {
|
|
1020
|
+
if (!/^[0-9a-f]{40}$/i.test(opts.mergeSha)) throw new Error('release recover-landing requires a full 40-hex merge SHA');
|
|
1021
|
+
const root = releaseRunUniverse({ root: deps.root });
|
|
1022
|
+
const runId = safeRunId(opts.runId);
|
|
1023
|
+
// Share one run-scoped lock with manual `release recover`: the dedupe read, append, and resume must not race.
|
|
1024
|
+
const landingLock = await acquireReleaseRecoveryLock(root, runId);
|
|
1025
|
+
try {
|
|
1026
|
+
return await recoverReleaseRunFromLandingLocked(opts, deps, root, runId);
|
|
1027
|
+
} finally { landingLock.release(); }
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
async function recoverReleaseRunFromLandingLocked(opts: RecoverReleaseLandingOptions, deps: RecoverReleaseDeps,
|
|
1031
|
+
root: string, runId: string): Promise<RecoverReleaseLandingResult> {
|
|
1032
|
+
const pick = [opts.mergeSha.toLowerCase()];
|
|
1033
|
+
const statePath = join(root, 'graph-runs', 'release-loop', `${runId}.json`);
|
|
1034
|
+
if (existsSync(statePath)) {
|
|
1035
|
+
const savedRun = readRecoverJson(statePath, 'release run 상태') as GraphRunState;
|
|
1036
|
+
const versionNode = savedRun.nodes.find((node) => node.nodeId === 'version-release');
|
|
1037
|
+
const recorded = lastJsonObject(versionNode?.output);
|
|
1038
|
+
const version = typeof recorded?.version === 'string' ? recorded.version : '';
|
|
1039
|
+
if (savedRun.graphId === 'release-loop' && savedRun.runId === runId && isReleaseVersion(version)) {
|
|
1040
|
+
const ledgerPath = join(root, 'release', version, 'resume-attempts', `${runId}.json`);
|
|
1041
|
+
if (existsSync(ledgerPath)) {
|
|
1042
|
+
const ledger = readRecoverJson(ledgerPath, 'resume attempts 원장') as { recoveries?: unknown };
|
|
1043
|
+
if (ledger.recoveries !== undefined && !Array.isArray(ledger.recoveries)) throw new Error(`release resume attempts ledger invalid: ${ledgerPath}`);
|
|
1044
|
+
const records = (ledger.recoveries ?? []) as RecoverStageRecord[];
|
|
1045
|
+
const matchesPick = (entry: RecoverStageRecord): boolean => Array.isArray(entry.pick)
|
|
1046
|
+
&& entry.pick.some((sha) => typeof sha === 'string' && sha.toLowerCase() === pick[0]);
|
|
1047
|
+
let completedIndex = -1;
|
|
1048
|
+
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
1049
|
+
const entry = records[index]!;
|
|
1050
|
+
if (matchesPick(entry) && entry.stage === 'resume' && entry.outcome === 'ok') {
|
|
1051
|
+
completedIndex = index;
|
|
1052
|
+
break;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (completedIndex >= 0) {
|
|
1056
|
+
let cycleStart = completedIndex;
|
|
1057
|
+
for (let index = completedIndex; index >= 0; index -= 1) {
|
|
1058
|
+
const entry = records[index]!;
|
|
1059
|
+
if (matchesPick(entry) && entry.stage === 'append-dry-run') {
|
|
1060
|
+
cycleStart = index;
|
|
1061
|
+
break;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
const completed = records[completedIndex]!;
|
|
1065
|
+
return { runId, version, branch: `release/${version}`, fromNode: completed.from ?? opts.from ?? RECOVER_FIRST_NODE,
|
|
1066
|
+
stages: records.slice(cycleStart, completedIndex + 1).filter(matchesPick), duplicate: true };
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
const result = await recoverReleaseRunLocked({ runId, pick,
|
|
1072
|
+
...(opts.from === undefined ? {} : { from: opts.from }),
|
|
1073
|
+
...(opts.commandRoot === undefined ? {} : { commandRoot: opts.commandRoot }),
|
|
1074
|
+
...(opts.partial === undefined ? {} : { partial: opts.partial }),
|
|
1075
|
+
...(opts.repoRoot === undefined ? {} : { repoRoot: opts.repoRoot }) }, deps, root);
|
|
1076
|
+
return { ...result, duplicate: false };
|
|
1077
|
+
}
|
|
@@ -1,9 +1,31 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { lastJsonObject, STOPPED_NODE_ERROR, type GraphRunState } from '../../src/graph-runner/runner.js';
|
|
2
3
|
import { publicWaiverReason } from './known-issues-node.js';
|
|
3
4
|
import { checkGateExecutorChoice, type GateExecutor } from './gate-executor.js';
|
|
4
5
|
|
|
5
6
|
export interface AcceptedRegression { id: string; note: string }
|
|
6
7
|
|
|
8
|
+
/** Match the graph runner's approval boundary before release resume performs tip refresh or writes an attempt. */
|
|
9
|
+
export function assertReleaseResumeApprovalBoundary(state: GraphRunState, from: string): void {
|
|
10
|
+
if (!Array.isArray(state.path)) return;
|
|
11
|
+
const start = state.path.indexOf(from);
|
|
12
|
+
if (start < 0 || state.path.lastIndexOf(from) !== start) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
for (let index = 0; index < state.nodes.length; index++) {
|
|
16
|
+
const record = state.nodes[index]!;
|
|
17
|
+
if (typeof record.decidedAt !== 'string') continue;
|
|
18
|
+
if (index >= start) throw new Error('cannot restart across an approval decision — start a new release run with the full gate');
|
|
19
|
+
if (!record.ok) throw new Error('cannot preserve a failed predecessor when resuming');
|
|
20
|
+
let decision: { nodeId?: unknown; decision?: unknown; decidedAt?: unknown };
|
|
21
|
+
try { decision = JSON.parse(readFileSync(`${state.statePath}.${index + 1}.decision.json`, 'utf8')); }
|
|
22
|
+
catch { throw new Error(`missing approval decision for ${record.nodeId}`); }
|
|
23
|
+
if (decision.nodeId !== record.nodeId || decision.decision !== 'approved' || decision.decidedAt !== record.decidedAt) {
|
|
24
|
+
throw new Error(`invalid approval decision for ${record.nodeId}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
7
29
|
/** Merge explicit public issues with the saved release input without changing the ledger shape. */
|
|
8
30
|
export function releaseResumeAcceptedRegressions(state: GraphRunState, additions: AcceptedRegression[], from?: string): AcceptedRegression[] {
|
|
9
31
|
if (!Array.isArray(additions) || additions.some((issue) => !issue || typeof issue.id !== 'string' || !issue.id.trim() || typeof issue.note !== 'string' || !issue.note.trim())) {
|
|
@@ -154,6 +176,32 @@ export interface GatePodPoolOverride {
|
|
|
154
176
|
previous: { gatePodPool: unknown };
|
|
155
177
|
}
|
|
156
178
|
|
|
179
|
+
/** One shard keeps its component identity while the run-time scheduler chooses a compatible member. */
|
|
180
|
+
export interface GateShardRerouteOverride {
|
|
181
|
+
kind: 'gate-shard-reroute'; at: string; from: string;
|
|
182
|
+
set: { gateRerouteShard: string; gateShardHost?: string; gateShardCapabilities?: Record<string, { os: 'darwin' | 'linux' }> };
|
|
183
|
+
previous: { gateRerouteShard: unknown; gateShardHost: unknown; gateShardCapabilities: unknown };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function releaseResumeGateShardReroute(state: GraphRunState, opts: {
|
|
187
|
+
from: string; shard: unknown; host?: unknown; os?: unknown; at?: string;
|
|
188
|
+
}): GateShardRerouteOverride {
|
|
189
|
+
const input = state.input as Record<string, unknown> | undefined;
|
|
190
|
+
if (!input || typeof input.gatePodPool !== 'string' || !input.gatePodPool.trim())
|
|
191
|
+
throw new Error('shard reroute requires the saved Pod gate pool');
|
|
192
|
+
if (opts.from !== 'gate' || !state.path.includes('gate')) throw new Error('shard reroute requires --from gate');
|
|
193
|
+
if (typeof opts.shard !== 'string' || !/^(?:auto|pod-\d+)$/.test(opts.shard))
|
|
194
|
+
throw new Error('--reroute-shard requires auto or pod-N');
|
|
195
|
+
if (opts.host !== undefined && (typeof opts.host !== 'string' || !opts.host.trim())) throw new Error('--shard-host requires a pool context');
|
|
196
|
+
if (opts.os !== undefined && (opts.shard === 'auto' || opts.os !== 'darwin' && opts.os !== 'linux'))
|
|
197
|
+
throw new Error('--shard-os requires pod-N and darwin or linux');
|
|
198
|
+
return { kind: 'gate-shard-reroute', at: opts.at ?? new Date().toISOString(), from: opts.from,
|
|
199
|
+
set: { gateRerouteShard: opts.shard, ...(opts.host ? { gateShardHost: opts.host.trim() } : {}),
|
|
200
|
+
...(opts.os ? { gateShardCapabilities: { [opts.shard]: { os: opts.os } } } : {}) },
|
|
201
|
+
previous: { gateRerouteShard: input.gateRerouteShard ?? null, gateShardHost: input.gateShardHost ?? null,
|
|
202
|
+
gateShardCapabilities: input.gateShardCapabilities ?? null } };
|
|
203
|
+
}
|
|
204
|
+
|
|
157
205
|
export function releaseResumeGatePodPool(state: GraphRunState, opts: { from: string; pool: unknown; at?: string }): GatePodPoolOverride {
|
|
158
206
|
const input = state.input;
|
|
159
207
|
if (!input || typeof input !== 'object' || Array.isArray(input) || typeof (input as { version?: unknown }).version !== 'string') throw new Error('release run has no input');
|
|
@@ -42,7 +42,10 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
|
|
|
42
42
|
return finishNode(node!, version, { outcome: 'fail', verdict: 'fail', summary });
|
|
43
43
|
}
|
|
44
44
|
const result = await applyReleaseTrafficEvent(context, event, options);
|
|
45
|
-
|
|
45
|
+
// Replaying the terminal restore after this run already restored its pre-cut traffic is a successful no-op.
|
|
46
|
+
const alreadyRestored = event === 'failed' && result.reason === 'already-restored';
|
|
47
|
+
const applied = result.applied || alreadyRestored;
|
|
48
|
+
if (!applied) {
|
|
46
49
|
const consequence = event === 'failed' && result.reason === 'published-keeps-full'
|
|
47
50
|
? 'npm 발행은 완료됐으므로 전체 트래픽을 유지합니다. 후속 발행 단계 실패를 확인하세요.'
|
|
48
51
|
: event === 'verify-complete' || event === 'npm-published'
|
|
@@ -52,8 +55,8 @@ export async function runTrafficNode(node = process.argv[2], env: NodeJS.Process
|
|
|
52
55
|
: 'gate는 성공했으나 트래픽 감속이 적용되지 않았습니다.';
|
|
53
56
|
(deps.report ?? reportFailure)(`RELEASE-POSTPUB-TRAFFIC ${version} ${runId || 'unknown'} ${node} 무시 — ${result.reason ?? 'traffic state unchanged'}. ${consequence}`);
|
|
54
57
|
}
|
|
55
|
-
return finishNode(node!, version, { outcome:
|
|
56
|
-
summary: `${event} ${
|
|
58
|
+
return finishNode(node!, version, { outcome: applied ? 'ok' : 'fail', verdict: applied ? 'pass' : 'fail',
|
|
59
|
+
summary: `${event} ${alreadyRestored ? 'already-restored' : applied ? 'applied' : result.reason ?? 'ignored'}` });
|
|
57
60
|
} catch (error) {
|
|
58
61
|
const detail = errorResult(error).summary;
|
|
59
62
|
const isLockTimeout = detail.startsWith('traffic lock timeout:');
|
|
@@ -49,6 +49,7 @@ export interface ReleaseLoopConfig {
|
|
|
49
49
|
export interface ReleaseRunInput extends ReleaseLoopConfig {
|
|
50
50
|
version: string;
|
|
51
51
|
previousVersion: string;
|
|
52
|
+
triggerEvent?: { id: string; type: 'release.cut.due'; version: string; cutAt: string; at: string };
|
|
52
53
|
cutCommit?: string;
|
|
53
54
|
/** Required for the pod executor (the default); a host-executor run may have none. */
|
|
54
55
|
gatePodPool?: string;
|
|
@@ -143,7 +144,7 @@ export function buildReleaseRunInput(version: string, deps: UnattendedReleaseDep
|
|
|
143
144
|
|
|
144
145
|
/** Fail closed at the entry boundary, before any graph node can change a release. */
|
|
145
146
|
export async function runUnattendedRelease(
|
|
146
|
-
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
|
|
147
|
+
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string; triggerEvent?: ReleaseRunInput['triggerEvent'] },
|
|
147
148
|
deps: UnattendedReleaseDeps = {},
|
|
148
149
|
): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
|
|
149
150
|
// A dry run only builds the input and mutates nothing, so it neither creates nor waits on the OP-seat lock.
|
|
@@ -154,7 +155,7 @@ export async function runUnattendedRelease(
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
async function runUnattendedReleaseLocked(
|
|
157
|
-
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string },
|
|
158
|
+
opts: { version: string; dryRun?: boolean; cutCommit?: string; forceFreeze?: boolean; forceNew?: boolean; mainCut?: boolean; prerelease?: PrereleaseKind; publishAt?: string; gateExecutor?: string; gateHost?: string; triggerEvent?: ReleaseRunInput['triggerEvent'] },
|
|
158
159
|
deps: UnattendedReleaseDeps,
|
|
159
160
|
): Promise<{ input: ReleaseRunInput; dryRun: boolean; state?: GraphRunState }> {
|
|
160
161
|
// RELEASE-BRANCH (10-06): the default cut is a release branch, so main keeps landing and the run no longer needs a
|
|
@@ -171,6 +172,7 @@ async function runUnattendedReleaseLocked(
|
|
|
171
172
|
}
|
|
172
173
|
const version = resolveRunVersion(opts.version, opts.prerelease, deps);
|
|
173
174
|
const input = buildReleaseRunInput(version, deps, opts.cutCommit, { executor: opts.gateExecutor, host: opts.gateHost });
|
|
175
|
+
if (opts.triggerEvent) input.triggerEvent = opts.triggerEvent;
|
|
174
176
|
if (opts.publishAt !== undefined) {
|
|
175
177
|
const invalid = publishAtError(opts.publishAt);
|
|
176
178
|
if (invalid) throw new Error(invalid);
|
|
@@ -465,14 +465,19 @@ export function main(args: string[] = process.argv.slice(2), repo = process.cwd(
|
|
|
465
465
|
const location = process.env.ELANOUS_GRAPH_CONTEXT;
|
|
466
466
|
const context = JSON.parse(location.trimStart().startsWith('{') ? location : readFileSync(location, 'utf8')) as {
|
|
467
467
|
runId?: unknown; input?: { version?: unknown; cutCommit?: unknown; forceFreeze?: unknown; branchCut?: unknown };
|
|
468
|
-
outputs?: Record<string, { commit?: unknown; base?: unknown; branch?: unknown } | undefined> };
|
|
468
|
+
outputs?: Record<string, { outcome?: unknown; commit?: unknown; base?: unknown; branch?: unknown } | undefined> };
|
|
469
469
|
if (context.runId !== undefined) {
|
|
470
470
|
if (typeof context.runId !== 'string') throw new Error('runId must be a string');
|
|
471
471
|
runId = context.runId;
|
|
472
472
|
}
|
|
473
473
|
if (!version && typeof context.input?.version === 'string') version = context.input.version;
|
|
474
474
|
forceFreeze = context.input?.forceFreeze === true;
|
|
475
|
-
|
|
475
|
+
const releasedBranch = context.outputs?.['version-release']?.branch;
|
|
476
|
+
branchCut = context.input?.branchCut === true
|
|
477
|
+
|| (kind === 'dev-bump'
|
|
478
|
+
&& context.outputs?.['version-release']?.outcome === 'ok'
|
|
479
|
+
&& typeof version === 'string'
|
|
480
|
+
&& releasedBranch === `release/${version}`);
|
|
476
481
|
if (kind === 'release' && cutCommit === undefined && context.input?.cutCommit !== undefined) {
|
|
477
482
|
if (typeof context.input.cutCommit !== 'string') throw new Error('cutCommit must be a commit SHA');
|
|
478
483
|
cutCommit = context.input.cutCommit;
|