elanous 0.2.36 → 0.2.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → RrjXTwz-urRAbChP1bX02}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{9250-072df22eaadaa6b5.js → 1026-44010f405704050c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1118.a8a74ea29aac3e46.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1204-2aa830479adf80f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1341-43b7d2dc77d45c9d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1440-c3ea8882badfd9bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4304.e9b2512834a9e23e.js → 1632.7d87f998e714bede.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1698-9c11cbf97f13717e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1770-3b1fc92253ef9f2f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1923-5c1695332240246a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1938.0e6c60ee91c4d7f5.js +148 -0
- package/apps/pwa/out/_next/static/chunks/{7784.1f0ac4a16d190cf4.js → 2104.70e086d5174a4736.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/213-20675f20673ba22a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6566.926a1d1b7c911442.js → 214.186a430da28338f1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2277.3bd4c0ac7f1b5996.js +93 -0
- package/apps/pwa/out/_next/static/chunks/240.e09f433f71c78ff4.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{5711.957b095705c12560.js → 2415.738b777642a02233.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2417.02151393605a7cd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2463.cd2641a76daaa9ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2576.b9e9c629c4b84c52.js +1 -0
- package/apps/pwa/out/_next/static/chunks/265-c021de53f0865bd9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2998-b1cc13956cd6a6e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2e4782d3.ba2f0ab2b63a53e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4286.b70c4520306817e8.js → 3086.fb14b60ce98b53d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3148-98efd4869d660a87.js → 3124-561f7f037943cde2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3130.626b54382b4dc379.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3221-1bb3f99a55088613.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3280.24edb1ffd03a7f3f.js +24 -0
- package/apps/pwa/out/_next/static/chunks/3288-eb3c22a1cbf2ae68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3727.7949ccaacaa71242.js → 3291.f6c1005387d514bd.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/33.ef99e8374f96c6c4.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3341-f09b9efbd7a82f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3389.012c70767a872f9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3410-d7c0f14b329d6fda.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3421.c87d6313a7d670f7.js +287 -0
- package/apps/pwa/out/_next/static/chunks/3422-83a3953f9bd67e26.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8904.7d51af2dc033965c.js → 3528.e4b8ab70d339f965.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3587-002e24e368c515a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3644.f12a7d821ab940af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3660.d7516588e2d875c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3695.b82e2610f4cd4ea9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3858.ca1f5bc0f577eac9.js +94 -0
- package/apps/pwa/out/_next/static/chunks/3959-e6ac4a139d9d4bcc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3f0de3de-2340c532c00395db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4032.53f62176514ec0ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3031-85328a92fcce4da6.js → 4199-f7c6eb40cd0322f8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4204-ef8484cbecd5d62f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4259-163129ca79913c4b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/428-eaa77df3d0c85969.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4432-97e400ffa1e161f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/449-aa427cf2cbb75f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4491-39a0fd32bbaf3840.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4588.b7cf06d382953979.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4690.70ec8060825366ab.js +32 -0
- package/apps/pwa/out/_next/static/chunks/4804.6012b5cd33c22792.js +90 -0
- package/apps/pwa/out/_next/static/chunks/4881.e4d22d730e3b9df5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4937.ce2e340dc29cad3e.js +79 -0
- package/apps/pwa/out/_next/static/chunks/5-bb1c08d84bd71df3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5005.de98df23b37cdf3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5035.dc64a89cad5573cc.js +74 -0
- package/apps/pwa/out/_next/static/chunks/5041-5aada92ca215ffdd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{528.38ce20ef8aa251ff.js → 5203.51e5d8b5f1dcfe53.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/5207.1a224035455790fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5255-1b60d50eabe9f057.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5269.d0436ee6d773382a.js +286 -0
- package/apps/pwa/out/_next/static/chunks/5300.677c97e0393742e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2683.c9773a0c589b031b.js → 5307.d7328020227880c2.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/5309.3367c4d50f52b489.js +1 -0
- package/apps/pwa/out/_next/static/chunks/552-689827d49780fa00.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5539.34cc9d60622cb69b.js +8 -0
- package/apps/pwa/out/_next/static/chunks/5568.218dea0c00ca2ec7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{552.b7d061ad91eeac51.js → 5638.107314dc479e2f37.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/5645.b7e3b055455a04ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5662.18cc0f30353cbd96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5671.da24fc9dc5e98325.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5b141171.324c40fd49cb6821.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6142.913b44e260a3f925.js +64 -0
- package/apps/pwa/out/_next/static/chunks/6175-d4621f6a10b0cfab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6290.3eb53a20746b72ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6415-9307f3eafa90837f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6925.562d532110879886.js → 6493.27ad77de7aac7966.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6579-3de35d00aacd71b5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/680-2f324c4e039a5b67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6845-331b3c7481da3ba3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7000.28157393caaf5343.js +62 -0
- package/apps/pwa/out/_next/static/chunks/{275-02ea4c2a33980422.js → 7161-8f22db3ec5ed005e.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/7311-60a7ea80d23c7521.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7401.525d0a12f7d02c06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7438.c2cf72b66dca6aa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3136.30aaaa5fa03662a3.js → 7488.966b116789f3d41a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4116.a84a92a2492c080e.js → 7524.ce973525d30b3d52.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7573.2376e13c9c21f1f8.js +77 -0
- package/apps/pwa/out/_next/static/chunks/7577.ea6e9ac72e035520.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3324.a09703033abcbd2f.js → 7628.6b16863ac3972333.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7657.8d1fb3d2f7ebe273.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7903-1435d5d2f0dd7807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3410.7f9af5ab77cbe65f.js → 7954.4f7a8832663ee979.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7978-30a91192c5dbe3c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{771.c3cdb6c1f7f47c02.js → 8019.21ca554a821dad01.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/8025-8795821525cf1a07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8191.3674a00f0b565c23.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8217-604955d8885c5ef2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8298-eed6f5f2585a63f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4615.8525eb0add35eba6.js → 8487.291666583bebe640.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8509.3a54d4e61893d7cb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8758.ac421f726a189125.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6943.c6b24124853a47b2.js → 8879.ac06755bcf3e44a1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/891.d6c723a51a067b36.js +149 -0
- package/apps/pwa/out/_next/static/chunks/{575.d929793a53f27c4b.js → 8911.bf5e712acb25fb6c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9f31e478.fefcbcd3c388ebc5.js → 8994d93e.33ba491f95345fc2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{da5ecc5c-b005ad8cac737d57.js → 89d6c04f-6b8ab341748e468a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{599.c39c571b6733f2a2.js → 9223.cbcc88e0e9950426.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9299.24e2a7d9b6d1f91b.js +226 -0
- package/apps/pwa/out/_next/static/chunks/9317.6a9869039c231bb3.js +24 -0
- package/apps/pwa/out/_next/static/chunks/9383.e4755f275b3c11ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8037.e254fd797aa224d1.js → 9581.da63ddf41acfad46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9715.8c5529a0a5c41a36.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9817.3009d65f0889ef1f.js +2 -0
- package/apps/pwa/out/_next/static/chunks/{ceba0382.650712b56b85b582.js → 9fe1b809.738da85c8b17e3fb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-ed32b6ec5dea8f23.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-fdd40e15babf3d25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-384efc78e85e400b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-866aac5ff226a7f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d91809c5aeed319a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-128f3522f89a81ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-595961abf3b43ad1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-f41dccb76d8c0263.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-a3205c2e28b7d0d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-c39a80277814a3f4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1ab3f98194513195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-a5246f7e254d77d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0f92287bdb81217d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-5a37131ff63793ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-ac4bd14bb2348bfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c348a3959c5b5fc6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-2addd81c0d3e2906.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-da6b765f58a3eefb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-0661e40f8780c32c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-b89af1b1c8e4875b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-da8aeb82ba751cdb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-cc71317b239439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-bde7fc1d64ffe369.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e2797214f713fa58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-2a35bcda70defaf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-3af03deedde45032.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d1d1210f99f620ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-46d1b5500885861d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-e7abc34a48a6f71e.js → page-291ab19fe2acb173.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-7f569e533ddeecba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-990129c9fee46d8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-7737f785ebb1cdaf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f3c73a4f7ffd2f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-03be7fb347f01058.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-96b0280625e4df2b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-56855545049eb56d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-76873dd80da4f715.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-79a909c9981f0338.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-a9d9a3e66459c1e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-c383cbf2013f7069.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f57c6a2e54dbdca4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-c15958835534404b.js → page-6b140f77593576eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-6b767ae2d9d0adfc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-116d6198d5da0e3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-b9c9b778c7501370.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-a6c78392ab8f6335.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-455183deb4ed0cfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-6a63e99526bd63d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31c22caafe788c08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3a3d9966.cc4d4585a2708e7c.js → b7459770.6539b11860eb8a9c.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/{053a429e.9a63436527cc751c.js → b7478fef.5917716286213fcc.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/bb6987f9.3647e1a23b0e3217.js +122 -0
- package/apps/pwa/out/_next/static/chunks/{2812ca6a.3b9f4a7a08885744.js → de8b930d.d94c0f42acf807fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{873d6c12.09bd499f57171543.js → eaad0cba.771200940c529df7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-4b12a946f352cee2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-941155250ed2644b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f8b6765abed6d149.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-78432194c85388f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-cdf7390829512b1b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-d5f0964a9c2e92d2.js +1 -0
- package/apps/pwa/out/_next/static/css/{b5d88eba78a3d7bf.css → be9019f696217521.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/graphs/drafts/recipes.yaml +27 -7
- package/graphs/release/recipes.yaml +9 -0
- package/graphs/release/release-loop.yaml +26 -18
- package/graphs/work/draft-close.yaml +54 -0
- package/graphs/work/green-close.yaml +37 -0
- package/graphs/work/recipes.yaml +46 -1
- package/graphs/work/run-research.yaml +35 -0
- package/package.json +1 -1
- package/scripts/__fixtures__/authoring-paths-10x3.json +32 -0
- package/scripts/ask-op-inbox-once.ts +17 -0
- package/scripts/boundary-proof-mapping-audit.ts +88 -0
- package/scripts/brand/check.ts +13 -4
- package/scripts/codex-fallback-live-probe.ts +17 -15
- package/scripts/fleet/node-setup.sh +32 -2
- package/scripts/fleet/run-light-worker.sh +9 -0
- package/scripts/gate-0226-accepted-remaining-90.txt +91 -0
- package/scripts/gate-0226-failure-census.ts +546 -0
- package/scripts/goals/authoring-metrics.ts +258 -0
- package/scripts/graph-units/draft-close-node.ts +222 -0
- package/scripts/hook-order-sweep.ts +7 -4
- package/scripts/intake-collect-telegram-saved-stage.ts +48 -0
- package/scripts/land-global-checks.ts +69 -1
- package/scripts/measure-nl-routing-corpus.ts +207 -2
- package/scripts/provision-export-redactions.ts +15 -0
- package/scripts/public-copy-lint.ts +122 -0
- package/scripts/public-export.ts +69 -2
- package/scripts/ratchets/next-tui-entry-budget.ts +124 -0
- package/scripts/ratchets/train-side-invariants.sh +7 -1
- package/scripts/release-loop/cut-branch.ts +61 -12
- package/scripts/release-loop/docs-node.ts +140 -0
- package/scripts/release-loop/gate-closure.ts +48 -0
- package/scripts/release-loop/gate-diff.ts +2 -1
- package/scripts/release-loop/gate-node.ts +161 -25
- package/scripts/release-loop/gate-partial.ts +82 -3
- package/scripts/release-loop/gate-prewarm-plan.ts +35 -0
- package/scripts/release-loop/gate-radius-live.ts +4 -2
- package/scripts/release-loop/known-issues-node.ts +6 -4
- package/scripts/release-loop/notes-repair.ts +216 -0
- package/scripts/release-loop/prepare-node.ts +61 -5
- package/scripts/release-loop/resume-cap.ts +2 -1
- package/scripts/release-loop/resume-release.ts +406 -7
- package/scripts/release-loop/resume-test-traffic.ts +10 -0
- package/scripts/release-loop/resume.ts +13 -5
- package/scripts/release-loop/traffic-node.ts +12 -4
- package/scripts/release-loop/traffic-transition.ts +53 -13
- package/scripts/release-loop/version-node.ts +109 -15
- package/scripts/train/green-leg.ts +176 -0
- package/scripts/tui-continue-real-evidence.ts +47 -0
- package/scripts/ux-sim.ts +1 -1
- package/scripts/video-pipeline.ts +2 -2
- package/src/acp/client.ts +31 -5
- package/src/acp/core-turn-bridge.ts +12 -1
- package/src/acp/stderr-summary.ts +49 -0
- package/src/agent/monad-agent-turn.ts +1 -0
- package/src/agent/run-context.ts +1 -1
- package/src/agent/task-notification.ts +3 -1
- package/src/agent/terminal-surface.ts +217 -0
- package/src/agent-substrate/pr-reviewer.ts +11 -3
- package/src/agent-substrate/review-observation.ts +10 -0
- package/src/agent-substrate/self-review-cli.ts +537 -57
- package/src/auto-research/question-ladder-contract.ts +137 -0
- package/src/auto-research/research-turn.ts +134 -0
- package/src/autopilot/discovery/doc-lint.ts +1 -1
- package/src/autopilot/lineage/sources.ts +1 -1
- package/src/autopilot/pr-manager.ts +64 -13
- package/src/boot/daemon-runtime.ts +35 -3
- package/src/boot/daemon-tools/pty-shell.ts +2 -1
- package/src/bots/screen-probe.ts +1 -1
- package/src/budget/run-usage-rollup.ts +12 -7
- package/src/budget/unified-usage.ts +20 -0
- package/src/capture/tui-self-report.ts +42 -5
- package/src/chat/index.ts +1 -0
- package/src/chat/tool-render/dispatch.ts +8 -1
- package/src/cli/autopilot-cli.ts +24 -9
- package/src/cli/decisions-cli.ts +7 -4
- package/src/cli/doctor-cli.ts +116 -7
- package/src/cli/guardian-cli.ts +3 -1
- package/src/cli/intake-cli.ts +48 -12
- package/src/cli/lesson-cli.ts +69 -9
- package/src/cli/logs-cli.ts +357 -2
- package/src/cli/memory-cli.ts +30 -1
- package/src/cli/msg-cli.ts +15 -11
- package/src/cli/pr-granularity.ts +5 -3
- package/src/cli/pr-land-overlap-evidence.ts +38 -5
- package/src/cli/pr-land-refusal.ts +1 -1
- package/src/cli/pty-takeover-cli.ts +58 -5
- package/src/cli/release-checklist-read-cli.ts +112 -4
- package/src/cli/release-cli.ts +89 -18
- package/src/cli/review-carry.ts +77 -0
- package/src/cli/self-update.ts +24 -6
- package/src/cli/self-utterances-cli.ts +13 -16
- package/src/cli/tasks-card-show.ts +31 -1
- package/src/cli/tasks-cli.ts +370 -33
- package/src/cli/tasks-read-cli.ts +13 -5
- package/src/cli/usage-cli.ts +12 -10
- package/src/context-bus/claude-memory-import.ts +99 -0
- package/src/context-bus/context-now.ts +86 -2
- package/src/context-bus/long-term-memory.ts +125 -0
- package/src/core-turn/run-core-turn.ts +9 -2
- package/src/core-turn/run-goal-loop.ts +5 -2
- package/src/core-turn/types.ts +5 -0
- package/src/dashboard/chat/acp-chat.ts +9 -0
- package/src/dashboard/index.ts +115 -30
- package/src/dashboard/input/agent-wake.ts +43 -0
- package/src/dashboard/input/chat-main-acp-dispatch.ts +21 -1
- package/src/dashboard/input/terminal-modal-router.ts +8 -159
- package/src/dashboard/replay-preview.ts +25 -0
- package/src/dashboard/side-basic-runtime.ts +61 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +19 -8
- package/src/dashboard/synchronized-frame-output.ts +74 -0
- package/src/dashboard/term-slash-runtime.ts +2 -0
- package/src/dashboard/turn-preamble.ts +28 -4
- package/src/display/coordinator.ts +45 -12
- package/src/display/resize-listener.ts +41 -20
- package/src/display/synchronized-output-region.ts +60 -0
- package/src/domains/guardian-tick.ts +9 -0
- package/src/domains/surface-events.ts +80 -1
- package/src/exec-requests/planner.ts +37 -2
- package/src/execution-loop/launch-gate.ts +4 -3
- package/src/git-fs/cli-stderr.ts +45 -0
- package/src/git-fs/gh-cli.ts +6 -3
- package/src/git-fs/git-cli.ts +11 -5
- package/src/git-fs/watcher.ts +27 -22
- package/src/git-fs/worktree.ts +19 -3
- package/src/graph-runner/graph-cli.ts +26 -2
- package/src/graph-runner/graph-grow.ts +37 -9
- package/src/graph-runner/graph-run-api.ts +3 -2
- package/src/graph-runner/runner.ts +34 -7
- package/src/graph-wizard/generate.ts +29 -6
- package/src/grok/credential.ts +107 -36
- package/src/harness/harness-cli-command.ts +179 -33
- package/src/harness/review-adapter.ts +26 -6
- package/src/hq/fence-wrapper.ts +1 -1
- package/src/hq/transfer.ts +1 -1
- package/src/index.ts +151 -18
- package/src/instance/test-write-guard-real-transport.fixture.ts +52 -0
- package/src/intake-plane/collect-telegram-saved.ts +54 -5
- package/src/interactive-terminal-modal.ts +3 -0
- package/src/keybindings.ts +17 -22
- package/src/llm/provider-credentials.ts +1 -1
- package/src/llm/route-decision.ts +13 -0
- package/src/llm-tool-name.ts +37 -0
- package/src/llm-tool-schema.ts +55 -0
- package/src/llm.ts +203 -21
- package/src/loops/checker.ts +100 -0
- package/src/loops/consensus.ts +26 -2
- package/src/loops/loop-cli.ts +9 -4
- package/src/loops/orchestrator/anomaly-cluster.ts +243 -0
- package/src/loops/orchestrator/inbox-once.ts +28 -0
- package/src/loops/orchestrator/inbox.ts +130 -1
- package/src/loops/orchestrator/tick.ts +55 -7
- package/src/maturity/feature-maturity.ts +1 -0
- package/src/model-tier/llm-tier-map.ts +12 -17
- package/src/msg/msg-store.ts +17 -0
- package/src/native-tool-catalog.ts +1 -1
- package/src/nexus/api/channel-ask-op.ts +96 -0
- package/src/nexus/api/discord-trigger-bot.ts +28 -1
- package/src/nexus/api/graph-wizard-api.ts +27 -2
- package/src/nexus/api/grid.ts +24 -4
- package/src/nexus/api/http-server.ts +1 -1
- package/src/nexus/api/ops-api.ts +14 -6
- package/src/nexus/api/telegram-trigger-bot.ts +24 -0
- package/src/nexus/api/terminals.ts +28 -6
- package/src/nexus/state/state.ts +7 -1
- package/src/oauth/codex-account-store.ts +34 -1
- package/src/ops/loops-map.ts +46 -0
- package/src/ops/overview-cache.ts +22 -4
- package/src/ops/overview.ts +41 -5
- package/src/patents/ledger.ts +18 -1
- package/src/policy/llm-policy.ts +3 -1
- package/src/preview/terminal.ts +4 -2
- package/src/prompt-cache/anthropic.ts +2 -0
- package/src/prompt-enhance/enhance.ts +9 -4
- package/src/pty-shell/availability.ts +7 -0
- package/src/pty-shell/herdr-agent-report.ts +40 -31
- package/src/pty-shell/herdr-prune-stale.ts +17 -0
- package/src/pty-shell/herdr-sequence-lease.ts +60 -0
- package/src/pty-shell/pty-control-ipc.ts +187 -18
- package/src/pty-shell/pty-manifest.ts +16 -2
- package/src/pty-shell/registry.ts +25 -15
- package/src/pty-shell/tui-run-state.ts +235 -1
- package/src/release-loop/checklist.ts +50 -5
- package/src/release-loop/feature-store.ts +9 -9
- package/src/release-loop/gate-shards.ts +61 -0
- package/src/release-loop/landed-but-yellow.ts +93 -12
- package/src/release-loop/light-rc.ts +7 -4
- package/src/release-loop/rehearsal.ts +1 -1
- package/src/repl/index.ts +18 -0
- package/src/research/axis-fanout.ts +133 -0
- package/src/research/edge-decision.ts +88 -0
- package/src/research/output-router.ts +127 -0
- package/src/research-bridge/format.ts +16 -1
- package/src/research-bridge/index.ts +1 -0
- package/src/research-bridge/invoke.ts +41 -5
- package/src/seat-loop/seat-report.ts +1 -0
- package/src/self-dev/ask-launch-flow.ts +86 -10
- package/src/self-dev/decompose-proposal.ts +29 -18
- package/src/self-dev/decompose.ts +45 -9
- package/src/self-dev/dev-pipeline.ts +29 -7
- package/src/self-dev/draft-residue.ts +221 -2
- package/src/self-dev/draft-sweep.ts +254 -16
- package/src/self-dev/draft-triage-rules.ts +19 -5
- package/src/self-dev/orchestrate.ts +1 -0
- package/src/self-dev/review-provider-fallback.ts +45 -7
- package/src/self-dev/run-supervisor.ts +22 -7
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-run.ts +310 -6
- package/src/self-implement/comment-only-source.ts +54 -0
- package/src/self-implement/doctor-visit.ts +108 -0
- package/src/self-implement/gate-artifacts.ts +6 -2
- package/src/self-implement/gate-baseline.ts +99 -1
- package/src/self-implement/gate-cli.ts +231 -82
- package/src/self-implement/gate-failure-signature.ts +44 -0
- package/src/self-implement/gate-remote.ts +30 -4
- package/src/self-implement/gate-scope.ts +13 -1
- package/src/self-implement/goal-author.ts +407 -21
- package/src/self-implement/goal-doc/section.ts +22 -0
- package/src/self-implement/goal-supervisor-fill.ts +110 -27
- package/src/self-implement/importer-test-index.ts +215 -2
- package/src/self-implement/knowhow-pack.ts +125 -12
- package/src/self-implement/l8-merge-queue.ts +198 -32
- package/src/self-implement/off-diff-evidence.ts +23 -0
- package/src/self-implement/orchestrator.ts +369 -33
- package/src/self-implement/pod-inplace-worktree.ts +82 -0
- package/src/self-implement/pod-warm-pool-profile.ts +2 -2
- package/src/self-implement/premise-review.ts +7 -5
- package/src/self-implement/review-round-brief.ts +300 -0
- package/src/self-implement/rework-policy.ts +14 -0
- package/src/self-implement/run-ledger.ts +428 -6
- package/src/self-implement/run-stop.ts +1 -1
- package/src/self-implement/running-runs.ts +17 -3
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +26 -2
- package/src/self-implement/stop-autoheal.ts +1 -0
- package/src/self-implement/test-seams.ts +13 -1
- package/src/self-implement/train-reach-grouping.ts +8 -2
- package/src/session/channel-ask-op-envelope.ts +36 -0
- package/src/session/channel-op-reply-route.ts +200 -0
- package/src/session/chat.ts +10 -1
- package/src/session/id-prefix.ts +60 -0
- package/src/session/index.ts +6 -0
- package/src/session-runtime/index.ts +18 -23
- package/src/skills/runner.ts +7 -3
- package/src/skills/tools/dev-harness.ts +2 -2
- package/src/skills/tools/pty.ts +5 -0
- package/src/skills/tools/schedule-wakeup.ts +1 -6
- package/src/status/bar.ts +3 -3
- package/src/status/metrics.ts +12 -1
- package/src/steward/rejudge-tick.ts +12 -5
- package/src/steward/resync-tick.ts +31 -4
- package/src/storage/file-lock.ts +17 -4
- package/src/task-agent/actions.ts +199 -4
- package/src/task-agent/adopt-pr.ts +63 -2
- package/src/task-agent/boundary-proof-mapping.ts +12 -0
- package/src/task-agent/boundary-proof-producer.ts +147 -0
- package/src/task-agent/completion-evidence.ts +2 -1
- package/src/task-agent/evidence-command-normalize.ts +42 -0
- package/src/task-agent/export-redactions-provision.ts +43 -0
- package/src/task-agent/green-close-recipe.ts +239 -0
- package/src/task-agent/green-sweep.ts +64 -10
- package/src/task-agent/live-moves.ts +48 -3
- package/src/task-agent/map-host-adapter.ts +134 -0
- package/src/task-agent/map-reduce.ts +158 -0
- package/src/task-agent/map-run-ledger.ts +226 -0
- package/src/task-agent/parent-host.ts +74 -17
- package/src/task-agent/rejudge-on-head.ts +72 -5
- package/src/task-agent/repair-cycle.ts +108 -8
- package/src/task-agent/repair-next-move.ts +12 -2
- package/src/task-agent/repair-worker.ts +37 -1
- package/src/task-agent/review-prior-fix.ts +12 -2
- package/src/task-agent/shadow.ts +188 -5
- package/src/task-agent/task-hand.ts +253 -20
- package/src/task-agent/task-parent-role-llm.ts +9 -0
- package/src/task-agent/tier-split.ts +40 -5
- package/src/task-agent/work-order.ts +53 -2
- package/src/task-orchestrator/dispatcher.ts +46 -1
- package/src/task-orchestrator/generator-prompt.ts +6 -0
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +16 -6
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +31 -4
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +8 -6
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +97 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +77 -8
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +15 -9
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +338 -79
- package/src/task-orchestrator/surfaces/self-implement.ts +37 -2
- package/src/terminal/session-registry.ts +1 -0
- package/src/terminal/terminal-modal-router.ts +156 -0
- package/src/testing/linux-env.ts +177 -0
- package/src/tool-hints/gate.ts +6 -4
- package/src/tool-hints/signals.ts +33 -1
- package/src/tui/chat/background-job-status.ts +8 -2
- package/src/tui/chat/goal-interim.ts +5 -1
- package/src/tui/entry.ts +722 -30
- package/src/tui/input/at-picker.ts +55 -0
- package/src/tui/sidetab/attach.ts +304 -0
- package/src/tui/sidetab/list.ts +176 -0
- package/src/tui/sidetab/nl-tool.ts +136 -0
- package/src/tui/slash/resume-command.ts +23 -0
- package/src/tui/slash/resume.ts +38 -35
- package/src/tui/slash/signals.ts +114 -0
- package/src/tui/slash/term-next.ts +72 -0
- package/src/tui-client/local-chat-turn.ts +31 -9
- package/src/user-config.ts +30 -3
- package/src/ux-sim/boot-daemon-tools-pty-shell-device.ts +136 -0
- package/src/ux-sim/classic-tui-seat-scenarios.ts +158 -4
- package/src/ux-sim/fake-gh.ts +174 -0
- package/src/ux-sim/fake-kubectl.ts +142 -0
- package/src/ux-sim/fake-llm.ts +17 -6
- package/src/ux-sim/next-tui-seat-scenarios.ts +176 -6
- package/src/ux-sim/resume-picker-device.ts +173 -0
- package/src/ux-sim/session-id-seed.ts +60 -0
- package/src/ux-sim/skills-tools-shell-device.ts +156 -0
- package/src/ux-sim/task-agent-parent-host-device.ts +264 -0
- package/src/ux-sim/task-agent-rejudge-on-head-device.ts +114 -0
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/109.f196abf3484bb97f.js +0 -287
- package/apps/pwa/out/_next/static/chunks/1201.afbfc5b1c5e5cbe4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1517.9275b89572098ff3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1574.ad11f8db8396ab45.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1614.7a8c5c40aee0261f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1871-f1eb29d8dbb4065b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/190.285c832bd9626f8c.js +0 -64
- package/apps/pwa/out/_next/static/chunks/2193.0ea24c4a7aa1d004.js +0 -36
- package/apps/pwa/out/_next/static/chunks/223-ad865c1e80491d78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2265-f0d90b6dbec05f81.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2267.eb4a62a209f09ec4.js +0 -149
- package/apps/pwa/out/_next/static/chunks/2341-86cd06c0cf01aa33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2352.9abc42422bade876.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2397-c1653a816061842d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2500.cbc1257b774aae52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2709.8e1889034255fc5f.js +0 -24
- package/apps/pwa/out/_next/static/chunks/284.ac77c0db55bfd3a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2941.3c3546f96bc6efa7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2976.b000698a3c6512dc.js +0 -24
- package/apps/pwa/out/_next/static/chunks/2986.45d7652e4f13557e.js +0 -2
- package/apps/pwa/out/_next/static/chunks/3155.2587a55196ecb394.js +0 -226
- package/apps/pwa/out/_next/static/chunks/3277.785db1cf09f98601.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3320-f9c77381f85a2c0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3386-91049feabff9a94e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/339.80afb7e8839372af.js +0 -8
- package/apps/pwa/out/_next/static/chunks/3538.a445e50bb359d068.js +0 -32
- package/apps/pwa/out/_next/static/chunks/3540c645.2595a6af01a97672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3621.581b2054cc14bee4.js +0 -286
- package/apps/pwa/out/_next/static/chunks/3676.8c28d16fd84ca843.js +0 -1
- package/apps/pwa/out/_next/static/chunks/375-b84540853523fe52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3757-180308bc61021b05.js +0 -1
- package/apps/pwa/out/_next/static/chunks/379.ee262e37586fe1ae.js +0 -74
- package/apps/pwa/out/_next/static/chunks/3840-5825d56d46976a92.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3969-81054381eaec4c4c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4067-bf9aa60547972937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4176.e5729c93c29151eb.js +0 -104
- package/apps/pwa/out/_next/static/chunks/4227-e6c84686bc8946ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4327.b9e08a71c0009348.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4946.bd106eafd668f248.js +0 -148
- package/apps/pwa/out/_next/static/chunks/5085-bce4904931767ca6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5125.2eb9968b83767193.js +0 -93
- package/apps/pwa/out/_next/static/chunks/5128-75b733fd74a9a1eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5489f3fe.4bb1458b09f6dead.js +0 -122
- package/apps/pwa/out/_next/static/chunks/5560-2a4648f6a30f16e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5772-e29e14f8ff127122.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5887.5b2f5b44ce0f7e9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5899-4c112a2d908cf97f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6041.1b65efb796ebc2a5.js +0 -79
- package/apps/pwa/out/_next/static/chunks/6057-765c0a26028c86b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6142.f289e0d039f453a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6199-6bcd61d2a3910e04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6236.cdf7293e710a9b2d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6387-237e079dac9a3ae6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6448-b6d272d074276e14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6466-92e65a48490b68a9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6580-cdc0e4a634ce4708.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6659-b242dc31b20b3f29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6690.c5d9b321b1f79384.js +0 -1
- package/apps/pwa/out/_next/static/chunks/671-e9b6ad0c771c5514.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6862.bcbf89dd0f4496b3.js +0 -27
- package/apps/pwa/out/_next/static/chunks/6912-7b375ee48732c767.js +0 -1
- package/apps/pwa/out/_next/static/chunks/727.7deb87749b0a343a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7481.39f7a4654907bf6e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7545.b1aa42cd15503319.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-416d298c2dc4a9ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7870-309de463786ac1fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7888-e29b151bb6a3bb8b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7891.03f910124a737985.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7905-6ba78d9234608f79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8207.f527886b7c464529.js +0 -1
- package/apps/pwa/out/_next/static/chunks/856.50bda61439d465e6.js +0 -62
- package/apps/pwa/out/_next/static/chunks/9031-92e9b487edb681ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9060.5410d9f6a95f6287.js +0 -90
- package/apps/pwa/out/_next/static/chunks/914.8abefd48d579547b.js +0 -94
- package/apps/pwa/out/_next/static/chunks/9187-c87a2207d4ac5d46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9289.3e92f278821e18e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9319.7816d3cc741cd34f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9327-e016bb9f1674d24f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9365.3de48bf75193ff9a.js +0 -77
- package/apps/pwa/out/_next/static/chunks/9383.5b5e9aa56b5fea97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9387-cb929573ef436f39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9489.c27becacc3283dec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9514.131c52cc75fe6439.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9536.28962663c7734376.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9686-12286f5bbf15a5a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9776.289d40cc5a4c342a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9789.60f723d18097ec30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/989.f0c17523c5e61f65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9972-1df393aa8d4f7d52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4105e5bbbb043024.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5d4fa75c2acb113e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a79a9bdb3b2142bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f1c2047484992a35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-15ffc49b2d8c70e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-67d53a3fc8734135.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-e30c8dc696491336.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-2136e04fa77696e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-fecd2814790c2e12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-280aa4a98fc5bb44.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c14e96b65fb4b883.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-187dac74825ed6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-6bc7246ea8c92448.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-083747f1d4518f61.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-15ca95a16ec96e1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a8ac031b3810eb64.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-8835f93110494cda.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-e2de2e59111c9aad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-f2b2e4bf6e981450.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-5b92b7f75df20948.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-468eddf2b1a84ecb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2af16e62c12f22df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-18b40867a77a5793.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-fe934f2979f5a4a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-3eec435537357170.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ff88cbfd2aa99fc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a5b39fb780c70ab5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ae465dde1d1b3933.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-cab9674fde9ead26.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-1da49bc8712284d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5e8aa657551ac912.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-fcc36f36c6dc1fe2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d22db22f2a7296ca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-d12dc7b1773878f0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-99debec9ea7fbec0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-96750aba7430959f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-2a088a78bad7e924.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-2e8ed57db7e69993.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-4b3a16213c2ae8fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f098747ddf38c98b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-68279e3c843c1fd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a34a74453367a39d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-8767a7db2155a5bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-bd5976683460d7f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-b7364835e7f2ef54.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-311af16b3ee0ffca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fb670757274ae990.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ccae1c4b-0cf414f5eff1b10d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/f9a61e4d.1fc2c1dbdd49fe46.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-6d497cbf1eef04c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-012f5edec932f337.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-da5aeb809edc94a2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-5ee319af6f749dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-4bc84653a718d6c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-8a96cf6775c73d6e.js +0 -1
- package/scripts/migrate-monad-to-elanous.ts +0 -195
- package/scripts/rebrand-elanous.ts +0 -242
- /package/apps/pwa/out/_next/static/{uC-N0emedeNOFuaBQFv-I → RrjXTwz-urRAbChP1bX02}/_ssgManifest.js +0 -0
|
@@ -14,24 +14,46 @@
|
|
|
14
14
|
//
|
|
15
15
|
// index.ts 의 액션은 옵션 파싱과 실제 의존 배선만 남는다(얇은 껍데기).
|
|
16
16
|
|
|
17
|
-
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import { homedir, tmpdir } from 'node:os';
|
|
19
19
|
import { dirname, join } from 'node:path';
|
|
20
20
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
21
21
|
import { tierModel } from '../llm/model-defaults.js';
|
|
22
|
-
import { buildReviewObservation, safeLogText } from './review-observation.js';
|
|
22
|
+
import { buildReviewObservation, reviewIntentFingerprint, safeLogText } from './review-observation.js';
|
|
23
|
+
import { checkCitedReviewPathsAtHead } from './review-finding-key.js';
|
|
23
24
|
import { DEFAULT_REVIEW_BACKEND, defaultAcpReviewTimeoutSec } from './acp-reviewer.js';
|
|
24
25
|
import { ZAI_OPENROUTER_FALLBACK_MODEL } from '../llm/zai-coding-plan.js';
|
|
25
26
|
import { debug } from '../debug/log.js';
|
|
26
27
|
import { ACP_BACKENDS, canonicalizeBackendId } from '../acp/backend-registry.js';
|
|
27
28
|
import { buildManualReviewIntent, intentFromPr, prIntentSectionCoverage, reviewIntentTruncationFromPr, reviewIntentTruncationObservation } from './review-intent.js';
|
|
28
|
-
import { budgetReviewerContext, reviewDiffBudgetObservation, splitDiffByFile, type ReviewImage, type ReviewInput, type ReviewerContextItem, type ReviewResult } from './pr-reviewer.js';
|
|
29
|
+
import { budgetReviewerContext, isNoneSentinelFinding, reviewDiffBudgetObservation, splitDiffByFile, type ReviewImage, type ReviewInput, type ReviewerContextItem, type ReviewResult } from './pr-reviewer.js';
|
|
29
30
|
import type { ReferencedFileReader } from '../self-implement/goal-file-reader.js';
|
|
30
|
-
import { getUserConfig, type LLMProviderName, type RoleLlmSpec, type SubscriptionReviewerSpec } from '../user-config.js';
|
|
31
|
+
import { getUserConfig, type LLMProviderName, type ReviewerMixSpec, type RoleLlmSpec, type SubscriptionReviewerSpec } from '../user-config.js';
|
|
32
|
+
import { loadLlmPolicy } from '../policy/llm-policy.js';
|
|
31
33
|
import { listRunLedgers, lookupPrGoalAcceptance, lookupRunLedger, type PrGoalAcceptanceLookup, type RunLedgerEntry, type RunLedgerLookup } from '../self-implement/run-ledger.js';
|
|
32
34
|
|
|
33
35
|
/** SUBSCRIPTION-EXHAUST-REROUTE — API reviewer when the configured `roleLlm.reviewer` lane is unavailable (off codex). */
|
|
34
36
|
export const ROLE_REVIEWER_FALLBACK_MODEL = 'openrouter/z-ai/glm-5.3';
|
|
37
|
+
export const DEFAULT_REVIEWER_MIX: ReviewerMixSpec = { sol: 1, claude: 1, glm: 1 };
|
|
38
|
+
type ReviewLane = keyof ReviewerMixSpec;
|
|
39
|
+
const REVIEW_LANES: readonly ReviewLane[] = ['sol', 'claude', 'glm'];
|
|
40
|
+
|
|
41
|
+
/** A PR keeps the same primary lane through every review round, regardless of head SHA. */
|
|
42
|
+
export function reviewLaneOrder(pr: string, mix: ReviewerMixSpec): ReviewLane[] {
|
|
43
|
+
if (!/^\d+$/.test(pr)) return [];
|
|
44
|
+
const total = REVIEW_LANES.reduce((sum, lane) => sum + mix[lane], 0);
|
|
45
|
+
if (!Number.isSafeInteger(total) || total <= 0) return [];
|
|
46
|
+
const slot = Number(BigInt(pr) % BigInt(total));
|
|
47
|
+
let offset = 0;
|
|
48
|
+
let primary: ReviewLane | undefined;
|
|
49
|
+
for (const lane of REVIEW_LANES) {
|
|
50
|
+
offset += mix[lane];
|
|
51
|
+
if (slot < offset) { primary = lane; break; }
|
|
52
|
+
}
|
|
53
|
+
if (!primary) return [];
|
|
54
|
+
const start = REVIEW_LANES.indexOf(primary);
|
|
55
|
+
return [...REVIEW_LANES.slice(start), ...REVIEW_LANES.slice(0, start)].filter(lane => mix[lane] > 0);
|
|
56
|
+
}
|
|
35
57
|
|
|
36
58
|
/** Only an explicit provider+model role setting selects the API review model. */
|
|
37
59
|
export function apiReviewModelFromRoleSpec(spec: RoleLlmSpec | undefined): string | undefined {
|
|
@@ -54,6 +76,15 @@ export interface SelfReviewCliOpts {
|
|
|
54
76
|
reviewDenyBackends?: string[] | undefined;
|
|
55
77
|
/** `roleLlm.reviewer` (same resolution as the harness). Used only when no explicit reviewer flag is given. */
|
|
56
78
|
roleReviewer?: SubscriptionReviewerSpec | undefined;
|
|
79
|
+
/** Default resolves from policy/config; null keeps the legacy single reviewer for isolated callers. */
|
|
80
|
+
reviewerMix?: ReviewerMixSpec | null | undefined;
|
|
81
|
+
/** Internal route metadata; the user-facing mix is selected once per PR. */
|
|
82
|
+
reviewerMixPrimaryLane?: ReviewLane | undefined;
|
|
83
|
+
reviewerMixAttemptLane?: ReviewLane | undefined;
|
|
84
|
+
reviewerMixExpectedHead?: string | undefined;
|
|
85
|
+
/** Internal: the same command already claimed its single over-cap exception for this pinned head. */
|
|
86
|
+
reviewerMixCapClaimed?: boolean | undefined;
|
|
87
|
+
disableClaudeGlmFallback?: boolean | undefined;
|
|
57
88
|
acpTimeout?: string | undefined;
|
|
58
89
|
context?: string[] | undefined;
|
|
59
90
|
contextText?: string[] | undefined;
|
|
@@ -67,6 +98,8 @@ export interface SelfReviewCliOpts {
|
|
|
67
98
|
|
|
68
99
|
/** 셸/네트워크/로그 경계 — 테스트는 전부 대역으로 채운다. */
|
|
69
100
|
export interface SelfReviewCliDeps {
|
|
101
|
+
/** Explicit repository root for exact-head cited-path checks; absent means unmeasured. */
|
|
102
|
+
reviewRepositoryPath?: string;
|
|
70
103
|
/** `gh` 호출. 실패도 예외 없이 `{status, stdout, stderr}` 로 돌려준다(spawnSync 동형). */
|
|
71
104
|
gh(args: string[], timeoutMs: number): { status: number | null; stdout: string; stderr: string };
|
|
72
105
|
/** ⭐ `git` 호출(선택). 주입되면 리뷰 증거를 **이 PR 이 저작한 변경**으로 좁힌다(아래 참조).
|
|
@@ -138,37 +171,77 @@ export interface SelfReviewCliOutcome {
|
|
|
138
171
|
failed: number;
|
|
139
172
|
}
|
|
140
173
|
|
|
141
|
-
|
|
142
|
-
|
|
174
|
+
interface SavedReviewEvidence {
|
|
175
|
+
heads: string[];
|
|
176
|
+
records: Array<{ head: string; reviewedAt: number }>;
|
|
177
|
+
unreadableFiles: string[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function savedReviewEvidence(
|
|
181
|
+
pr: string,
|
|
182
|
+
root = effectiveInstanceRoot(),
|
|
183
|
+
onUnreadable?: (file: string) => void,
|
|
184
|
+
): SavedReviewEvidence {
|
|
143
185
|
const dir = join(root, 'review-results');
|
|
144
186
|
let names: string[];
|
|
145
|
-
try { names = readdirSync(dir); } catch { return []; }
|
|
146
|
-
|
|
147
|
-
const
|
|
187
|
+
try { names = readdirSync(dir); } catch { return { heads: [], records: [], unreadableFiles: [] }; }
|
|
188
|
+
const escapedPr = pr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
189
|
+
const matchingPrInName = new RegExp(`(?:^|-)${escapedPr}(?:-|\\.json$)`, 'i');
|
|
148
190
|
const records = new Map<string, { head: string; reviewedAt: number }>();
|
|
191
|
+
const unreadableFiles: string[] = [];
|
|
149
192
|
for (const name of names) {
|
|
150
|
-
|
|
151
|
-
|
|
193
|
+
if (!name.toLowerCase().endsWith('.json')) continue;
|
|
194
|
+
const nameSuggestsPr = matchingPrInName.test(name);
|
|
152
195
|
try {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
const raw: unknown = JSON.parse(readFileSync(join(dir, name), 'utf8'));
|
|
197
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
198
|
+
if (nameSuggestsPr) { unreadableFiles.push(name); onUnreadable?.(name); }
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
156
201
|
const result = raw as Record<string, unknown>;
|
|
202
|
+
const recordPr = typeof result.pr === 'string' ? result.pr
|
|
203
|
+
: Number.isSafeInteger(result.pr) ? String(result.pr) : undefined;
|
|
204
|
+
if (recordPr !== pr) {
|
|
205
|
+
// A parseable, explicit foreign PR identity outranks a misleading filename.
|
|
206
|
+
if (recordPr === undefined && nameSuggestsPr) { unreadableFiles.push(name); onUnreadable?.(name); }
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (result.reviewed !== true) continue;
|
|
157
210
|
const head = result.headCommit;
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
211
|
+
if (typeof head !== 'string' || !COMMIT_OID.test(head)) {
|
|
212
|
+
unreadableFiles.push(name);
|
|
213
|
+
onUnreadable?.(name);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
// The result payload is authoritative: round-tagged filenames and stale suffixes
|
|
217
|
+
// still count by their full PR/head identity, then deduplicate on that identity.
|
|
218
|
+
const recordPath = join(dir, name);
|
|
162
219
|
const reviewedAt = typeof result.reviewedAt === 'number' && Number.isFinite(result.reviewedAt)
|
|
163
220
|
? result.reviewedAt : statSync(recordPath).mtimeMs;
|
|
221
|
+
const normalized = head.toLowerCase();
|
|
222
|
+
const existing = records.get(normalized);
|
|
164
223
|
if (!existing || reviewedAt > existing.reviewedAt) records.set(normalized, { head: normalized, reviewedAt });
|
|
165
|
-
} catch {
|
|
224
|
+
} catch {
|
|
225
|
+
// A filename can associate an unreadable record with this PR; fail closed at the cap.
|
|
226
|
+
if (nameSuggestsPr) { unreadableFiles.push(name); onUnreadable?.(name); }
|
|
227
|
+
}
|
|
166
228
|
}
|
|
167
|
-
|
|
229
|
+
const saved = [...records.values()];
|
|
230
|
+
return { heads: saved.map(({ head }) => head), records: saved, unreadableFiles };
|
|
168
231
|
}
|
|
169
232
|
|
|
170
233
|
export function savedReviewHeads(pr: string, root = effectiveInstanceRoot()): string[] {
|
|
171
|
-
return
|
|
234
|
+
return savedReviewEvidence(pr, root).heads;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function isReplaceableErrorArtifact(path: string, pr: string, head: string): boolean {
|
|
238
|
+
try {
|
|
239
|
+
const result = JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>;
|
|
240
|
+
const recordPr = typeof result.pr === 'string' ? result.pr
|
|
241
|
+
: Number.isSafeInteger(result.pr) ? String(result.pr) : undefined;
|
|
242
|
+
return recordPr === pr && result.headCommit === head && result.reviewed === false
|
|
243
|
+
&& typeof result.error === 'string' && result.error.length > 0;
|
|
244
|
+
} catch { return false; }
|
|
172
245
|
}
|
|
173
246
|
|
|
174
247
|
function normalizeReviewPrArg(input: string, deps: SelfReviewCliDeps): string | null {
|
|
@@ -1371,6 +1444,105 @@ function createDetachedReviewWorktree(commit: string, deps: SelfReviewCliDeps):
|
|
|
1371
1444
|
return { cwd, commit, remove };
|
|
1372
1445
|
}
|
|
1373
1446
|
|
|
1447
|
+
// ── 착지 관문(landing gate) — requested/actual ────────────────────────────────────────────
|
|
1448
|
+
//
|
|
1449
|
+
// ⚠️ 왜 생산자(여기)에 있나: 착지 관문(`src/cli/pr-land-overlap-evidence.ts`)도 결과 JSON 을 읽어
|
|
1450
|
+
// 같은 대조를 하지만, 그 파일을 **만드는 자리**가 틀린 requested/actual 을 적으면 관문은 그
|
|
1451
|
+
// 거짓을 근거로 판정한다. 그리고 `acp.reviewDenyBackends` 가 금지한 백엔드가 낸 판정은 파일이
|
|
1452
|
+
// 되기 **전에** 거부돼야 `--save` 로도 착지 근거가 될 수 없다(fail-closed).
|
|
1453
|
+
//
|
|
1454
|
+
// ⛔ 못 잰 값(`unmeasured`)을 «일치»로 적지 않는다 — 부재와 미지를 같은 값으로 두지 않는다.
|
|
1455
|
+
|
|
1456
|
+
/** 사람이 요청한 리뷰어. `value` 는 화면·로그에 그대로 나가는 **요청 원문**이다. */
|
|
1457
|
+
interface LandingGateRequest {
|
|
1458
|
+
kind: 'api-model' | 'acp-backend' | 'role-lane';
|
|
1459
|
+
value: string;
|
|
1460
|
+
/** api-model 요청의 정규화된 기대 모델(짧은 별칭 → 정본 ID). */
|
|
1461
|
+
model?: string;
|
|
1462
|
+
/** acp-backend 요청의 기대 백엔드. */
|
|
1463
|
+
backend?: string;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
/** 실제로 판정을 낸 리뷰어. `value` 는 실측 원문(`api:<model>` · `acp:<backend>` · `unmeasured`). */
|
|
1467
|
+
interface LandingGateActual {
|
|
1468
|
+
kind: 'api' | 'acp' | 'unknown';
|
|
1469
|
+
value: string;
|
|
1470
|
+
model?: string;
|
|
1471
|
+
backend?: string;
|
|
1472
|
+
respondedProvider?: string;
|
|
1473
|
+
respondedModel?: string;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
type LandingGateOutcome =
|
|
1477
|
+
| { state: 'consistent'; requested: string; actual: string }
|
|
1478
|
+
| { state: 'mismatch'; requested: string; actual: string; warning: string }
|
|
1479
|
+
| { state: 'denied'; requested: string; actual: string; warning: string; refusal: 'review-forbidden-acp-backend'; deniedBackend: string };
|
|
1480
|
+
|
|
1481
|
+
function normalizedReviewBackend(value: string | undefined): string {
|
|
1482
|
+
return canonicalizeBackendId((value ?? '').trim()).trim().toLowerCase();
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/** 금지 목록 대조. 문자열 엔트리와 구조형(`{backend, expiresAt}`) 엔트리가 같은 값으로 접힌다. */
|
|
1486
|
+
function deniedReviewBackend(backend: string | undefined, denyBackends: readonly string[]): boolean {
|
|
1487
|
+
const target = normalizedReviewBackend(backend);
|
|
1488
|
+
if (!target) return false;
|
|
1489
|
+
return denyBackends.some((denied) => normalizedReviewBackend(denied) === target);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
function landingGateAgrees(request: LandingGateRequest, actual: LandingGateActual): boolean {
|
|
1493
|
+
// ⛔ 실측을 못 잰 것을 «일치»로 적지 않는다(요청이 있으면 불일치로 남긴다).
|
|
1494
|
+
if (actual.kind === 'unknown') return false;
|
|
1495
|
+
if (request.kind === 'api-model') {
|
|
1496
|
+
if (actual.kind !== 'api') return false;
|
|
1497
|
+
const requested = (request.model ?? request.value).trim().toLowerCase();
|
|
1498
|
+
const selected = (actual.model ?? '').trim().toLowerCase();
|
|
1499
|
+
if (selected && selected === requested) return true;
|
|
1500
|
+
const respondedProvider = (actual.respondedProvider ?? '').trim().toLowerCase();
|
|
1501
|
+
const respondedModel = (actual.respondedModel ?? '').trim().toLowerCase();
|
|
1502
|
+
// 요청이 공급자 한정 형태(`openrouter/<model>`)일 때만 공급자·응답 모델을 대조한다 —
|
|
1503
|
+
// 맨 모델 ID(`gpt-6-sol`·`claude-sonnet-4`)는 공급자와 비교할 수 없다.
|
|
1504
|
+
const slash = requested.indexOf('/');
|
|
1505
|
+
if (slash <= 0) return false;
|
|
1506
|
+
const requestedProvider = requested.slice(0, slash);
|
|
1507
|
+
const requestedTail = requested.slice(slash + 1);
|
|
1508
|
+
return Boolean(respondedProvider) && respondedProvider === requestedProvider
|
|
1509
|
+
&& Boolean(respondedModel) && (respondedModel === requestedTail || respondedModel === requested);
|
|
1510
|
+
}
|
|
1511
|
+
if (request.kind === 'acp-backend') {
|
|
1512
|
+
return actual.kind === 'acp' && normalizedReviewBackend(actual.backend) === normalizedReviewBackend(request.backend);
|
|
1513
|
+
}
|
|
1514
|
+
// role-lane: 설정이 고른 레인은 provider 표기(`claude-acp`)와 백엔드 표기(`claude`)가 달라
|
|
1515
|
+
// 백엔드 이름으로 비교하지 않는다 — 「ACP 레인으로 판정했나」만 본다.
|
|
1516
|
+
return actual.kind === 'acp';
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
function landingGateOutcome(input: {
|
|
1520
|
+
pr: string;
|
|
1521
|
+
reviewRoute: 'api' | 'acp' | 'acp-fallback';
|
|
1522
|
+
request?: LandingGateRequest | undefined;
|
|
1523
|
+
actual: LandingGateActual;
|
|
1524
|
+
deniedBackends: readonly string[];
|
|
1525
|
+
}): LandingGateOutcome {
|
|
1526
|
+
const requested = input.request?.value ?? '(none)';
|
|
1527
|
+
const actual = input.actual.value;
|
|
1528
|
+
if (input.actual.kind === 'acp' && deniedReviewBackend(input.actual.backend, input.deniedBackends)) {
|
|
1529
|
+
const deniedBackend = normalizedReviewBackend(input.actual.backend);
|
|
1530
|
+
return {
|
|
1531
|
+
state: 'denied', requested, actual, refusal: 'review-forbidden-acp-backend', deniedBackend,
|
|
1532
|
+
warning: `⛔ LANDING GATE DENY (fail-closed): requested=${requested} actual=${actual} denied=${deniedBackend} reviewRoute=${input.reviewRoute} — acp.reviewDenyBackends 가 금지한 백엔드가 판정을 냈습니다. PR ${input.pr} 의 이 리뷰는 착지 근거가 아니며 self review 는 실패(rc≠0)로 끝납니다.`,
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
if (!input.request || landingGateAgrees(input.request, input.actual)) return { state: 'consistent', requested, actual };
|
|
1536
|
+
const responded = [
|
|
1537
|
+
input.actual.respondedProvider ? `respondedProvider=${input.actual.respondedProvider}` : '',
|
|
1538
|
+
input.actual.respondedModel ? `respondedModel=${input.actual.respondedModel}` : '',
|
|
1539
|
+
].filter(Boolean).join(' ');
|
|
1540
|
+
return {
|
|
1541
|
+
state: 'mismatch', requested, actual,
|
|
1542
|
+
warning: `⚠️ LANDING GATE REQUESTED/ACTUAL MISMATCH: requested=${requested} actual=${actual} reviewRoute=${input.reviewRoute}${responded ? ` ${responded}` : ''} — 요청한 리뷰어와 실제로 판정을 낸 리뷰어가 다릅니다(PR ${input.pr}). 이 리뷰를 착지 근거로 쓰기 전에 확인하세요.`,
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1374
1546
|
/** `elanous self review <pr...>` 본문. read-only — 머지하지 않는다. */
|
|
1375
1547
|
/**
|
|
1376
1548
|
* `self review --json` stdout 한 줄 — 결과가 하나면 객체, 여럿이면 배열. index.ts 액션이 이것을 그대로 쓴다.
|
|
@@ -1380,27 +1552,13 @@ export function selfReviewJsonLine(results: readonly unknown[]): string {
|
|
|
1380
1552
|
return JSON.stringify(results.length === 1 ? results[0] : results) + '\n';
|
|
1381
1553
|
}
|
|
1382
1554
|
|
|
1383
|
-
// Normalize only whole-item forms whose complete text is an explicit no-finding
|
|
1384
|
-
// sentinel. Any extra clause stays in mustFix (fail-closed).
|
|
1385
|
-
const NO_FINDING_REVIEW_NOTE_ALLOWLIST: readonly RegExp[] = [
|
|
1386
|
-
/^(?:없음|해당\s*없음|none|nothing|n\/a)[.!。]?$/i,
|
|
1387
|
-
/^\((?:없음|해당\s*없음|none|nothing|n\/a)\)[.!。]?$/i,
|
|
1388
|
-
/^\(이 조각에서 확인된 코어 결함 없음\s*[—–-]\s*명시 intent 기준 mustFix 는 비움\)$/i,
|
|
1389
|
-
/^\(이 조각에서 검증된 (?:차단 )?결함은 발견되지 않음(?:\s*\(mustFix \[\]\))?\)$/i,
|
|
1390
|
-
];
|
|
1391
|
-
|
|
1392
|
-
function isNoFindingReviewNote(finding: string): boolean {
|
|
1393
|
-
const wholeItem = finding.trim();
|
|
1394
|
-
return NO_FINDING_REVIEW_NOTE_ALLOWLIST.some((form) => form.test(wholeItem));
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
1555
|
function normalizeSavedReviewMustFix(result: Record<string, unknown>): { result: Record<string, unknown>; count: number } {
|
|
1398
1556
|
if (!Array.isArray(result.mustFix)) return { result, count: 0 };
|
|
1399
1557
|
const noFindingNotes = result.mustFix.filter((finding): finding is string =>
|
|
1400
|
-
typeof finding === 'string' &&
|
|
1558
|
+
typeof finding === 'string' && isNoneSentinelFinding(finding));
|
|
1401
1559
|
if (noFindingNotes.length === 0) return { result, count: 0 };
|
|
1402
1560
|
const retained = result.mustFix.filter((finding) =>
|
|
1403
|
-
typeof finding !== 'string' || !
|
|
1561
|
+
typeof finding !== 'string' || !isNoneSentinelFinding(finding));
|
|
1404
1562
|
const existingNotes = Array.isArray(result.noFindingNotes) ? result.noFindingNotes : [];
|
|
1405
1563
|
return {
|
|
1406
1564
|
result: { ...result, mustFix: retained, noFindingNotes: [...existingNotes, ...noFindingNotes] },
|
|
@@ -1415,6 +1573,141 @@ export async function runSelfReviewCliCommand(
|
|
|
1415
1573
|
): Promise<SelfReviewCliOutcome> {
|
|
1416
1574
|
const pins: ReviewWorktree[] = [];
|
|
1417
1575
|
try {
|
|
1576
|
+
// Resolve inside the leaf CLI seam so src/index.ts needs no mix-specific registration.
|
|
1577
|
+
const reviewerMix = opts.reviewerMix === undefined
|
|
1578
|
+
? loadLlmPolicy().policy.roles.reviewerMix ?? getUserConfig().roleLlm?.reviewerMix ?? DEFAULT_REVIEWER_MIX
|
|
1579
|
+
: opts.reviewerMix;
|
|
1580
|
+
// The route is chosen outside the existing single-review pipeline so explicit CLI flags retain
|
|
1581
|
+
// their exact behavior. A failed reviewer is retried on the same pinned PR diff by the next lane.
|
|
1582
|
+
if (reviewerMix && (opts.save === undefined || /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(opts.save)) &&
|
|
1583
|
+
!opts.model?.trim() && !opts.acp && !opts.acpBackend && !opts.acpModel && !deps.envModel()) {
|
|
1584
|
+
const results: Array<Record<string, unknown>> = [];
|
|
1585
|
+
let failed = 0;
|
|
1586
|
+
for (const arg of prArgs) {
|
|
1587
|
+
const pr = normalizeReviewPrArg(arg, deps);
|
|
1588
|
+
const lanes = pr ? reviewLaneOrder(pr, reviewerMix) : [];
|
|
1589
|
+
if (!pr || lanes.length === 0) {
|
|
1590
|
+
const outcome = await runSelfReviewCliCommandPinned([arg], { ...opts, reviewerMix: undefined }, deps, pins);
|
|
1591
|
+
results.push(...outcome.results);
|
|
1592
|
+
failed += outcome.failed;
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
const initialHead = prHeadCommit(pr, deps);
|
|
1596
|
+
if (initialHead.state !== 'known') {
|
|
1597
|
+
deps.log('review.lane-head-unknown', { pr, headState: initialHead.state }, { level: 'warn' });
|
|
1598
|
+
results.push({ pr, reviewed: false, refusal: 'reviewer-mix-head-unknown',
|
|
1599
|
+
failureReason: 'PR head could not be pinned before review', reviewerMixPrimaryLane: lanes[0] });
|
|
1600
|
+
failed += 1;
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
let completed: Record<string, unknown> | undefined;
|
|
1604
|
+
let last: Record<string, unknown> | undefined;
|
|
1605
|
+
let completedLane: ReviewLane | undefined;
|
|
1606
|
+
let attemptedLane: ReviewLane | undefined;
|
|
1607
|
+
let completedWithError = false;
|
|
1608
|
+
const pinnedHead = initialHead.value;
|
|
1609
|
+
const priorEvidence = savedReviewEvidence(pr);
|
|
1610
|
+
const capWasActive = priorEvidence.heads.length + priorEvidence.unreadableFiles.length >= 5;
|
|
1611
|
+
const canonicalResultPath = join(effectiveInstanceRoot(), 'review-results', `pr-${pr}-${pinnedHead.slice(0, 12)}.json`);
|
|
1612
|
+
if (capWasActive && existsSync(canonicalResultPath)
|
|
1613
|
+
&& !isReplaceableErrorArtifact(canonicalResultPath, pr, pinnedHead)) {
|
|
1614
|
+
const message = `PR ${pr}: 동일 head 리뷰 결과가 이미 저장되어 있습니다: ${canonicalResultPath}`;
|
|
1615
|
+
results.push({ pr, reviewed: false, refusal: 'cap-refused', existingResult: canonicalResultPath, headCommit: pinnedHead });
|
|
1616
|
+
deps.error(message);
|
|
1617
|
+
failed += 1;
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
let capClaimed = false;
|
|
1621
|
+
let pendingFallback: { from: ReviewLane; reason: string } | undefined;
|
|
1622
|
+
for (const [index, lane] of lanes.entries()) {
|
|
1623
|
+
const observedHead = prHeadCommit(pr, deps);
|
|
1624
|
+
if (observedHead.state !== 'known' || observedHead.value !== pinnedHead) {
|
|
1625
|
+
last = { pr, reviewed: false, refusal: 'reviewer-mix-head-changed', stale: true,
|
|
1626
|
+
expectedHeadCommit: pinnedHead, headCommit: observedHead.value };
|
|
1627
|
+
deps.log('review.lane-head-changed', { pr, expectedHead: pinnedHead,
|
|
1628
|
+
observedHead: observedHead.value }, { level: 'warn' });
|
|
1629
|
+
break;
|
|
1630
|
+
}
|
|
1631
|
+
const laneOpts: SelfReviewCliOpts = {
|
|
1632
|
+
...opts, reviewerMix: undefined, reviewerMixPrimaryLane: lanes[0], reviewerMixAttemptLane: lane,
|
|
1633
|
+
reviewerMixExpectedHead: pinnedHead, reviewerMixCapClaimed: capClaimed,
|
|
1634
|
+
disableClaudeGlmFallback: true, json: true,
|
|
1635
|
+
...(lane === 'claude' ? { acp: true, acpBackend: 'claude', model: undefined } :
|
|
1636
|
+
{ acp: false, acpBackend: undefined, model: lane === 'sol' ? 'gpt-6-sol' : ROLE_REVIEWER_FALLBACK_MODEL }),
|
|
1637
|
+
};
|
|
1638
|
+
let reviewerStarted = false;
|
|
1639
|
+
const laneDeps: SelfReviewCliDeps = { ...deps,
|
|
1640
|
+
reviewPullRequest: (input, llm) => { reviewerStarted = true; return deps.reviewPullRequest(input, llm); },
|
|
1641
|
+
observeDoctorReview: (event, data, options) => {
|
|
1642
|
+
if (event === 'cap-override') capClaimed = true;
|
|
1643
|
+
deps.observeDoctorReview?.(event, data, options);
|
|
1644
|
+
},
|
|
1645
|
+
};
|
|
1646
|
+
try {
|
|
1647
|
+
const outcome = await runSelfReviewCliCommandPinned([arg], laneOpts, laneDeps, pins);
|
|
1648
|
+
last = outcome.results[0];
|
|
1649
|
+
completedWithError = last?.reviewed === true && outcome.failed > 0;
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
last = { pr, reviewed: false, failureReason: safeLogText(error instanceof Error ? error.message : String(error), 400) };
|
|
1652
|
+
}
|
|
1653
|
+
if (last?.refusal === 'reviewer-mix-head-changed' || last?.refusal === 'cap-refused' || last?.stale === true) break;
|
|
1654
|
+
if (reviewerStarted) {
|
|
1655
|
+
attemptedLane = lane;
|
|
1656
|
+
deps.log('review.lane-chosen', { pr, lane, primaryLane: lanes[0], attempt: index + 1, weights: reviewerMix });
|
|
1657
|
+
}
|
|
1658
|
+
if (pendingFallback && reviewerStarted) {
|
|
1659
|
+
deps.log('review.lane-fallback', { pr, from: pendingFallback.from, to: lane,
|
|
1660
|
+
reason: pendingFallback.reason }, { level: 'warn' });
|
|
1661
|
+
pendingFallback = undefined;
|
|
1662
|
+
}
|
|
1663
|
+
if (last?.reviewed === true) {
|
|
1664
|
+
completed = last;
|
|
1665
|
+
completedLane = lane;
|
|
1666
|
+
break;
|
|
1667
|
+
}
|
|
1668
|
+
if (last?.refusal || last?.stale === true || index === lanes.length - 1) break;
|
|
1669
|
+
pendingFallback = { from: lane,
|
|
1670
|
+
reason: safeLogText(String(last?.failureReason ?? last?.error ?? 'reviewed=false'), 400) };
|
|
1671
|
+
}
|
|
1672
|
+
const result = completed ?? last;
|
|
1673
|
+
if (result) {
|
|
1674
|
+
const selectedLane = completedLane ?? attemptedLane;
|
|
1675
|
+
const enriched: Record<string, unknown> = { ...result, reviewerMixPrimaryLane: lanes[0],
|
|
1676
|
+
...(selectedLane ? { reviewerMixSelectedLane: selectedLane } : {}) };
|
|
1677
|
+
results.push(enriched);
|
|
1678
|
+
if (completed && completed.reviewed === true) {
|
|
1679
|
+
try {
|
|
1680
|
+
mkdirSync(dirname(canonicalResultPath), { recursive: true });
|
|
1681
|
+
const normalized = normalizeSavedReviewMustFix(enriched);
|
|
1682
|
+
if (enriched.headCommit !== pinnedHead || enriched.stale === true
|
|
1683
|
+
|| enriched.currentHeadCommit !== pinnedHead) {
|
|
1684
|
+
throw new Error('review result is not pinned to the confirmed current PR head');
|
|
1685
|
+
}
|
|
1686
|
+
writeFileSync(canonicalResultPath, selfReviewJsonLine([normalized.result]));
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
failed += 1;
|
|
1689
|
+
deps.error(`PR ${pr}: 리뷰 결과 저장 실패 (${canonicalResultPath}): ${error instanceof Error ? error.message : String(error)}`);
|
|
1690
|
+
if (capWasActive) {
|
|
1691
|
+
const authorization = readCapOverride(pr);
|
|
1692
|
+
const claimPath = authorization?.head === pinnedHead.toLowerCase()
|
|
1693
|
+
? join(effectiveInstanceRoot(), 'review-results', `cap-override-${pr}.json`)
|
|
1694
|
+
: join(effectiveInstanceRoot(), 'review-results', `cap-override-${pr}-head-${pinnedHead.toLowerCase()}.json`);
|
|
1695
|
+
try { unlinkSync(claimPath); } catch { /* already absent or owned by another lane */ }
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
} else if (capWasActive && !completed && capClaimed) {
|
|
1699
|
+
const authorization = readCapOverride(pr);
|
|
1700
|
+
const claimPath = authorization?.head === pinnedHead.toLowerCase()
|
|
1701
|
+
? join(effectiveInstanceRoot(), 'review-results', `cap-override-${pr}.json`)
|
|
1702
|
+
: join(effectiveInstanceRoot(), 'review-results', `cap-override-${pr}-head-${pinnedHead.toLowerCase()}.json`);
|
|
1703
|
+
try { unlinkSync(claimPath); } catch { /* already absent or owned by another lane */ }
|
|
1704
|
+
}
|
|
1705
|
+
if (!opts.json) deps.print(`\n━━ PR ${pr} (${String(enriched.model ?? 'review failed')}) ━━\n${deps.renderReview(enriched as unknown as ReviewResult)}`);
|
|
1706
|
+
}
|
|
1707
|
+
if (!completed || completedWithError) failed += 1;
|
|
1708
|
+
}
|
|
1709
|
+
return { results, failed };
|
|
1710
|
+
}
|
|
1418
1711
|
return await runSelfReviewCliCommandPinned(prArgs, opts, deps, pins);
|
|
1419
1712
|
} finally {
|
|
1420
1713
|
for (const pin of pins) pin.remove();
|
|
@@ -1468,8 +1761,10 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1468
1761
|
deps.error(message);
|
|
1469
1762
|
return { results: opts.json ? [{ error: message }] : [], failed: 1 };
|
|
1470
1763
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1764
|
+
// 이 명령이 실제로 적용하는 금지 목록 — 자동 폴백 후보에서 빼는 데 쓰이고(종전과 같은 값),
|
|
1765
|
+
// 이제 착지 관문이 «실제로 판정을 낸 백엔드»를 대조하는 데에도 같은 값으로 쓰인다.
|
|
1766
|
+
const effectiveReviewDenyBackends: string[] = opts.reviewDenyBackends === undefined ? configuredReviewDenyBackends : reviewDenyBackends;
|
|
1767
|
+
const automaticFallbacks = orderedAcpReviewFallbackBackends(opts.configuredAcpBackend, effectiveReviewDenyBackends);
|
|
1473
1768
|
const fallbackAcpBackends = opts.acpBackend
|
|
1474
1769
|
? [canonicalizeBackendId(opts.acpBackend)]
|
|
1475
1770
|
: automaticFallbacks.backends;
|
|
@@ -1520,7 +1815,13 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1520
1815
|
const made = deps.makeRoleReviewerLlm
|
|
1521
1816
|
? deps.makeRoleReviewerLlm(opts.roleReviewer, { timeoutMs: acpTimeoutSec * 1000 })
|
|
1522
1817
|
: { reason: 'role-reviewer-seam-missing' };
|
|
1523
|
-
|
|
1818
|
+
// ⛔ 금지 목록(acp.reviewDenyBackends)은 «자동 폴백 후보»만이 아니라 **자동 선택 전부**를 막는다.
|
|
1819
|
+
// 역할 레인은 사람이 친 플래그가 아니라 설정이 고른 자동 선택이므로, 금지된 백엔드가 레인을
|
|
1820
|
+
// 잡으면 그 리뷰가 그대로 착지 근거가 됐다(fail-open). ⇒ 레인을 쓰지 않고 같은 사유 코드로
|
|
1821
|
+
// API 로 내려간다(`role-reviewer-fallback · stage=availability` 가 관측에 남는다).
|
|
1822
|
+
if ('llm' in made && deniedReviewBackend(made.backend, effectiveReviewDenyBackends)) {
|
|
1823
|
+
roleReviewerUnavailableReason = 'denied-by-acp.reviewDenyBackends';
|
|
1824
|
+
} else if ('llm' in made) roleReviewer = { llm: made.llm, model: `acp:${made.backend}` };
|
|
1524
1825
|
else roleReviewerUnavailableReason = safeLogText(made.reason, 400);
|
|
1525
1826
|
} catch (error) {
|
|
1526
1827
|
roleReviewerUnavailableReason = roleAcpErrorReason(error);
|
|
@@ -1546,8 +1847,12 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1546
1847
|
const providedReviewerContext = loadReviewerContext(opts, deps.readReferencedFile);
|
|
1547
1848
|
const hasProvidedReviewerContext = (opts.contextOrder?.length ?? 0) > 0 || (opts.context?.length ?? 0) > 0 || (opts.contextText?.length ?? 0) > 0;
|
|
1548
1849
|
for (const pr of normalizedPrArgs as string[]) {
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1850
|
+
const savedEvidence = savedReviewEvidence(pr, undefined, (file) => {
|
|
1851
|
+
deps.observeDoctorReview?.('saved-result-unreadable', { pr, file }, { level: 'warn' });
|
|
1852
|
+
});
|
|
1853
|
+
const previousHeads = savedEvidence.heads;
|
|
1854
|
+
const previousRoundCount = previousHeads.length + savedEvidence.unreadableFiles.length;
|
|
1855
|
+
const shouldPersistCapResult = previousRoundCount >= 5 && !opts.reviewerMixPrimaryLane;
|
|
1551
1856
|
const reason = opts.overCapReason?.trim();
|
|
1552
1857
|
const authorization = previousRoundCount >= 5 ? readCapOverride(pr) : undefined;
|
|
1553
1858
|
deps.observeDoctorReview?.('round-counted', { pr, previousRoundCount, round: previousRoundCount + 1 });
|
|
@@ -1555,7 +1860,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1555
1860
|
if (previousRoundCount >= 5) {
|
|
1556
1861
|
deps.observeDoctorReview?.('cap-attempt', { pr, previousRoundCount, round: previousRoundCount + 1, reasonProvided: Boolean(reason), opAllowed }, { level: 'warn' });
|
|
1557
1862
|
}
|
|
1558
|
-
if (previousRoundCount >= 5 && (!opAllowed || (!authorization && !reason))) {
|
|
1863
|
+
if (previousRoundCount >= 5 && (!opAllowed || (!authorization && !reason && !opts.reviewerMixCapClaimed))) {
|
|
1559
1864
|
const refusalHead = prHeadCommit(pr, deps).value;
|
|
1560
1865
|
const message = !opAllowed
|
|
1561
1866
|
? `PR ${pr}: r5 상한 초과 리뷰는 OP traffic.json reviewOverCapAllow 허용 목록에 있어야 합니다.`
|
|
@@ -1567,11 +1872,45 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1567
1872
|
continue;
|
|
1568
1873
|
}
|
|
1569
1874
|
const diffRead = readDiffAtHead(pr, deps);
|
|
1570
|
-
|
|
1571
|
-
|
|
1875
|
+
const canonicalResultPath = COMMIT_OID.test(diffRead.headCommit)
|
|
1876
|
+
? join(effectiveInstanceRoot(), 'review-results', `pr-${pr}-${diffRead.headCommit.slice(0, 12)}.json`)
|
|
1877
|
+
: undefined;
|
|
1878
|
+
let claimedCapPath: string | undefined;
|
|
1879
|
+
const releaseCapClaim = (): void => {
|
|
1880
|
+
if (!claimedCapPath || opts.reviewerMixPrimaryLane) return;
|
|
1881
|
+
try {
|
|
1882
|
+
unlinkSync(claimedCapPath);
|
|
1883
|
+
claimedCapPath = undefined;
|
|
1884
|
+
} catch (error) {
|
|
1885
|
+
deps.error(`PR ${pr}: 상한 예외 사용 기록 롤백 실패 (${claimedCapPath}): ${error instanceof Error ? error.message : String(error)}`);
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1888
|
+
if (shouldPersistCapResult && canonicalResultPath && existsSync(canonicalResultPath)
|
|
1889
|
+
&& !isReplaceableErrorArtifact(canonicalResultPath, pr, diffRead.headCommit)) {
|
|
1890
|
+
const message = `PR ${pr}: 동일 head 리뷰 결과가 이미 저장되어 있습니다: ${canonicalResultPath}`;
|
|
1891
|
+
deps.error(message);
|
|
1892
|
+
results.push({ pr, error: message, reviewed: false, refusal: 'cap-refused', previousRoundCount, headCommit: diffRead.headCommit, existingResult: canonicalResultPath });
|
|
1893
|
+
failed += 1;
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1896
|
+
if (opts.reviewerMixExpectedHead && diffRead.headCommit !== opts.reviewerMixExpectedHead) {
|
|
1897
|
+
const refusal = { pr, reviewed: false, verdict: null, mustFix: [], shouldFix: [],
|
|
1898
|
+
refusal: 'reviewer-mix-head-changed', stale: true, headCommit: diffRead.headCommit,
|
|
1899
|
+
expectedHeadCommit: opts.reviewerMixExpectedHead };
|
|
1900
|
+
deps.log('review.lane-head-changed', { pr, expectedHead: opts.reviewerMixExpectedHead,
|
|
1901
|
+
observedHead: diffRead.headCommit }, { level: 'warn' });
|
|
1902
|
+
if (opts.json || opts.save !== undefined) results.push(refusal);
|
|
1903
|
+
failed += 1;
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
if (previousRoundCount >= 5 && authorization && diffRead.headCommit.toLowerCase() === authorization.head && !opts.reviewerMixCapClaimed) {
|
|
1907
|
+
const resultExists = canonicalResultPath && existsSync(canonicalResultPath);
|
|
1908
|
+
const message = resultExists
|
|
1909
|
+
? `PR ${pr}: 동일 head 리뷰 결과가 이미 저장되어 있습니다: ${canonicalResultPath}`
|
|
1910
|
+
: `PR ${pr}: 동일 head는 이미 승인 소모 기록이 있습니다. 결과 파일을 찾을 수 없어 재리뷰하지 않습니다.`;
|
|
1572
1911
|
deps.observeDoctorReview?.('cap-refused', { pr, head: diffRead.headCommit, previousRoundCount, round: previousRoundCount + 1, reason: 'same-head-already-used' }, { level: 'warn' });
|
|
1573
1912
|
deps.error(message);
|
|
1574
|
-
if (opts.json || opts.save !== undefined) results.push({ pr, error: message, reviewed: false, refusal: 'cap-refused', previousRoundCount, headCommit: diffRead.headCommit });
|
|
1913
|
+
if (opts.json || opts.save !== undefined || resultExists) results.push({ pr, error: message, reviewed: false, refusal: 'cap-refused', previousRoundCount, headCommit: diffRead.headCommit, ...(resultExists ? { existingResult: canonicalResultPath } : {}) });
|
|
1575
1914
|
failed += 1;
|
|
1576
1915
|
continue;
|
|
1577
1916
|
}
|
|
@@ -1584,7 +1923,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1584
1923
|
continue;
|
|
1585
1924
|
}
|
|
1586
1925
|
let mergeOnlyDeltaRange: { base: string; head: string } | undefined;
|
|
1587
|
-
if (previousRoundCount >= 5 && authorization) {
|
|
1926
|
+
if (previousRoundCount >= 5 && authorization && !opts.reviewerMixCapClaimed) {
|
|
1588
1927
|
const git = deps.git;
|
|
1589
1928
|
if (!git || diffRead.source !== 'sha-pinned' || diffRead.headCommitState !== 'known'
|
|
1590
1929
|
|| diffRead.currentHeadCommitState !== 'known' || diffRead.stale || diffRead.diff.status !== 0) {
|
|
@@ -1594,7 +1933,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1594
1933
|
failed += 1;
|
|
1595
1934
|
continue;
|
|
1596
1935
|
}
|
|
1597
|
-
const savedRecords =
|
|
1936
|
+
const savedRecords = savedEvidence.records;
|
|
1598
1937
|
const latestReviewed = savedRecords.reduce<(typeof savedRecords)[number] | undefined>(
|
|
1599
1938
|
(latest, record) => !latest || record.reviewedAt > latest.reviewedAt ? record : latest, undefined,
|
|
1600
1939
|
);
|
|
@@ -1629,7 +1968,9 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1629
1968
|
}
|
|
1630
1969
|
if (commits.stdout.trim() === '') mergeOnlyDeltaRange = { base: classificationBase, head: diffRead.headCommit.toLowerCase() };
|
|
1631
1970
|
}
|
|
1632
|
-
} else if (previousRoundCount >= 5 && reason) {
|
|
1971
|
+
} else if (previousRoundCount >= 5 && reason && !opts.reviewerMixCapClaimed) {
|
|
1972
|
+
// Claim only the exact SHA-pinned diff that the reviewer will see; never spend an OP
|
|
1973
|
+
// authorization on a separately read head that may move before readDiffAtHead.
|
|
1633
1974
|
const claimable = diffRead.source === 'sha-pinned' && diffRead.headCommitState === 'known'
|
|
1634
1975
|
&& diffRead.currentHeadCommitState === 'known' && !diffRead.stale && diffRead.diff.status === 0;
|
|
1635
1976
|
if (!claimable || !claimCapOverride(pr, diffRead.headCommit, reason, deps.now())) {
|
|
@@ -1640,6 +1981,9 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1640
1981
|
failed += 1;
|
|
1641
1982
|
continue;
|
|
1642
1983
|
}
|
|
1984
|
+
claimedCapPath = join(effectiveInstanceRoot(), 'review-results', authorization
|
|
1985
|
+
? `cap-override-${pr}-head-${diffRead.headCommit.toLowerCase()}.json`
|
|
1986
|
+
: `cap-override-${pr}.json`);
|
|
1643
1987
|
deps.observeDoctorReview?.('cap-override', { pr, head: diffRead.headCommit, previousRoundCount, round: previousRoundCount + 1, reason }, { level: 'warn' });
|
|
1644
1988
|
}
|
|
1645
1989
|
if (mergeOnlyDeltaRange) {
|
|
@@ -1648,19 +1992,24 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1648
1992
|
const message = `PR ${pr}: merge-only 후속 head의 delta diff를 고정할 수 없습니다.`;
|
|
1649
1993
|
deps.error(message);
|
|
1650
1994
|
if (opts.json || opts.save !== undefined) results.push({ pr, error: message, reviewed: false, refusal: 'cap-refused', previousRoundCount, headCommit: diffRead.headCommit });
|
|
1995
|
+
releaseCapClaim();
|
|
1651
1996
|
failed += 1;
|
|
1652
1997
|
continue;
|
|
1653
1998
|
}
|
|
1654
1999
|
diffRead.diff = delta;
|
|
1655
2000
|
diffRead.baseCommit = mergeOnlyDeltaRange.base;
|
|
1656
2001
|
}
|
|
1657
|
-
if (previousRoundCount >= 5 && authorization && !claimCapOverride(pr, diffRead.headCommit, authorization.reason, deps.now())) {
|
|
2002
|
+
if (previousRoundCount >= 5 && authorization && !opts.reviewerMixCapClaimed && !claimCapOverride(pr, diffRead.headCommit, authorization.reason, deps.now())) {
|
|
1658
2003
|
const message = `PR ${pr}: 후속 head 승인 사용 기록을 남기지 못했습니다.`;
|
|
1659
2004
|
deps.error(message);
|
|
1660
2005
|
if (opts.json || opts.save !== undefined) results.push({ pr, error: message, reviewed: false, refusal: 'cap-refused', previousRoundCount, headCommit: diffRead.headCommit });
|
|
1661
2006
|
failed += 1;
|
|
1662
2007
|
continue;
|
|
1663
2008
|
}
|
|
2009
|
+
if (previousRoundCount >= 5 && authorization && !opts.reviewerMixCapClaimed) {
|
|
2010
|
+
claimedCapPath = join(effectiveInstanceRoot(), 'review-results', `cap-override-${pr}-head-${diffRead.headCommit.toLowerCase()}.json`);
|
|
2011
|
+
deps.observeDoctorReview?.('cap-override', { pr, head: diffRead.headCommit, previousRoundCount, round: previousRoundCount + 1, reason: authorization.reason }, { level: 'warn' });
|
|
2012
|
+
}
|
|
1664
2013
|
const localHeadComparison = compareLocalHead(pr, { value: diffRead.headCommit, state: diffRead.headCommitState }, deps);
|
|
1665
2014
|
const { diff } = diffRead;
|
|
1666
2015
|
const baseReviewerContext = hasProvidedReviewerContext
|
|
@@ -1751,8 +2100,24 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1751
2100
|
{ pr: safeLogText(pr), exit: diff.status, error: safeLogText(diff.stderr || err, 160) },
|
|
1752
2101
|
{ level: 'warn' });
|
|
1753
2102
|
const contextOutput = reviewerContextOutput(reviewerContext, decompositionContext);
|
|
1754
|
-
|
|
2103
|
+
const errorResult = { pr, error: err, reviewed: false, headCommit: diffRead.headCommit,
|
|
2104
|
+
currentHeadCommit: diffRead.currentHeadCommit, stale: diffRead.stale,
|
|
2105
|
+
...contextOutput, ...comparisonData, ...(mismatchWarning ?? {}) };
|
|
2106
|
+
if (opts.json || opts.save !== undefined) results.push(errorResult);
|
|
2107
|
+
if (canonicalResultPath && COMMIT_OID.test(diffRead.headCommit)
|
|
2108
|
+
&& diffRead.headCommitState === 'known' && diffRead.currentHeadCommitState === 'known'
|
|
2109
|
+
&& diffRead.currentHeadCommit === diffRead.headCommit && !diffRead.stale) {
|
|
2110
|
+
try {
|
|
2111
|
+
if (!existsSync(canonicalResultPath) || isReplaceableErrorArtifact(canonicalResultPath, pr, diffRead.headCommit)) {
|
|
2112
|
+
mkdirSync(dirname(canonicalResultPath), { recursive: true });
|
|
2113
|
+
writeFileSync(canonicalResultPath, selfReviewJsonLine([errorResult]));
|
|
2114
|
+
}
|
|
2115
|
+
} catch (error) {
|
|
2116
|
+
deps.error(`PR ${pr}: 오류 리뷰 결과 저장 실패 (${canonicalResultPath}): ${error instanceof Error ? error.message : String(error)}`);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
1755
2119
|
if (!opts.json) deps.error(`PR ${pr}: ${err}\n${reviewerContextStatus}`);
|
|
2120
|
+
releaseCapClaim();
|
|
1756
2121
|
failed += 1;
|
|
1757
2122
|
continue;
|
|
1758
2123
|
}
|
|
@@ -1814,6 +2179,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1814
2179
|
};
|
|
1815
2180
|
if (opts.json || opts.save !== undefined) results.push(refused);
|
|
1816
2181
|
if (!opts.json) deps.print(`\n━━ PR ${pr} (${primaryModel}) ━━\n${renderStaleRefusal(diffRead)}\n${reviewerContextStatus}`);
|
|
2182
|
+
releaseCapClaim();
|
|
1817
2183
|
continue;
|
|
1818
2184
|
}
|
|
1819
2185
|
// ⭐ 머지된 PR 만 머지 커밋 detached worktree 에서 리뷰한다 — main 최신이 아니라 그 PR 이 머지된 시점의 코드를 본다.
|
|
@@ -1867,6 +2233,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1867
2233
|
...(deps.readReferencedFile ? { readReferencedFile: deps.readReferencedFile } : {}),
|
|
1868
2234
|
...(goalAcceptance.acceptance ? { acceptance: goalAcceptance.acceptance } : {}),
|
|
1869
2235
|
};
|
|
2236
|
+
const intentFingerprint = reviewIntentFingerprint(reviewInput.phaseIntent);
|
|
1870
2237
|
// ⭐ Role-config ACP lane: a completed ACP review is used as-is; anything else (throw, reviewed≠true)
|
|
1871
2238
|
// falls to the same API review this command ran before the lane existed — and is observed.
|
|
1872
2239
|
// ⚠️ Stricter than the harness lane on purpose: `runSubscriptionReviewer` accepts any non-empty text;
|
|
@@ -1936,7 +2303,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1936
2303
|
// An explicit `--acp-model` is the operator's model choice: it is not silently swapped for GLM.
|
|
1937
2304
|
// GLM that throws or also ends without a verdict keeps the claude result, and says so (`glmReviewed`).
|
|
1938
2305
|
let claudeFallbackGlm: { reason: string; model: string; glmReviewed: boolean; glmFailureReason?: string } | undefined;
|
|
1939
|
-
if (useAcp && !roleReview && !acpModel && canonicalizeBackendId(acpBackend) === 'claude' && review.reviewed !== true) {
|
|
2306
|
+
if (useAcp && !roleReview && !acpModel && !opts.disableClaudeGlmFallback && canonicalizeBackendId(acpBackend) === 'claude' && review.reviewed !== true) {
|
|
1940
2307
|
const reason = safeLogText(review.failureReason || 'reviewed=false', 400);
|
|
1941
2308
|
debug.log('review.acp', 'claude-fallback-glm', { reason }, { level: 'warn' });
|
|
1942
2309
|
fallbackAttempted = true;
|
|
@@ -1967,6 +2334,50 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1967
2334
|
...(reviewPin && reviewedOnPin ? { reviewCwdCommit: reviewPin.commit } : {}),
|
|
1968
2335
|
...(reviewCwdPinFallbackReason ? { reviewCwdPinFallbackReason } : {}),
|
|
1969
2336
|
};
|
|
2337
|
+
// ⭐ 착지 관문 — 요청한 리뷰어와 실제로 판정을 낸 리뷰어를 대조한다. 판정이 나온 리뷰에만
|
|
2338
|
+
// 의미가 있다(리뷰가 없으면 근거도 없고, 실패는 기존 회계가 이미 센다).
|
|
2339
|
+
const acpActual = (backendText: string | undefined): LandingGateActual => {
|
|
2340
|
+
const backend = normalizedReviewBackend(backendText);
|
|
2341
|
+
return backend ? { kind: 'acp', value: `acp:${backend}`, backend } : { kind: 'unknown', value: 'unmeasured' };
|
|
2342
|
+
};
|
|
2343
|
+
const landingGateRequest: LandingGateRequest | undefined = requestedModel
|
|
2344
|
+
? { kind: 'api-model', value: opts.model?.trim() || requestedModel, model: explicitApiModel ?? requestedModel }
|
|
2345
|
+
: useAcp
|
|
2346
|
+
? { kind: 'acp-backend', value: `acp:${canonicalizeBackendId(acpBackend)}`, backend: acpBackend }
|
|
2347
|
+
: reviewerSource === 'role-config' && opts.roleReviewer
|
|
2348
|
+
? { kind: 'role-lane', value: `acp:${opts.roleReviewer.provider}` }
|
|
2349
|
+
: undefined;
|
|
2350
|
+
const landingGateActual: LandingGateActual = reviewRoute === 'acp-fallback'
|
|
2351
|
+
? acpActual(fallbackBackend)
|
|
2352
|
+
: reviewRoute === 'acp'
|
|
2353
|
+
? acpActual(roleReview && prRoleReviewer ? prRoleReviewer.model.replace(/^acp:/, '') : acpBackend)
|
|
2354
|
+
: ((): LandingGateActual => {
|
|
2355
|
+
const apiModel = (selectedModel ?? '').trim();
|
|
2356
|
+
if (!apiModel) return { kind: 'unknown', value: 'unmeasured' };
|
|
2357
|
+
return {
|
|
2358
|
+
kind: 'api', value: `api:${apiModel}`, model: apiModel,
|
|
2359
|
+
...(responded.provider ? { respondedProvider: responded.provider } : {}),
|
|
2360
|
+
...(responded.model ? { respondedModel: responded.model } : {}),
|
|
2361
|
+
};
|
|
2362
|
+
})();
|
|
2363
|
+
const landingGate = review.reviewed === true
|
|
2364
|
+
? landingGateOutcome({ pr, reviewRoute, request: landingGateRequest, actual: landingGateActual, deniedBackends: effectiveReviewDenyBackends })
|
|
2365
|
+
: undefined;
|
|
2366
|
+
if (landingGate && landingGate.state !== 'consistent') {
|
|
2367
|
+
deps.error(landingGate.warning);
|
|
2368
|
+
deps.log('landing-gate', {
|
|
2369
|
+
pr: safeLogText(pr), decision: landingGate.state, reviewRoute,
|
|
2370
|
+
requested: safeLogText(landingGate.requested), actual: safeLogText(landingGate.actual),
|
|
2371
|
+
...(landingGate.state === 'denied' ? { deniedBackend: landingGate.deniedBackend } : {}),
|
|
2372
|
+
}, { level: 'warn' });
|
|
2373
|
+
}
|
|
2374
|
+
const landingGateAdmitted = landingGate?.state !== 'denied';
|
|
2375
|
+
const landingGateDeniedBackend = landingGate?.state === 'denied' ? landingGate.deniedBackend : undefined;
|
|
2376
|
+
const landingGateFields: Record<string, unknown> = landingGate?.state === 'mismatch'
|
|
2377
|
+
? { landingGateDecision: 'mismatch', landingGateRequested: landingGate.requested, landingGateActual: landingGate.actual }
|
|
2378
|
+
: landingGate?.state === 'denied'
|
|
2379
|
+
? { landingGateDecision: 'denied', landingGateRequested: landingGate.requested, landingGateActual: landingGate.actual, landingGateDeniedBackend: landingGate.deniedBackend }
|
|
2380
|
+
: {};
|
|
1970
2381
|
if (!fallbackAttempted) {
|
|
1971
2382
|
fallbackNotAttemptedReason = useAcp ? 'explicit-acp' : requestedModel && review.reviewed !== true ? 'explicit-model' : 'primary-review-succeeded';
|
|
1972
2383
|
}
|
|
@@ -1991,13 +2402,33 @@ async function runSelfReviewCliCommandPinned(
|
|
|
1991
2402
|
const reviewerSelection = {
|
|
1992
2403
|
...reviewerSourceObservation(),
|
|
1993
2404
|
...(roleReviewerFallbackReason !== undefined ? { roleReviewerFallbackReason } : {}),
|
|
2405
|
+
...(opts.reviewerMixPrimaryLane ? { reviewerMixPrimaryLane: opts.reviewerMixPrimaryLane,
|
|
2406
|
+
reviewerMixSelectedLane: opts.reviewerMixAttemptLane } : {}),
|
|
1994
2407
|
};
|
|
2408
|
+
// The reviewer owns its finding severity. A missing citation is separate evidence,
|
|
2409
|
+
// never a new must-fix or a verified defect. Keep unreadable heads distinct too.
|
|
2410
|
+
const reviewRepositoryPath = deps.reviewRepositoryPath;
|
|
2411
|
+
const findingPathHeadChecks = review.reviewed === true && landingGateAdmitted
|
|
2412
|
+
&& diffRead.headCommitState === 'known' && reviewRepositoryPath
|
|
2413
|
+
? ([...review.mustFix.map(finding => ({ severity: 'mustFix' as const, finding })),
|
|
2414
|
+
...review.shouldFix.map(finding => ({ severity: 'shouldFix' as const, finding }))]
|
|
2415
|
+
.map(({ severity, finding }) => ({ severity, finding,
|
|
2416
|
+
...checkCitedReviewPathsAtHead(finding, reviewRepositoryPath, diffRead.headCommit) }))
|
|
2417
|
+
.filter(check => check.paths.length > 0))
|
|
2418
|
+
: [];
|
|
2419
|
+
const pathWarnings = findingPathHeadChecks.flatMap(check => check.paths
|
|
2420
|
+
.filter(path => path.status !== 'present')
|
|
2421
|
+
.map(path => `${path.status === 'missing' ? '경로 미확인' : '경로 검증 불가'}: ${path.path} (PR head ${check.head})`));
|
|
2422
|
+
if (pathWarnings.length) deps.log('finding-path-head-check', {
|
|
2423
|
+
pr, headCommit: diffRead.headCommit, missing: findingPathHeadChecks.flatMap(check => check.paths).filter(path => path.status === 'missing').length,
|
|
2424
|
+
unreadable: findingPathHeadChecks.flatMap(check => check.paths).filter(path => path.status === 'unreadable').length,
|
|
2425
|
+
}, { level: 'warn' });
|
|
1995
2426
|
// ⚠️ verdict=fail 은 **정상 동작**(리뷰가 일한 것)이라 warn 으로 올리지 않는다. 대신 `reviewed:false`
|
|
1996
2427
|
// (fail-soft pass·리뷰 미실행)는 조용히 통과로 읽히면 위험하므로 warn 이다.
|
|
1997
2428
|
deps.log('done',
|
|
1998
2429
|
{
|
|
1999
2430
|
...buildReviewObservation({
|
|
2000
|
-
pr, model: selectedModel, intent, review, durationMs: deps.now() - startedAt,
|
|
2431
|
+
pr, model: selectedModel, intent, intentFingerprint, review, durationMs: deps.now() - startedAt,
|
|
2001
2432
|
...reviewProvenance,
|
|
2002
2433
|
...intentTruncation,
|
|
2003
2434
|
...reviewDiffBudgetObservation(review),
|
|
@@ -2011,6 +2442,7 @@ async function runSelfReviewCliCommandPinned(
|
|
|
2011
2442
|
acceptanceChars: goalAcceptance.acceptanceChars,
|
|
2012
2443
|
...reviewerSelection,
|
|
2013
2444
|
...reviewCwdOutput,
|
|
2445
|
+
...landingGateFields,
|
|
2014
2446
|
},
|
|
2015
2447
|
{ level: review.reviewed === false ? 'warn' : 'info' });
|
|
2016
2448
|
// --acp 는 대표가 명시 요청한 리뷰 — fail-soft(reviewed=false)면 조용히 pass 하지 말고 표면화(관측=acp-review).
|
|
@@ -2019,17 +2451,43 @@ async function runSelfReviewCliCommandPinned(
|
|
|
2019
2451
|
const contextOutput = reviewerContextOutput(reviewerContext, decompositionContext);
|
|
2020
2452
|
if (opts.json || opts.save !== undefined) results.push({ pr, error: err, ...review, verdict: null, ...reviewProvenance, ...respondedOutput, ...reviewerSelection, ...reviewCwdOutput, ...provenance, ...contextOutput });
|
|
2021
2453
|
if (!opts.json) deps.error(`PR ${pr}: ${err}\n${renderProvenance(diffRead)}\n${reviewerContextStatus}`);
|
|
2454
|
+
releaseCapClaim();
|
|
2022
2455
|
failed += 1;
|
|
2023
2456
|
continue;
|
|
2024
2457
|
}
|
|
2025
2458
|
const contextOutput = reviewerContextOutput(reviewerContext, decompositionContext);
|
|
2026
2459
|
const jsonReview = review.reviewed === true ? review : { ...review, verdict: null };
|
|
2027
|
-
|
|
2028
|
-
|
|
2460
|
+
// ⛔ 금지 백엔드가 낸 판정은 «근거 거부»다 — verdict·mustFix 를 그대로 실어 보내면 읽는 쪽이
|
|
2461
|
+
// 그것을 착지 판정으로 쓴다(관문도 reviewed===true 를 근거 조건으로 본다).
|
|
2462
|
+
const refusalOverride = landingGateAdmitted ? {} : { reviewed: false, verdict: null, refusal: 'review-forbidden-acp-backend' as const };
|
|
2463
|
+
const resultOutput = { pr, model: selectedModel, ...jsonReview, ...reviewProvenance, ...respondedOutput, ...reviewerSelection, ...reviewCwdOutput, ...provenance, ...contextOutput, ...refusalOverride, ...landingGateFields, findingPathHeadChecks, pathWarnings, evidence: evidence.evidence, prDiffFiles: evidence.prDiffFiles, ...(evidence.authoredFiles !== undefined ? { authoredFiles: evidence.authoredFiles } : {}) };
|
|
2464
|
+
if (opts.json || opts.save !== undefined || shouldPersistCapResult) results.push(resultOutput);
|
|
2465
|
+
if (!shouldPersistCapResult
|
|
2466
|
+
&& landingGateAdmitted && review.reviewed === true && diffRead.headCommitState === 'known'
|
|
2467
|
+
&& diffRead.currentHeadCommitState === 'known' && diffRead.currentHeadCommit === diffRead.headCommit
|
|
2468
|
+
&& !diffRead.stale && COMMIT_OID.test(diffRead.headCommit)) {
|
|
2469
|
+
const path = canonicalResultPath ?? join(effectiveInstanceRoot(), 'review-results', `pr-${pr}-${diffRead.headCommit.slice(0, 12)}.json`);
|
|
2470
|
+
const normalized = normalizeSavedReviewMustFix(resultOutput);
|
|
2471
|
+
try {
|
|
2472
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
2473
|
+
// A successful review replaces an earlier error artifact for the same PR head.
|
|
2474
|
+
writeFileSync(path, selfReviewJsonLine([normalized.result]));
|
|
2475
|
+
if (normalized.count > 0 && opts.save === undefined) deps.log('mustfix-normalized', { pr, head: diffRead.headCommit, count: normalized.count });
|
|
2476
|
+
} catch (error) {
|
|
2477
|
+
deps.error(`PR ${pr}: 리뷰 결과 저장 실패 (${path}): ${error instanceof Error ? error.message : String(error)}`);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
if (!opts.json) {
|
|
2481
|
+
if (!landingGateAdmitted) deps.print(`\n━━ PR ${pr} (${selectedModel}) ━━\n${renderProvenance(diffRead)}\n⛔ LANDING GATE: review-forbidden-acp-backend (${landingGateDeniedBackend ?? 'unknown'}) — 이 리뷰는 착지 근거가 아닙니다.\n${reviewerContextStatus}`);
|
|
2482
|
+
else deps.print(`\n━━ PR ${pr} (${selectedModel}) ━━\n${renderProvenance(diffRead)}\n${evidenceLine}\n${reviewerContextStatus}\n${deps.renderReview(review)}${pathWarnings.length ? `\n${pathWarnings.join('\n')}` : ''}`);
|
|
2483
|
+
}
|
|
2029
2484
|
// REVIEW-ERROR-RC — 기본(API)·role 경로가 1차·폴백 모두 실패해 reviewed≠true 로 끝나도 «못 리뷰한 PR» 이다(verdict:null).
|
|
2030
|
-
|
|
2485
|
+
// ⛔ 착지 관문 거부도 같은 회계다 — 금지 백엔드가 판정을 냈으면 리뷰가 «성공»해도 근거가 아니다(rc≠0).
|
|
2486
|
+
if (review.reviewed !== true || !landingGateAdmitted) failed += 1;
|
|
2031
2487
|
if (opts.save !== undefined) {
|
|
2032
|
-
if (
|
|
2488
|
+
if (!landingGateAdmitted) {
|
|
2489
|
+
deps.error(`PR ${pr}: --save 거부 — acp.reviewDenyBackends 가 금지한 백엔드(${landingGateDeniedBackend ?? 'unknown'})의 결과는 착지 근거로 저장하지 않습니다.`);
|
|
2490
|
+
} else if (review.reviewed !== true || diffRead.headCommitState !== 'known' || diffRead.currentHeadCommitState !== 'known' || diffRead.currentHeadCommit !== diffRead.headCommit || diffRead.headCommit.length !== 40) {
|
|
2033
2491
|
deps.error(`PR ${pr}: --save 불가 — 완료된 리뷰와 확인된 40자 PR head가 필요합니다.`);
|
|
2034
2492
|
if (review.reviewed === true) failed += 1;
|
|
2035
2493
|
} else {
|
|
@@ -2049,6 +2507,28 @@ async function runSelfReviewCliCommandPinned(
|
|
|
2049
2507
|
}
|
|
2050
2508
|
}
|
|
2051
2509
|
}
|
|
2510
|
+
if (shouldPersistCapResult) {
|
|
2511
|
+
if (!landingGateAdmitted || review.reviewed !== true || !canonicalResultPath
|
|
2512
|
+
|| diffRead.headCommitState !== 'known' || diffRead.currentHeadCommitState !== 'known'
|
|
2513
|
+
|| diffRead.currentHeadCommit !== diffRead.headCommit || !COMMIT_OID.test(diffRead.headCommit)) {
|
|
2514
|
+
releaseCapClaim();
|
|
2515
|
+
deps.error(`PR ${pr}: 상한 예외 리뷰 결과가 저장 가능한 상태가 아니어서 승인 사용 기록을 되돌렸습니다.`);
|
|
2516
|
+
} else {
|
|
2517
|
+
const rawResult = results.at(-1);
|
|
2518
|
+
const normalized = rawResult && typeof rawResult === 'object'
|
|
2519
|
+
? normalizeSavedReviewMustFix(rawResult)
|
|
2520
|
+
: { result: rawResult, count: 0 };
|
|
2521
|
+
try {
|
|
2522
|
+
mkdirSync(dirname(canonicalResultPath), { recursive: true });
|
|
2523
|
+
writeFileSync(canonicalResultPath, selfReviewJsonLine([normalized.result]));
|
|
2524
|
+
if (normalized.count > 0) deps.log('mustfix-normalized', { pr, head: diffRead.headCommit, count: normalized.count });
|
|
2525
|
+
} catch (error) {
|
|
2526
|
+
releaseCapClaim();
|
|
2527
|
+
failed += 1;
|
|
2528
|
+
deps.error(`PR ${pr}: 상한 예외 리뷰 결과 저장 실패${canonicalResultPath ? ` (${canonicalResultPath})` : ''}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2052
2532
|
}
|
|
2053
2533
|
return { results, failed };
|
|
2054
2534
|
}
|