elanous 0.2.32 → 0.2.35
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/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/{997ce8a5-2ad501927003b41b.js → 0368f25d-ad84af92fefee68f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1165.bad13fc9151a7288.js +1 -0
- package/apps/pwa/out/_next/static/chunks/13.02a07436dec15f89.js +32 -0
- package/apps/pwa/out/_next/static/chunks/{5525.86debf231e181813.js → 1530.0e612369b6fd7ea0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1540.af38731fa7c4a3e0.js +41 -0
- package/apps/pwa/out/_next/static/chunks/{7391.ebae168025c91b6a.js → 1674.2a65fd5668e1cc5f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1850.f2b470ba5a471da0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/193.62da94cd0b88323b.js +74 -0
- package/apps/pwa/out/_next/static/chunks/1975.260f6d249fac5f7c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1982-67cd8dfe8682325f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7947-d0759430f8042240.js → 1993-c0007b7ad0c0ef41.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1999-bb415351c8a2846c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{60fb96c9.360b01d8d4927b7b.js → 1c735de4.f993a30bbd3c57c4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1dc5a567.9b092bfae3d7099b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5307.60f2209e809bd627.js → 2018.a741297bce759490.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2085.8f950b4bda1cbbc7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2159-6179a961376956b9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216.b5c2a421aa1d28a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2251.49fbe369f74c372c.js +148 -0
- package/apps/pwa/out/_next/static/chunks/2286-2939577360d3dcc0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3902.e1e71aaac850a045.js → 2625.5d870aa999f33db1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2668.34d21539e62bf111.js +77 -0
- package/apps/pwa/out/_next/static/chunks/2833-0fe47af99ed13c29.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2930.d0edb4f108787ef7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2973.77318e7d85f59477.js +62 -0
- package/apps/pwa/out/_next/static/chunks/3103.0df8da390948bb84.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3123.2bee9ba15d320bd2.js +90 -0
- package/apps/pwa/out/_next/static/chunks/3162-6cf652bf7124d8cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4174.92ed04922709ac9e.js → 3408.21d1570602f4b6c0.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/3577-7340009f7007a795.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3589.5444a06bcf6e775c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/359-2250789a2d3499e7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3618.f514792d6c59d7fd.js +36 -0
- package/apps/pwa/out/_next/static/chunks/3669.343f9119aa276001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9398-6aed28f0a96d0d6f.js → 3677-4215f826f6aa68e5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3736.c2efad489604cc3b.js +79 -0
- package/apps/pwa/out/_next/static/chunks/3817.f9aeab2bb6675d73.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3f7c4ee5.3dde54f01a816613.js +122 -0
- package/apps/pwa/out/_next/static/chunks/4182-0b9b1251c29cc0cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7371.80c05c653b08e907.js → 4346.8601952e73bf5803.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4441-875b97f5821ed58a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4582.bd68683c076649e3.js +286 -0
- package/apps/pwa/out/_next/static/chunks/4605-6e623f55aec77422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4607.5cc27468628eb4bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4627.f07508b3936fd6c0.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{2940-619bfd8ef337c611.js → 4705-194e64e47ca0fbdb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1335-6731f392c35216eb.js → 4718-17d2af636796a330.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/4747-c22262c093e46807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4775-178215aed0daba61.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4815-6ffb8c3c991cc45d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4834-beca8b47ad29a898.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5023-fb4802b6d2492b02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7502.ccd5b8065545edd0.js → 5101.e32282718f99ed6f.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/{2001.6c7e0fd02190886c.js → 5154.a118f3957ed14e36.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5210-a2e0015e3d4d5d9c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5356.84ac712cc15928bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5401.22352141b6fd336e.js +23 -0
- package/apps/pwa/out/_next/static/chunks/{5173-233188df2f42c626.js → 5483-dd5d7697f6bdb106.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5637.281e5dec4ea6ecf1.js +94 -0
- package/apps/pwa/out/_next/static/chunks/5662.02eaa029af05ad46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3267c0e6.d34854e7b3984a6e.js → 56d372f2.681ab4ec4c998fae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5760.22456f433c6f42c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5852.db1f7449f4f74612.js +287 -0
- package/apps/pwa/out/_next/static/chunks/5891-0f11a7d7362a9072.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5943.264d14a44ca32513.js +1 -0
- package/apps/pwa/out/_next/static/chunks/604-1b3c39df083c02aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6121-5f97072330738caf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6193.07d8ca5af4b09086.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6203-b3fea3a60d69ff14.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6433.fc573af22bbc9ab7.js → 6236.e18cced2d5ba6afb.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/6286.d638fb5953e2e757.js +8 -0
- package/apps/pwa/out/_next/static/chunks/630.fe5a46428d63b013.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6301.ae0245f507e4395e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6673.fbbe5699f6ea5991.js +27 -0
- package/apps/pwa/out/_next/static/chunks/6777-f6d301efb5de828d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6786.423f24c88de034c7.js +93 -0
- package/apps/pwa/out/_next/static/chunks/6854-cef64a948c5e122d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6866-79aa5b4e47cac6aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6544cb48.a87fe20ddf137be2.js → 6b89c8bd.24df687a820500b3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7037-fe27f001db30d323.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7080.2d826860534fd88e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7118.8731be28c3ebb14b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7196.44241bc1df3e26fe.js +149 -0
- package/apps/pwa/out/_next/static/chunks/7235.c7c4e6cb5c693c2d.js +104 -0
- package/apps/pwa/out/_next/static/chunks/{2871.0bca6137343b3f76.js → 7385.50a308a477bdfc21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2985.39ccdecd36848b12.js → 7644.17e37ba6f1d4226e.js} +4 -4
- package/apps/pwa/out/_next/static/chunks/7706-58bfb1546637f217.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7839.5b91455338c9fd3f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7857-31f13ee96f4011aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/81.f74cb60312fe4001.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8018.a32663f04a6fc5f9.js → 8219.baa715d1deee80e2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4111.2bfedbb233ad206b.js → 8256.e3ac8421a71f82f4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8310-444a6a895c402f12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8423.f7dda8aca4fcaae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8437-8d5492094b166cfa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2213.2ccbada92c33ea0f.js → 8444.4c26b379ca2b9596.js} +17 -17
- package/apps/pwa/out/_next/static/chunks/8496.75649af8652c74f4.js +226 -0
- package/apps/pwa/out/_next/static/chunks/8557-e7898769499cd56f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8576.65bb654b524f3614.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8609.558ce2f8f9aea2be.js +64 -0
- package/apps/pwa/out/_next/static/chunks/8614.caec96cfb91f4ae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6092.a7f99e68b188ca66.js → 8637.47f5b35afcae5e6b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8714.ff0b7ce3ec3e3ae2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8732.367f44a916202353.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9141-7fd1f73a5400bdc2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/921.c4267d0d69a65404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3278.2e5d15b53a4091c8.js → 9214.63a510d39178e997.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9261.3962c3321b047691.js +115 -0
- package/apps/pwa/out/_next/static/chunks/936-26efd1a0efdd44d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9221.7d22eaf353a2d4e7.js → 938.66ad1ef0e5abae1c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6157.0c9af021de4fb31b.js → 9512.5a76207a730e0491.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/9627-4b9efe616b27e2e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{69-0a2c8209c6d4a158.js → 967-2aa39b2d04b4c690.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9734.40eb1e558c29ccea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9908.3aa54f7a586a73dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7862.30a8d6482bb238eb.js → 9952.5daf5f94063a2b3e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9954-ca8d29752bdbb45a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9974-507fa67cb94066dd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fa59d6d17daa3389.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-53f68bbe7bd4b774.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-1c500fbb9f4a7a04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-99d72673aa5c307c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-d2d5adc1e9484c88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c607c5aae637f1cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-fa8c23596d555c0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-bdab8c5e855c8416.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a27b1e5a0d1b308f.js → page-406b47745b53f269.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-a4276386b7051f50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8a26bab9ad375f25.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-775f48aa1dc7a2d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-1c4dd5f959d0c558.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-c48938a8377c9f70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c6e7905859425aa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c280f4252452e830.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-cf8c57498f20970a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-ca3a17caf409fc83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-26e6c18a1a750cf6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-3abc872cc6aeee39.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7481b7b2174fe3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1ae7ef8077680337.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-32866485913d6a04.js → page-47c5d4db587266bd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-415f1e243c7ccb13.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-73f380f17b76f95c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-2991073ad66ee2d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-1f5a60f70cb4dd0c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fbad60d1e358fb2c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-bac7dee94e5eae4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-398047d6949e8ca5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2d33a2231540ca89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-5bf1e6bf70bcb96c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-ba59c19376a0917d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-111f187139add30f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-acaae197d5be5779.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0df20b0f546a0d7d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-ce9e00f7fb45506a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-27e1202d24b8fe35.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-bda78c50a78363e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-da592691293d9c9f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f832994317a2e9f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-04d00ae9b0890349.js → page-dc0a532f5b9196ec.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-cfccbbeae0473404.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-527e2073af84c81b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-bac86e9112671357.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6d59dff410e9d76a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-53a61f8be09f02d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-db8f559e763c17f9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c02aa4cc66455093.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7a1887c4.f36b88fba31da8fd.js → b2f6ba4d.fb56fdbbf798df33.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/b6705a4f-b9a9047de9eecfd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b313b242.cb1265eea908db61.js → cbe38a15.5a032598e39970c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{409f1e57.d0649fc47691ebfe.js → d06f9160.a3d9062f95de8ad7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{0bb0ff36.b8d418980951e5ba.js → da5176c6.6773b6c5994e1d99.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/framework-50e6acebc630849b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-7a1ae342d5ed75e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-7ad7d505ba1f92b4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-02edba75232cb235.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d6ebae3e8eee4a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-e986adaa429163e8.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +22 -22
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +21 -21
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +22 -22
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +22 -22
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +22 -22
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.mjs +10 -2
- package/catalog/providers/opengateway.yaml +25 -0
- package/docs/goal-context/01-goal-authoring-checks.md +8 -0
- package/docs/goal-context/03-verification-menu.md +24 -0
- package/docs/goal-context/04-mock-scenarios.md +100 -0
- package/docs/goal-context/README.md +5 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/default-loop.yaml +76 -5
- package/graphs/document-loop.yaml +71 -4
- package/graphs/implement-loop.yaml +59 -4
- package/graphs/journey/SCENARIOS.md +33 -0
- package/graphs/journey/recipes.yaml +1 -0
- package/graphs/journey/ta-journey.yaml +94 -0
- package/graphs/journey/units/author/author.yaml +20 -0
- package/graphs/journey/units/author/recipes.yaml +2 -0
- package/graphs/journey/units/author-dialogue/author-dialogue.yaml +42 -0
- package/graphs/journey/units/author-dialogue/observe.ts +96 -0
- package/graphs/journey/units/author-dialogue/recipes.yaml +10 -0
- package/graphs/journey/units/green/green.yaml +20 -0
- package/graphs/journey/units/green/recipes.yaml +2 -0
- package/graphs/journey/units/pr/pr.yaml +20 -0
- package/graphs/journey/units/pr/recipes.yaml +2 -0
- package/graphs/journey/units/remember/recipes.yaml +2 -0
- package/graphs/journey/units/remember/remember.yaml +20 -0
- package/graphs/operate-loop.yaml +71 -4
- package/graphs/overlays/stop-heal-ladder.yaml +1 -1
- package/graphs/release-rehearsal/recipes.yaml +1 -1
- package/graphs/release-rehearsal/release-rehearsal.yaml +2 -2
- package/graphs/research-loop.yaml +159 -4
- package/graphs/test-diet/test-diet.yaml +3 -0
- package/graphs/units/dispatch-pod/dispatch-pod.yaml +50 -0
- package/graphs/units/dispatch-pod/recipes.yaml +14 -0
- package/graphs/units/entry/entry.yaml +50 -0
- package/graphs/units/entry/recipes.yaml +14 -0
- package/graphs/units/harvest/harvest.yaml +25 -0
- package/graphs/units/harvest/recipes.yaml +4 -0
- package/graphs/units/land/land.yaml +45 -0
- package/graphs/units/land/recipes.yaml +3 -0
- package/graphs/units/recipes.yaml +7 -0
- package/graphs/units/recover.yaml +39 -0
- package/graphs/units/reflect/recipes.yaml +3 -0
- package/graphs/units/reflect/reflect.yaml +15 -0
- package/graphs/units/task-agent-recover-review.yaml +17 -0
- package/graphs/units/task-agent-recover-wait.yaml +17 -0
- package/graphs/units/verbs/make-doc.yaml +34 -0
- package/graphs/units/verbs/recipes.yaml +33 -0
- package/graphs/units/verbs/verify.yaml +55 -0
- package/graphs/work/recipes.yaml +2 -0
- package/graphs/work/research-report.yaml +28 -0
- package/package.json +1 -1
- package/scripts/ci-typecheck-changed.ts +69 -16
- package/scripts/coord-role-pack.ts +19 -6
- package/scripts/docs/generate-graph-anatomy.ts +19 -2
- package/scripts/graph-harvest-unit.ts +22 -0
- package/scripts/graph-units/journey-stub.ts +6 -0
- package/scripts/graph-units/land-enqueue.ts +78 -0
- package/scripts/graph-units/recover-plan.ts +15 -0
- package/scripts/graph-units/reflect-node.ts +88 -0
- package/scripts/graph-units/ta-journey-observe.ts +109 -0
- package/scripts/graph-units/verbs/headless-goal-loop.ts +152 -0
- package/scripts/graph-units/verbs/verify-stage.ts +75 -0
- package/scripts/graph-units/verbs-envelope.ts +139 -0
- package/scripts/hq/standby-snapshot.ts +30 -0
- package/scripts/public-export.ts +43 -1
- package/scripts/ratchets/train-side-invariants.sh +45 -0
- package/scripts/release-loop/auto-approve-node.ts +30 -16
- package/scripts/release-loop/docs-land-node.ts +21 -5
- package/scripts/release-loop/gate-baseline-receipt.ts +155 -0
- package/scripts/release-loop/gate-closure.ts +5 -1
- package/scripts/release-loop/gate-diff.ts +1 -1
- package/scripts/release-loop/gate-node.ts +675 -49
- package/scripts/release-loop/gate-partial.ts +68 -14
- package/scripts/release-loop/gate-radius-live.ts +177 -0
- package/scripts/release-loop/gate-radius-shadow.ts +138 -0
- package/scripts/release-loop/gate-traffic-mode.ts +128 -0
- package/scripts/release-loop/gate-triage-node.ts +5 -2
- package/scripts/release-loop/known-issues-0.2.33-approved.json +194 -0
- package/scripts/release-loop/known-issues-node.ts +77 -8
- package/scripts/release-loop/node-verdict.ts +12 -1
- package/scripts/release-loop/npm-publish-node.ts +66 -12
- package/scripts/release-loop/preflight.ts +93 -7
- package/scripts/release-loop/probe-pod-host-dirs-live.ts +5 -0
- package/scripts/release-loop/publish-node.ts +60 -9
- package/scripts/release-loop/pwa-remote.ts +166 -0
- package/scripts/release-loop/pwa-scenarios-node.ts +19 -3
- package/scripts/release-loop/resume-cap.ts +56 -0
- package/scripts/release-loop/resume-release.ts +155 -19
- package/scripts/release-loop/resume.ts +24 -1
- package/scripts/release-loop/tui-sim-node.ts +8 -2
- package/scripts/release-loop/unattended-release.ts +10 -2
- package/scripts/test-diet/lib.ts +6 -1
- package/scripts/test-diet/node.ts +194 -13
- package/scripts/ux-sim.ts +39 -0
- package/src/acp/core-turn-bridge.ts +20 -5
- package/src/acp/reasoning-thought-buffer.ts +45 -0
- package/src/agent/self-ambient-cache.ts +8 -0
- package/src/agent-substrate/acp-reviewer.ts +90 -15
- package/src/agent-substrate/pr-reviewer.ts +17 -0
- package/src/agent-substrate/review-finding-key.ts +53 -0
- package/src/agent-substrate/self-review-cli.ts +188 -18
- package/src/ask-user-question/pending-questions.ts +1 -1
- package/src/autopilot/pr-manager.ts +393 -65
- package/src/budget/run-usage-rollup.ts +1 -0
- package/src/chat/index.ts +0 -2
- package/src/chat/pickers/modal-runtime.ts +10 -27
- package/src/chat/tool-render/dispatch.ts +62 -10
- package/src/cli/agent-cli.ts +13 -2
- package/src/cli/control-cli.ts +24 -7
- package/src/cli/doctor-cli.ts +4 -1
- package/src/cli/guardian-cli.ts +10 -0
- package/src/cli/light-entry-route.ts +75 -0
- package/src/cli/llm-detect-cli.ts +23 -1
- package/src/cli/pod-cli.ts +77 -3
- package/src/cli/pr-cli.ts +81 -14
- package/src/cli/pr-land-base-read.ts +2 -0
- package/src/cli/pr-land-overlap-evidence.ts +154 -16
- package/src/cli/pr-land-pod-shadow-job.ts +3 -0
- package/src/cli/pr-land-recovery.ts +64 -0
- package/src/cli/pty-takeover-cli.ts +6 -5
- package/src/cli/release-checklist-read-cli.ts +79 -0
- package/src/cli/release-cli.ts +189 -132
- package/src/cli/release-run-if-ready.ts +29 -28
- package/src/cli/release-single-flight.ts +178 -0
- package/src/cli/remote-resolve.ts +1 -1
- package/src/cli/root-tui-entry.ts +48 -0
- package/src/cli/seat-cli.ts +37 -0
- package/src/cli/setup-cli.ts +40 -1
- package/src/cli/sim-cli.ts +22 -0
- package/src/cli/tasks-cli.ts +667 -14
- package/src/cli/usage-cli.ts +13 -11
- package/src/clipboard/history.ts +179 -13
- package/src/config.ts +8 -1
- package/src/control/control-status.ts +8 -3
- package/src/control-plane/ledger.ts +5 -3
- package/src/control-plane/server.ts +5 -3
- package/src/core-turn/short-question.ts +5 -1
- package/src/dashboard/chat/acp-chat.ts +8 -0
- package/src/dashboard/index.ts +114 -77
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +3 -0
- package/src/dashboard/input/dashboard-plain-turn-stream.ts +4 -0
- package/src/dashboard/input/seat-autowake.ts +56 -3
- package/src/dashboard/render/theme-resolver.ts +33 -4
- package/src/dashboard/seat-boot-preamble.ts +1 -1
- package/src/dashboard/slash-runtime/ask-clarification.ts +48 -0
- package/src/dashboard/slash-runtime/dashboard-delegated-slashes.ts +68 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +62 -322
- package/src/dashboard/slash-runtime/dashboard-log-slash.ts +208 -0
- package/src/dashboard/turn-preamble.ts +47 -1
- package/src/dashboard/turn-stream-formatter.ts +34 -1
- package/src/dashboard/turn-stream-runtime.ts +2 -0
- package/src/dashboard/turn-typeahead-prompt-zone.ts +67 -0
- package/src/domains/guardian-tick.ts +54 -2
- package/src/domains/outbound-alert.ts +3 -2
- package/src/domains/schedule-registry.ts +16 -2
- package/src/extractors.ts +3 -1
- package/src/git-fs/idle-refresh.ts +76 -0
- package/src/git-fs/index.ts +18 -7
- package/src/git-fs/retry.ts +4 -0
- package/src/git-fs/runner.ts +2 -0
- package/src/git-fs/worktree.ts +42 -4
- package/src/graph-kinds/registry.ts +2 -2
- package/src/graph-runner/runner.ts +267 -54
- package/src/grok/credential.ts +15 -3
- package/src/harness/harness-cli-command.ts +151 -38
- package/src/harness/harness-incidents.ts +37 -0
- package/src/harness/harness-pod-dispatch.ts +38 -1
- package/src/harness/harness-salvage-cli.ts +14 -2
- package/src/harness/launch-utterance-ledger.ts +208 -0
- package/src/impl-discipline/detector.ts +16 -4
- package/src/impl-discipline/system-prompt.ts +11 -0
- package/src/impl-discipline/types.ts +2 -2
- package/src/index.ts +683 -146
- package/src/intake-plane/already-built-hint.ts +91 -0
- package/src/intake-plane/requirement-funnel.ts +29 -1
- package/src/knowhow/pack.ts +199 -0
- package/src/light-cli.ts +111 -0
- package/src/llm/opengateway-models.ts +11 -0
- package/src/llm/provider-credentials.ts +14 -0
- package/src/llm/provider-detect.ts +8 -1
- package/src/llm/runtime-model-family.ts +2 -1
- package/src/llm.ts +268 -65
- package/src/loops/budget.ts +95 -0
- package/src/loops/msg-wake.ts +8 -2
- package/src/loops/orchestrator/traffic.ts +132 -1
- package/src/model-tier/llm-tier-map.ts +15 -1
- package/src/mss/logging/log-store.ts +4 -5
- package/src/native-tool-catalog.ts +15 -1
- package/src/nexus/api/graphs-api.ts +22 -3
- package/src/nexus/api/http-server.ts +1 -1
- package/src/nexus/api/loop-resources.ts +13 -1
- package/src/nexus/api/ops-api.ts +12 -0
- package/src/oauth/claude-pty-maturity.ts +66 -0
- package/src/oauth/codex-access-mirror.ts +55 -0
- package/src/oauth/codex.ts +7 -0
- package/src/oauth/fallback-chain.ts +1 -1
- package/src/onboarding/first-run.ts +5 -3
- package/src/onboarding/non-interactive.ts +64 -13
- package/src/onboarding.ts +10 -2
- package/src/ops/overview-collect.ts +54 -4
- package/src/ops/overview.ts +31 -7
- package/src/pr-tool/census.ts +282 -30
- package/src/pr-tool/inflight-registry.ts +160 -7
- package/src/prompt-library/gpt6-family-addendum.ts +14 -1
- package/src/pty-shell/herdr-agent-report.ts +442 -0
- package/src/pty-shell/pty-control-ipc.ts +5 -5
- package/src/pty-shell/pty-manifest.ts +7 -0
- package/src/pty-shell/pty-write-outcome.ts +24 -6
- package/src/pty-shell/tui-run-state.ts +117 -3
- package/src/release-loop/checklist.ts +103 -5
- package/src/release-loop/feature-store.ts +19 -2
- package/src/release-loop/landed-but-yellow.ts +129 -5
- package/src/release-loop/light-rc.ts +1 -0
- package/src/release-loop/placement.ts +10 -3
- package/src/release-loop/rehearsal.ts +205 -25
- package/src/release-loop/rubric.ts +16 -2
- package/src/repl/index.ts +174 -17
- package/src/repl/ops-msg-bridge.ts +32 -0
- package/src/self-dev/ask-launch-flow.ts +44 -5
- package/src/self-dev/cli-help-text.ts +17 -0
- package/src/self-dev/dev-cli.ts +11 -9
- package/src/self-dev/draft-residue.ts +37 -1
- package/src/self-dev/draft-sweep.ts +188 -13
- package/src/self-dev/draft-triage-rules.ts +107 -0
- package/src/self-dev/fleet-reduce.ts +3 -2
- package/src/self-dev/graph-front-nodes.ts +112 -0
- package/src/self-dev/graph-journey-nodes.ts +11 -6
- package/src/self-dev/graph-journey-route.ts +60 -6
- package/src/self-dev/harness-run-cli.ts +3 -15
- package/src/self-dev/orchestrate-cli-text.ts +10 -0
- package/src/self-dev/orchestrate.ts +79 -3
- package/src/self-dev/review-provider-fallback.ts +35 -1
- package/src/self-dev/run-supervisor.ts +107 -12
- package/src/self-dev/run-types.ts +5 -1
- package/src/self-dev/self-orchestrate-runtime.ts +1 -11
- package/src/self-dev/shard-aggregate.ts +42 -5
- package/src/self-dev/shard-run.ts +8 -1
- package/src/self-implement/blocked-draft-policy.ts +20 -0
- package/src/self-implement/classify-shadow.ts +3 -7
- package/src/self-implement/doctor-visit.ts +18 -1
- package/src/self-implement/gate-baseline.ts +95 -12
- package/src/self-implement/gate-cli.ts +1 -0
- package/src/self-implement/gate-remote.ts +40 -25
- package/src/self-implement/goal-author-decompose.ts +275 -47
- package/src/self-implement/goal-author-identity.ts +66 -0
- package/src/self-implement/goal-author-preservation-obligation.ts +35 -0
- package/src/self-implement/goal-author.ts +254 -120
- package/src/self-implement/goal-elaborate.ts +295 -0
- package/src/self-implement/goal-refine.ts +270 -0
- package/src/self-implement/graph-overlay-yaml.ts +28 -2
- package/src/self-implement/graph-unit-flatten.ts +103 -0
- package/src/self-implement/graph-unit-resolver.ts +107 -0
- package/src/self-implement/graph-yaml.ts +151 -1
- package/src/self-implement/headless-elanous-driver.ts +1 -0
- package/src/self-implement/knowhow-pack.ts +103 -0
- package/src/self-implement/orchestrator.ts +160 -45
- package/src/self-implement/pod-warm-pool-profile.ts +45 -0
- package/src/self-implement/review-mustfix-class.ts +203 -0
- package/src/self-implement/rework-budget-judgment.ts +5 -8
- package/src/self-implement/run-stop.ts +17 -2
- package/src/self-implement/running-runs.ts +29 -1
- package/src/self-implement/scope-adjacency.ts +113 -0
- package/src/self-implement/seams.ts +138 -4
- package/src/self-implement/stop-autoheal.ts +112 -2
- package/src/session/chat.ts +27 -0
- package/src/session-runtime/index.ts +1 -1
- package/src/session-runtime/retry-policy.ts +2 -1
- package/src/setup/llm-env-detect.ts +5 -0
- package/src/skills/tools/agent.ts +140 -14
- package/src/skills/tools/msg-ask.ts +100 -0
- package/src/skills/tools/ops-ledger.ts +2 -1
- package/src/skills/tools/schedule-wakeup.ts +66 -17
- package/src/steward/hold-hygiene.ts +24 -6
- package/src/steward/release-day.ts +7 -3
- package/src/steward/triage-budget.ts +194 -0
- package/src/steward/triage.ts +57 -7
- package/src/task-agent/actions.ts +69 -1
- package/src/task-agent/green-sweep.ts +4 -0
- package/src/task-agent/harvest-unit.ts +141 -0
- package/src/task-agent/live-moves.ts +257 -15
- package/src/task-agent/parent-host.ts +216 -6
- package/src/task-agent/postland-card.ts +80 -1
- package/src/task-agent/rejudge-on-head.ts +112 -26
- package/src/task-agent/repair-branch-cas.ts +4 -1
- package/src/task-agent/repair-cycle.ts +16 -3
- package/src/task-agent/run-identity.ts +4 -3
- package/src/task-agent/shadow.ts +19 -3
- package/src/task-agent/task-hand.ts +96 -36
- package/src/task-agent/tier-split.ts +127 -0
- package/src/task-agent/work-order-build.ts +24 -0
- package/src/task-agent/work-order.ts +3 -2
- package/src/task-orchestrator/generator-prompt.ts +60 -11
- package/src/task-orchestrator/surfaces/pod-account-broker.ts +72 -3
- package/src/task-orchestrator/surfaces/pod-child-providers.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +47 -8
- package/src/task-orchestrator/surfaces/pod-image-source.ts +89 -1
- package/src/task-orchestrator/surfaces/pod-lease.ts +17 -8
- package/src/task-orchestrator/surfaces/pod-lifecycle-stages.ts +86 -8
- package/src/task-orchestrator/surfaces/pod-manifest-test-normalize.ts +8 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +73 -17
- package/src/task-orchestrator/surfaces/pod-provenance.ts +76 -0
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +72 -4
- package/src/task-orchestrator/surfaces/pod-warm-provenance.ts +81 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-pod.ts +32 -5
- package/src/task-orchestrator/surfaces/pod-warm-worker-protocol.ts +3 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +421 -0
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +2 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +588 -71
- package/src/task-orchestrator/surfaces/self-implement.ts +106 -2
- package/src/tool-runtime/ops-ledger-runtimes.ts +10 -2
- package/src/tool-runtime/truncation-store.ts +4 -0
- package/src/tui/README.md +2 -0
- package/src/tui/chat/background-job-status.ts +54 -0
- package/src/tui/chat/log-view.ts +80 -0
- package/src/tui/entry.ts +406 -0
- package/src/tui/input/composer.ts +144 -0
- package/src/tui/seat-mail-boot.ts +59 -0
- package/src/tui/slash/compact.ts +100 -0
- package/src/tui/slash/new-session.ts +35 -0
- package/src/tui-client/dashboard-session.ts +10 -1
- package/src/tui-client/local-chat-turn.ts +239 -0
- package/src/tui-client/local-harness-command.ts +63 -0
- package/src/tui-client/local-provider-command.ts +184 -0
- package/src/user-config/coercion.ts +36 -0
- package/src/user-config.ts +22 -43
- package/src/ux-sim/event-monkey.ts +378 -0
- package/src/ux-sim/fake-bin.ts +42 -7
- package/src/ux-sim/fault-nextmove-dispatch.ts +266 -0
- package/src/ux-sim/fault-pod-codex-503.ts +211 -0
- package/src/ux-sim/fault-scope-escape-lift.ts +189 -0
- package/src/ux-sim/scenario.ts +72 -13
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/1034-8b639cee57b8e4a8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1085-6c2d35526f11f840.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1220.1ff7d5cdc34b1ae5.js +0 -27
- package/apps/pwa/out/_next/static/chunks/1247.5884ec81b39fe2e7.js +0 -287
- package/apps/pwa/out/_next/static/chunks/1302.87681521a7a4661e.js +0 -62
- package/apps/pwa/out/_next/static/chunks/1324-59dae3002413623b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1343-f40b5870ac1eda5a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/146-b6cd56be11dda017.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1654.32de4b324981a188.js +0 -23
- package/apps/pwa/out/_next/static/chunks/1834-98b64172bc1efe77.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1849.1ce38efa6c527d9a.js +0 -149
- package/apps/pwa/out/_next/static/chunks/1913.7cc6fb6055b1ed3f.js +0 -41
- package/apps/pwa/out/_next/static/chunks/2030.41fae98c7e907304.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2099-08b2f2dfba742c9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2112-e4efc21a6505d5bc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2134.ecee03a550ee7250.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2236.66061832181cb745.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2422.b0bd272e7020d7e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2534.e85ae21ad0dac829.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2898.fd781bc7cf01a7b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3040.92ff9ffc9f971cb9.js +0 -115
- package/apps/pwa/out/_next/static/chunks/3144.6861096c7b4edef7.js +0 -64
- package/apps/pwa/out/_next/static/chunks/3541-ce3a5424ed5ad7f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3583.a05e88cc8d594628.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3771.e928edf2867d28c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4063-03284b5de59965f3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4300-c7ec4eeabbb5f3f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4483.646d5065199e1b55.js +0 -36
- package/apps/pwa/out/_next/static/chunks/4519.28199ff26b2fd80d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4581.ef2443578054544f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4608-8d894e9cbe4f08bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4639.ae48a01d1bb50a93.js +0 -286
- package/apps/pwa/out/_next/static/chunks/4739.08742ac668aada67.js +0 -77
- package/apps/pwa/out/_next/static/chunks/4745.67d6f99c245af3f7.js +0 -226
- package/apps/pwa/out/_next/static/chunks/4882-16cef71d6a1a32be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4999.c064a4000c2a9e94.js +0 -79
- package/apps/pwa/out/_next/static/chunks/5260-69f35419bca263e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5272.b92d1b72a15c25fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5304.04d24756f242a094.js +0 -148
- package/apps/pwa/out/_next/static/chunks/5415-997980f828667736.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5458-16cd7a2b51b74c8c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5543.a438b8ab5a13ea47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5572-42c9084f27ca761e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5661-80470aac963d9f36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5723.421338408b5c30c4.js +0 -74
- package/apps/pwa/out/_next/static/chunks/5809.64c3664267c50bce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6000.64bc21ac8627f2f5.js +0 -94
- package/apps/pwa/out/_next/static/chunks/6013.39459b9c68eabf5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/607-06466dff012b03ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6192.a190b989b81e9743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6323.8672b4817a306928.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6444.7d1fdcb9b09ae71c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6517.4147878ac06d584d.js +0 -90
- package/apps/pwa/out/_next/static/chunks/6532.e34302010b2f0b68.js +0 -32
- package/apps/pwa/out/_next/static/chunks/6546.74bc4ed82f19016d.js +0 -104
- package/apps/pwa/out/_next/static/chunks/6684-e7523ce7c2fad075.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6726.ebdc82feb161431e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6739.00fb6f892e5e392b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6745.934c4921bf15513a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6767.ea607cf0c20a25af.js +0 -24
- package/apps/pwa/out/_next/static/chunks/6843.67bfd020da0926c5.js +0 -93
- package/apps/pwa/out/_next/static/chunks/6948.1062d4594652b5bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7138-eaf841fc1dc03463.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7174-cda29ee13d7790e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7304-3c07583f4e405f4f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7557.e325503a090a4310.js +0 -8
- package/apps/pwa/out/_next/static/chunks/7569-9ce9432c7f10989f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7886-55854585fadeded1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8097-9530c03bcc550f02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8215-1661812b6e9ad71f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8260-19c921a51ca8ec3e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8440.3a5cf5c85aa76db8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-4599be2249161e68.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8547.b028b90c5c01ace6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8587-cbe02a953a7f4990.js +0 -1
- package/apps/pwa/out/_next/static/chunks/877-2b977259c4b15655.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9002.e5658224e3dc1b6f.js +0 -24
- package/apps/pwa/out/_next/static/chunks/9019-0a4b40c50c7314fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9138.b6e80031afb1d973.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9188-d36e4fc869ddc166.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9230.44f5039e796e4ab4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9279-d28c5b7754d010ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/939.336acce73e98482c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9407.e49124dedf0f5e72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9610.c07cce4bfcc14ed7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9667-ce78c2ce91050ff9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989.3694300e5f000b14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-339fabb3499da0e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-504204b58c82c253.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-a9717a67c2eecbaf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-5c9ec30aee62dfbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-584191533721fa16.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-73c4b4bb3ac7ab5f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-c0f1ca371f710b06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-46430d45a9e04eac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b2ebf1dc64ff0be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-25654b82094f7d22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-8ea49a4c6dd2c6e7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-ce5113aefea0a8d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-0e5a41fac1c75396.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-31aaf1ed37c6df9a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-339fabb3499da0e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-3dcc1fa411e1f4e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c69247e7134d329d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-66dc5cd3a0999abe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-26ff7ff651a7217d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-617b3976981efa96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-d256f5d5d9552823.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-ce8f1907b8a6101c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-33e182a316263116.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-7607cca9b84950c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-eeab86176b03dc3b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5b93dbae3432b996.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-77a6cb90a9cf2b10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f47e768b5b242b5a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-eb41aa3367fc2505.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-4d13ab0b8a267e3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-84dad32ff0fc0831.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-95306f892aa2d0ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-940fb6c217f6b470.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-8c66a3b24a745630.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7b675dfd35c36fc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8bd0d3cf7665bcfc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-339fabb3499da0e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c219f2ed171131be.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-40f8fa21bdae955b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-468f8313e4f1fbd9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-7bbb8fa353a246c4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-4214b855d9233ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-936752c7aa58762e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-ec052ad359df1797.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-8305570551091338.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-fad650e4793adb65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-d90e64743826e1d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a16bcfd49af86a32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/e5012ffe.f727e23c231f4342.js +0 -122
- package/apps/pwa/out/_next/static/chunks/ea695e3c-1f1d70d740cf3031.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ebc7d829.264a0bcb6c750b50.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-906af9bd029e166c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-a24847f9f684d469.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-7bdcd8aa7c8397d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-6ba46a5066fc226d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-a8dc6d07591f2a25.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-f13df28c15c55eee.js +0 -1
- package/src/cli/g6a-registration-probe.ts +0 -16
- /package/apps/pwa/out/_next/static/{IVKr3AlRUVLKsw2fh7eXF → T1mBQqmkwKDLGtyisDCOd}/_ssgManifest.js +0 -0
package/src/pr-tool/census.ts
CHANGED
|
@@ -5,12 +5,17 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
6
6
|
import { withFileLockSync } from '../storage/file-lock.js';
|
|
7
7
|
import { resolveHostId } from '../platform/host-id.js';
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
8
9
|
|
|
9
10
|
export interface PrToolCommandResult { ok: boolean; out: string; err?: string }
|
|
10
11
|
export type PrToolCommand = (command: string, args: readonly string[], opts?: { cwd?: string }) => PrToolCommandResult;
|
|
12
|
+
export interface PrToolCall { command: string; args: readonly string[]; opts?: { cwd?: string } }
|
|
13
|
+
/** PR-CENSUS-PARALLEL: run every call with at most `concurrency` in flight; results stay in input order. */
|
|
14
|
+
export type PrToolParallelCommand = (calls: readonly PrToolCall[], concurrency: number) => PrToolCommandResult[];
|
|
11
15
|
export type Seat = 'OP' | 'TC' | 'MK' | 'UX';
|
|
12
16
|
export interface OpenPr {
|
|
13
17
|
number: number;
|
|
18
|
+
headRefName?: string;
|
|
14
19
|
title: string;
|
|
15
20
|
draft: boolean;
|
|
16
21
|
updatedAt: string;
|
|
@@ -20,6 +25,10 @@ export interface OpenPr {
|
|
|
20
25
|
seat: Seat | null;
|
|
21
26
|
lastCommitAt?: string;
|
|
22
27
|
cellId: string | null;
|
|
28
|
+
cardId?: string | null;
|
|
29
|
+
cardMarkerPresent?: boolean;
|
|
30
|
+
/** «조각 i/N — 부모 <칸>» 표지가 있으면 그 조각 번호(없으면 null) — sibling-slice 판정용. */
|
|
31
|
+
slice?: { slice: number; total: number } | null;
|
|
23
32
|
hunks?: readonly DiffHunk[];
|
|
24
33
|
}
|
|
25
34
|
export type Inventory = { status: 'checked'; prs: readonly OpenPr[] } | { status: 'unknown'; reason: string };
|
|
@@ -31,7 +40,9 @@ export type GuardCode = 'inventory-unknown' | 'candidate-unknown' | 'goal-only'
|
|
|
31
40
|
export interface GuardReason { code: GuardCode; detail: string; prs?: readonly number[]; files?: readonly string[] }
|
|
32
41
|
/** PR-GUARD-HUNK-WARN (대표 10-17): 줄 겹침은 기본 경고 — 착지 쪽 스마트 머지가 푼다. 'refuse' = 옛 거부 동작. */
|
|
33
42
|
export type HunkOverlapMode = 'refuse' | 'warn';
|
|
34
|
-
export interface GuardVerdict { status: 'allow' | 'refuse' | 'unknown'; reasons: readonly GuardReason[]; warnings: readonly GuardReason[]
|
|
43
|
+
export interface GuardVerdict { status: 'allow' | 'refuse' | 'unknown'; reasons: readonly GuardReason[]; warnings: readonly GuardReason[];
|
|
44
|
+
sameCellConflicts?: readonly { pr: number; cardId: string | null; slice?: { slice: number; total: number } | null }[];
|
|
45
|
+
sameCellReuse?: { number: number; branch: string; headSha: string; cellId: string; cardId: string; legacyBound?: true } }
|
|
35
46
|
|
|
36
47
|
const SEATS = ['OP', 'TC', 'MK', 'UX'] as const;
|
|
37
48
|
const DEFAULT_SEAT_CAP = 30;
|
|
@@ -41,6 +52,126 @@ const MAX_FILES = 3000; // GitHub does not expose more than 3000 PR files.
|
|
|
41
52
|
const UNMEASURED_WINDOW_MS = 60 * 60 * 1000;
|
|
42
53
|
const UNMEASURED_ALLOW_COUNT = 3;
|
|
43
54
|
|
|
55
|
+
export const DEFAULT_DIFF_CONCURRENCY = 6;
|
|
56
|
+
const DIFF_CACHE_MAX = 2000;
|
|
57
|
+
const DIFF_CACHE_MAX_BYTES = 64 * 1024 * 1024;
|
|
58
|
+
const DIFF_CACHE_ENTRY_MAX_BYTES = 4 * 1024 * 1024;
|
|
59
|
+
const PARALLEL_CALL_TIMEOUT_MS = 120_000;
|
|
60
|
+
/** Whole pool ceiling, independent of the call count — a stuck pool fails (inventory unknown) instead of hanging the guard. */
|
|
61
|
+
const PARALLEL_POOL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
62
|
+
const PARALLEL_MAX_BUFFER = 256 * 1024 * 1024;
|
|
63
|
+
|
|
64
|
+
/** `gh pr diff` patches keyed by PR number ⊕ head sha. A head sha fixes the diff, so a retry or a second guard
|
|
65
|
+
* call in the same process never refetches it. Bounded by entry count and total bytes (the oldest entry leaves
|
|
66
|
+
* first); a single patch above the per-entry limit is not cached at all. */
|
|
67
|
+
export class CensusDiffCache {
|
|
68
|
+
private readonly entries = new Map<string, { patch: string; bytes: number }>();
|
|
69
|
+
private totalBytes = 0;
|
|
70
|
+
constructor(private readonly max = DIFF_CACHE_MAX, private readonly maxBytes = DIFF_CACHE_MAX_BYTES,
|
|
71
|
+
private readonly entryMaxBytes = DIFF_CACHE_ENTRY_MAX_BYTES) {}
|
|
72
|
+
private key(number: number, headSha: string): string { return `${number}@${headSha}`; }
|
|
73
|
+
private drop(key: string): void {
|
|
74
|
+
const entry = this.entries.get(key);
|
|
75
|
+
if (!entry) return;
|
|
76
|
+
this.totalBytes -= entry.bytes;
|
|
77
|
+
this.entries.delete(key);
|
|
78
|
+
}
|
|
79
|
+
get(number: number, headSha: string): string | undefined { return this.entries.get(this.key(number, headSha))?.patch; }
|
|
80
|
+
set(number: number, headSha: string, patch: string): void {
|
|
81
|
+
const key = this.key(number, headSha);
|
|
82
|
+
this.drop(key);
|
|
83
|
+
const bytes = Buffer.byteLength(patch, 'utf8');
|
|
84
|
+
if (bytes > this.entryMaxBytes || bytes > this.maxBytes) return;
|
|
85
|
+
while (this.entries.size > 0 && (this.entries.size >= this.max || this.totalBytes + bytes > this.maxBytes)) {
|
|
86
|
+
const oldest = this.entries.keys().next().value;
|
|
87
|
+
if (oldest === undefined) break;
|
|
88
|
+
this.drop(oldest);
|
|
89
|
+
}
|
|
90
|
+
this.entries.set(key, { patch, bytes });
|
|
91
|
+
this.totalBytes += bytes;
|
|
92
|
+
}
|
|
93
|
+
get size(): number { return this.entries.size; }
|
|
94
|
+
get bytes(): number { return this.totalBytes; }
|
|
95
|
+
clear(): void { this.entries.clear(); this.totalBytes = 0; }
|
|
96
|
+
}
|
|
97
|
+
/** Process-lifetime cache for production callers (PR creation guard · `pr open-guard`). Tests pass their own. */
|
|
98
|
+
export const sharedCensusDiffCache = new CensusDiffCache();
|
|
99
|
+
export function resetCensusDiffCache(): void { sharedCensusDiffCache.clear(); }
|
|
100
|
+
|
|
101
|
+
// Runs inside a child `bun`/`node` so the synchronous inventory API can still fan out. stdin: { calls, concurrency,
|
|
102
|
+
// timeoutMs } · stdout: one result per call, input order. Each call runs in its own process group (detached) so a
|
|
103
|
+
// per-call timeout, or this pool ending on SIGTERM/SIGINT/SIGHUP from the outer ceiling, kills the whole group —
|
|
104
|
+
// no `gh` (or its children) outlives the pool.
|
|
105
|
+
const PARALLEL_SCRIPT = `
|
|
106
|
+
const { spawn } = require('node:child_process');
|
|
107
|
+
const live = new Set();
|
|
108
|
+
const killGroup = pid => { try { process.kill(-pid, 'SIGKILL'); } catch { try { process.kill(pid, 'SIGKILL'); } catch {} } };
|
|
109
|
+
const killAll = () => { for (const pid of live) killGroup(pid); live.clear(); };
|
|
110
|
+
for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP']) process.on(signal, () => { killAll(); process.exit(124); });
|
|
111
|
+
process.on('exit', killAll);
|
|
112
|
+
let input = '';
|
|
113
|
+
process.stdin.setEncoding('utf8');
|
|
114
|
+
process.stdin.on('data', chunk => { input += chunk; });
|
|
115
|
+
process.stdin.on('end', () => {
|
|
116
|
+
const { calls, concurrency, timeoutMs } = JSON.parse(input);
|
|
117
|
+
const results = new Array(calls.length);
|
|
118
|
+
let next = 0;
|
|
119
|
+
const one = index => new Promise(resolve => {
|
|
120
|
+
const call = calls[index];
|
|
121
|
+
const out = []; const err = [];
|
|
122
|
+
let settled = false; let timedOut = false; let timer;
|
|
123
|
+
let child;
|
|
124
|
+
const finish = result => {
|
|
125
|
+
if (settled) return; settled = true; clearTimeout(timer);
|
|
126
|
+
if (child && child.pid) { killGroup(child.pid); live.delete(child.pid); }
|
|
127
|
+
results[index] = result; resolve();
|
|
128
|
+
};
|
|
129
|
+
try { child = spawn(call.command, call.args, { cwd: call.cwd || undefined, stdio: ['ignore', 'pipe', 'pipe'], detached: true }); }
|
|
130
|
+
catch (error) { results[index] = { ok: false, out: '', err: String(error && error.message || error) }; resolve(); return; }
|
|
131
|
+
if (child.pid) live.add(child.pid);
|
|
132
|
+
timer = setTimeout(() => { timedOut = true; if (child.pid) killGroup(child.pid); }, timeoutMs);
|
|
133
|
+
child.stdout.on('data', chunk => out.push(chunk));
|
|
134
|
+
child.stderr.on('data', chunk => err.push(chunk));
|
|
135
|
+
child.on('error', error => finish({ ok: false, out: '', err: String(error && error.message || error) }));
|
|
136
|
+
child.on('close', (code, signal) => finish({ ok: code === 0 && !signal && !timedOut, out: Buffer.concat(out).toString('utf8').trim(),
|
|
137
|
+
err: [Buffer.concat(err).toString('utf8').trim(), timedOut ? 'timed out after ' + timeoutMs + 'ms' : signal ? 'terminated by ' + signal : ''].filter(Boolean).join('\\n') }));
|
|
138
|
+
});
|
|
139
|
+
const worker = async () => { while (next < calls.length) { const index = next++; await one(index); } };
|
|
140
|
+
Promise.all(Array.from({ length: Math.max(1, Math.min(concurrency, calls.length)) }, worker))
|
|
141
|
+
.then(() => process.stdout.write(JSON.stringify(results)));
|
|
142
|
+
});
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
/** Real-spawn {@link PrToolParallelCommand}: one child runtime fans out the calls with a bounded pool. Any failure of
|
|
146
|
+
* the pool itself (including the fixed pool ceiling) returns every call as failed, so the caller's conservative
|
|
147
|
+
* unknown rule applies. `timeoutMs` = per call · `poolTimeoutMs` = whole pool (tests inject short values). */
|
|
148
|
+
export function spawnParallelCommands(calls: readonly PrToolCall[], options: { concurrency?: number; env?: NodeJS.ProcessEnv; timeoutMs?: number; poolTimeoutMs?: number } = {}): PrToolCommandResult[] {
|
|
149
|
+
if (calls.length === 0) return [];
|
|
150
|
+
const concurrency = Math.max(1, Math.floor(options.concurrency ?? DEFAULT_DIFF_CONCURRENCY));
|
|
151
|
+
const timeoutMs = options.timeoutMs ?? PARALLEL_CALL_TIMEOUT_MS;
|
|
152
|
+
const failAll = (err: string) => calls.map(() => ({ ok: false, out: '', err }));
|
|
153
|
+
try {
|
|
154
|
+
const r = spawnSync(process.execPath, ['-e', PARALLEL_SCRIPT], {
|
|
155
|
+
input: JSON.stringify({ calls: calls.map(call => ({ command: call.command, args: [...call.args], cwd: call.opts?.cwd })), concurrency, timeoutMs }),
|
|
156
|
+
encoding: 'utf-8', maxBuffer: PARALLEL_MAX_BUFFER, timeout: options.poolTimeoutMs ?? PARALLEL_POOL_TIMEOUT_MS, killSignal: 'SIGTERM',
|
|
157
|
+
...(options.env ? { env: options.env } : {}),
|
|
158
|
+
});
|
|
159
|
+
if ((r.error as NodeJS.ErrnoException | undefined)?.code === 'ENOBUFS') return failAll('parallel output exceeded 256MiB (ENOBUFS)');
|
|
160
|
+
if ((r.error as NodeJS.ErrnoException | undefined)?.code === 'ETIMEDOUT' || r.signal) return failAll(`parallel runner timed out or was killed (${r.signal ?? 'ETIMEDOUT'})`);
|
|
161
|
+
if (r.status !== 0) return failAll(`parallel runner rc=${r.status}: ${(r.stderr ?? '').trim().slice(0, 300)}`);
|
|
162
|
+
const parsed: unknown = JSON.parse(r.stdout ?? '');
|
|
163
|
+
if (!Array.isArray(parsed) || parsed.length !== calls.length) return failAll('parallel runner result shape mismatch');
|
|
164
|
+
return parsed.map(row => {
|
|
165
|
+
const value = row as { ok?: unknown; out?: unknown; err?: unknown } | null;
|
|
166
|
+
return value && typeof value.ok === 'boolean' && typeof value.out === 'string'
|
|
167
|
+
? { ok: value.ok, out: value.out, err: typeof value.err === 'string' ? value.err : '' }
|
|
168
|
+
: { ok: false, out: '', err: 'parallel runner row missing' };
|
|
169
|
+
});
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return failAll(`parallel runner: ${error instanceof Error ? error.message : String(error)}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
44
175
|
function unknown(reason: string): Inventory { return { status: 'unknown', reason }; }
|
|
45
176
|
function seatValue(value: unknown): Seat | null { return SEATS.find(seat => seat === value) ?? null; }
|
|
46
177
|
function seatFromLabels(labels: readonly string[]): Seat | null {
|
|
@@ -49,6 +180,22 @@ function seatFromLabels(labels: readonly string[]): Seat | null {
|
|
|
49
180
|
export function cellIdFromBody(body: string): string | null {
|
|
50
181
|
return /^\s*(?:[-*]\s*)?칸\s*:\s*([A-Z][A-Z0-9-]{2,})\s*$/m.exec(body)?.[1] ?? null;
|
|
51
182
|
}
|
|
183
|
+
export function cardIdFromBody(body: string): string | null {
|
|
184
|
+
const ids = [...body.matchAll(/^\s*(?:[-*]\s*)?카드\s*:\s*([^\r\n]*)$/gm)].map(match => match[1]!.trim());
|
|
185
|
+
return ids.length > 0 && ids.every(id => /^[A-Za-z0-9][A-Za-z0-9._:-]{2,}$/.test(id) && id === ids[0]) ? ids[0]! : null;
|
|
186
|
+
}
|
|
187
|
+
export function hasCardMarker(body: string): boolean {
|
|
188
|
+
return /^\s*(?:[-*]\s*)?카드\s*:/m.test(body);
|
|
189
|
+
}
|
|
190
|
+
/** 골/PR 본문의 «조각 i/N — 부모 <칸>» 표지 → {slice,total}. 표지 없으면 null, 1≤i≤N 아니면 null. */
|
|
191
|
+
export function sliceMarkerFromBody(body: string): { slice: number; total: number } | null {
|
|
192
|
+
const m = /^\s*(?:[-*]\s*)?(?:«\s*)?조각\s*(\d+)\s*\/\s*(\d+)\s*(?:»)?\s*—\s*부모\s+\S[^\r\n]*$/m.exec(body);
|
|
193
|
+
if (!m) return null;
|
|
194
|
+
const slice = Number(m[1]);
|
|
195
|
+
const total = Number(m[2]);
|
|
196
|
+
return Number.isSafeInteger(slice) && Number.isSafeInteger(total) && slice >= 1 && total >= 1 && slice <= total
|
|
197
|
+
? { slice, total } : null;
|
|
198
|
+
}
|
|
52
199
|
|
|
53
200
|
export function parseDiffHunks(patch: string): DiffHunk[] {
|
|
54
201
|
const hunks: DiffHunk[] = [];
|
|
@@ -81,8 +228,8 @@ export function parseDiffHunks(patch: string): DiffHunk[] {
|
|
|
81
228
|
}
|
|
82
229
|
|
|
83
230
|
/** `gh api --paginate --slurp` must return every REST page, including the last page. */
|
|
84
|
-
function
|
|
85
|
-
|
|
231
|
+
function pagesArgs(endpoint: string): string[] { return ['api', '--paginate', '--slurp', endpoint]; }
|
|
232
|
+
function parsePages(result: PrToolCommandResult, endpoint: string): readonly unknown[] {
|
|
86
233
|
if (!result.ok) throw new Error(`gh api ${endpoint}: ${result.err || result.out || '조회 실패'}`);
|
|
87
234
|
let parsed: unknown;
|
|
88
235
|
try { parsed = JSON.parse(result.out); } catch { throw new Error(`gh api ${endpoint}: JSON 오류`); }
|
|
@@ -91,6 +238,9 @@ function pages(run: PrToolCommand, endpoint: string, cwd?: string): readonly unk
|
|
|
91
238
|
}
|
|
92
239
|
return parsed.flat();
|
|
93
240
|
}
|
|
241
|
+
function pages(run: PrToolCommand, endpoint: string, cwd?: string): readonly unknown[] {
|
|
242
|
+
return parsePages(run('gh', pagesArgs(endpoint), cwd ? { cwd } : undefined), endpoint);
|
|
243
|
+
}
|
|
94
244
|
|
|
95
245
|
function object(value: unknown): Record<string, unknown> {
|
|
96
246
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) throw new Error('PR 응답 행 형식 오류');
|
|
@@ -105,29 +255,59 @@ function date(value: unknown, name: string): string {
|
|
|
105
255
|
if (!Number.isFinite(Date.parse(raw))) throw new Error(`${name} 날짜 오류`);
|
|
106
256
|
return raw;
|
|
107
257
|
}
|
|
108
|
-
function
|
|
109
|
-
|
|
258
|
+
function filesEndpoint(number: number): string { return `repos/{owner}/{repo}/pulls/${number}/files?per_page=100`; }
|
|
259
|
+
function pathsFromFilesResult(result: PrToolCommandResult, number: number): string[] {
|
|
260
|
+
const rows = parsePages(result, filesEndpoint(number));
|
|
110
261
|
if (rows.length >= MAX_FILES) throw new Error(`PR #${number}: 파일 ${MAX_FILES}개 GitHub 상한에 닿음`);
|
|
111
262
|
return rows.map(row => text(object(row).filename, `PR #${number} 파일명`));
|
|
112
263
|
}
|
|
113
264
|
|
|
114
|
-
|
|
115
|
-
|
|
265
|
+
interface FileProjection {
|
|
266
|
+
files: Map<number, string[]>;
|
|
267
|
+
/** Set when the one-shot list produced nothing usable — every PR then takes the REST file fallback. */
|
|
268
|
+
failure?: string;
|
|
269
|
+
/** Rows the projection could not vouch for (malformed · file list shorter than changedFiles, e.g. >100 files). */
|
|
270
|
+
skipped: number;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Fast file projection; the REST page census below remains the authority for which PRs exist. A failed or partial
|
|
274
|
+
* projection is never silent: the reason and skipped row count go to the `diff-scan` observation. */
|
|
275
|
+
function projectedFiles(run: PrToolCommand, count: number, cwd?: string): FileProjection {
|
|
116
276
|
const result = run('gh', ['pr', 'list', '--state', 'open', '--limit', String(Math.max(count, 1)), '--json', 'number,changedFiles,files'], cwd ? { cwd } : undefined);
|
|
117
|
-
if (!result.ok) return new Map();
|
|
277
|
+
if (!result.ok) return { files: new Map(), skipped: 0, failure: `gh pr list 실패: ${(result.err || result.out || 'rc≠0').trim().slice(0, 200)}` };
|
|
118
278
|
let rows: unknown;
|
|
119
|
-
try { rows = JSON.parse(result.out); } catch { return new Map(); }
|
|
120
|
-
if (!Array.isArray(rows)) return new Map();
|
|
279
|
+
try { rows = JSON.parse(result.out); } catch { return { files: new Map(), skipped: 0, failure: `gh pr list JSON 오류 (${result.out.length}자)` }; }
|
|
280
|
+
if (!Array.isArray(rows)) return { files: new Map(), skipped: 0, failure: 'gh pr list 응답이 배열 아님' };
|
|
121
281
|
const projection = new Map<number, string[]>();
|
|
282
|
+
let skipped = 0;
|
|
122
283
|
for (const value of rows) {
|
|
123
284
|
try {
|
|
124
285
|
const row = object(value);
|
|
125
|
-
if (!Number.isSafeInteger(row.number) || (row.number as number) <= 0 || !Number.isSafeInteger(row.changedFiles) || (row.changedFiles as number) < 0 || !Array.isArray(row.files)) continue;
|
|
286
|
+
if (!Number.isSafeInteger(row.number) || (row.number as number) <= 0 || !Number.isSafeInteger(row.changedFiles) || (row.changedFiles as number) < 0 || !Array.isArray(row.files)) { skipped += 1; continue; }
|
|
126
287
|
const paths = row.files.map(file => text(object(file).path, 'PR 파일명'));
|
|
127
288
|
if (paths.length === row.changedFiles && paths.length < MAX_FILES && !projection.has(row.number as number)) projection.set(row.number as number, paths);
|
|
128
|
-
|
|
289
|
+
else skipped += 1;
|
|
290
|
+
} catch { skipped += 1; /* malformed projection: REST file endpoint below decides this PR */ }
|
|
291
|
+
}
|
|
292
|
+
if (rows.length > 0 && projection.size === 0) return { files: projection, skipped, failure: `gh pr list ${rows.length}행 모두 사용 불가` };
|
|
293
|
+
return { files: projection, skipped };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Run `calls` through the bounded pool when given, else one by one with the injected runner, stopping at the first
|
|
297
|
+
* failure (the historical order and call count). The result may be shorter than `calls` only in that sequential case. */
|
|
298
|
+
function runCalls(run: PrToolCommand, calls: readonly PrToolCall[], runParallel: PrToolParallelCommand | undefined, concurrency: number, what: string): PrToolCommandResult[] {
|
|
299
|
+
if (runParallel) {
|
|
300
|
+
const results = runParallel(calls, concurrency);
|
|
301
|
+
if (results.length !== calls.length) throw new Error(`${what} 결과 ${results.length}/${calls.length}건 · 판정 불가`);
|
|
302
|
+
return results;
|
|
129
303
|
}
|
|
130
|
-
|
|
304
|
+
const results: PrToolCommandResult[] = [];
|
|
305
|
+
for (const call of calls) {
|
|
306
|
+
const result = run(call.command, call.args, call.opts);
|
|
307
|
+
results.push(result);
|
|
308
|
+
if (!result.ok) break;
|
|
309
|
+
}
|
|
310
|
+
return results;
|
|
131
311
|
}
|
|
132
312
|
|
|
133
313
|
export function readOpenPrs(run: PrToolCommand, options: {
|
|
@@ -135,10 +315,28 @@ export function readOpenPrs(run: PrToolCommand, options: {
|
|
|
135
315
|
seatOfPr?: (pr: { number: number; headRefName: string; body: string; labels: readonly string[] }) => Seat | null;
|
|
136
316
|
includeDraftCommitDates?: boolean;
|
|
137
317
|
candidateFiles?: readonly string[];
|
|
318
|
+
/** Bounded parallel REST-file fallback and diff fetch. Absent ⇒ the injected `run`, one by one (historical order and call count). */
|
|
319
|
+
runParallel?: PrToolParallelCommand;
|
|
320
|
+
/** Operations default {@link DEFAULT_DIFF_CONCURRENCY} (6) — production callers do not pass it; for test injection. */
|
|
321
|
+
diffConcurrency?: number;
|
|
322
|
+
/** Absent ⇒ no cache. Production callers pass {@link sharedCensusDiffCache}. */
|
|
323
|
+
diffCache?: CensusDiffCache;
|
|
138
324
|
} = {}): Inventory {
|
|
325
|
+
const started = Date.now();
|
|
326
|
+
const callOpts = options.cwd ? { opts: { cwd: options.cwd } } : {};
|
|
327
|
+
const concurrency = options.runParallel ? Math.max(1, Math.floor(options.diffConcurrency ?? DEFAULT_DIFF_CONCURRENCY)) : 1;
|
|
328
|
+
const scan = { openPrs: 0, projected: 0, restFallback: 0, projectionFailed: false, projectionReason: undefined as string | undefined,
|
|
329
|
+
projectionSkipped: 0, fileOverlap: 0, patchFetched: 0, cacheHits: 0 };
|
|
330
|
+
let status: 'checked' | 'inventory-unknown' | 'files-failed' | 'diff-failed' | 'commit-date-failed' = 'inventory-unknown';
|
|
331
|
+
const logDiffScan = () => {
|
|
332
|
+
debug.log('pr-tool.census', 'diff-scan', { ...scan, concurrency, elapsedMs: Date.now() - started, status });
|
|
333
|
+
};
|
|
139
334
|
try {
|
|
140
335
|
const rows = pages(run, 'repos/{owner}/{repo}/pulls?state=open&per_page=100', options.cwd);
|
|
141
|
-
const
|
|
336
|
+
const projection = projectedFiles(run, rows.length, options.cwd);
|
|
337
|
+
scan.projectionFailed = projection.failure !== undefined;
|
|
338
|
+
scan.projectionReason = projection.failure;
|
|
339
|
+
scan.projectionSkipped = projection.skipped;
|
|
142
340
|
const prs: OpenPr[] = [];
|
|
143
341
|
const seen = new Set<number>();
|
|
144
342
|
for (const value of rows) {
|
|
@@ -155,26 +353,69 @@ export function readOpenPrs(run: PrToolCommand, options: {
|
|
|
155
353
|
const headSha = text(head.sha, `PR #${number} head sha`);
|
|
156
354
|
const body = typeof row.body === 'string' ? row.body : '';
|
|
157
355
|
const seat = seatValue(options.seatOfPr?.({ number: number as number, headRefName, body, labels: labelNames })) ?? seatFromLabels(labelNames);
|
|
158
|
-
|
|
159
|
-
number: number as number, title: text(row.title, `PR #${number} title`), draft: row.draft,
|
|
356
|
+
prs.push({
|
|
357
|
+
number: number as number, headRefName, title: text(row.title, `PR #${number} title`), draft: row.draft,
|
|
160
358
|
updatedAt: date(row.updated_at, `PR #${number} updated_at`), headSha, labels: labelNames,
|
|
161
|
-
files:
|
|
162
|
-
cellId: cellIdFromBody(body),
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
359
|
+
files: projection.files.get(number as number) ?? [], seat,
|
|
360
|
+
cellId: cellIdFromBody(body), cardId: cardIdFromBody(body), cardMarkerPresent: hasCardMarker(body),
|
|
361
|
+
slice: sliceMarkerFromBody(body),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
scan.openPrs = prs.length;
|
|
365
|
+
// Files: PRs the one-shot projection did not cover take the REST file endpoint — in the pool when given.
|
|
366
|
+
const restPrs = prs.filter(pr => !projection.files.has(pr.number));
|
|
367
|
+
scan.projected = prs.length - restPrs.length;
|
|
368
|
+
scan.restFallback = restPrs.length;
|
|
369
|
+
if (restPrs.length) {
|
|
370
|
+
status = 'files-failed';
|
|
371
|
+
const results = runCalls(run, restPrs.map(pr => ({ command: 'gh', args: pagesArgs(filesEndpoint(pr.number)), ...callOpts })), options.runParallel, concurrency, 'PR 파일 조회');
|
|
372
|
+
// The first failure in input order decides the reason; one failure keeps the whole inventory unknown.
|
|
373
|
+
// Invariant: a result is missing only after an earlier failure (sequential stop), which throws first. Still,
|
|
374
|
+
// a missing result is a failure (unknown), never an empty file list.
|
|
375
|
+
restPrs.forEach((pr, index) => {
|
|
376
|
+
const result = results[index];
|
|
377
|
+
if (!result) throw new Error(`PR #${pr.number}: 파일 조회 결과 없음 · 판정 불가`);
|
|
378
|
+
pr.files = pathsFromFilesResult(result, pr.number);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
// Diffs: only PRs sharing a candidate file; a cached (number, headSha) patch is not refetched.
|
|
382
|
+
const pendingDiffs: OpenPr[] = [];
|
|
383
|
+
for (const pr of prs) {
|
|
384
|
+
if (!options.candidateFiles?.some(file => pr.files.includes(file))) continue;
|
|
385
|
+
scan.fileOverlap += 1;
|
|
386
|
+
const cached = options.diffCache?.get(pr.number, pr.headSha);
|
|
387
|
+
if (cached !== undefined) { pr.hunks = parseDiffHunks(cached); scan.cacheHits += 1; }
|
|
388
|
+
else pendingDiffs.push(pr);
|
|
389
|
+
}
|
|
390
|
+
if (pendingDiffs.length) {
|
|
391
|
+
status = 'diff-failed';
|
|
392
|
+
const results = runCalls(run, pendingDiffs.map(pr => ({ command: 'gh', args: ['pr', 'diff', String(pr.number), '--patch'], ...callOpts })), options.runParallel, concurrency, '수정 줄 조회');
|
|
393
|
+
scan.patchFetched = results.length;
|
|
394
|
+
pendingDiffs.forEach((pr, index) => {
|
|
395
|
+
const patch = results[index];
|
|
396
|
+
if (!patch) throw new Error(`PR #${pr.number}: 수정 줄 조회 결과 없음 · 판정 불가`);
|
|
397
|
+
if (!patch.ok) throw new Error(`PR #${pr.number}: 수정 줄 조회 실패: ${patch.err || patch.out}`);
|
|
398
|
+
});
|
|
399
|
+
pendingDiffs.forEach((pr, index) => {
|
|
400
|
+
const patch = results[index]!;
|
|
167
401
|
pr.hunks = parseDiffHunks(patch.out);
|
|
402
|
+
options.diffCache?.set(pr.number, pr.headSha, patch.out);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
if (options.includeDraftCommitDates) {
|
|
406
|
+
status = 'commit-date-failed';
|
|
407
|
+
for (const pr of prs) {
|
|
408
|
+
if (!pr.draft) continue;
|
|
409
|
+
const commit = run('gh', ['api', `repos/{owner}/{repo}/commits/${pr.headSha}`, '--jq', '.commit.committer.date'], options.cwd ? { cwd: options.cwd } : undefined);
|
|
410
|
+
if (!commit.ok) throw new Error(`PR #${pr.number}: 마지막 커밋 날짜 조회 실패: ${commit.err || commit.out}`);
|
|
411
|
+
pr.lastCommitAt = date(commit.out.trim(), `PR #${pr.number} 마지막 커밋`);
|
|
168
412
|
}
|
|
169
|
-
if (pr.draft && options.includeDraftCommitDates) {
|
|
170
|
-
const commit = run('gh', ['api', `repos/{owner}/{repo}/commits/${headSha}`, '--jq', '.commit.committer.date'], options.cwd ? { cwd: options.cwd } : undefined);
|
|
171
|
-
if (!commit.ok) throw new Error(`PR #${number}: 마지막 커밋 날짜 조회 실패: ${commit.err || commit.out}`);
|
|
172
|
-
pr.lastCommitAt = date(commit.out.trim(), `PR #${number} 마지막 커밋`);
|
|
173
|
-
}
|
|
174
|
-
prs.push(pr);
|
|
175
413
|
}
|
|
414
|
+
status = 'checked';
|
|
415
|
+
logDiffScan();
|
|
176
416
|
return { status: 'checked', prs };
|
|
177
417
|
} catch (error) {
|
|
418
|
+
logDiffScan();
|
|
178
419
|
return unknown(error instanceof Error ? error.message : String(error));
|
|
179
420
|
}
|
|
180
421
|
}
|
|
@@ -211,7 +452,7 @@ export function censusOpenPrs(inventory: Inventory, now = Date.now()): Census {
|
|
|
211
452
|
return result;
|
|
212
453
|
}
|
|
213
454
|
|
|
214
|
-
export function guardNewPr(input: { files: readonly string[] | null; seat?: Seat | null; seatCap?: number; cellId?: string | null; hunks?: readonly DiffHunk[]; hunkOverlap?: HunkOverlapMode }, inventory: Inventory): GuardVerdict {
|
|
455
|
+
export function guardNewPr(input: { files: readonly string[] | null; seat?: Seat | null; seatCap?: number; cellId?: string | null; cardId?: string | null; slice?: { slice: number; total: number } | null; boundPrNumber?: number; hunks?: readonly DiffHunk[]; hunkOverlap?: HunkOverlapMode }, inventory: Inventory): GuardVerdict {
|
|
215
456
|
if (!input.files || input.files.length === 0 || input.files.some(file => !file || file.startsWith('/') || file.includes('..'))) {
|
|
216
457
|
const reasons: GuardReason[] = [{ code: 'candidate-unknown', detail: '새 PR 변경 파일을 확인할 수 없음' }];
|
|
217
458
|
debug.log('pr.tool', 'guard-refuse', { status: 'unknown', reasons });
|
|
@@ -260,7 +501,18 @@ export function guardNewPr(input: { files: readonly string[] | null; seat?: Seat
|
|
|
260
501
|
const status = reasons.some(reason => reason.code === 'candidate-unknown') ? 'unknown' : reasons.length ? 'refuse' : 'allow';
|
|
261
502
|
if (status !== 'allow') debug.log('pr.tool', 'guard-refuse', { status, reasons, warnings });
|
|
262
503
|
else if (warnings.length) debug.log('pr.tool', 'guard-warn', { warnings });
|
|
263
|
-
|
|
504
|
+
const legacyBound = sameCell.length === 1 && sameCell[0]?.cardMarkerPresent === false
|
|
505
|
+
&& input.boundPrNumber === sameCell[0].number;
|
|
506
|
+
const reusable = status === 'refuse' && reasons.length === 1 && reasons[0]?.code === 'same-cell'
|
|
507
|
+
&& input.cardId && sameCell.length === 1 && (sameCell[0]?.cardId === input.cardId || legacyBound)
|
|
508
|
+
&& (input.slice?.slice ?? null) === (sameCell[0]?.slice?.slice ?? null)
|
|
509
|
+
&& (input.slice?.total ?? null) === (sameCell[0]?.slice?.total ?? null)
|
|
510
|
+
&& sameCell[0].headRefName && /^[0-9a-f]{40}$/i.test(sameCell[0].headSha)
|
|
511
|
+
? { number: sameCell[0].number, branch: sameCell[0].headRefName, headSha: sameCell[0].headSha,
|
|
512
|
+
cellId: input.cellId!, cardId: input.cardId, ...(legacyBound ? { legacyBound: true as const } : {}) } : undefined;
|
|
513
|
+
return { status, reasons, warnings,
|
|
514
|
+
...(sameCell.length ? { sameCellConflicts: sameCell.map(pr => ({ pr: pr.number, cardId: pr.cardId ?? null, slice: pr.slice ?? null })) } : {}),
|
|
515
|
+
...(reusable ? { sameCellReuse: reusable } : {}) };
|
|
264
516
|
}
|
|
265
517
|
|
|
266
518
|
/** Persist the unknown-read budget across harness processes sharing one instance state root. */
|
|
@@ -5,17 +5,27 @@
|
|
|
5
5
|
* 만지는 줄들의 의도를 읽는다. 착지하면 지운다. 정본은 PR 본문이고 이것은 «색인»이다 — 못 읽으면 빈 목록
|
|
6
6
|
* (머지는 종전 그대로 간다), 못 쓰면 발사·착지를 막지 않는다(호출부가 fail-soft).
|
|
7
7
|
*
|
|
8
|
-
* 폭증 막기: 런당 1줄(같은 칸 재발사는 덮어씀) · 의도 ≤300자 · 파일 ≤20
|
|
8
|
+
* 폭증 막기: 런당 1줄(같은 칸 재발사는 덮어씀) · 의도 ≤300자 · 파일 ≤20.
|
|
9
|
+
*
|
|
10
|
+
* 걷기: 닫힌 PR(`removeInflightForClosedPrs`)·끝난 런(`removeInflightForFinishedRuns`)은 그때 바로 걷고,
|
|
11
|
+
* 남은 줄은 `gcInflight` 가 6h 시한(`INFLIGHT_GC_TTL_MS`)으로 걷는다 — 6h 넘게 살아 있는 런은 없다.
|
|
12
|
+
* 48h 지연 필터(`INFLIGHT_TTL_MS`)는 GC 가 한 번도 안 돈 등록부의 백스톱이다(읽기는 쓰지 않는다).
|
|
13
|
+
*
|
|
14
|
+
* 쓰기는 임시 파일 → rename 원자 쓰기 — 반쯤 쓴 등록부를 동시에 읽는 쪽이 보지 않는다.
|
|
9
15
|
*/
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import {
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { randomUUID } from 'node:crypto';
|
|
18
|
+
import { basename, dirname, join } from 'node:path';
|
|
12
19
|
import { debug } from '../debug/log.js';
|
|
13
20
|
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
14
21
|
import { withFileLockSync } from '../storage/file-lock.js';
|
|
15
22
|
|
|
16
23
|
export const INFLIGHT_INTENT_MAX_CHARS = 300;
|
|
17
24
|
export const INFLIGHT_FILES_MAX = 20;
|
|
25
|
+
/** 지연 필터 시한 — GC 가 안 돈 등록부가 머지를 영원히 오염시키지 못하게 하는 백스톱. */
|
|
18
26
|
export const INFLIGHT_TTL_MS = 48 * 60 * 60 * 1000;
|
|
27
|
+
/** GC 시한 — 이만큼 지난 줄은 지금 살아 있는 런이 아니다. */
|
|
28
|
+
export const INFLIGHT_GC_TTL_MS = 6 * 60 * 60 * 1000;
|
|
19
29
|
export const INFLIGHT_REGISTRY_FILE = 'inflight-registry.json';
|
|
20
30
|
|
|
21
31
|
export interface InflightEntry {
|
|
@@ -33,6 +43,53 @@ export interface InflightOptions {
|
|
|
33
43
|
now?: () => Date;
|
|
34
44
|
}
|
|
35
45
|
|
|
46
|
+
/** 줄을 연 과제 카드의 최소 모양 — PR 상태(`state`)는 호출부가 gh 로 채워 준다. */
|
|
47
|
+
export interface InflightOwnerCard {
|
|
48
|
+
id: string;
|
|
49
|
+
runId?: string;
|
|
50
|
+
pr?: number | { number: number; state?: string };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface InflightGcInput extends InflightOptions {
|
|
54
|
+
/** 머지 없이 닫힌 PR 번호. */
|
|
55
|
+
closedPrs?: readonly number[];
|
|
56
|
+
/** 끝난 런 id — 정상 종료·중단 모두. */
|
|
57
|
+
finishedRuns?: readonly string[];
|
|
58
|
+
/** 과제 카드들 — `card.pr` 이 닫힌 PR 이면 그 카드의 줄을 걷는다. */
|
|
59
|
+
cards?: Iterable<InflightOwnerCard>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type InflightGcReason = 'expired' | 'finished-run' | 'closed-pr';
|
|
63
|
+
|
|
64
|
+
export interface InflightGcResult {
|
|
65
|
+
/** 걷고 남은 줄 수. */
|
|
66
|
+
kept: number;
|
|
67
|
+
removed: Array<{ entry: InflightEntry; reason: InflightGcReason }>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const CLOSED_PR_STATES = new Set(['CLOSED', 'closed', 'MERGED', 'merged']);
|
|
71
|
+
|
|
72
|
+
function prNumberOf(card: InflightOwnerCard): number | undefined {
|
|
73
|
+
return typeof card.pr === 'number' ? card.pr : card.pr?.number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isClosedOwner(card: InflightOwnerCard, closedPrs: ReadonlySet<number>): boolean {
|
|
77
|
+
const pr = card.pr;
|
|
78
|
+
if (pr === undefined) return false;
|
|
79
|
+
if (typeof pr === 'object' && pr.state !== undefined && CLOSED_PR_STATES.has(pr.state)) return true;
|
|
80
|
+
const number = prNumberOf(card);
|
|
81
|
+
return number !== undefined && closedPrs.has(number);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 닫힌 카드들의 카드 id·runId — 줄을 찾는 키. */
|
|
85
|
+
function closedOwnerKeys(cards: readonly InflightOwnerCard[], closedPrs: ReadonlySet<number>): { cardIds: Set<string>; runIds: Set<string> } {
|
|
86
|
+
const owners = cards.filter((card) => isClosedOwner(card, closedPrs));
|
|
87
|
+
return {
|
|
88
|
+
cardIds: new Set(owners.map((card) => card.id)),
|
|
89
|
+
runIds: new Set(owners.flatMap((card) => (card.runId ? [card.runId] : []))),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
36
93
|
/** 기본 자리 — 과제 카드 상태(`task-agent-actions.json`)와 같은 우주 뿌리. */
|
|
37
94
|
export function inflightRegistryPath(): string {
|
|
38
95
|
return join(effectiveInstanceRoot(), INFLIGHT_REGISTRY_FILE);
|
|
@@ -52,9 +109,30 @@ function isEntry(value: unknown): value is InflightEntry {
|
|
|
52
109
|
&& Array.isArray(entry.files) && typeof entry.registeredAt === 'string';
|
|
53
110
|
}
|
|
54
111
|
|
|
55
|
-
function
|
|
112
|
+
function olderThan(entry: InflightEntry, now: Date, ttlMs: number): boolean {
|
|
56
113
|
const at = Date.parse(entry.registeredAt);
|
|
57
|
-
return !Number.isFinite(at) || now.getTime() - at >
|
|
114
|
+
return !Number.isFinite(at) || now.getTime() - at > ttlMs;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isExpired(entry: InflightEntry, now: Date): boolean {
|
|
118
|
+
return olderThan(entry, now, INFLIGHT_TTL_MS);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 임시 파일 → rename. 임시 파일은 같은 디렉터리(=같은 파일시스템)에 만들어 rename 이 원자다 —
|
|
123
|
+
* 읽는 쪽은 «옛 등록부» 아니면 «완성된 새 등록부»만 본다(반쯤 쓴 JSON 을 파싱하지 않는다).
|
|
124
|
+
*/
|
|
125
|
+
function writeEntriesAtomic(path: string, entries: InflightEntry[]): void {
|
|
126
|
+
const dir = dirname(path);
|
|
127
|
+
mkdirSync(dir, { recursive: true });
|
|
128
|
+
const tmp = join(dir, `.${basename(path)}.${process.pid}.${randomUUID()}.tmp`);
|
|
129
|
+
try {
|
|
130
|
+
writeFileSync(tmp, `${JSON.stringify({ entries })}\n`, 'utf8');
|
|
131
|
+
renameSync(tmp, path);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
try { unlinkSync(tmp); } catch { /* rename 이 이미 끝났으면 지울 임시 파일이 없다 */ }
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
58
136
|
}
|
|
59
137
|
|
|
60
138
|
/** 잠금 안에서 읽고 고쳐 쓴다 — 48h 지난 줄은 쓸 때 걷는다(`expire` 관측). */
|
|
@@ -67,11 +145,24 @@ function mutate(opts: InflightOptions, update: (entries: InflightEntry[]) => Inf
|
|
|
67
145
|
if (live.length < all.length) {
|
|
68
146
|
try { debug.log('pr-tool.inflight', 'expire', { phase: 'write', runIds: all.filter((entry) => isExpired(entry, now)).map((entry) => entry.runId) }); } catch { /* fail-soft */ }
|
|
69
147
|
}
|
|
70
|
-
|
|
71
|
-
writeFileSync(path, JSON.stringify({ entries: update(live) }));
|
|
148
|
+
writeEntriesAtomic(path, update(live));
|
|
72
149
|
});
|
|
73
150
|
}
|
|
74
151
|
|
|
152
|
+
/** 잠금 한 번·원자 쓰기 한 번으로 걷는다 — 지울 줄이 없으면 디스크를 건드리지 않는다. */
|
|
153
|
+
function sweep(opts: InflightOptions, reason: string, drop: (entry: InflightEntry) => boolean): InflightEntry[] {
|
|
154
|
+
const path = opts.path ?? inflightRegistryPath();
|
|
155
|
+
let removed: InflightEntry[] = [];
|
|
156
|
+
withFileLockSync(`${path}.lock`, () => {
|
|
157
|
+
const all = readEntries(path);
|
|
158
|
+
removed = all.filter(drop);
|
|
159
|
+
if (removed.length === 0) return;
|
|
160
|
+
writeEntriesAtomic(path, all.filter((entry) => !drop(entry)));
|
|
161
|
+
});
|
|
162
|
+
try { debug.log('pr-tool.inflight', 'remove', { reason, removed: removed.map((entry) => entry.runId) }); } catch { /* fail-soft */ }
|
|
163
|
+
return removed;
|
|
164
|
+
}
|
|
165
|
+
|
|
75
166
|
/** 한 줄 등록 — 같은 runId 또는 같은 칸의 옛 줄을 덮어쓴다. 의도·파일은 상한에서 자른다. */
|
|
76
167
|
export function registerInflight(input: { runId: string; cell: string; intent: string; files: readonly string[]; cardId?: string }, opts: InflightOptions = {}): InflightEntry {
|
|
77
168
|
const now = (opts.now ?? (() => new Date()))();
|
|
@@ -141,3 +232,65 @@ export function removeInflightForPr(
|
|
|
141
232
|
}
|
|
142
233
|
return removed;
|
|
143
234
|
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* 닫힌 PR(머지 포함)의 줄을 지운다 — `closedPrs` 번호와 `card.pr` 의 닫힘 상태 양쪽을 본다.
|
|
238
|
+
* 열린 PR 카드는 건드리지 않고, 닫힌 카드가 없으면 디스크도 건드리지 않는다.
|
|
239
|
+
*/
|
|
240
|
+
export function removeInflightForClosedPrs(
|
|
241
|
+
closedPrs: readonly number[],
|
|
242
|
+
cards: Iterable<InflightOwnerCard>,
|
|
243
|
+
opts: InflightOptions = {},
|
|
244
|
+
): number {
|
|
245
|
+
const closed = new Set(closedPrs);
|
|
246
|
+
const { cardIds, runIds } = closedOwnerKeys([...cards], closed);
|
|
247
|
+
if (cardIds.size === 0 && runIds.size === 0) {
|
|
248
|
+
try { debug.log('pr-tool.inflight', 'remove', { reason: 'pr-closed', removed: [], cards: 0, closedPrs: [...closed] }); } catch { /* fail-soft */ }
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
return sweep(opts, `pr-closed:${[...closed].sort((a, b) => a - b).join(',')}`, (entry) => (
|
|
252
|
+
(entry.cardId !== undefined && cardIds.has(entry.cardId)) || runIds.has(entry.runId)
|
|
253
|
+
)).length;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** 끝난 런(정상 종료·중단 모두)의 줄을 지운다 — 카드가 없어도 runId 로 걷는다. */
|
|
257
|
+
export function removeInflightForFinishedRuns(runIds: readonly string[], opts: InflightOptions = {}): number {
|
|
258
|
+
const finished = new Set(runIds);
|
|
259
|
+
if (finished.size === 0) return 0;
|
|
260
|
+
return sweep(opts, 'run-finished', (entry) => finished.has(entry.runId)).length;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 등록부 청소 한 번 — 닫힌 PR 줄 · 끝난 런 줄 · 6h 넘은 줄을 한 잠금·원자 쓰기 한 번으로 걷는다.
|
|
265
|
+
* 지울 줄이 없으면 디스크를 건드리지 않고, 무엇이 왜 걷혔는지(`reason`)를 돌려준다.
|
|
266
|
+
* 우선순위: `expired` → `finished-run` → `closed-pr`(한 줄이 여러 규칙에 걸리면 시한이 먼저다).
|
|
267
|
+
*/
|
|
268
|
+
export function gcInflight(input: InflightGcInput = {}): InflightGcResult {
|
|
269
|
+
const path = input.path ?? inflightRegistryPath();
|
|
270
|
+
const now = (input.now ?? (() => new Date()))();
|
|
271
|
+
const finished = new Set(input.finishedRuns ?? []);
|
|
272
|
+
const { cardIds, runIds } = closedOwnerKeys([...(input.cards ?? [])], new Set(input.closedPrs ?? []));
|
|
273
|
+
const classify = (entry: InflightEntry): InflightGcReason | null => {
|
|
274
|
+
if (olderThan(entry, now, INFLIGHT_GC_TTL_MS)) return 'expired';
|
|
275
|
+
if (finished.has(entry.runId)) return 'finished-run';
|
|
276
|
+
if (runIds.has(entry.runId)) return 'closed-pr';
|
|
277
|
+
if (entry.cardId !== undefined && cardIds.has(entry.cardId)) return 'closed-pr';
|
|
278
|
+
return null;
|
|
279
|
+
};
|
|
280
|
+
let kept = 0;
|
|
281
|
+
let removed: InflightGcResult['removed'] = [];
|
|
282
|
+
withFileLockSync(`${path}.lock`, () => {
|
|
283
|
+
const all = readEntries(path);
|
|
284
|
+
removed = all.flatMap((entry) => {
|
|
285
|
+
const reason = classify(entry);
|
|
286
|
+
return reason === null ? [] : [{ entry, reason }];
|
|
287
|
+
});
|
|
288
|
+
kept = all.length - removed.length;
|
|
289
|
+
if (removed.length === 0) return;
|
|
290
|
+
writeEntriesAtomic(path, all.filter((entry) => classify(entry) === null));
|
|
291
|
+
});
|
|
292
|
+
try {
|
|
293
|
+
debug.log('pr-tool.inflight', 'gc', { kept, removed: removed.map(({ entry, reason }) => ({ runId: entry.runId, reason })) });
|
|
294
|
+
} catch { /* fail-soft */ }
|
|
295
|
+
return { kept, removed };
|
|
296
|
+
}
|