elanous 0.2.28 → 0.2.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{y1ltt7oCZxVpC6zVPhZ2O → LSXfY0u9QtfDbejY0Ukf_}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{ab4d57c2.e90bc2cb31949045.js → 0bb0ff36.b8d418980951e5ba.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +287 -0
- package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +62 -0
- package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5492-e05e26a033ba80d2.js → 1335-6731f392c35216eb.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +23 -0
- package/apps/pwa/out/_next/static/chunks/1834-50625904996a8fa8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +149 -0
- package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +41 -0
- package/apps/pwa/out/_next/static/chunks/2001.6c7e0fd02190886c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8578.dff22d988cc2b106.js → 2213.2ccbada92c33ea0f.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5081.a240b63fad28663a.js → 2871.0bca6137343b3f76.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2898.fd781bc7cf01a7b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2940-619bfd8ef337c611.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2846.d6a433b8663819a5.js → 2985.39ccdecd36848b12.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +115 -0
- package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +64 -0
- package/apps/pwa/out/_next/static/chunks/{f4dfbfc0.a69fb12f854882aa.js → 3267c0e6.d34854e7b3984a6e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3278.2e5d15b53a4091c8.js +2 -0
- package/apps/pwa/out/_next/static/chunks/3541-ce3a5424ed5ad7f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3143.257997a5f1ed79df.js → 3902.e1e71aaac850a045.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{16e57f67.3ea19e893461e611.js → 409f1e57.d0649fc47691ebfe.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1146.80d15ea7c6e6ee95.js → 4111.2bfedbb233ad206b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1516.83dd2c1b86b4e507.js → 4174.92ed04922709ac9e.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +36 -0
- package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +286 -0
- package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +77 -0
- package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +226 -0
- package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9938.b23e4d4dfa0114cd.js → 4999.c064a4000c2a9e94.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5173-233188df2f42c626.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1157.f08bf2b70915e176.js → 5304.04d24756f242a094.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{7752.5cbdd0b433c54af9.js → 5307.60f2209e809bd627.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9917-9e26abc5f7772735.js → 5458-16cd7a2b51b74c8c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6196.34d6b229079e9e28.js → 5525.86debf231e181813.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4593.10f5b9bd702d4a6c.js → 5543.a438b8ab5a13ea47.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +74 -0
- package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +94 -0
- package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7483.ba01c6c209b81bba.js → 6092.a7f99e68b188ca66.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/60fb96c9.360b01d8d4927b7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9974.e77b4ff4884557b3.js → 6157.0c9af021de4fb31b.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3309.364573ceedbdfd5f.js → 6323.8672b4817a306928.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{94.4cfed21c0b781a06.js → 6433.fc573af22bbc9ab7.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{949.1e7d45ccb71580aa.js → 6517.4147878ac06d584d.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +32 -0
- package/apps/pwa/out/_next/static/chunks/6544cb48.a87fe20ddf137be2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +104 -0
- package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2528.3868070ef1be69f9.js → 6767.ea607cf0c20a25af.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +93 -0
- package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7371.80c05c653b08e907.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7391.ebae168025c91b6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1743.563d3168e95825ef.js → 7502.ccd5b8065545edd0.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +8 -0
- package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5408.377c8c543635b81f.js → 7862.30a8d6482bb238eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7947-d0759430f8042240.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{65ebff95.081daeff622d238e.js → 7a1887c4.f36b88fba31da8fd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2697.76ca2ac3efef6f3b.js → 8018.a32663f04a6fc5f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8097-92f87bd108322f66.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +1 -0
- package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +24 -0
- package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4204.a409ab6250c0f6a8.js → 9221.7d22eaf353a2d4e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5785-ea8d2c633b6688e3.js → 9279-d28c5b7754d010ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9398-6aed28f0a96d0d6f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/997ce8a5-2ad501927003b41b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9311.24c8f5ea793253dd.js → 9989.3694300e5f000b14.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-bf3634a117322df7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-3535a4a69f1fafaa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b9f51447f757fbf2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-fa46d4588e8442c2.js → page-2ebb8fd59b9461f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a2fc4e49a4fa4ba8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-5a503541353e60a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-6025101384e361a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f4b630cc8b5273cd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a645f00a06483e9a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-61ac22cbab1ee4ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e9bab7413b028b28.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c992eafe028b642e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-e547e6ce55f1fb33.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d27e1b9850fd54bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-805ac2e137b7a121.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-0fec29c7ba01894b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-339a07410cd6f36b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-ced0bcfc6542b3fc.js → page-349f2752d8cd37ad.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-d5c6dcf31c25cecc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2fd93ed8c9f16580.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9b564dbff0fefc35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-62f4475d3c38fb9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-814ba51385225776.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-397d479626e5a459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-af1bba4e3251b728.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3ad3be5e7d3fdce9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-f219ccdbc0bf27df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4da93ad35d4a8d28.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-456666766e5ce6fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-7404e377e8adb8c5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-14456240ed2a36ad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-3a658d5fa6d16a25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-0157de6e85f8bb6b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-cb249e81ed339318.js → page-0a07363b130182a1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-841d73d4ac44ed49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-6f43a3d1146e6377.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-521ee23ac8c0abfa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-a9992ee285efaa83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-3d48e85fa89ec04f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d2ced71132fac9b2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-f08358bdf251adb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-233c71cd5f68f0d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-70151d78de2f375e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-dcac33a3e2cb72c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-149dc1caba411bb9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0448145ccd5c45c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9cb0c444903da65d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-17e504c396e0e389.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6def8a8e.c81f9ca41028eccf.js → b313b242.cb1265eea908db61.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{e87d89b9.2e71c0ae475d28a1.js → e5012ffe.f727e23c231f4342.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{e117f801.e92bf10242653497.js → ebc7d829.264a0bcb6c750b50.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +1 -0
- package/apps/pwa/out/_next/static/css/{72becfd42807b50d.css → 3f5fc6f96e2cf7b2.css} +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/bin/elanous.mjs +7 -3
- package/graphs/context/nightly-condense.yaml +1 -0
- package/graphs/intake/intake-daily.yaml +8 -1
- package/graphs/intake/recipes.yaml +5 -0
- package/graphs/lecture/lecture-note.yaml +2 -2
- package/graphs/ops/harness-gc.yaml +1 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +15 -7
- package/graphs/units/recipes.yaml +3 -0
- package/graphs/units/release-gate-recheck.yaml +15 -0
- package/package.json +2 -1
- package/plugins/agent-team/agents/research.md +1 -0
- package/scripts/check-native-tool-wiring.ts +3 -2
- package/scripts/ci-android-unit-tests.ts +16 -0
- package/scripts/ci-typecheck-changed.ts +40 -11
- package/scripts/coord-channel-watch.sh +45 -19
- package/scripts/coord-role-pack.ts +222 -0
- package/scripts/cron-run.ts +96 -3
- package/scripts/detect-test-interference.ts +38 -5
- package/scripts/gate-pr-cache.ts +65 -0
- package/scripts/gh-surface/capture.ts +126 -0
- package/scripts/gh-surface/gh +40 -0
- package/scripts/import-graph-scc.ts +38 -0
- package/scripts/install.ps1 +19 -0
- package/scripts/intake-social.ts +181 -0
- package/scripts/lecture-note/node.ts +71 -4
- package/scripts/lib/android-toolchain.ts +32 -0
- package/scripts/lib/deterministic-env.ts +62 -0
- package/scripts/lib/gate-test-env.ts +19 -0
- package/scripts/lib/morning-report-options.ts +41 -0
- package/scripts/measure-goal-grounding-corpus.ts +2 -1
- package/scripts/pr-land-extra-remote-gates.ts +49 -0
- package/scripts/ratchets/test-interference-patterns.allowlist.txt +257 -0
- package/scripts/ratchets/test-interference-patterns.ts +367 -0
- package/scripts/release-loop/cut-branch.ts +4 -3
- package/scripts/release-loop/export-check-node.ts +0 -1
- package/scripts/release-loop/fixtures/warm-failure-assertion/sample.junit.xml +8 -0
- package/scripts/release-loop/fixtures/warm-job-029/sample.json +1 -0
- package/scripts/release-loop/fixtures/warm-job-029/sample.junit.xml +10 -0
- package/scripts/release-loop/gate-batch-safe.ts +23 -0
- package/scripts/release-loop/gate-closure.ts +12 -8
- package/scripts/release-loop/gate-diff.ts +1 -1
- package/scripts/release-loop/gate-history.ts +422 -0
- package/scripts/release-loop/gate-host-mirror.ts +66 -0
- package/scripts/release-loop/gate-node.ts +806 -94
- package/scripts/release-loop/gate-partial.ts +106 -9
- package/scripts/release-loop/gate-pr-cache.ts +66 -0
- package/scripts/release-loop/gate-prewarm-plan.ts +66 -0
- package/scripts/release-loop/gate-r2-verdict.ts +189 -0
- package/scripts/release-loop/gate-recheck-plan-node.ts +52 -0
- package/scripts/release-loop/gate-triage-node.ts +190 -0
- package/scripts/release-loop/gate-warm-artifacts.ts +253 -0
- package/scripts/release-loop/import-graph-scc-run.ts +54 -0
- package/scripts/release-loop/import-graph-scc.ts +215 -0
- package/scripts/release-loop/node-verdict.ts +2 -0
- package/scripts/release-loop/probe-pod-host-dirs-live.ts +113 -0
- package/scripts/release-loop/resume-release.ts +33 -4
- package/scripts/release-loop/resume.ts +13 -5
- package/scripts/release-loop/spawn-lane.ts +51 -0
- package/scripts/release-loop/tui-node-checks.ts +23 -0
- package/scripts/release-loop/tui-sim-node.ts +3 -2
- package/scripts/release-loop/unattended-release.ts +16 -0
- package/scripts/release-loop/version-node.ts +62 -4
- package/scripts/test-deterministic.ts +46 -73
- package/scripts/train-reach-grouping.ts +48 -0
- package/scripts/tsc-incremental-cache.ts +86 -0
- package/scripts/tsc-incremental-diagnostics.ts +76 -0
- package/scripts/tsc-main-safety-net.ts +2 -2
- package/scripts/usage-rollup.ts +11 -3
- package/src/acp/client.ts +4 -1
- package/src/acp/core-turn-bridge.ts +145 -24
- package/src/acp/elanous-extensions.ts +2 -0
- package/src/acp/server.ts +2 -0
- package/src/ad-pipeline/run.ts +4 -2
- package/src/agent/ref-grounding.ts +18 -0
- package/src/agent/runner.ts +169 -3
- package/src/agent/subagent-callable.ts +3 -2
- package/src/agent-mission/review-loop.ts +6 -3
- package/src/agent-substrate/acp-implementer.ts +14 -2
- package/src/agent-substrate/pr-reviewer.ts +41 -21
- package/src/agent-substrate/self-review-cli.ts +30 -4
- package/src/autopilot/pr-manager.ts +62 -7
- package/src/briefing/brief-items.ts +24 -22
- package/src/budget/pty-usage-reemit.ts +7 -0
- package/src/budget/run-usage-rollup.ts +9 -0
- package/src/budget/usage-daily-store.ts +98 -0
- package/src/capture/tui-self-report.ts +37 -4
- package/src/chat/chat-cli.ts +10 -2
- package/src/chat/index.ts +107 -20
- package/src/chat/input-control-key.ts +5 -3
- package/src/chat/pickers/modal-runtime.ts +1 -0
- package/src/chat/pickers/modals.ts +3 -1
- package/src/chat/tool-interrupt-behavior.ts +38 -0
- package/src/chat/tool-render/dispatch.ts +5 -1
- package/src/chat/turn-typeahead.ts +191 -29
- package/src/cli/__fixtures__/agent-help-before/agent.txt +4 -2
- package/src/cli/agent-cli.ts +41 -13
- package/src/cli/config-audit.ts +123 -0
- package/src/cli/dashboard-name-flag.ts +48 -0
- package/src/cli/decisions-cli.ts +80 -6
- package/src/cli/doctor-cli.ts +80 -0
- package/src/cli/log-event-names.ts +19 -0
- package/src/cli/mcp-login.ts +21 -5
- package/src/cli/msg-cli.ts +133 -2
- package/src/cli/pr-cli.ts +546 -112
- package/src/cli/pr-land-heavy-remote.ts +233 -0
- package/src/cli/pr-land-overlap-evidence.ts +167 -14
- package/src/cli/pr-land-pod-shadow-job.ts +83 -0
- package/src/cli/pr-land-pod-shadow.ts +16 -3
- package/src/cli/pr-land-virtual-rebase.ts +108 -0
- package/src/cli/pty-takeover-cli.ts +125 -16
- package/src/cli/release-cli.ts +122 -14
- package/src/cli/remote-resolve.ts +2 -1
- package/src/cli/seat-cli.ts +164 -19
- package/src/cli/self-update.ts +5 -2
- package/src/cli/tasks-cli.ts +105 -21
- package/src/cli/usage-cli.ts +146 -12
- package/src/cli/usage-daily-cli.ts +84 -0
- package/src/context-bus/context-first.ts +13 -0
- package/src/context-bus/context-now.ts +95 -7
- package/src/context-bus/ops-events.ts +75 -0
- package/src/control-plane/lease-holder.ts +12 -8
- package/src/control-plane/ledger.ts +163 -4
- package/src/control-plane/server.ts +91 -11
- package/src/core-turn/auth-rejected-notice.ts +41 -0
- package/src/core-turn/run-core-turn.ts +14 -2
- package/src/core-turn/run-goal-loop.ts +239 -4
- package/src/core-turn/seat-turn-cap.ts +79 -0
- package/src/core-turn/short-question.ts +78 -13
- package/src/core-turn/types.ts +10 -0
- package/src/dashboard/acp-active-model.ts +40 -0
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/auto-compact-runtime.ts +2 -0
- package/src/dashboard/boot-banner.ts +80 -0
- package/src/dashboard/dashboard-status-lines.ts +11 -0
- package/src/dashboard/detail-viewer-runtime.ts +15 -2
- package/src/dashboard/event-loop-stall-monitor.ts +4 -0
- package/src/dashboard/goal-loop-thinking.ts +16 -0
- package/src/dashboard/hitl-boot.ts +9 -4
- package/src/dashboard/index.ts +448 -998
- package/src/dashboard/input/chat-main-entry-runtime.ts +2 -0
- package/src/dashboard/input/chat-main-log-actions.ts +4 -0
- package/src/dashboard/input/chat-main-log-command.ts +12 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +176 -4
- package/src/dashboard/input/chat-main-session.ts +14 -3
- package/src/dashboard/input/chat-main-slash-command.ts +7 -0
- package/src/dashboard/input/chat-main-turn.ts +14 -4
- package/src/dashboard/input/compact-prepass-recovery.ts +130 -0
- package/src/dashboard/input/dashboard-chord-actions.ts +10 -1
- package/src/dashboard/input/dashboard-chord-runtime.ts +2 -0
- package/src/dashboard/input/external-input-host.ts +71 -0
- package/src/dashboard/input/internal-turn.ts +261 -0
- package/src/dashboard/input/log-fold-binding.ts +18 -0
- package/src/dashboard/input/mouse-wiring.ts +17 -4
- package/src/dashboard/input/seat-autowake.ts +318 -0
- package/src/dashboard/input/turn-steer-bridge.ts +164 -0
- package/src/dashboard/log-click-runtime.ts +5 -3
- package/src/dashboard/loop-scheduler.ts +72 -0
- package/src/dashboard/running-build-status.ts +74 -0
- package/src/dashboard/seat-boot-preamble.ts +131 -0
- package/src/dashboard/seat-local-instructions.ts +305 -0
- package/src/dashboard/skill-runtime.ts +2 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +220 -1203
- package/src/dashboard/slash-runtime/help-from-registry.ts +2 -1
- package/src/dashboard/slash-runtime/index.ts +13 -0
- package/src/dashboard/slash-runtime/model-choices.ts +35 -0
- package/src/dashboard/term-slash-runtime.ts +15 -0
- package/src/dashboard/turn-busy-teardown.ts +50 -0
- package/src/dashboard/turn-preamble.ts +30 -2
- package/src/dashboard/turn-session-layers.ts +176 -0
- package/src/dashboard/turn-stream-formatter.ts +14 -6
- package/src/dashboard/turn-stream-presentation-applier.ts +8 -0
- package/src/decisions/decision-ledger.ts +45 -4
- package/src/discord-self-message.ts +4 -4
- package/src/domains/release-tool.ts +26 -10
- package/src/domains/surface-events.ts +4 -1
- package/src/domains/watch-default-scheduler.ts +8 -9
- package/src/field-note/transcribe.ts +17 -0
- package/src/fold-stack.ts +24 -1
- package/src/git-fs/dirty.ts +32 -4
- package/src/git-fs/index.ts +43 -1
- package/src/github/remote-call.ts +81 -0
- package/src/goals/registry.ts +46 -6
- package/src/goals/types.ts +7 -1
- package/src/graph-runner/runner.ts +2 -0
- package/src/grok/credential.ts +26 -0
- package/src/harness/harness-clean.ts +2 -1
- package/src/harness/harness-cli-command.ts +52 -5
- package/src/harness/harness-gc.ts +34 -6
- package/src/harness/harness-incidents.ts +57 -20
- package/src/harness/harness-pod-dispatch.ts +87 -10
- package/src/harness/harness-queue.ts +53 -4
- package/src/harness/harness-stop.ts +4 -3
- package/src/harness/harness-substrate-default.ts +78 -7
- package/src/harness/harness-worktree-auto.ts +2 -1
- package/src/harness/harness-worktrees.ts +3 -2
- package/src/hooks/dispatch.ts +2 -1
- package/src/index.ts +166 -26
- package/src/input/external-input-queue.ts +251 -0
- package/src/intake-plane/slash.ts +14 -3
- package/src/interactive-terminal-modal.ts +13 -0
- package/src/keybindings.ts +4 -1
- package/src/llm/route-decision.ts +25 -0
- package/src/llm/tool-loop-steered-rejection.ts +33 -0
- package/src/llm-stream-retry-display.ts +59 -0
- package/src/llm.ts +391 -74
- package/src/log-entry.ts +3 -3
- package/src/log-pane/search.ts +29 -4
- package/src/loops/agent-reply.ts +256 -0
- package/src/loops/budget.ts +37 -7
- package/src/loops/consensus.ts +348 -0
- package/src/loops/loop-cli.ts +3 -2
- package/src/loops/msg-address.ts +57 -0
- package/src/loops/msg-wake.ts +484 -0
- package/src/loops/observe.ts +19 -3
- package/src/loops/orchestrator/inbox.ts +60 -3
- package/src/loops/orchestrator/tick.ts +80 -6
- package/src/loops/orchestrator/traffic.ts +64 -1
- package/src/loops/registry.ts +72 -4
- package/src/market/signed-index.ts +14 -0
- package/src/mcp/mcp-oauth.ts +54 -5
- package/src/memory.ts +20 -5
- package/src/models/prompts.ts +28 -0
- package/src/msg/msg-sse-client.ts +223 -0
- package/src/msg/msg-store.ts +86 -2
- package/src/mss/logging/log-store.ts +225 -1
- package/src/native-tool-catalog.ts +125 -0
- package/src/nexus/DAEMON-THIN-inventory-2026-10-15.md +122 -0
- package/src/nexus/api/http-server.ts +59 -15
- package/src/nexus/api/loop-resources.ts +19 -3
- package/src/nexus/api/ops-api.ts +13 -0
- package/src/nexus/api/project-autopsy.ts +127 -0
- package/src/nexus/api/terminals.ts +3 -0
- package/src/nexus/api/workflow-approvals.ts +2 -0
- package/src/nexus/daemon-thin-inventory.md +37 -0
- package/src/nexus/index.ts +70 -0
- package/src/nexus/onboarding/first-boot-wizard.ts +56 -2
- package/src/nexus/state/state.ts +1 -0
- package/src/oauth/codex-account-store.ts +15 -3
- package/src/oauth/codex-billing-route.ts +31 -0
- package/src/oauth/store.ts +32 -7
- package/src/onboarding/first-run.ts +28 -3
- package/src/ops/overview-cache.ts +16 -4
- package/src/ops/overview-collect.ts +19 -2
- package/src/ops/resources.ts +82 -0
- package/src/panes/log-pane.ts +67 -8
- package/src/persona/presets.ts +44 -0
- package/src/plugins/maker/connector-maker.ts +41 -0
- package/src/plugins/maker/skill-maker.ts +42 -0
- package/src/plugins/maker/wizard-generate.ts +6 -4
- package/src/pr-tool/census.ts +288 -0
- package/src/preview/terminal.ts +14 -1
- package/src/prompt-cache/types.ts +2 -0
- package/src/prompt-enhance/enhance.ts +24 -8
- package/src/prompt-library/chat-conciseness.ts +13 -0
- package/src/prompt-library/universal-preamble.ts +283 -61
- package/src/pty-shell/pty-control-ipc.ts +4 -1
- package/src/pty-shell/pty-manifest.ts +77 -5
- package/src/pty-shell/registry.ts +19 -2
- package/src/pty-shell/tui-run-state.ts +253 -0
- package/src/release-loop/gate-host-reservation.ts +97 -0
- package/src/release-loop/landing-freeze.ts +5 -0
- package/src/release-loop/light-rc.ts +32 -4
- package/src/release-loop/placement.ts +92 -1
- package/src/release-loop/rehearsal-shard-plan.ts +149 -0
- package/src/release-loop/rehearsal.ts +73 -17
- package/src/render.ts +5 -1
- package/src/repl/index.ts +20 -2
- package/src/seat-loop/seat-knowhow.ts +282 -0
- package/src/seat-loop/seat-loop.ts +41 -2
- package/src/seat-loop/seat-report.ts +42 -2
- package/src/seat-loop/traffic-signal.ts +3 -0
- package/src/self-dev/ask-launch-flow.ts +24 -3
- package/src/self-dev/decompose-compose-graph.ts +28 -27
- package/src/self-dev/decompose-proposal.ts +53 -38
- package/src/self-dev/decompose.ts +17 -0
- package/src/self-dev/dev-pipeline.ts +31 -2
- package/src/self-dev/draft-residue.ts +24 -9
- package/src/self-dev/draft-sweep.ts +59 -4
- package/src/self-dev/external-call-zero.ts +77 -0
- package/src/self-dev/launch-preflight-text.ts +6 -0
- package/src/self-dev/launch-preflight.ts +25 -3
- package/src/self-dev/local-supervisor-judge.ts +185 -0
- package/src/self-dev/run-supervisor.ts +300 -24
- package/src/self-dev/run-types.ts +20 -0
- package/src/self-dev/shard-aggregate.ts +15 -2
- package/src/self-dev/shard-plan.ts +47 -0
- package/src/self-dev/shard-run.ts +135 -12
- package/src/self-dev/spawn-lane-files.ts +19 -0
- package/src/self-health/signatures.ts +132 -0
- package/src/self-implement/app-api-budget.ts +51 -11
- package/src/self-implement/config-gate.ts +2 -1
- package/src/self-implement/doctor-visit.ts +118 -0
- package/src/self-implement/expose-gate.ts +2 -1
- package/src/self-implement/gate-artifacts.ts +405 -0
- package/src/self-implement/gate-cli.ts +212 -18
- package/src/self-implement/gate-remote.ts +110 -25
- package/src/self-implement/gate-scenario.ts +85 -0
- package/src/self-implement/goal-author-decompose.ts +80 -16
- package/src/self-implement/goal-author.ts +270 -76
- package/src/self-implement/goal-authoring-grounding.ts +1 -1
- package/src/self-implement/goal-file-name.ts +65 -0
- package/src/self-implement/graph-authority.ts +38 -6
- package/src/self-implement/graph-overlay-condition.ts +92 -1
- package/src/self-implement/ground-goal.ts +2 -1
- package/src/self-implement/heal-triage.ts +79 -0
- package/src/self-implement/host-regate.ts +74 -2
- package/src/self-implement/orchestrator.ts +14 -123
- package/src/self-implement/pr-title.ts +123 -0
- package/src/self-implement/review-mustfix-class.ts +115 -0
- package/src/self-implement/run-checkpoint.ts +235 -0
- package/src/self-implement/run-ledger.ts +266 -17
- package/src/self-implement/run-stop.ts +4 -3
- package/src/self-implement/run-ttl.ts +28 -4
- package/src/self-implement/running-runs.ts +10 -6
- package/src/self-implement/seams.ts +2 -0
- package/src/self-implement/self-implement-runtime.ts +32 -0
- package/src/self-implement/shadow-stage.ts +7 -6
- package/src/self-implement/stop-autoheal.ts +2 -1
- package/src/self-implement/train-gate-remote.ts +37 -8
- package/src/self-implement/train-reach-grouping.ts +157 -0
- package/src/self-implement/train-scenario-layers.ts +133 -0
- package/src/self-implement/uncommitted-work.ts +2 -0
- package/src/session/pending-input.ts +81 -11
- package/src/session/session-start.ts +224 -0
- package/src/session-runtime/index.ts +7 -1
- package/src/session-runtime/parallel-dispatch.ts +22 -5
- package/src/skills/tools/dev-harness.ts +6 -4
- package/src/skills/tools/ops-ledger.ts +707 -0
- package/src/skills/tools/persistent-grounding.ts +47 -7
- package/src/skills/tools/shell.ts +117 -1
- package/src/status/bar.ts +32 -2
- package/src/status/status-provider-info.ts +58 -0
- package/src/steward/resync-tick.ts +114 -23
- package/src/steward/ta-repair-event-collect.ts +158 -0
- package/src/task-agent/card-evidence.ts +25 -1
- package/src/task-agent/green-sweep.ts +8 -3
- package/src/task-agent/judge.ts +76 -0
- package/src/task-agent/land-queue.ts +79 -0
- package/src/task-agent/live-moves.ts +30 -9
- package/src/task-agent/postland-card.ts +5 -0
- package/src/task-agent/prelaunch-already-done.ts +98 -0
- package/src/task-agent/real-launch-guard.ts +37 -0
- package/src/task-agent/rejudge-on-head.ts +93 -13
- package/src/task-agent/remote-rejudge-bind.ts +62 -13
- package/src/task-agent/repair-cycle.ts +58 -7
- package/src/task-agent/repair-worker.ts +7 -0
- package/src/task-agent/run-identity.ts +200 -0
- package/src/task-agent/shadow.ts +50 -3
- package/src/task-agent/task-hand.ts +158 -5
- package/src/task-agent/train-landing.ts +77 -0
- package/src/task-agent/work-order.ts +104 -0
- package/src/task-orchestrator/surfaces/kubectl-transient-retry.ts +51 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +138 -22
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +88 -19
- package/src/task-orchestrator/surfaces/pod-host-acp-review.ts +2 -1
- package/src/task-orchestrator/surfaces/pod-image-source.ts +2 -1
- package/src/task-orchestrator/surfaces/pod-install-slots.ts +122 -15
- package/src/task-orchestrator/surfaces/pod-lease.ts +20 -6
- package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +169 -0
- package/src/task-orchestrator/surfaces/pod-mirror-seed.ts +103 -0
- package/src/task-orchestrator/surfaces/pod-result-marker-wait.ts +33 -0
- package/src/task-orchestrator/surfaces/pod-reuse-cleanup.ts +63 -0
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +19 -3
- package/src/task-orchestrator/surfaces/pod-warm-switch.ts +141 -0
- package/src/task-orchestrator/surfaces/pod-warm-ta-shadow.ts +85 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +153 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +208 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +237 -46
- package/src/telegram.ts +2 -4
- package/src/terminal/session-registry.ts +6 -0
- package/src/thinking-line.ts +12 -3
- package/src/tool-runtime/index.ts +4 -0
- package/src/tool-runtime/ops-ledger-runtimes.ts +84 -0
- package/src/tool-runtime/ref-consult-runtime.ts +17 -2
- package/src/tool-runtime/run-shell-runtime.ts +4 -1
- package/src/tool-runtime/self-cognition-runtimes.ts +5 -1
- package/src/tui/input/pickers.ts +732 -0
- package/src/tui/slash/mission.ts +492 -0
- package/src/tui/slash/term.ts +877 -0
- package/src/tui-client/dashboard-session.ts +9 -0
- package/src/tui.ts +53 -0
- package/src/user-config/gemini-server-tools.ts +9 -0
- package/src/user-config.ts +106 -21
- package/src/util/spawn-sync-output.ts +7 -0
- package/src/ux-sim/fake-bin.ts +154 -0
- package/src/ux-sim/fake-clock.ts +36 -0
- package/src/ux-sim/fake-llm.ts +29 -0
- package/src/ux-sim/headless-tui.ts +72 -0
- package/src/ux-sim/in-process-cli.ts +176 -0
- package/src/ux-sim/temp-git-remote.ts +82 -0
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/vlog.ts +25 -7
- package/apps/pwa/out/_next/static/chunks/1027.a0f04663d24c2779.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1038.676007ae505822e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1113.a37eadf7f740d955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/112-fa7c526c20ce1405.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1144.5fa1fa1d48dbcd12.js +0 -36
- package/apps/pwa/out/_next/static/chunks/119b405e.8fdd0e44d1ff37aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1296.04560d98e73fbf51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1314-f604ba013ec535b0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1454-29d1d773d5996a49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1464.f3209af0d8ae4ff2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1729-d73dd25e564679d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1843.5924c24295243261.js +0 -27
- package/apps/pwa/out/_next/static/chunks/1987.e28149724b92918b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2066-5ca18ffa5a4be68b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2240.d10a66695155fd93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2391.e2cbc98d4abbb41a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2566-734cb83bbd405886.js +0 -1
- package/apps/pwa/out/_next/static/chunks/275.0e6bad414729bfa0.js +0 -32
- package/apps/pwa/out/_next/static/chunks/2803-33bdae771855a8d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3259-86f3be56e43eaf6a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3280-e177c33bfbdcfb0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3294-2b6a6d45a46a6c1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3522.758840fc5e7fe630.js +0 -287
- package/apps/pwa/out/_next/static/chunks/3525-a7c989c1a661e880.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3694.b1a66387756458c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/370.124f9e678a76fc01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3797-9643105335e660fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4084.15ecaf4240bd9e77.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4089-b3382b8843114eee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4293.928a8d573c16aab7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4402.f9238a3b18a7bd43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4490-9b090b0f9427f902.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4548.13502d0ca3b4f862.js +0 -8
- package/apps/pwa/out/_next/static/chunks/4565-67e4c620597965c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4599-548bc96cda44a5a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4625.206574e33a9e7ae8.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4862-392c9a71f0d8dfb4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5054.2906d1b02542edc5.js +0 -149
- package/apps/pwa/out/_next/static/chunks/5102-c822bbbee5a022a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5194-8c93f2e7e4b6f717.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5305-bacd7eca0b9a5848.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5311.9071ee3f44fd7ff5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5369.9f34353442daa421.js +0 -1
- package/apps/pwa/out/_next/static/chunks/589-49d730cb8138b397.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5904-264a6412ae3c9028.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5980.728531d5c566535c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/599-56f2c0a5cc4a7295.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6011.f525002e21bef0ac.js +0 -23
- package/apps/pwa/out/_next/static/chunks/6132.4381ab93683f1acf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6149.2e698635e454d8f6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6356-0afafc4230cf9767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6406.64756073f7df35b8.js +0 -77
- package/apps/pwa/out/_next/static/chunks/6479-16e62aedafa0e310.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6599.b5246b46a2453329.js +0 -64
- package/apps/pwa/out/_next/static/chunks/6610.5c26eec011565acb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/666.37d18474edc1c552.js +0 -2
- package/apps/pwa/out/_next/static/chunks/6904.a0b5b1ed4f2d2c20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7247-1f4aba94aba50d9f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-e71a7c5b7760cd02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7290.1fcaeef3adb5d0d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7477-2c64e42760f82f33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7505-47002b76ff1aef6c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7600-11884ca4da68e5d9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7607.40d8bb3689697ccb.js +0 -62
- package/apps/pwa/out/_next/static/chunks/7763.f51fcc19ca1414ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7785.846140c859b1adf6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7807-a0fdac6393e0bf36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7837-20a63b4b7e9342b1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/800.7815414cf280545d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8085-1e5fce59500a2cc2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8100.415f2105faa63e97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8290.701aa42ed13aa767.js +0 -226
- package/apps/pwa/out/_next/static/chunks/83a80514-b897b64df43ce108.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8444.d9c74214a3145a43.js +0 -93
- package/apps/pwa/out/_next/static/chunks/8569.98c915534928c7f9.js +0 -24
- package/apps/pwa/out/_next/static/chunks/8591.b1c7f876e5ec7cfb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8715-f63756021d3ab989.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8879.776c2ec7732aa114.js +0 -115
- package/apps/pwa/out/_next/static/chunks/8881.ddca15b3c82c490c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8946.5da5c87f32a2e1a8.js +0 -41
- package/apps/pwa/out/_next/static/chunks/9117.080b9b75be90e14d.js +0 -74
- package/apps/pwa/out/_next/static/chunks/9167.f3ecb28f367d6e69.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9224.66095370ee28c93b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9238-0682b013c83d6449.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9449.d3ed795da4befb1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9638-d268329f2c6f4f8d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/973cbf1b.3bbcf2b41548eca7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9842-d6644eb53d3dc6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9863.94ca3263e760696e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-5ab1e47978577c5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-24453f6a302816b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-6c131d30bb1cf871.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-dc0772b11861af51.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-66ad46ed0279feb9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-5159dc8752c6cd29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c5a2478861c08b87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-f722a9dec084e08b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-256ffd365d32e100.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e0de78ae7b99f01c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-cfbdc44b708d9801.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4c741eb760f2f77c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-e9ba98f5f1440a21.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-dafc901bb1b72b5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2f6f303fa114ec24.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-1634081c5272006c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-bb1f05cd51c38944.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-646b50f63b6fe732.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-db069b48a59c8c00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-f736324afa2117c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6f131b51193a36f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-435f5e2faf02bc01.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-497e0eedf20e7890.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-a8d28fdf995e16ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8966452228753c5e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fd5d18656cb432b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-5663779cb1a37609.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-66a67eb19ebd976b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-3b492aa78cf3c4a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-c71267e7681da982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-a21d1e1797172692.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-781cfc5114e097da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-478157214db6a023.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d9babd8795f396a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-86141f12cf090b7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-7256c73bb1c36751.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5c738ca7adef3de5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-0138c8091ec17224.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-cc2f24f16d85e735.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-979d318a534e92da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-182d394f7e6e096b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-21050928a697ed7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-44b9d807005a9030.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-601fb77340e63c9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-75d3fb5d47af0edf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-11bf9e5725a72c22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/dcf24383-2f82e35f2d827393.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-bf135b997155e90e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-5571537543bea91c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-25c51c7df7f3a442.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-e3b2c1f5d82abd65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-083f2e23e9bc3641.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e94d22b7a028217b.js +0 -1
- package/src/dashboard/bench-slash-runtime.ts +0 -40
- package/src/dashboard/input/search-modal-router.ts +0 -9
- package/src/dashboard/modals/pane-key-router.ts +0 -27
- package/src/dashboard/view-slash-runtime.ts +0 -48
- /package/apps/pwa/out/_next/static/{y1ltt7oCZxVpC6zVPhZ2O → LSXfY0u9QtfDbejY0Ukf_}/_ssgManifest.js +0 -0
package/src/cli/pr-cli.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
1
|
+
import { existsSync, realpathSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
2
|
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { loadavg } from 'node:os';
|
|
3
4
|
import { createInterface } from 'node:readline';
|
|
4
5
|
import type { Command } from 'commander';
|
|
5
6
|
import {
|
|
@@ -57,11 +58,16 @@ import { branchLineageSlug, findSiblingPrs } from './pr-lineage.js';
|
|
|
57
58
|
import { parseReleaseNoteSection } from '../release-loop/release-note.js';
|
|
58
59
|
import { landingFreezeMessage } from '../release-loop/landing-freeze.js';
|
|
59
60
|
import { admitLandingMerge } from '../self-implement/frozen-merges.js';
|
|
60
|
-
import { verifyAncestorAutoPass, verifyOverlapEvidence } from './pr-land-overlap-evidence.js';
|
|
61
|
+
import { verifyAllSameHeadReviews, verifyAncestorAutoPass, verifyOverlapEvidence } from './pr-land-overlap-evidence.js';
|
|
62
|
+
import { verifyVirtualRebasePatch } from './pr-land-virtual-rebase.js';
|
|
61
63
|
import { observePrLandRefusal } from './pr-land-refusal.js';
|
|
64
|
+
import { ensurePrLandHeavyRemote, type HeavyRemoteInput, type HeavyRemoteResult } from './pr-land-heavy-remote.js';
|
|
65
|
+
import { runHeavyOnPodShadow } from './pr-land-pod-shadow.js';
|
|
66
|
+
import type { LandingPodShadowInput, LandingPodShadowVerdict } from './pr-land-pod-shadow-job.js';
|
|
62
67
|
import { taskAgentStatePath } from '../task-agent/task-hand.js';
|
|
63
68
|
import { PR_LABEL_NAMES } from '../github/pr-labels.js';
|
|
64
69
|
import { enqueueL8ShadowQueue, runL8ShadowQueue, shadowPrNumber, statusL8ShadowQueue, type L8ShadowQueueDeps } from '../self-implement/l8-merge-queue.js';
|
|
70
|
+
import { censusOpenPrs, guardNewPr, parseDiffHunks, readOpenPrs, type Seat } from '../pr-tool/census.js';
|
|
65
71
|
|
|
66
72
|
export { branchLineageSlug, findSiblingPrs };
|
|
67
73
|
|
|
@@ -89,6 +95,10 @@ export interface PrLandOpts {
|
|
|
89
95
|
allowPublicLeak?: boolean;
|
|
90
96
|
/** Pinned landing only: JSON evidence (rebased · re-gated · reviewed head) that replaces the unavailable non-interactive overlap approval. */
|
|
91
97
|
overlapEvidence?: string;
|
|
98
|
+
/** Pinned landing opt-in: run the heavy changed-file gate on node-b at the reviewed head and current main tip. */
|
|
99
|
+
heavyRemote?: boolean;
|
|
100
|
+
/** Opt-in comparison of the heavy remote verdict with a Pod Job; the remote verdict still decides landing. */
|
|
101
|
+
heavyOnPodShadow?: boolean;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
export interface PrGranularityOpts {
|
|
@@ -126,6 +136,8 @@ export interface PrLandDeps {
|
|
|
126
136
|
runPublicLeakGate?: (changedFiles: readonly string[], out: { log: (message: string) => void; error: (message: string) => void }) => number;
|
|
127
137
|
/** Test seam — the post-transform export leak check on changed files. */
|
|
128
138
|
runExportLeakCheck?: (changedFiles: readonly string[], cwd: string) => ExportLeakCheck;
|
|
139
|
+
/** Test seam — local 1-minute and 5-minute load around a heavy remote gate. Default `os.loadavg`. */
|
|
140
|
+
loadavg?: () => readonly [number, number, number];
|
|
129
141
|
/** Test seam — included files must not import a module the public export excludes. */
|
|
130
142
|
runExportImportCheck?: (changedFiles: readonly string[], cwd: string) => ExportImportCheck | Promise<ExportImportCheck>;
|
|
131
143
|
/** 공개 문서의 `elanous …` 호출 ↔ 실제 `--help` 대조(경고 전용) — 바뀐 공개 문서 경로를 받아 어긋남 목록을 돌려준다. */
|
|
@@ -139,6 +151,8 @@ export interface PrLandDeps {
|
|
|
139
151
|
/** iOS 시험 게이트 심(시험 주입용). */
|
|
140
152
|
runIosGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
|
|
141
153
|
runPwaGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
|
|
154
|
+
runHeavyRemote?: (input: HeavyRemoteInput) => HeavyRemoteResult;
|
|
155
|
+
runLandingPodShadow?: (input: LandingPodShadowInput) => Promise<LandingPodShadowVerdict>;
|
|
142
156
|
isInteractive?: () => boolean;
|
|
143
157
|
/** Clock for the recent-landings window (test seam · default Date.now). */
|
|
144
158
|
now?: () => number;
|
|
@@ -811,13 +825,22 @@ function renderConflictRunIds(runIds: readonly { runId: string; status: string }
|
|
|
811
825
|
return runIds.map(({ runId, status }) => `${runId}) status=${status}`).join(', ');
|
|
812
826
|
}
|
|
813
827
|
|
|
828
|
+
function currentChangePaths(
|
|
829
|
+
run: CmdRunner, cwd: string, staged: PorcelainStatusRecord[] | null, base: string,
|
|
830
|
+
out: { log: (message: string) => void }, strict: true,
|
|
831
|
+
): string[] | null;
|
|
832
|
+
function currentChangePaths(
|
|
833
|
+
run: CmdRunner, cwd: string, staged: PorcelainStatusRecord[] | null, base: string,
|
|
834
|
+
out: { log: (message: string) => void }, strict?: false,
|
|
835
|
+
): string[];
|
|
814
836
|
function currentChangePaths(
|
|
815
837
|
run: CmdRunner,
|
|
816
838
|
cwd: string,
|
|
817
839
|
staged: PorcelainStatusRecord[] | null,
|
|
818
840
|
base: string,
|
|
819
841
|
out: { log: (message: string) => void },
|
|
820
|
-
|
|
842
|
+
strict = false,
|
|
843
|
+
): string[] | null {
|
|
821
844
|
const fromStatus = staged
|
|
822
845
|
? staged.flatMap(({ path, originalPath }) => (originalPath ? [path, originalPath] : [path]))
|
|
823
846
|
: [];
|
|
@@ -840,6 +863,7 @@ function currentChangePaths(
|
|
|
840
863
|
}
|
|
841
864
|
|
|
842
865
|
const diff = run('git', ['diff', '--name-only', '--no-renames', `${comparisonBase}...HEAD`], { cwd });
|
|
866
|
+
if (strict && !diff.ok) return null;
|
|
843
867
|
const fromDiff = diff.ok ? parseNameOnlyList(diff.out) : [];
|
|
844
868
|
return unionPaths(fromStatus, fromDiff);
|
|
845
869
|
}
|
|
@@ -1007,12 +1031,13 @@ function emitLandingOverlapAdvisory(
|
|
|
1007
1031
|
currentFiles: readonly string[],
|
|
1008
1032
|
out: { log: (message: string) => void },
|
|
1009
1033
|
overlappingLandings: string[] = [],
|
|
1034
|
+
resolvedLandings?: NonNullable<ReturnType<typeof collectResolvedBaseLandings>>,
|
|
1010
1035
|
): boolean {
|
|
1011
1036
|
if (currentFiles.length === 0) {
|
|
1012
1037
|
record('overlap', true, { outcome: 'no-changed-files' });
|
|
1013
1038
|
return false;
|
|
1014
1039
|
}
|
|
1015
|
-
const commits = collectResolvedBaseLandings(run, cwd, DEFAULT_GRANULARITY_SINCE, base);
|
|
1040
|
+
const commits = resolvedLandings ?? collectResolvedBaseLandings(run, cwd, DEFAULT_GRANULARITY_SINCE, base);
|
|
1016
1041
|
if (commits === null) {
|
|
1017
1042
|
record('overlap', false, { outcome: 'lookup-failed' });
|
|
1018
1043
|
return false;
|
|
@@ -1034,6 +1059,88 @@ function emitLandingOverlapAdvisory(
|
|
|
1034
1059
|
return false;
|
|
1035
1060
|
}
|
|
1036
1061
|
|
|
1062
|
+
/** The pinned overlap evidence decision is shared by landing and its read-only preview. */
|
|
1063
|
+
function verifyPinnedOverlapDecision(input: {
|
|
1064
|
+
opts: PrLandOpts;
|
|
1065
|
+
deps: PrLandDeps;
|
|
1066
|
+
run: CmdRunner;
|
|
1067
|
+
cwd: string;
|
|
1068
|
+
base: string;
|
|
1069
|
+
readFile: (path: string) => string;
|
|
1070
|
+
overlappingLandings: readonly string[];
|
|
1071
|
+
decision: OverlapLandingDecisionOutcome | undefined;
|
|
1072
|
+
out: { log: (message: string) => void; error: (message: string) => void };
|
|
1073
|
+
refuse: (message: string) => void;
|
|
1074
|
+
}): boolean {
|
|
1075
|
+
const { opts, deps, run, cwd, base, readFile, overlappingLandings, decision, out, refuse } = input;
|
|
1076
|
+
if (decision === undefined || decision === 'user-merge') return true;
|
|
1077
|
+
let accepted = false;
|
|
1078
|
+
let ancestorResyncNeeded = false;
|
|
1079
|
+
if (decision === 'unavailable' && opts.overlapEvidence !== undefined) {
|
|
1080
|
+
const ref = resolveRemoteBranchRef(run, cwd, base);
|
|
1081
|
+
const verdict = verifyOverlapEvidence({
|
|
1082
|
+
evidencePath: opts.overlapEvidence, pr: Number(opts.pr), expectedHead: opts.expectedHead!,
|
|
1083
|
+
remote: ref.remote, branch: ref.branch, cwd, run, readFile,
|
|
1084
|
+
reviewResultsDir: deps.reviewResultsDir ?? join(dirname(taskAgentStatePath()), 'review-results'),
|
|
1085
|
+
listDir: (dir) => readdirSync(dir), reviewMtime: (path) => statSync(path).mtimeMs,
|
|
1086
|
+
reviewRealPath: (path) => realpathSync(path), overlappingLandings,
|
|
1087
|
+
});
|
|
1088
|
+
accepted = verdict.ok;
|
|
1089
|
+
if (verdict.ok) {
|
|
1090
|
+
out.log(verdict.mode === 'already-ancestor'
|
|
1091
|
+
? `✓ overlap-evidence: already-ancestor — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass must-fix 0 (${verdict.reviewResult})`
|
|
1092
|
+
: verdict.mode === 'base-advanced-disjoint'
|
|
1093
|
+
? `✓ overlap-evidence: base-advanced-disjoint — gate base ${verdict.gateBase.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} 까지 ${verdict.windowCommits}개 커밋이 PR 파일을 안 건드린다 · review pass|warn must-fix 0 (${verdict.reviewResult})`
|
|
1094
|
+
: `✓ overlap-evidence: ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · gate passed on that base · review pass must-fix 0 (${verdict.reviewResult})`);
|
|
1095
|
+
} else refuse(`✗ overlap-evidence: ${verdict.reason}`);
|
|
1096
|
+
} else if (decision === 'unavailable') {
|
|
1097
|
+
const verdict = verifyAncestorAutoPass({
|
|
1098
|
+
pr: Number(opts.pr), expectedHead: opts.expectedHead!, cwd, run, readFile, overlappingLandings,
|
|
1099
|
+
reviewResultsDir: deps.reviewResultsDir ?? join(dirname(taskAgentStatePath()), 'review-results'),
|
|
1100
|
+
listDir: (dir) => readdirSync(dir),
|
|
1101
|
+
});
|
|
1102
|
+
accepted = verdict.ok;
|
|
1103
|
+
if (verdict.ok) {
|
|
1104
|
+
out.log(`✓ overlap-evidence: already-ancestor-auto — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass|warn must-fix 0 (${verdict.reviewResult})`);
|
|
1105
|
+
} else {
|
|
1106
|
+
ancestorResyncNeeded = verdict.reason.startsWith('ancestor-auto-not-ancestor:');
|
|
1107
|
+
if (ancestorResyncNeeded) {
|
|
1108
|
+
const ref = resolveRemoteBranchRef(run, cwd, base);
|
|
1109
|
+
const remote = run('git', ['ls-remote', ref.remote, `refs/heads/${ref.branch}`], { cwd });
|
|
1110
|
+
const baseTip = remote.ok ? remote.out.trim().split(/\s+/u, 1)[0] ?? '' : '';
|
|
1111
|
+
const local = run('git', ['rev-parse', '--verify', `refs/remotes/${ref.remote}/${ref.branch}`], { cwd });
|
|
1112
|
+
const reviews = verifyAllSameHeadReviews({
|
|
1113
|
+
pr: Number(opts.pr), expectedHead: opts.expectedHead!, readFile,
|
|
1114
|
+
reviewResultsDir: deps.reviewResultsDir ?? join(dirname(taskAgentStatePath()), 'review-results'),
|
|
1115
|
+
listDir: (dir) => readdirSync(dir),
|
|
1116
|
+
});
|
|
1117
|
+
const patch = reviews.ok && /^[a-f0-9]{40}$/iu.test(baseTip) && local.ok && local.out.trim() === baseTip
|
|
1118
|
+
? verifyVirtualRebasePatch({ cwd, baseTip, head: opts.expectedHead! })
|
|
1119
|
+
: { ok: false as const, reason: reviews.ok ? 'base-stale-or-unmeasured' : reviews.reason };
|
|
1120
|
+
accepted = patch.ok;
|
|
1121
|
+
ancestorResyncNeeded = !accepted;
|
|
1122
|
+
record('virtual-rebase', patch.ok, {
|
|
1123
|
+
pr: Number(opts.pr), head: opts.expectedHead, baseTip: baseTip || null,
|
|
1124
|
+
reason: patch.ok ? 'same-patch-id' : patch.reason,
|
|
1125
|
+
reviewResult: reviews.ok ? reviews.reviewResult : null,
|
|
1126
|
+
});
|
|
1127
|
+
if (patch.ok) {
|
|
1128
|
+
out.log(`✓ overlap-evidence: virtual-rebase — ${ref.remote}/${ref.branch} ${baseTip.slice(0, 12)} 와 head ${opts.expectedHead!.slice(0, 12)} 충돌 없음 · patch-id ${patch.patchId.slice(0, 12)} 동일 · review pass|warn must-fix 0 (${reviews.reviewResult})`);
|
|
1129
|
+
} else refuse(`✗ virtual-rebase: ${patch.reason} · ${verdict.reason}`);
|
|
1130
|
+
} else refuse(`✗ ancestor-auto: ${verdict.reason}`);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (!accepted) {
|
|
1134
|
+
if (ancestorResyncNeeded && !opts.dryRun) {
|
|
1135
|
+
const marked = run('gh', ['pr', 'edit', String(opts.pr), '--add-label', PR_LABEL_NAMES.needsRebase], { cwd });
|
|
1136
|
+
record('resync-marker', marked.ok, { pr: Number(opts.pr), reason: 'ancestor-auto-not-ancestor' });
|
|
1137
|
+
if (!marked.ok) out.error('⚠ resync-marker: needs-rebase 라벨을 남기지 못했습니다');
|
|
1138
|
+
}
|
|
1139
|
+
out.error('✗ expected-head: overlap merge approval unavailable for the reviewed head');
|
|
1140
|
+
}
|
|
1141
|
+
return accepted;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1037
1144
|
const SOURCE_SPLIT_PR_LIST_LIMIT = 200;
|
|
1038
1145
|
/**
|
|
1039
1146
|
* merged 조회의 «폭주 방지» 절대 상한 — ⛔ 창을 덮는 값이 «아니라» 그 위의 천장이다.
|
|
@@ -1146,9 +1253,9 @@ const GITHUB_MERGEABLE = new Set(['MERGEABLE', 'CONFLICTING', 'UNKNOWN']);
|
|
|
1146
1253
|
const GITHUB_MERGE_STATE = new Set(['BEHIND', 'BLOCKED', 'CLEAN', 'DIRTY', 'DRAFT', 'HAS_HOOKS', 'UNKNOWN', 'UNSTABLE']);
|
|
1147
1254
|
|
|
1148
1255
|
/** Read-only identity check shared by pinned landing and its dry-run. Later landing gates still run only on a real landing. */
|
|
1149
|
-
function inspectPinnedPr(run: CmdRunner, cwd: string, pr: number, expectedHead: string) {
|
|
1150
|
-
const viewed = run('gh', ['pr', 'view', String(pr), '--json', 'headRefOid,headRefName,state,isDraft,url'], { cwd });
|
|
1151
|
-
let value: { headRefOid?: string; headRefName?: string; state?: string; isDraft?: boolean; url?: string } = {};
|
|
1256
|
+
function inspectPinnedPr(run: CmdRunner, cwd: string, pr: number, expectedHead: string, requireMainBase = false) {
|
|
1257
|
+
const viewed = run('gh', ['pr', 'view', String(pr), '--json', 'headRefOid,headRefName,baseRefName,state,isDraft,url'], { cwd });
|
|
1258
|
+
let value: { headRefOid?: string; headRefName?: string; baseRefName?: string; state?: string; isDraft?: boolean; url?: string } = {};
|
|
1152
1259
|
try {
|
|
1153
1260
|
if (viewed.ok) {
|
|
1154
1261
|
const parsed: unknown = JSON.parse(viewed.out);
|
|
@@ -1162,13 +1269,24 @@ function inspectPinnedPr(run: CmdRunner, cwd: string, pr: number, expectedHead:
|
|
|
1162
1269
|
if (value.isDraft !== false) reasons.push(value.isDraft === true ? 'PR is draft' : 'PR ready status is unavailable');
|
|
1163
1270
|
if (value.headRefOid?.toLowerCase() !== expectedHead.toLowerCase()) reasons.push('PR head differs from --expected-head or is unavailable');
|
|
1164
1271
|
if (!value.url?.endsWith(`/pull/${pr}`) || !value.headRefName) reasons.push('PR identity or head branch is unavailable');
|
|
1272
|
+
// Heavy-remote evidence is an exact head/main pair. A retarget, or an unreadable base, is not that pair.
|
|
1273
|
+
if (requireMainBase && value.baseRefName !== 'main') reasons.push(value.baseRefName ? `PR base is ${value.baseRefName}, not main` : 'PR base is unavailable');
|
|
1165
1274
|
if (!local.ok || local.out.trim().toLowerCase() !== expectedHead.toLowerCase()) reasons.push('local HEAD differs from --expected-head or is unavailable');
|
|
1166
1275
|
if (!dirty.ok || dirty.out.trim()) reasons.push(dirty.ok ? 'local worktree is dirty' : 'local worktree status is unavailable');
|
|
1167
|
-
return { ok: reasons.length === 0, reasons, branch: value.headRefName, url: value.url };
|
|
1276
|
+
return { ok: reasons.length === 0, reasons, branch: value.headRefName, url: value.url, baseRefName: value.baseRefName };
|
|
1168
1277
|
}
|
|
1169
1278
|
|
|
1170
1279
|
export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): Promise<number> {
|
|
1171
1280
|
const out = deps.out ?? liveOut;
|
|
1281
|
+
const landStartedAt = Date.now();
|
|
1282
|
+
const stage = (name: string, startedAt: number, ok: boolean, extra: Record<string, unknown> = {}): void => {
|
|
1283
|
+
if (!opts.heavyRemote) return;
|
|
1284
|
+
debug.log('pr.land', 'stage-timing', {
|
|
1285
|
+
pr: Number(opts.pr), stage: name, startedAt: new Date(startedAt).toISOString(),
|
|
1286
|
+
endedAt: new Date().toISOString(), elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
1287
|
+
totalElapsedMs: Math.max(0, Date.now() - landStartedAt), ok, ...extra,
|
|
1288
|
+
});
|
|
1289
|
+
};
|
|
1172
1290
|
// First slice: observe pinned identity/branch, overlap decision, and GitHub merge refusals.
|
|
1173
1291
|
// Option syntax and prerequisite gate errors do not describe a merge recovery edge.
|
|
1174
1292
|
const refuse = (message: string, context: { isDraft?: boolean } = {}): void => {
|
|
@@ -1195,6 +1313,14 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1195
1313
|
out.error('✗ active-run-files: --include-active-run-files와 --exclude-active-run-files는 함께 사용할 수 없습니다.');
|
|
1196
1314
|
return 1;
|
|
1197
1315
|
}
|
|
1316
|
+
if (opts.heavyRemote && (opts.pr === undefined || opts.expectedHead === undefined || opts.hold)) {
|
|
1317
|
+
out.error('✗ heavy-remote: --pr ⊕ --expected-head 머리 고정 착지에서만 쓴다.');
|
|
1318
|
+
return 1;
|
|
1319
|
+
}
|
|
1320
|
+
if (opts.heavyOnPodShadow && !opts.heavyRemote) {
|
|
1321
|
+
out.error('✗ heavy-on-pod-shadow: --heavy-remote 머리 고정 착지에서만 쓴다.');
|
|
1322
|
+
return 1;
|
|
1323
|
+
}
|
|
1198
1324
|
|
|
1199
1325
|
const readFile = deps.readFile ?? ((path: string) => readFileSync(path, 'utf8'));
|
|
1200
1326
|
let title = opts.title;
|
|
@@ -1288,8 +1414,8 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1288
1414
|
out.error('✗ dry-run: --hold 는 머리 고정 읽기 전용 예행과 함께 사용할 수 없습니다.');
|
|
1289
1415
|
return 1;
|
|
1290
1416
|
}
|
|
1291
|
-
if (opts.overlapEvidence !== undefined) {
|
|
1292
|
-
out.error('✗
|
|
1417
|
+
if (opts.overlapEvidence !== undefined && !opts.overlapEvidence.trim()) {
|
|
1418
|
+
out.error('✗ overlap-evidence: --overlap-evidence <file> 는 비어 있을 수 없습니다.');
|
|
1293
1419
|
return 1;
|
|
1294
1420
|
}
|
|
1295
1421
|
const inspected = inspectPinnedPr(run, cwd, pr, opts.expectedHead!);
|
|
@@ -1300,8 +1426,55 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1300
1426
|
for (const reason of reasons) out.log(`[dry-run] ✗ preflight: ${reason}`);
|
|
1301
1427
|
if (reasons.includes('PR is draft')) out.log(`[dry-run] 선행: eln gh pr ready ${pr}`);
|
|
1302
1428
|
if (reasons.length === 0) out.log('[dry-run] ✓ preflight: PR OPEN non-draft, matching PR/local HEAD and branch, clean worktree');
|
|
1303
|
-
|
|
1304
|
-
|
|
1429
|
+
if (reasons.length > 0) return 1;
|
|
1430
|
+
// A dry-run may inspect the remote tip but must not update refs, FETCH_HEAD, objects or PR labels.
|
|
1431
|
+
const readOnlyRun: CmdRunner = (command, args, options) => {
|
|
1432
|
+
if ((command === 'git' && /^(fetch|pull|push|merge|commit|checkout|switch|worktree|update-ref|reset|apply|am|rebase)$/u.test(args[0] ?? ''))
|
|
1433
|
+
|| (command === 'gh' && args[0] === 'pr' && /^(edit|ready|merge|create|close|reopen)$/u.test(args[1] ?? ''))) {
|
|
1434
|
+
return { ok: false, out: '', err: 'dry-run forbids mutation' };
|
|
1435
|
+
}
|
|
1436
|
+
return run(command, args, options);
|
|
1437
|
+
};
|
|
1438
|
+
const base = (deps.resolveBase ?? resolveDeliverableBase)(readOnlyRun, cwd, opts.base);
|
|
1439
|
+
if (!base) { out.error('✗ base: 비교 base를 선택하지 못했습니다.'); return 1; }
|
|
1440
|
+
const ref = resolveRemoteBranchRef(readOnlyRun, cwd, base);
|
|
1441
|
+
const measuredBase = `refs/remotes/${ref.remote}/${ref.branch}`;
|
|
1442
|
+
const remoteTip = readOnlyRun('git', ['ls-remote', ref.remote, `refs/heads/${ref.branch}`], { cwd });
|
|
1443
|
+
const tip = remoteTip.ok ? remoteTip.out.trim().split(/\s+/u, 1)[0] : undefined;
|
|
1444
|
+
const localTip = readOnlyRun('git', ['rev-parse', '--verify', measuredBase], { cwd });
|
|
1445
|
+
if (!tip || !/^[a-f0-9]{40}$/iu.test(tip) || !localTip.ok || localTip.out.trim() !== tip) {
|
|
1446
|
+
out.error(`✗ overlap-preflight: ${base} 최신 SHA를 로컬에서 읽지 못했다. 읽기 전용 예행은 ref를 갱신하지 않는다.`);
|
|
1447
|
+
return 1;
|
|
1448
|
+
}
|
|
1449
|
+
const staged = stagedPaths(readOnlyRun, cwd);
|
|
1450
|
+
if (staged === null) {
|
|
1451
|
+
out.error('✗ overlap-preflight: PR 변경 파일을 읽지 못했다. 겹침을 미측정 상태로 둔다.');
|
|
1452
|
+
return 1;
|
|
1453
|
+
}
|
|
1454
|
+
const changed = readOnlyRun('git', ['diff', '--name-only', '--no-renames', `${measuredBase}...HEAD`], { cwd });
|
|
1455
|
+
if (!changed.ok) {
|
|
1456
|
+
out.error('✗ overlap-preflight: PR 변경 파일을 읽지 못했다. 겹침을 미측정 상태로 둔다.');
|
|
1457
|
+
return 1;
|
|
1458
|
+
}
|
|
1459
|
+
const changedPaths = unionPaths(staged.flatMap(({ path, originalPath }) => originalPath ? [path, originalPath] : [path]), parseNameOnlyList(changed.out));
|
|
1460
|
+
const landings = collectResolvedBaseLandings(readOnlyRun, cwd, DEFAULT_GRANULARITY_SINCE, measuredBase);
|
|
1461
|
+
if (landings === null) {
|
|
1462
|
+
out.error('✗ overlap-preflight: 최근 착지 조회가 실패했다. 겹침을 미측정 상태로 둔다.');
|
|
1463
|
+
return 1;
|
|
1464
|
+
}
|
|
1465
|
+
const overlappingLandings: string[] = [];
|
|
1466
|
+
const overlapFound = emitLandingOverlapAdvisory(readOnlyRun, cwd, measuredBase, changedPaths, out, overlappingLandings, landings);
|
|
1467
|
+
emitSiblingPrAdvisory(branch!, deps.listOpenPrs ?? (() => lookupOpenPrs(readOnlyRun, cwd)), out);
|
|
1468
|
+
if (overlapFound && !verifyPinnedOverlapDecision({
|
|
1469
|
+
opts, deps, run: readOnlyRun, cwd, base, readFile, overlappingLandings,
|
|
1470
|
+
decision: 'unavailable', out, refuse: out.error,
|
|
1471
|
+
})) {
|
|
1472
|
+
out.log('[dry-run] Remaining landing gates, freeze and merge: UNMEASURED; no PR or git state changed.');
|
|
1473
|
+
return 1;
|
|
1474
|
+
}
|
|
1475
|
+
out.log(`[dry-run] ✓ overlap: ${overlapFound ? 'evidence accepted' : 'no recent overlapping landing'}`);
|
|
1476
|
+
out.log('[dry-run] Remaining landing gates, freeze and merge: UNMEASURED; no PR or git state changed.');
|
|
1477
|
+
return 0;
|
|
1305
1478
|
}
|
|
1306
1479
|
if (opts.overlapEvidence !== undefined && (!pinned || !opts.overlapEvidence.trim())) {
|
|
1307
1480
|
out.error('✗ overlap-evidence: --overlap-evidence <file> 는 --pr ⊕ --expected-head 머리 고정 착지에서만 쓴다.');
|
|
@@ -1315,9 +1488,12 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1315
1488
|
out.error('✗ expected-head: --pr and --expected-head (full SHA) are required together without --hold');
|
|
1316
1489
|
return 1;
|
|
1317
1490
|
}
|
|
1318
|
-
const inspected = inspectPinnedPr(run, cwd, pr, opts.expectedHead
|
|
1491
|
+
const inspected = inspectPinnedPr(run, cwd, pr, opts.expectedHead!, opts.heavyRemote === true);
|
|
1319
1492
|
if (!inspected.ok) {
|
|
1320
|
-
|
|
1493
|
+
const baseReason = inspected.reasons.find((reason) => reason.startsWith('PR base'));
|
|
1494
|
+
refuse(baseReason
|
|
1495
|
+
? `✗ heavy-remote: ${baseReason} — 재타기팅된 PR은 통과한 head/main 쌍이 아니다. 병합하지 않는다.`
|
|
1496
|
+
: '✗ expected-head: PR OPEN non-draft, matching local HEAD and clean worktree required', {
|
|
1321
1497
|
isDraft: inspected.reasons.includes('PR is draft'),
|
|
1322
1498
|
});
|
|
1323
1499
|
return 1;
|
|
@@ -1542,7 +1718,213 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1542
1718
|
}
|
|
1543
1719
|
}
|
|
1544
1720
|
|
|
1545
|
-
const
|
|
1721
|
+
const currentMain = (): string | undefined => {
|
|
1722
|
+
// The landing pair names the remote's current tip, not a possibly stale tracking ref.
|
|
1723
|
+
const fetched = run('git', ['fetch', '--quiet', 'origin', 'main'], { cwd });
|
|
1724
|
+
if (!fetched.ok) return undefined;
|
|
1725
|
+
const tip = run('git', ['rev-parse', '--verify', 'origin/main'], { cwd });
|
|
1726
|
+
return tip.ok && /^[a-f0-9]{40}$/i.test(tip.out.trim()) ? tip.out.trim() : undefined;
|
|
1727
|
+
};
|
|
1728
|
+
const currentPinnedBase = (): string | undefined => {
|
|
1729
|
+
if (opts.heavyRemote) return currentMain();
|
|
1730
|
+
const ref = resolveRemoteBranchRef(run, cwd, base);
|
|
1731
|
+
const fetched = run('git', ['fetch', '--quiet', ref.remote, ref.branch], { cwd });
|
|
1732
|
+
if (!fetched.ok) return undefined;
|
|
1733
|
+
const tip = run('git', ['rev-parse', '--verify', `${ref.remote}/${ref.branch}`], { cwd });
|
|
1734
|
+
return tip.ok && /^[a-f0-9]{40}$/i.test(tip.out.trim()) ? tip.out.trim() : undefined;
|
|
1735
|
+
};
|
|
1736
|
+
let overlapCheckedBase: string | undefined;
|
|
1737
|
+
if (pinned) {
|
|
1738
|
+
overlapCheckedBase = currentPinnedBase();
|
|
1739
|
+
if (!overlapCheckedBase) {
|
|
1740
|
+
out.error(`✗ overlap-preflight: ${base} 최신 SHA를 가져오지 못했다. 겹침 조상을 판정하지 않고 착지하지 않는다.`);
|
|
1741
|
+
return 1;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
// Pinned landings reject a known non-ancestor before local or remote heavy gates.
|
|
1745
|
+
// Unpinned landings keep their existing gate and advisory order.
|
|
1746
|
+
const initialChangedPaths = pinned ? currentChangePaths(run, cwd, staged, base, out, true) : [];
|
|
1747
|
+
if (initialChangedPaths === null) {
|
|
1748
|
+
out.error('✗ overlap-preflight: PR 변경 파일을 읽지 못했다. 겹침을 미측정 상태로 두고 무거운 게이트를 시작하지 않는다.');
|
|
1749
|
+
return 1;
|
|
1750
|
+
}
|
|
1751
|
+
let changedPaths: string[] = initialChangedPaths;
|
|
1752
|
+
let hold = !!opts.hold;
|
|
1753
|
+
const checkOverlap = async (): Promise<number | undefined> => {
|
|
1754
|
+
const overlappingLandings: string[] = [];
|
|
1755
|
+
const overlapFound = emitLandingOverlapAdvisory(run, cwd, base, changedPaths, out, overlappingLandings);
|
|
1756
|
+
const siblingFound = emitSiblingPrAdvisory(
|
|
1757
|
+
branch,
|
|
1758
|
+
deps.listOpenPrs ?? (() => lookupOpenPrs(run, cwd)),
|
|
1759
|
+
out,
|
|
1760
|
+
);
|
|
1761
|
+
const advisoryShown = recentAdvisoryShown || overlapFound || siblingFound;
|
|
1762
|
+
const reason = opts.landReason;
|
|
1763
|
+
const landReasonNotice = formatLandReasonNotice({ reason, advisoryShown });
|
|
1764
|
+
if (landReasonNotice) out.log(landReasonNotice);
|
|
1765
|
+
record('land-reason', true, {
|
|
1766
|
+
advisoryShown,
|
|
1767
|
+
hasReason: !!reason,
|
|
1768
|
+
reasonLength: reason ? codePointLength(reason) : 0,
|
|
1769
|
+
reasonLengthUnit: 'codepoint',
|
|
1770
|
+
});
|
|
1771
|
+
let overlapDecision: OverlapLandingDecision | undefined;
|
|
1772
|
+
if (overlapFound && !opts.hold && !opts.dryRun) {
|
|
1773
|
+
overlapDecision = await (deps.decideOverlapLanding ?? (() => decideOverlapLanding({
|
|
1774
|
+
isInteractive: deps.isInteractive,
|
|
1775
|
+
requestConfirmation: deps.requestConfirmation,
|
|
1776
|
+
channels: deps.overlapConfirmChannels,
|
|
1777
|
+
timeoutMs: deps.overlapConfirmTimeoutMs,
|
|
1778
|
+
})))();
|
|
1779
|
+
record('overlap-decision', true, {
|
|
1780
|
+
outcome: overlapDecision.outcome,
|
|
1781
|
+
channel: overlapDecision.channel,
|
|
1782
|
+
interactive: overlapDecision.interactive,
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
hold = !!opts.hold || overlapDecision?.outcome === 'user-hold';
|
|
1786
|
+
if (pinned && !verifyPinnedOverlapDecision({ opts, deps, run, cwd, base, readFile, overlappingLandings, decision: overlapDecision?.outcome, out, refuse })) return 1;
|
|
1787
|
+
return undefined;
|
|
1788
|
+
};
|
|
1789
|
+
if (pinned && (await checkOverlap()) !== undefined) return 1;
|
|
1790
|
+
|
|
1791
|
+
|
|
1792
|
+
const readLoad = deps.loadavg ?? loadavg;
|
|
1793
|
+
// gh pr merge pins only the PR head. A long gate — and the UNKNOWN merge wait — can
|
|
1794
|
+
// outlive the base it measured, so re-read origin/main after every gate and rerun until
|
|
1795
|
+
// the pair is stable. The bound fails closed; it is not an atomic base pin GitHub does not expose.
|
|
1796
|
+
const HEAVY_BASE_STABILITY_RETRIES = 3;
|
|
1797
|
+
let heavyInterferenceMeasured: boolean | null = null;
|
|
1798
|
+
let heavyGateHost: string | undefined;
|
|
1799
|
+
const heavyGate = (main: string, phase: 'initial' | 'late-recheck' | 'post-merge'): HeavyRemoteResult | undefined => {
|
|
1800
|
+
const before = readLoad();
|
|
1801
|
+
const started = Date.now();
|
|
1802
|
+
const verdict = (deps.runHeavyRemote ?? ensurePrLandHeavyRemote)({
|
|
1803
|
+
pr: Number(opts.pr), head: opts.expectedHead!, main, repo: cwd,
|
|
1804
|
+
});
|
|
1805
|
+
if (verdict.ok) {
|
|
1806
|
+
heavyGateHost = verdict.host;
|
|
1807
|
+
heavyInterferenceMeasured = verdict.interferenceMeasured ?? null;
|
|
1808
|
+
if (heavyInterferenceMeasured === false) out.error(`⚠ test-interference-gate: ${verdict.host ?? 'remote'} 검사 미측정 — 경고 전용이므로 착지는 계속한다.`);
|
|
1809
|
+
if (heavyInterferenceMeasured === null) out.error(`⚠ test-interference-gate: ${verdict.host ?? 'remote'} 검사 상태 미상 — 경고 전용이므로 착지는 계속한다.`);
|
|
1810
|
+
}
|
|
1811
|
+
const after = readLoad();
|
|
1812
|
+
// One paired observation per gate, including a late rerun after main moved.
|
|
1813
|
+
// These are the local mbp loads around the remote call, not a load sample from the selected host.
|
|
1814
|
+
debug.log('pr.land', 'heavy-remote-load', {
|
|
1815
|
+
pr: Number(opts.pr), head: opts.expectedHead, main, host: verdict.host ?? null, attemptedHost: verdict.attemptedHost ?? null, phase,
|
|
1816
|
+
elapsedMs: Math.max(0, Date.now() - started), gateElapsedMs: verdict.elapsedMs,
|
|
1817
|
+
mbpLoad1Before: before[0], mbpLoad5Before: before[1],
|
|
1818
|
+
mbpLoad1After: after[0], mbpLoad5After: after[1],
|
|
1819
|
+
source: verdict.source, ok: verdict.ok,
|
|
1820
|
+
});
|
|
1821
|
+
record('heavy-remote', verdict.ok, {
|
|
1822
|
+
host: verdict.host ?? null, attemptedHost: verdict.attemptedHost ?? null, head: opts.expectedHead, main, phase, source: verdict.source,
|
|
1823
|
+
elapsedMs: verdict.elapsedMs, reason: verdict.reason, rootPlan: verdict.rootPlan ?? null,
|
|
1824
|
+
});
|
|
1825
|
+
stage(`heavy-remote-${phase}`, started, verdict.ok, { head: opts.expectedHead, main, source: verdict.source });
|
|
1826
|
+
return verdict;
|
|
1827
|
+
};
|
|
1828
|
+
let verifiedHeavyBase: string | undefined;
|
|
1829
|
+
if (opts.heavyRemote) {
|
|
1830
|
+
const main = overlapCheckedBase!;
|
|
1831
|
+
const verdict = heavyGate(main, 'initial');
|
|
1832
|
+
if (!verdict?.ok) { out.error(`✗ heavy-remote: 원격 게이트 ${verdict?.reason ?? 'unmeasured'} — 로컬 게이트로 폴백하지 않는다.`); return 1; }
|
|
1833
|
+
verifiedHeavyBase = main;
|
|
1834
|
+
out.log(`✓ heavy-remote: host=${verdict.host ?? 'unknown'} head=${opts.expectedHead!.slice(0, 12)} main=${main.slice(0, 12)} source=${verdict.source} elapsed=${verdict.elapsedMs}ms`);
|
|
1835
|
+
out.log(`✓ typecheck-roots: ${JSON.stringify(verdict.rootPlan ?? { mode: 'not-applicable', roots: null, promoted: false, reason: 'no-root-plan-in-gate-output' })}`);
|
|
1836
|
+
if (opts.heavyOnPodShadow) {
|
|
1837
|
+
await runHeavyOnPodShadow({
|
|
1838
|
+
enabled: true, pr: Number(opts.pr), head: opts.expectedHead!,
|
|
1839
|
+
local: { measured: verdict.rootPlan != null, ok: verdict.ok, rootPlan: verdict.rootPlan ?? null },
|
|
1840
|
+
runPod: async () => {
|
|
1841
|
+
const runPod = deps.runLandingPodShadow ?? (await import('./pr-land-pod-shadow-job.js')).runLandingPodShadowJob;
|
|
1842
|
+
return runPod({ head: opts.expectedHead!, main });
|
|
1843
|
+
},
|
|
1844
|
+
log: (category, event, data) => debug.log(category, event, { ...data, main, phase: 'initial' }),
|
|
1845
|
+
warn: (message) => out.error(`⚠ heavy-on-pod-shadow: ${message}`),
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
// Call immediately before the first mergePrOutcome and again before every UNKNOWN retry.
|
|
1850
|
+
// A move during the wait is the same race as a move during the gate: rerun or fail closed.
|
|
1851
|
+
// gh merge pins the head only. A retargeted PR would land on a branch the heavy gate never measured.
|
|
1852
|
+
// Unreadable baseRefName is not "main". Normal pr land (no --heavy-remote) does not use this check.
|
|
1853
|
+
const confirmHeavyPrBase = (): number | undefined => {
|
|
1854
|
+
if (!opts.heavyRemote) return undefined;
|
|
1855
|
+
const viewed = run('gh', ['pr', 'view', String(opts.pr), '--json', 'baseRefName,url'], { cwd });
|
|
1856
|
+
// JSON only. A bare "main" from another view (tests, --jq) is not this answer; unreadable is not main.
|
|
1857
|
+
let baseName = '';
|
|
1858
|
+
try {
|
|
1859
|
+
if (viewed.ok) {
|
|
1860
|
+
const parsed: unknown = JSON.parse(viewed.out);
|
|
1861
|
+
const name = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
1862
|
+
? (parsed as { baseRefName?: unknown }).baseRefName : undefined;
|
|
1863
|
+
if (typeof name === 'string') baseName = name.trim();
|
|
1864
|
+
}
|
|
1865
|
+
} catch { /* unreadable is not main */ }
|
|
1866
|
+
if (baseName !== 'main') {
|
|
1867
|
+
record('heavy-remote-pr-base', false, { pr: Number(opts.pr), baseRefName: baseName || null });
|
|
1868
|
+
out.error(`✗ heavy-remote: PR base=${baseName || 'unreadable'} ≠ main — 재타기팅된 PR은 통과한 head/main 쌍이 아니다. 병합하지 않는다.`);
|
|
1869
|
+
return 1;
|
|
1870
|
+
}
|
|
1871
|
+
return undefined;
|
|
1872
|
+
};
|
|
1873
|
+
const recheckOverlapAtBase = async (currentBase: string): Promise<number | undefined> => {
|
|
1874
|
+
if (currentBase === overlapCheckedBase) return undefined;
|
|
1875
|
+
const refreshedPaths = currentChangePaths(run, cwd, staged, base, out, true);
|
|
1876
|
+
if (refreshedPaths === null) {
|
|
1877
|
+
out.error('✗ overlap-preflight: 움직인 base의 PR 변경 파일을 읽지 못했다. 재게이트하지 않는다.');
|
|
1878
|
+
return 1;
|
|
1879
|
+
}
|
|
1880
|
+
changedPaths = refreshedPaths;
|
|
1881
|
+
const refused = await checkOverlap();
|
|
1882
|
+
if (refused !== undefined) return refused;
|
|
1883
|
+
overlapCheckedBase = currentBase;
|
|
1884
|
+
return undefined;
|
|
1885
|
+
};
|
|
1886
|
+
const confirmHeavyBaseStable = async (): Promise<number | undefined> => {
|
|
1887
|
+
if (!opts.heavyRemote) return undefined;
|
|
1888
|
+
const retargeted = confirmHeavyPrBase();
|
|
1889
|
+
if (retargeted !== undefined) return retargeted;
|
|
1890
|
+
let stable = false;
|
|
1891
|
+
for (let attempt = 0; attempt <= HEAVY_BASE_STABILITY_RETRIES; attempt += 1) {
|
|
1892
|
+
const main = currentMain();
|
|
1893
|
+
if (!main) { out.error('✗ heavy-remote: 병합 직전 origin/main SHA를 읽지 못했다.'); return 1; }
|
|
1894
|
+
if (main.toLowerCase() === verifiedHeavyBase?.toLowerCase()) { stable = true; break; }
|
|
1895
|
+
if (attempt === HEAVY_BASE_STABILITY_RETRIES) break;
|
|
1896
|
+
// A new overlap can deterministically refuse this head. Check the new base
|
|
1897
|
+
// before spending another remote gate on a pair that cannot land.
|
|
1898
|
+
const staleOverlap = await recheckOverlapAtBase(main);
|
|
1899
|
+
if (staleOverlap !== undefined) return staleOverlap;
|
|
1900
|
+
const verdict = heavyGate(main, 'late-recheck');
|
|
1901
|
+
if (!verdict?.ok) {
|
|
1902
|
+
out.error(`✗ heavy-remote: 병합 직전 원격 게이트 ${verdict?.reason ?? 'unmeasured'} — 로컬 게이트로 폴백하지 않는다.`);
|
|
1903
|
+
return 1;
|
|
1904
|
+
}
|
|
1905
|
+
verifiedHeavyBase = main;
|
|
1906
|
+
out.log(`✓ heavy-remote: merge-pair head=${opts.expectedHead!.slice(0, 12)} main=${main.slice(0, 12)} source=${verdict.source}`);
|
|
1907
|
+
}
|
|
1908
|
+
if (!stable) {
|
|
1909
|
+
out.error('✗ heavy-remote: origin/main 이 게이트 동안 계속 움직여 통과한 head/main 쌍으로 병합할 수 없다.');
|
|
1910
|
+
return 1;
|
|
1911
|
+
}
|
|
1912
|
+
// A late regate can take minutes. Re-read baseRefName after that wait and before success:
|
|
1913
|
+
// the pre-loop check is stale if the PR was retargeted while origin/main was being remeasured.
|
|
1914
|
+
const retargetedAfter = confirmHeavyPrBase();
|
|
1915
|
+
if (retargetedAfter !== undefined) return retargetedAfter;
|
|
1916
|
+
return undefined;
|
|
1917
|
+
};
|
|
1918
|
+
const confirmPinnedOverlapFresh = async (): Promise<number | undefined> => {
|
|
1919
|
+
if (!pinned) return undefined;
|
|
1920
|
+
const currentBase = opts.heavyRemote ? verifiedHeavyBase : currentPinnedBase();
|
|
1921
|
+
if (!currentBase) {
|
|
1922
|
+
out.error(`✗ overlap-preflight: 병합 직전 ${base} 최신 SHA를 가져오지 못했다.`);
|
|
1923
|
+
return 1;
|
|
1924
|
+
}
|
|
1925
|
+
return recheckOverlapAtBase(currentBase);
|
|
1926
|
+
};
|
|
1927
|
+
const typecheckOk = opts.heavyRemote || (deps.runTypecheckGate ?? ((o) => runPrLandTypecheckGate(o, cwd)))(out);
|
|
1546
1928
|
record('typecheck', typecheckOk);
|
|
1547
1929
|
if (!typecheckOk) {
|
|
1548
1930
|
// ⛔⭐ 「왜 막혔나」를 «단정하지 않는다» — 이 게이트는 불리언만 돌려주므로 여기서는 이유를 모른다.
|
|
@@ -1556,7 +1938,9 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1556
1938
|
out.error('✗ typecheck: scripts/ci-typecheck-changed.ts blocked pr land.');
|
|
1557
1939
|
return 1;
|
|
1558
1940
|
}
|
|
1559
|
-
out.log(
|
|
1941
|
+
out.log(opts.heavyRemote
|
|
1942
|
+
? '✓ typecheck: node-b exact-head heavy gate PASS — changed files have no new type errors.'
|
|
1943
|
+
: '✓ typecheck: scripts/ci-typecheck-changed.ts PASS — changed files have no new type errors.');
|
|
1560
1944
|
|
|
1561
1945
|
const isolation = gateVerdict('isolation-gate', deps.runIsolationGate ?? ((o) => runPrLandIsolationGate(o, cwd)), out);
|
|
1562
1946
|
// ⭐ 관측에 «잰 것»과 «못 잰 것»을 다른 값으로 남긴다 — 둘이 같은 모양이면 그 자는 거짓을 낸다.
|
|
@@ -1602,7 +1986,7 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1602
1986
|
out.log('✓ daemon-port-gate: scripts/ci-daemon-port-gate.ts PASS — no new daemon port literals.');
|
|
1603
1987
|
}
|
|
1604
1988
|
|
|
1605
|
-
|
|
1989
|
+
if (!pinned) changedPaths = currentChangePaths(run, cwd, staged, base, out);
|
|
1606
1990
|
nextMdWarning(body ?? '', changedPaths, out);
|
|
1607
1991
|
if (!conflictMarkerBlock(changedPaths, cwd, out)) return 1;
|
|
1608
1992
|
publicLeakWarning(changedPaths, deps.runPublicLeakGate ?? ((files, sink) => runPrLandPublicLeakGate(files, sink, cwd, base)), out);
|
|
@@ -1613,27 +1997,29 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1613
1997
|
const docsRoot = top.ok && top.out.trim() ? top.out.trim() : cwd;
|
|
1614
1998
|
docsCliWarning(publicDocPaths(changedPaths, (f) => existsSync(join(docsRoot, f))), deps.runDocsCliCheck ?? runPrLandDocsCliCheck(docsRoot), out);
|
|
1615
1999
|
}
|
|
1616
|
-
const testInterference =
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
2000
|
+
const testInterference = opts.heavyRemote
|
|
2001
|
+
? { measured: heavyInterferenceMeasured }
|
|
2002
|
+
: await testInterferenceGateVerdict(
|
|
2003
|
+
deps.runTestInterferenceGate ?? ((o, files) => runTestInterferenceGate({ args: ['--changed-files', ...files], log: o.log, cwd })),
|
|
2004
|
+
changedPaths,
|
|
2005
|
+
out,
|
|
2006
|
+
);
|
|
2007
|
+
record('test-interference-gate', true, { measured: testInterference.measured, ...(opts.heavyRemote ? { host: heavyGateHost ?? null } : {}) });
|
|
1622
2008
|
// LAND-GLOBAL-CHECKS — 바뀐 파일의 경로 꼴이 부르는 «전역 검사»를 끌어와 돌린다(표 = scripts/land-global-checks.ts).
|
|
1623
|
-
//
|
|
1624
|
-
const landGlobal = gateVerdict(
|
|
2009
|
+
// --heavy-remote 는 같은 head/main 통합 트리에서 이미 측정했다. 원격의 실패·미측정은 heavyGate 가 막는다.
|
|
2010
|
+
const landGlobal = opts.heavyRemote ? { ok: true, measured: true } : gateVerdict(
|
|
1625
2011
|
'land-global-checks',
|
|
1626
2012
|
deps.runLandGlobalChecks
|
|
1627
2013
|
? (o) => deps.runLandGlobalChecks!(o, changedPaths)
|
|
1628
2014
|
: (o) => runLandGlobalChecksGate({ changedFiles: changedPaths, cwd, log: o.log, error: o.error }),
|
|
1629
2015
|
out,
|
|
1630
2016
|
);
|
|
1631
|
-
record('land-global-checks', landGlobal.ok, { measured: landGlobal.measured });
|
|
2017
|
+
record('land-global-checks', landGlobal.ok, { measured: landGlobal.measured, ...(opts.heavyRemote ? { host: heavyGateHost ?? null } : {}) });
|
|
1632
2018
|
if (!landGlobal.ok) {
|
|
1633
2019
|
out.error('✗ land-global-checks: scripts/land-global-checks.ts blocked pr land — 바뀐 파일이 끌어온 전역 시험이 실패했다.');
|
|
1634
2020
|
return 1;
|
|
1635
2021
|
}
|
|
1636
|
-
if (landGlobal.measured && landGlobalChecks(changedPaths).length > 0) {
|
|
2022
|
+
if (!opts.heavyRemote && landGlobal.measured && landGlobalChecks(changedPaths).length > 0) {
|
|
1637
2023
|
out.log(`✓ land-global-checks: scripts/land-global-checks.ts PASS — 끌려온 전역 시험 ${landGlobalChecks(changedPaths).join(' · ')} 실패 0.`);
|
|
1638
2024
|
}
|
|
1639
2025
|
|
|
@@ -1672,7 +2058,7 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1672
2058
|
}
|
|
1673
2059
|
|
|
1674
2060
|
// PWA 는 bun test/tsc 밖의 정적 번들이다. 측정 불가도 착지 불가로 판정한다.
|
|
1675
|
-
const pwaGate = gateVerdict('pwa-gate', deps.runPwaGate
|
|
2061
|
+
const pwaGate = gateVerdict('pwa-gate', opts.heavyRemote ? () => true : deps.runPwaGate
|
|
1676
2062
|
? (o) => deps.runPwaGate!(o, changedPaths)
|
|
1677
2063
|
: (o) => runPwaBuildGate({ args: ['--changed-files', ...changedPaths], cwd, log: o.log, error: o.error }) === 0, out, true);
|
|
1678
2064
|
record('pwa-gate', pwaGate.ok && pwaGate.measured, { measured: pwaGate.measured });
|
|
@@ -1681,83 +2067,8 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1681
2067
|
return 1;
|
|
1682
2068
|
}
|
|
1683
2069
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
const siblingFound = emitSiblingPrAdvisory(
|
|
1687
|
-
branch,
|
|
1688
|
-
deps.listOpenPrs ?? (() => lookupOpenPrs(run, cwd)),
|
|
1689
|
-
out,
|
|
1690
|
-
);
|
|
1691
|
-
const advisoryShown = recentAdvisoryShown || overlapFound || siblingFound;
|
|
1692
|
-
const reason = opts.landReason;
|
|
1693
|
-
const landReasonNotice = formatLandReasonNotice({ reason, advisoryShown });
|
|
1694
|
-
if (landReasonNotice) out.log(landReasonNotice);
|
|
1695
|
-
record('land-reason', true, {
|
|
1696
|
-
advisoryShown,
|
|
1697
|
-
hasReason: !!reason,
|
|
1698
|
-
reasonLength: reason ? codePointLength(reason) : 0,
|
|
1699
|
-
reasonLengthUnit: 'codepoint',
|
|
1700
|
-
});
|
|
1701
|
-
let overlapDecision: OverlapLandingDecision | undefined;
|
|
1702
|
-
if (overlapFound && !opts.hold && !opts.dryRun) {
|
|
1703
|
-
overlapDecision = await (deps.decideOverlapLanding ?? (() => decideOverlapLanding({
|
|
1704
|
-
isInteractive: deps.isInteractive,
|
|
1705
|
-
requestConfirmation: deps.requestConfirmation,
|
|
1706
|
-
channels: deps.overlapConfirmChannels,
|
|
1707
|
-
timeoutMs: deps.overlapConfirmTimeoutMs,
|
|
1708
|
-
})))();
|
|
1709
|
-
record('overlap-decision', true, {
|
|
1710
|
-
outcome: overlapDecision.outcome,
|
|
1711
|
-
channel: overlapDecision.channel,
|
|
1712
|
-
interactive: overlapDecision.interactive,
|
|
1713
|
-
});
|
|
1714
|
-
}
|
|
1715
|
-
const hold = !!opts.hold || overlapDecision?.outcome === 'user-hold';
|
|
1716
|
-
// TA-LAND-OVERLAP — 비대화형 «승인 불가»만 증거로 대신한다(사람의 hold 는 증거로 뒤집지 않는다) · 증거가 없거나 어긋나면 종전 거부 그대로.
|
|
1717
|
-
let overlapEvidenceAccepted = false;
|
|
1718
|
-
let ancestorResyncNeeded = false;
|
|
1719
|
-
if (pinned && overlapDecision?.outcome === 'unavailable' && opts.overlapEvidence !== undefined) {
|
|
1720
|
-
const ref = resolveRemoteBranchRef(run, cwd, base);
|
|
1721
|
-
const verdict = verifyOverlapEvidence({
|
|
1722
|
-
evidencePath: opts.overlapEvidence, pr: Number(opts.pr), expectedHead: opts.expectedHead!,
|
|
1723
|
-
remote: ref.remote, branch: ref.branch, cwd, run, readFile, overlappingLandings,
|
|
1724
|
-
});
|
|
1725
|
-
overlapEvidenceAccepted = verdict.ok;
|
|
1726
|
-
if (verdict.ok) {
|
|
1727
|
-
out.log(verdict.mode === 'already-ancestor'
|
|
1728
|
-
? `✓ overlap-evidence: already-ancestor — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass must-fix 0 (${verdict.reviewResult})`
|
|
1729
|
-
: verdict.mode === 'base-advanced-disjoint'
|
|
1730
|
-
? `✓ overlap-evidence: base-advanced-disjoint — gate base ${verdict.gateBase.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} 까지 ${verdict.windowCommits}개 커밋이 PR 파일을 안 건드린다 · review pass|warn must-fix 0 (${verdict.reviewResult})`
|
|
1731
|
-
: `✓ overlap-evidence: ${ref.remote}/${ref.branch} ${verdict.baseTip.slice(0, 12)} ⊂ head ${opts.expectedHead!.slice(0, 12)} · gate passed on that base · review pass must-fix 0 (${verdict.reviewResult})`);
|
|
1732
|
-
}
|
|
1733
|
-
else refuse(`✗ overlap-evidence: ${verdict.reason}`);
|
|
1734
|
-
} else if (pinned && overlapDecision?.outcome === 'unavailable') {
|
|
1735
|
-
// 증거 파일이 없을 때 — 겹친 착지가 전부 머리의 조상이고 표준 자리의 리뷰가 pass|warn ⊕ must-fix 0 이면 통과(아니면 종전 거부).
|
|
1736
|
-
const verdict = verifyAncestorAutoPass({
|
|
1737
|
-
pr: Number(opts.pr), expectedHead: opts.expectedHead!, cwd, run, readFile, overlappingLandings,
|
|
1738
|
-
reviewResultsDir: deps.reviewResultsDir ?? join(dirname(taskAgentStatePath()), 'review-results'),
|
|
1739
|
-
listDir: (dir) => readdirSync(dir),
|
|
1740
|
-
});
|
|
1741
|
-
overlapEvidenceAccepted = verdict.ok;
|
|
1742
|
-
if (verdict.ok) {
|
|
1743
|
-
out.log(`✓ overlap-evidence: already-ancestor-auto — 겹친 최근 착지 ${overlappingLandings.length}개가 전부 head ${opts.expectedHead!.slice(0, 12)} 의 조상 · review pass|warn must-fix 0 (${verdict.reviewResult})`);
|
|
1744
|
-
}
|
|
1745
|
-
else {
|
|
1746
|
-
refuse(`✗ ancestor-auto: ${verdict.reason}`);
|
|
1747
|
-
ancestorResyncNeeded = verdict.reason.startsWith('ancestor-auto-not-ancestor:');
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
if (pinned && overlapDecision && overlapDecision.outcome !== 'user-merge' && !overlapEvidenceAccepted) {
|
|
1751
|
-
// A clean GitHub PR can still be behind a recently landed overlapping head. Preserve the cue
|
|
1752
|
-
// across hosts; the current steward tick acts only if it can also prove local run ownership.
|
|
1753
|
-
if (ancestorResyncNeeded && !opts.dryRun) {
|
|
1754
|
-
const marked = run('gh', ['pr', 'edit', String(opts.pr), '--add-label', PR_LABEL_NAMES.needsRebase], { cwd });
|
|
1755
|
-
record('resync-marker', marked.ok, { pr: Number(opts.pr), reason: 'ancestor-auto-not-ancestor' });
|
|
1756
|
-
if (!marked.ok) out.error('⚠ resync-marker: needs-rebase 라벨을 남기지 못했습니다');
|
|
1757
|
-
}
|
|
1758
|
-
out.error('✗ expected-head: overlap merge approval unavailable for the reviewed head');
|
|
1759
|
-
return 1;
|
|
1760
|
-
}
|
|
2070
|
+
|
|
2071
|
+
if (!pinned && (await checkOverlap()) !== undefined) return 1;
|
|
1761
2072
|
|
|
1762
2073
|
const manager = deps.manager ?? makePrManager(run);
|
|
1763
2074
|
const existing = manager.findPrForBranchOutcome(branch, cwd);
|
|
@@ -1938,35 +2249,74 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1938
2249
|
}
|
|
1939
2250
|
const landing = admission;
|
|
1940
2251
|
if (pinned) {
|
|
1941
|
-
const view = run('gh', ['pr', 'view', String(opts.pr), '--json', 'headRefOid,state,isDraft'], { cwd });
|
|
1942
|
-
let current: { headRefOid?: string; state?: string; isDraft?: boolean } = {};
|
|
2252
|
+
const view = run('gh', ['pr', 'view', String(opts.pr), '--json', 'headRefOid,state,isDraft,baseRefName'], { cwd });
|
|
2253
|
+
let current: { headRefOid?: string; state?: string; isDraft?: boolean; baseRefName?: string } = {};
|
|
1943
2254
|
try { if (view.ok) current = JSON.parse(view.out) as typeof current; } catch { /* fail closed */ }
|
|
1944
2255
|
const finalHead = run('git', ['rev-parse', 'HEAD'], { cwd });
|
|
1945
2256
|
if (current.headRefOid?.toLowerCase() !== opts.expectedHead!.toLowerCase() || current.state !== 'OPEN' || current.isDraft !== false
|
|
2257
|
+
|| (opts.heavyRemote && current.baseRefName !== 'main')
|
|
1946
2258
|
|| !finalHead.ok || finalHead.out.trim().toLowerCase() !== opts.expectedHead!.toLowerCase()) {
|
|
1947
2259
|
landing.end(false);
|
|
1948
2260
|
out.error('✗ expected-head: PR head/state changed before merge');
|
|
1949
2261
|
return 1;
|
|
1950
2262
|
}
|
|
1951
2263
|
}
|
|
1952
|
-
|
|
2264
|
+
// gh pr merge pins only the PR head (--match-head-commit). It exposes no atomic base
|
|
2265
|
+
// precondition. Re-read origin/main as close as possible to every mergePrOutcome,
|
|
2266
|
+
// including UNKNOWN retries after the wait. The last read and the merge call are
|
|
2267
|
+
// still not one atomic pin; a move inside that gap is caught after merge.
|
|
2268
|
+
{
|
|
2269
|
+
const started = Date.now();
|
|
2270
|
+
const unstable = await confirmHeavyBaseStable();
|
|
2271
|
+
stage('pre-merge-base-check', started, unstable === undefined, { verifiedBase: verifiedHeavyBase ?? null });
|
|
2272
|
+
if (unstable !== undefined) { landing.end(false); return unstable; }
|
|
2273
|
+
const staleOverlap = await confirmPinnedOverlapFresh();
|
|
2274
|
+
if (staleOverlap !== undefined) { landing.end(false); return staleOverlap; }
|
|
2275
|
+
}
|
|
2276
|
+
let merged: ReturnType<typeof manager.mergePrOutcome> | undefined;
|
|
2277
|
+
let heavyRetryRefused = false;
|
|
2278
|
+
let mergeAttempt = 0;
|
|
2279
|
+
const mergeOnce = (): ReturnType<typeof manager.mergePrOutcome> => {
|
|
2280
|
+
const started = Date.now();
|
|
2281
|
+
const attempt = ++mergeAttempt;
|
|
2282
|
+
let outcome: ReturnType<typeof manager.mergePrOutcome> | undefined;
|
|
2283
|
+
try {
|
|
2284
|
+
outcome = manager.mergePrOutcome(upsert.url, pinned ? opts.expectedHead : undefined, pinned ? cwd : undefined);
|
|
2285
|
+
return outcome;
|
|
2286
|
+
} finally {
|
|
2287
|
+
stage('merge', started, outcome?.ok === true, { attempt, verifiedBase: verifiedHeavyBase ?? null });
|
|
2288
|
+
}
|
|
2289
|
+
};
|
|
1953
2290
|
try {
|
|
1954
|
-
merged =
|
|
2291
|
+
merged = mergeOnce();
|
|
1955
2292
|
// `GIT-S14` — 병합이 «미완료(OPEN)»로 끝났고 GitHub 가 아직 `mergeable=UNKNOWN`(병합 가능 여부 계산 중)이면
|
|
1956
2293
|
// 짧게 기다렸다 다시 병합한다. 표본 셋(08-25 · 09-24 🅢 #20233 등)이 모두 «같은 명령 재시도에 병합»이었다.
|
|
1957
2294
|
// ⛔ UNKNOWN 일 때만 재시도한다 — CONFLICTING 등 다른 값은 기다려도 안 풀린다(그대로 실패를 낸다).
|
|
1958
2295
|
const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
|
|
1959
2296
|
// pinned 도 재시도한다 — mergePrOutcome 이 머리를 다시 확인하고 --match-head-commit 으로 병합하므로 바뀐 머리로는 안 들어간다.
|
|
2297
|
+
// heavy-remote 는 대기 중 main 이 움직일 수 있으므로 재병합 직전에 같은 안정성 검사를 다시 탄다.
|
|
2298
|
+
// 그 검사가 실패하면 두 번째 병합을 하지 않고, 이미 찍힌 게이트 오류를 유지한 채 반환한다.
|
|
1960
2299
|
for (let attempt = 1; !merged.ok && merged.kind === 'not-merged' && merged.state === 'OPEN' && attempt <= PR_LAND_MERGEABLE_UNKNOWN_RETRIES; attempt += 1) {
|
|
1961
2300
|
const mergeable = run('gh', ['pr', 'view', upsert.url, '--json', 'mergeable', '--jq', '.mergeable'], { cwd });
|
|
1962
2301
|
const value = mergeable.ok ? mergeable.out.trim() : '';
|
|
1963
2302
|
if (value !== 'UNKNOWN') break;
|
|
1964
2303
|
record('merge-retry', true, { attempt, mergeable: value, waitMs: PR_LAND_MERGEABLE_UNKNOWN_WAIT_MS });
|
|
1965
2304
|
out.log(`⏳ merge: mergeable=UNKNOWN — GitHub 가 병합 가능 여부를 아직 계산 중입니다 · ${PR_LAND_MERGEABLE_UNKNOWN_WAIT_MS / 1000}초 뒤 다시 병합 (${attempt}/${PR_LAND_MERGEABLE_UNKNOWN_RETRIES})`);
|
|
2305
|
+
const waitStarted = Date.now();
|
|
1966
2306
|
await sleep(PR_LAND_MERGEABLE_UNKNOWN_WAIT_MS);
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2307
|
+
stage('merge-retry-wait', waitStarted, true, { attempt });
|
|
2308
|
+
const checkStarted = Date.now();
|
|
2309
|
+
const unstable = await confirmHeavyBaseStable();
|
|
2310
|
+
stage('pre-merge-base-check', checkStarted, unstable === undefined, { attempt, verifiedBase: verifiedHeavyBase ?? null });
|
|
2311
|
+
if (unstable !== undefined) { heavyRetryRefused = true; break; }
|
|
2312
|
+
const staleOverlap = await confirmPinnedOverlapFresh();
|
|
2313
|
+
if (staleOverlap !== undefined) { heavyRetryRefused = true; break; }
|
|
2314
|
+
merged = mergeOnce();
|
|
2315
|
+
}
|
|
2316
|
+
} finally {
|
|
2317
|
+
landing.end(!heavyRetryRefused && merged?.ok === true);
|
|
2318
|
+
}
|
|
2319
|
+
if (heavyRetryRefused || !merged) return 1;
|
|
1970
2320
|
if (!merged.ok) {
|
|
1971
2321
|
// `GIT-S76` — 「미완료(OPEN)」만으로는 «충돌»과 «아직 계산 중»이 안 갈린다. GitHub 의 병합 가능 상태를 같이 싣는다.
|
|
1972
2322
|
// 되읽기에 실패하면 종전 문면 그대로다(못 읽은 것을 「충돌 아님」으로 적지 않는다).
|
|
@@ -2000,6 +2350,58 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
2000
2350
|
if (merged.remoteBranchDeletion) {
|
|
2001
2351
|
out.log(`⚠ remote branch deletion: ${merged.remoteBranchDeletion.detail}`);
|
|
2002
2352
|
}
|
|
2353
|
+
if (opts.heavyRemote && verifiedHeavyBase) {
|
|
2354
|
+
// Squash merge has one parent: the base GitHub actually merged onto. Compare it with the
|
|
2355
|
+
// base that passed the heavy gate. Do not revert a landed commit; a mismatch means the
|
|
2356
|
+
// non-atomic gap between the last re-read and gh merge moved main.
|
|
2357
|
+
const viewed = run('gh', ['pr', 'view', upsert.url, '--json', 'mergeCommit,state'], { cwd });
|
|
2358
|
+
let mergeOid = '';
|
|
2359
|
+
let state = '';
|
|
2360
|
+
try {
|
|
2361
|
+
if (viewed.ok) {
|
|
2362
|
+
const parsed = JSON.parse(viewed.out) as { state?: unknown; mergeCommit?: { oid?: unknown } | null };
|
|
2363
|
+
state = typeof parsed.state === 'string' ? parsed.state : '';
|
|
2364
|
+
mergeOid = typeof parsed.mergeCommit?.oid === 'string' ? parsed.mergeCommit.oid : '';
|
|
2365
|
+
}
|
|
2366
|
+
} catch { /* unreadable is not a match */ }
|
|
2367
|
+
let firstParent = '';
|
|
2368
|
+
if (/^[a-f0-9]{40}$/i.test(mergeOid)) {
|
|
2369
|
+
const fetched = run('git', ['fetch', '--quiet', 'origin', mergeOid], { cwd });
|
|
2370
|
+
if (fetched.ok) {
|
|
2371
|
+
const parent = run('git', ['rev-parse', '--verify', `${mergeOid}^1`], { cwd });
|
|
2372
|
+
if (parent.ok && /^[a-f0-9]{40}$/i.test(parent.out.trim())) firstParent = parent.out.trim();
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
if (state === 'MERGED' && firstParent.toLowerCase() === verifiedHeavyBase.toLowerCase()) {
|
|
2376
|
+
record('heavy-remote-base', true, { verifiedBase: verifiedHeavyBase, mergedOnto: firstParent, mergeCommit: mergeOid });
|
|
2377
|
+
} else {
|
|
2378
|
+
const reason = state !== 'MERGED' ? 'merge-state-unconfirmed'
|
|
2379
|
+
: !firstParent ? 'merge-parent-unreadable' : 'merged-base-mismatch';
|
|
2380
|
+
record('heavy-remote-base', false, {
|
|
2381
|
+
verifiedBase: verifiedHeavyBase, mergedOnto: firstParent || null, mergeCommit: mergeOid || null, state: state || null, reason,
|
|
2382
|
+
});
|
|
2383
|
+
debug.log('pr.land', 'post-merge-gate-required', {
|
|
2384
|
+
pr: Number(opts.pr), head: opts.expectedHead, verifiedBase: verifiedHeavyBase,
|
|
2385
|
+
mergedOnto: firstParent || null, mergeCommit: mergeOid || null, verifiedHost: heavyGateHost ?? null, reason,
|
|
2386
|
+
});
|
|
2387
|
+
out.log(`post-merge gate required: verified=${verifiedHeavyBase.slice(0, 12)} mergedOnto=${firstParent ? firstParent.slice(0, 12) : 'unreadable'} (${reason})`);
|
|
2388
|
+
// A missing first parent must not be gated against the stale verified base — that
|
|
2389
|
+
// would claim coverage of a pair that was never measured. Merge already happened.
|
|
2390
|
+
if (!firstParent) {
|
|
2391
|
+
record('heavy-remote', false, { host: heavyGateHost ?? null, phase: 'post-merge', reason: 'unmeasured', mergedOnto: null });
|
|
2392
|
+
out.error('✗ heavy-remote: post-merge 원격 게이트 unmeasured — squash 첫 부모를 읽지 못해 사후 게이트를 돌리지 않는다. 병합은 되돌리지 않는다.');
|
|
2393
|
+
out.log(`✓ merge: squash ${upsert.url} → ${base}`);
|
|
2394
|
+
return 1;
|
|
2395
|
+
}
|
|
2396
|
+
const post = heavyGate(firstParent, 'post-merge');
|
|
2397
|
+
if (!post?.ok) {
|
|
2398
|
+
out.error(`✗ heavy-remote: post-merge 원격 게이트 ${post?.reason ?? 'unmeasured'} — 병합은 되돌리지 않는다.`);
|
|
2399
|
+
out.log(`✓ merge: squash ${upsert.url} → ${base}`);
|
|
2400
|
+
return 1;
|
|
2401
|
+
}
|
|
2402
|
+
out.log(`✓ heavy-remote: post-merge host=${post.host ?? 'unknown'} head=${opts.expectedHead!.slice(0, 12)} main=${firstParent.slice(0, 12)} source=${post.source}`);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2003
2405
|
const baseRef = upsert.reused
|
|
2004
2406
|
? run('gh', ['pr', 'view', upsert.url, '--json', 'baseRefName', '--jq', '.baseRefName'], { cwd })
|
|
2005
2407
|
: undefined;
|
|
@@ -2018,6 +2420,36 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
2018
2420
|
|
|
2019
2421
|
export function registerPrCommands(program: Command, deps: PrLandDeps = {}, queueDeps: L8ShadowQueueDeps = {}): void {
|
|
2020
2422
|
const pr = program.command('pr').description('PR landing commands');
|
|
2423
|
+
const prToolRun = deps.run ?? defaultCmdRunner;
|
|
2424
|
+
const prToolOut = deps.out ?? liveOut;
|
|
2425
|
+
const prToolExit = deps.setExitCode ?? ((code: number) => { process.exitCode = code; });
|
|
2426
|
+
pr.command('census')
|
|
2427
|
+
.description('열린 PR 전체·골 문서만·2일 무커밋 draft·미닫힌 superseded 집계')
|
|
2428
|
+
.option('--cwd <path>', '저장소 경로')
|
|
2429
|
+
.action((opts: { cwd?: string }) => {
|
|
2430
|
+
const result = censusOpenPrs(readOpenPrs(prToolRun, { cwd: opts.cwd, includeDraftCommitDates: true }));
|
|
2431
|
+
prToolOut.log(JSON.stringify(result));
|
|
2432
|
+
if (result.status === 'unknown') prToolExit(1);
|
|
2433
|
+
});
|
|
2434
|
+
pr.command('open-guard')
|
|
2435
|
+
.description('새 PR 전에 같은 파일 OPEN PR·골 문서만·자리별 상한 검사')
|
|
2436
|
+
.option('--cwd <path>', '작업 트리 경로')
|
|
2437
|
+
.option('--base <ref>', '비교 base ref', 'origin/main')
|
|
2438
|
+
.option('--seat <seat>', 'PR 소유 자리(OP|TC|MK|UX)')
|
|
2439
|
+
.option('--cell <id>', '릴리스 칸 id (같은 칸 OPEN PR 검사)')
|
|
2440
|
+
.option('--seat-cap <number>', '자리별 열린 PR 경고 상한', '30')
|
|
2441
|
+
.action((opts: { cwd?: string; base: string; seat?: string; cell?: string; seatCap: string }) => {
|
|
2442
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
2443
|
+
const diff = prToolRun('git', ['-C', cwd, 'diff', '--name-only', '-z', `${opts.base}...HEAD`]);
|
|
2444
|
+
const files = diff.ok ? diff.out.split('\0').filter(Boolean) : null;
|
|
2445
|
+
const patch = prToolRun('git', ['-C', cwd, 'diff', '--unified=0', `${opts.base}...HEAD`]);
|
|
2446
|
+
const inventory = readOpenPrs(prToolRun, { cwd, ...(files ? { candidateFiles: files } : {}) });
|
|
2447
|
+
const result = guardNewPr({ files: patch.ok ? files : null, seat: opts.seat as Seat | undefined, seatCap: Number(opts.seatCap), cellId: opts.cell, hunks: patch.ok ? parseDiffHunks(patch.out) : undefined }, inventory);
|
|
2448
|
+
// Inspection never spends the three-creation unmeasured allowance.
|
|
2449
|
+
// Only pr-manager records an actual new-PR attempt in that ledger.
|
|
2450
|
+
prToolOut.log(JSON.stringify(result));
|
|
2451
|
+
if (result.status !== 'allow') prToolExit(1);
|
|
2452
|
+
});
|
|
2021
2453
|
const queue = pr.command('queue').description('Shadow merge queue (never lands PRs)');
|
|
2022
2454
|
const queueAction = async (action: () => Promise<unknown>) => {
|
|
2023
2455
|
try {
|
|
@@ -2037,6 +2469,8 @@ export function registerPrCommands(program: Command, deps: PrLandDeps = {}, queu
|
|
|
2037
2469
|
.option('--cwd <path>', '작업 트리 경로')
|
|
2038
2470
|
.option('--pr <number>', '기존 PR 번호 고정 (--expected-head 와 함께)')
|
|
2039
2471
|
.option('--expected-head <sha>', '리뷰된 PR 머리 SHA 고정 (--pr 와 함께)')
|
|
2472
|
+
.option('--heavy-remote', '머리 고정 착지의 무거운 게이트를 node-b·node-c 중 빈 호스트에서 실행; head⊕main 증거가 없거나 실패하면 거부(로컬 폴백 없음)')
|
|
2473
|
+
.option('--heavy-on-pod-shadow', '--heavy-remote 판정을 유지하고 같은 head/main의 Pod 타입·변경 시험 결과를 비교만 한다')
|
|
2040
2474
|
.option('--dry-run', '변경 없이 landing 계획만 출력')
|
|
2041
2475
|
.option('--hold', 'PR을 생성 또는 갱신한 뒤 병합하지 않고 쌓아 둔다')
|
|
2042
2476
|
.option('--title <title>', 'PR title')
|