elanous 0.2.40 → 0.2.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_buildManifest.js +1 -1
- package/apps/pwa/out/_next/static/chunks/0244e5b5.6fc7670f8223d82d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4769.9fd582071301f382.js → 1063.2fb8a9dc55c731f2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1121.13ba7a8aff19df81.js +36 -0
- package/apps/pwa/out/_next/static/chunks/1147-5196d19c222051b3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{470.51a72a2348306455.js → 1262.e804feb8fd1acb8c.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/1319.b9b815ae0f181e06.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1345-31427b833438f521.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7219-15bdd9a80661af8e.js → 1455-530ca16c7e71baff.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1281.d81877e35b9068f4.js → 1487.2f67b79444062da6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1574.878c18ecc97b1bd4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1586.fafc33a9a5835cb7.js +32 -0
- package/apps/pwa/out/_next/static/chunks/1744.855fd60ae4c4a8c8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/193.21a47a3e07ef6860.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1945.7b54990d578c119b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2004.e839fda7ea5a738a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2044-dd681a5c08779ff8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2101.2248decb38829b03.js +286 -0
- package/apps/pwa/out/_next/static/chunks/2160.a097e7b138ae1c55.js +23 -0
- package/apps/pwa/out/_next/static/chunks/2300.41ea70ac1bef1446.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2357-07b5eeb5fb12628d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2364.28652020695a6db3.js +34 -0
- package/apps/pwa/out/_next/static/chunks/{1350.59f40e39e02d91b1.js → 2456.b6de0e1c05f28843.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2526.c3e50ac1fec064bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2549-cda50b716ff4250c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2550-5f1e88f615568045.js +1 -0
- package/apps/pwa/out/_next/static/chunks/261-319078c954e40d66.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2690.0521c738b7749931.js +115 -0
- package/apps/pwa/out/_next/static/chunks/{5436.fdf6fa8ae3e9588d.js → 2974.595d1e29d040cb6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5383.bdfa5d886e39a826.js → 3207.a756c95e9ec8c3c8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3285.89ee2f298682f5e5.js +93 -0
- package/apps/pwa/out/_next/static/chunks/{2177.df8547f9d7215c4e.js → 3347.f846266b5e5f3ede.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/3447.06edc7b092021417.js +41 -0
- package/apps/pwa/out/_next/static/chunks/{9184.45afbf44fec87574.js → 3482.a9ae9d2ad6a5bd97.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3507.97e1977bc4d5fb0a.js +2 -0
- package/apps/pwa/out/_next/static/chunks/3510-9ca392698bfe4391.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3541.772e5ce425aaa611.js +24 -0
- package/apps/pwa/out/_next/static/chunks/{8099.1cccf31c1baa7e1c.js → 3741.9cf76acaa2ee5298.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3837.a210d7d0db8d8abd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3898-955c896f394217a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3947-ef57acea76eaa8d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4864a2ca.1c1c280a7772c5e5.js → 3d0154af.5331fec737ffe8c3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4037-3b82ebc7d78afdc7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4043.6b91203436c9c86b.js +309 -0
- package/apps/pwa/out/_next/static/chunks/405.9022d4a478202b67.js +77 -0
- package/apps/pwa/out/_next/static/chunks/417-fb2cc9a9910e5968.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3330.7443183b7e6fdbdf.js → 4276.336d76d858fcdf1b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4302.6ced9aa4535de947.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4320.583cbab64db6be02.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4768-8eac183fd291ca53.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4770.769c9cd9c7034eba.js +148 -0
- package/apps/pwa/out/_next/static/chunks/4895.65605ec3e6504e89.js +74 -0
- package/apps/pwa/out/_next/static/chunks/{3b355b3d.6f73c98bf6f9c749.js → 50061469.0326382241ef3b2d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5112.4b9f300124fab7b3.js +149 -0
- package/apps/pwa/out/_next/static/chunks/5335.375ee922bcd5bc83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5342.023a2e729689b320.js +27 -0
- package/apps/pwa/out/_next/static/chunks/{8d173e38.18a2f7b4e1848bdb.js → 53c73f84.373b6160adfa89a4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1537-6bb97b5214928ea9.js → 5431-72a1dbde59400691.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5440-4236de26c5ea5856.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5486-b318c165f645d8ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5506-c7d2ec25c62a7c94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5570-c66ecbea26b4e3b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5696.ffca5aa259d8451e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5793-8c584aea2aa87560.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5848-6de6b8c173351a90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5885.0a3e52c8faccec98.js +287 -0
- package/apps/pwa/out/_next/static/chunks/5999.94e3c83040200f31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5a7901d6-2d8d9b5587cf9042.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{261.0d471e8aded799f3.js → 6131.348b7d6bdcd267ce.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/6164.86133a0172611184.js +90 -0
- package/apps/pwa/out/_next/static/chunks/{9175.2de5209a5ce6f68a.js → 6377.bc7df239edbff244.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6515.e07e8b2b8a707a87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6522-c005514f87d18982.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2226.2cf61ec781827219.js → 6632.0eac1c554f8f2ef3.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6642.648adb1767f39ecf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6848.138805a09d001d09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7049-c09e2b7a253a24f3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7072.1e00447d9fa39972.js +104 -0
- package/apps/pwa/out/_next/static/chunks/7106.64201c539c5e220c.js +94 -0
- package/apps/pwa/out/_next/static/chunks/7151-1f742b455986530e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7199.18d6b25e001154ce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7279-c199ea83f34338fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/728b938d.ffaa7ad583d60e58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7309.171c3de54e0322f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/733.7a1ab3342e03306f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{b84cad00.594027ebe0531fe2.js → 73d2eb0e.4966ad55293c801a.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/{6257.c0037b8949239f5e.js → 7411.9b4b8b747dc7b802.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/7504.d5cf1e7cd89b1b08.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7573-0fd4af62d65d4598.js +1 -0
- package/apps/pwa/out/_next/static/chunks/759.6f8267df4689c401.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7825-668b046f72f14483.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7852-1538768b6eb19388.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7868.cff810430bb63c91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9189505a.980427ac048c8c60.js → 7a98bc80.fa935989e829f9c2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8084-dfcc796874ab4e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8138.6c10b2a356d52fca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2566.beaffc3a5e1fcd2e.js → 8262.ddc03e06b733f184.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8266.6bf9b131b2584678.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8403.7b81d5e64074a725.js +8 -0
- package/apps/pwa/out/_next/static/chunks/8470.1998cbd0866a7eac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8505.fd803c6c33b54cc3.js +79 -0
- package/apps/pwa/out/_next/static/chunks/{8272-025653884f9645d2.js → 8546-6f03f8ea34dd61d9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{6051-0ae955eedb10adb9.js → 8673-bab689a55f7e13b9.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/8687-246fda8433d3477c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/898-280022fb082936bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9098-50eefc1844212e3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9211-330b6736a63d4cb3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4351.0f37a6db2b34d50b.js → 9249.976f1e69a9d80754.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9255-cf88fceda5666645.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9306-e523ba58bb8adc42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9324.a9866d717ba62cb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9325.a8510e8aa87c8143.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9353-12bfff0c130104cf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9415.69e4a21c60c7d2db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6749.0744ce6eb08d40e9.js → 9427.96aa912ddb5801e0.js} +16 -16
- package/apps/pwa/out/_next/static/chunks/9479-154a697d4b6e4e98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9540-65aa6ccc31b3fd78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7611.85f045bf232053ac.js → 9633.ce5bddc56784da72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{3746-f80a78f220bf37b5.js → 9656-374232558fd528d0.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9832.f4a98c878ecf2c0a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/9865.f094440645f02d98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9a852a62-6192a6ea9346ced3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-e772b4710df5d936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-4361368e6a76b967.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-802f349fdd65248d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-7f35513988a2a495.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-798121656540e33e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-20a089490affe98c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-e5e4ec786d70e604.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-d12fe5d0801430e4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-d8cf21022fecc026.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-21ce565c9c57ee97.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-b56563025f619c9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-52f14dd133da8348.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-c0ed7fdcdba76633.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-af8c3020cb520f15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-72eda67af9f5e35c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-e772b4710df5d936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-67a6620d31f0a46f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-f059363aa0880ec2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-f9f917d6a57f69fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3ae55eee2f0826c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83d27757e2b8060a.js → page-0e3d7b9421b79dac.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-ba738f7e233eb211.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-33438b98c74fd599.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-0b34d5fd990d793a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-0f291d3c8fd55a3a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-3fced192be5279c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-04cb3cf182a7c1ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-6ccbd2ab23a21542.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-aa0518c48a5f4e56.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-4b32226d341ad7d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3224fd8f6383b61f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-efbc97459f2914e2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-2fe487d88cd5d97c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-48861dd1a858bab7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-197d5d4d21d228ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a763ba89fbdd9c27.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-7edb253510b1c136.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-0c875702c61965ea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-e772b4710df5d936.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-e425c951151095f7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-9c90e0aec4e27f77.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-733e09a02e727d42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-4f899e14c34d77a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-139d722de1dcd6c1.js → page-bf067887b3307423.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-7af10868ff87d04e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-efe5304057aa11c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-76bcd29aaa8a26c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-4cfd88e2273ce50f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-0a1ddb47efba0a32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2a176c707152c892.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-efe03031b8797fbf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{409c787b.e48c5bec8ec10456.js → b450776b.3db34d8d965f82d3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{ca3702f8.2913182e73889fa2.js → e8543907.5b040a5f22a0b0e5.js} +7 -7
- package/apps/pwa/out/_next/static/chunks/framework-d0d2d673bfd8a8a9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-4c9fceb2baa09cb7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-b0ab8d8fc9a97556.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-7e2cc62a2e5cec96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-22324935f753ec5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-9dc1bfa25334998a.js +1 -0
- package/apps/pwa/out/_next/static/css/{b9d8151e48998417.css → 62c858ad53854f47.css} +1 -1
- 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 +23 -24
- 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 +22 -22
- 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 +23 -24
- package/apps/pwa/out/live-run/index.html +1 -1
- package/apps/pwa/out/live-run/index.txt +23 -24
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +21 -21
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +21 -21
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +21 -21
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +21 -21
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +21 -21
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.mjs +6 -1
- package/catalog/models/openai/gpt-6.1-sol.yaml +16 -0
- package/catalog/models/openai/gpt-6.1-terra.yaml +16 -0
- package/docs/goal-context/04-mock-scenarios.md +13 -0
- package/graphs/catalog/catalog.yaml +1 -1
- package/graphs/demo/persona-teacher/lesson-pack.yaml +16 -0
- package/graphs/demo/persona-teacher/recipes.yaml +17 -0
- package/graphs/demo/persona-teacher/weekly-lesson.yaml +36 -0
- package/graphs/heal/heal-loop.yaml +1 -0
- package/graphs/overlays/stop-heal-ladder.yaml +136 -2
- package/graphs/release/release-loop.yaml +3 -1
- package/graphs/units/gate/gate.yaml +20 -0
- package/graphs/units/git/git.yaml +26 -0
- package/graphs/units/implement/implement.yaml +19 -0
- package/graphs/units/landing/landing.yaml +18 -0
- package/graphs/units/review/review.yaml +20 -0
- package/graphs/units/verbs/recipes.yaml +13 -10
- package/graphs/units/verbs/research.yaml +20 -0
- package/graphs/work/research-report.yaml +21 -3
- package/package.json +1 -1
- package/scripts/disk-steward.ts +46 -0
- package/scripts/fleet/node-setup.sh +50 -0
- package/scripts/graph-units/verbs/research.ts +55 -0
- package/scripts/heal/run-role.ts +99 -5
- package/scripts/land-global-checks.ts +6 -1
- package/scripts/lib/decompose-ab.ts +1 -0
- package/scripts/lib/fixtures/tui-regress/help-insufficient-commands.txt +15 -0
- package/scripts/lib/fixtures/tui-regress/help-live.txt +42 -0
- package/scripts/lib/fixtures/tui-regress/help-missing-keys.txt +21 -0
- package/scripts/lib/fixtures/tui-regress/help-original.txt +51 -0
- package/scripts/lib/fixtures/tui-regress/help-shifted-input.txt +26 -0
- package/scripts/lib/tui-regress-checks.ts +33 -13
- package/scripts/llm-cost-ledger.ts +179 -8
- package/scripts/measure-nl-routing-corpus.ts +10 -2
- package/scripts/persona-teacher/weekly-lesson-node.ts +172 -0
- package/scripts/public-copy-lint.ts +163 -19
- package/scripts/ratchets/train-side-invariants.sh +0 -1
- package/scripts/release-loop/gate-autoheal.ts +117 -0
- package/scripts/release-loop/gate-baseline-receipt.ts +55 -10
- package/scripts/release-loop/gate-diff.ts +22 -3
- package/scripts/release-loop/gate-image-pin.ts +81 -0
- package/scripts/release-loop/gate-node-sim.ts +207 -0
- package/scripts/release-loop/gate-node.ts +489 -62
- package/scripts/release-loop/gate-pod-image-sticky-plan.ts +99 -0
- package/scripts/release-loop/gate-radius-live.ts +97 -13
- package/scripts/release-loop/gate-shard-dispatch.ts +43 -0
- package/scripts/release-loop/gate-shard-load-flaky.ts +256 -0
- package/scripts/release-loop/gate-train-receipts.ts +122 -0
- package/scripts/release-loop/gate-triage-node.ts +70 -10
- package/scripts/release-loop/resume-release.ts +330 -23
- package/scripts/release-loop/traffic-node.ts +6 -3
- package/scripts/release-loop/unattended-release.ts +4 -2
- package/scripts/shadow-authoring-probe.ts +20 -0
- package/scripts/ux-sim.ts +37 -3
- package/scripts/warm-pool-provider-config.ts +263 -0
- package/skills/omni-crawl/SKILL.md +5 -2
- package/skills/omni-crawl/scripts/main.ts +84 -23
- package/src/acp/core-turn-bridge.ts +99 -4
- package/src/agent/registry.ts +2 -0
- package/src/agent/task-notification.ts +25 -5
- package/src/agent/types.ts +6 -0
- package/src/agent-mission/mission-cli.ts +24 -0
- package/src/agent-mission/mission-out.ts +595 -0
- package/src/agent-mission/tool-install.ts +88 -2
- package/src/agent-substrate/context-strategy.ts +4 -4
- package/src/agent-substrate/self-review-cli.ts +267 -40
- package/src/auto-research/research-turn.ts +133 -51
- package/src/autopilot/discovery/wiki-claim-proposer.ts +22 -1
- package/src/autopilot/pr-manager.ts +38 -7
- package/src/autopilot/pty-control-loop.ts +2 -0
- package/src/boot/daemon-tools/self-implement.ts +46 -9
- package/src/budget/shadow-arms.ts +52 -0
- package/src/capture/tui-self-report.ts +22 -6
- package/src/chat/index.ts +1 -1
- package/src/chat/pickers/state.ts +7 -1
- package/src/cli/fixtures/review-loop-breaker/opwaiver-32646-1694ccc7a170.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-32853-bd8f73595c83.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-33097-1cc85900413c.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-33106-972f31e4a3ca.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/opwaiver-33113-3ab51f05968f.json +1 -0
- package/src/cli/fixtures/review-loop-breaker/pr-33097-1cc85900413c.json +1 -0
- package/src/cli/log-event-names.ts +5 -0
- package/src/cli/mcp-login.ts +8 -18
- package/src/cli/msg-cli.ts +57 -1
- package/src/cli/pr-land-heavy-remote.ts +47 -9
- package/src/cli/pr-land-overlap-evidence.ts +100 -1
- package/src/cli/pr-land-pod-shadow-job.ts +60 -0
- package/src/cli/release-checklist-read-cli.ts +20 -10
- package/src/cli/release-cli.ts +27 -1
- package/src/cli/research-cli.ts +4 -5
- package/src/cli/sim-cli.ts +7 -1
- package/src/cli/tasks-cli.ts +12 -4
- package/src/codex/models.ts +30 -15
- package/src/compact/auto.ts +8 -1
- package/src/control-plane/ledger.ts +52 -2
- package/src/control-plane/server.ts +16 -2
- package/src/core-turn/run-core-turn.ts +313 -89
- package/src/core-turn/run-goal-loop.ts +106 -2
- package/src/core-turn/types.ts +2 -0
- package/src/dashboard/acp-boot.ts +72 -3
- package/src/dashboard/classic-sidetab-mission-job.ts +58 -0
- package/src/dashboard/classic-sidetab-mission.ts +185 -0
- package/src/dashboard/index.ts +155 -15
- package/src/dashboard/input/agent-wake.ts +23 -1
- package/src/dashboard/input/chat-main-plain-turn-runtime.ts +9 -0
- package/src/dashboard/mission-cli-consumer.ts +153 -0
- package/src/dashboard/seat-boot-preamble.ts +171 -1
- package/src/dashboard/side-basic-runtime.ts +54 -1
- package/src/dashboard/sidetab-runtime.ts +90 -1
- package/src/dashboard/skill-catalog-preamble.ts +101 -0
- package/src/dashboard/skill-detection-runtime.ts +55 -15
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +15 -0
- package/src/dashboard/turn-preamble.ts +65 -16
- package/src/dashboard/turn-prompt-runtime.ts +4 -2
- package/src/disk-steward/judge.ts +73 -0
- package/src/disk-steward/ledger.ts +122 -0
- package/src/disk-steward/steward.ts +373 -0
- package/src/domains/guardian-tick.ts +191 -2
- package/src/graph-runner/graph-cli.ts +44 -7
- package/src/graph-runner/graph-tick.ts +10 -2
- package/src/graph-runner/in-process-registry.ts +37 -0
- package/src/graph-runner/runner.ts +34 -4
- package/src/grok/agent-search.ts +14 -4
- package/src/grok/credential.ts +28 -11
- package/src/grounding/ref-index.ts +19 -0
- package/src/grounding/sources-cli.ts +9 -1
- package/src/harness/harness-cli-command.ts +267 -14
- package/src/harness/harness-incidents.ts +312 -7
- package/src/harness/harness-pod-dispatch.ts +81 -15
- package/src/harness/harness-space.ts +15 -1
- package/src/harness/harness-stop.ts +155 -11
- package/src/harness/progress-worker-entry.ts +59 -0
- package/src/harness/progress-worker-runtime.ts +11 -0
- package/src/index.ts +52 -7
- package/src/instance/dev-project.ts +25 -0
- package/src/instance/spawn-elanous.ts +6 -4
- package/src/intelligence-map/model-alias.ts +6 -5
- package/src/intelligence-map/model-catalog.ts +59 -2
- package/src/llm/model-defaults.ts +4 -4
- package/src/llm.ts +208 -19
- package/src/loops/orchestrator/coordinator-shadow.ts +226 -0
- package/src/loops/orchestrator/tick.ts +61 -0
- package/src/maturity/feature-maturity.ts +1 -1
- package/src/mcp/mcp-oauth.ts +3 -1
- package/src/model-tier/llm-tier-map.ts +9 -9
- package/src/model-tier/turn-tier.ts +1 -1
- package/src/msg/msg-mirror-github.ts +80 -0
- package/src/msg/msg-mirror-policy.ts +61 -0
- package/src/msg/msg-mirror-runner.ts +219 -0
- package/src/msg/msg-mirror-state.ts +33 -0
- package/src/msg/msg-store.ts +11 -0
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/harness-traversal.ts +11 -0
- package/src/output-truncation.ts +41 -12
- package/src/pr-tool/census.ts +3 -2
- package/src/project/project-store.ts +48 -1
- package/src/pty-shell/tui-run-state.ts +188 -2
- package/src/release-loop/checklist-target-path-hint.ts +13 -0
- package/src/release-loop/checklist.ts +9 -1
- package/src/release-loop/gate-host-reservation.ts +144 -0
- package/src/release-loop/release-schedule.ts +90 -1
- package/src/repl/index.ts +43 -2
- package/src/research/axis-fanout.ts +121 -16
- package/src/research/bloom-exemplars.ts +137 -0
- package/src/research/gap-followup.ts +13 -1
- package/src/research/plugin-discovery.ts +36 -14
- package/src/research-bridge/invoke.ts +22 -5
- package/src/research-bridge/types.ts +2 -0
- package/src/self-dev/ask-launch-flow.ts +39 -25
- package/src/self-dev/decompose.ts +119 -5
- package/src/self-dev/dev-cli.ts +17 -17
- package/src/self-dev/graph-front-nodes.ts +23 -0
- package/src/self-dev/launch-preflight-text.ts +5 -6
- package/src/self-dev/launch-preflight.ts +9 -4
- package/src/self-dev/orchestrate.ts +27 -2
- package/src/self-dev/review-provider-fallback.ts +9 -2
- package/src/self-dev/run-supervisor.ts +226 -6
- package/src/self-dev/run-types.ts +7 -0
- package/src/self-implement/doctor-visit.ts +88 -0
- package/src/self-implement/gate-artifacts.ts +13 -0
- package/src/self-implement/gate-cli.ts +54 -24
- package/src/self-implement/gate-remote.ts +23 -5
- package/src/self-implement/goal-author-clarification.ts +31 -12
- package/src/self-implement/goal-author.ts +403 -8
- package/src/self-implement/graph-overlay-yaml.ts +7 -0
- package/src/self-implement/graph-stage-units.ts +172 -0
- package/src/self-implement/graph-templates.ts +162 -1
- package/src/self-implement/graph-variant.ts +37 -4
- package/src/self-implement/headless-elanous-driver.ts +31 -2
- package/src/self-implement/heal-intake.ts +112 -4
- package/src/self-implement/heal-triage.ts +20 -0
- package/src/self-implement/host-regate.ts +31 -8
- package/src/self-implement/knowhow-pack.ts +38 -0
- package/src/self-implement/merge-train.ts +5 -0
- package/src/self-implement/orchestrator.ts +359 -30
- package/src/self-implement/regate-detail.ts +38 -1
- package/src/self-implement/rework-child-provider.ts +25 -0
- package/src/self-implement/run-chart.ts +119 -0
- package/src/self-implement/run-ledger.ts +176 -17
- package/src/self-implement/run-stop.ts +208 -5
- package/src/self-implement/running-runs.ts +35 -12
- package/src/self-implement/self-implement-cli.ts +36 -4
- package/src/self-implement/stop-autoheal-operational.ts +43 -0
- package/src/self-implement/stop-autoheal.ts +28 -6
- package/src/session/chat.ts +69 -58
- package/src/session/request-size-guard.ts +123 -0
- package/src/session-runtime/index.ts +98 -4
- package/src/session-runtime/tier-flip.ts +3 -3
- package/src/sidetab-core/model.ts +150 -0
- package/src/sidetab-core/visible-agent-mission.ts +186 -0
- package/src/skills/name-match.ts +20 -0
- package/src/skills/tools/agent.ts +30 -2
- package/src/skills/tools/omni-search.ts +67 -5
- package/src/task-agent/parent-host.ts +14 -1
- package/src/task-agent/pod-distributor-entry.ts +3 -0
- package/src/task-agent/task-hand.ts +31 -1
- package/src/task-orchestrator/surfaces/pod-distributor.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +38 -1
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +29 -4
- package/src/task-orchestrator/surfaces/pod-si-warm-entry.ts +33 -6
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +45 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +163 -13
- package/src/task-orchestrator/surfaces/pod-source-spec.ts +3 -3
- package/src/task-orchestrator/surfaces/pod-warm-worker-provider.ts +209 -39
- package/src/task-orchestrator/surfaces/pod-warm-worker-runtime.ts +80 -3
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +280 -60
- package/src/task-orchestrator/surfaces/self-implement.ts +18 -0
- package/src/tokens.ts +35 -2
- package/src/tool-runtime/agent-runtime.ts +1 -0
- package/src/tool-runtime/elanous-skills-list-runtime.ts +5 -1
- package/src/tool-runtime/skill-exec-runtime.ts +33 -13
- package/src/tool-runtime/types.ts +1 -0
- package/src/tui/entry.ts +24 -7
- package/src/tui/sidetab/attach.ts +47 -0
- package/src/tui/sidetab/list.ts +45 -0
- package/src/tui/sidetab/nl-tool.ts +2 -136
- package/src/tui/sidetab/peek.ts +52 -0
- package/src/tui-client/dashboard-session.ts +3 -0
- package/src/tui-client/local-chat-turn.ts +26 -13
- package/src/tui.ts +22 -0
- package/src/user-config.ts +11 -5
- package/src/ux-sim/fake-kubectl.ts +37 -3
- package/src/ux-sim/fault-needs-human-reason-repair.ts +107 -0
- package/src/ux-sim/fault-pod-child-result.ts +42 -0
- package/src/ux-sim/fault-pool-cap.ts +11 -6
- package/src/ux-sim/mock-scenario-catalog.json +41 -0
- package/src/ux-sim/mock-scenario-catalog.ts +156 -0
- package/src/ux-sim/release-sim-rig.ts +262 -0
- package/src/ux-sim/scenario.ts +116 -23
- package/src/ux-sim/seminar-rehearsal.ts +109 -0
- package/src/ux-sim/subagent-tools-device.ts +2 -2
- package/src/version/packed-revision.json +1 -1
- package/src/voice/voice-tts-singleton.ts +18 -3
- package/src/web-search/grok.ts +5 -9
- package/apps/pwa/out/_next/static/chunks/1060.014f1f01a17aec26.js +0 -115
- package/apps/pwa/out/_next/static/chunks/1415.79821ec7ae99acdb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1452.162bb2d9d699e82a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/147.f92f63bbc5e85fa5.js +0 -286
- package/apps/pwa/out/_next/static/chunks/1661.fb425a1d8a065b72.js +0 -149
- package/apps/pwa/out/_next/static/chunks/1671-1c22b40b50b208f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1758.0204d3f48783c49d.js +0 -90
- package/apps/pwa/out/_next/static/chunks/1806.027bf78b6a94d340.js +0 -104
- package/apps/pwa/out/_next/static/chunks/2047.8f23e210995d4548.js +0 -2
- package/apps/pwa/out/_next/static/chunks/2061-7f2bbc53ae96c88b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2066.a77257c8c350770a.js +0 -23
- package/apps/pwa/out/_next/static/chunks/2096-a938496bc996dcbd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2200-9a453b8f99687d49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2281-b8cede695c6c650e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2499.02fbfd18126c10c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2522.f2766b76d22c8bdf.js +0 -34
- package/apps/pwa/out/_next/static/chunks/2615.3c9738153de3ef3f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2727.e703d99ac414916d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2752.7904b8df0254d0ae.js +0 -27
- package/apps/pwa/out/_next/static/chunks/2762-5051a862dfa40e99.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2767-31784bfedf8e245d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2774-719820e3f5b2a3eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2812-f016aa0dda149287.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2829-734fe8bf2eee7347.js +0 -1
- package/apps/pwa/out/_next/static/chunks/311-32dcac79970985e8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3175-f93f7495cd28253c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3184.8da993f5502d2140.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3231-d7c981db0ff8bdcc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3250.151309c5176d6b4b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3517.a5aa1797a9dbf2ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3567-45f676877ed9e8a5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3819.8a1de79b2e8c7298.js +0 -24
- package/apps/pwa/out/_next/static/chunks/39.9b37b0be4c128a83.js +0 -77
- package/apps/pwa/out/_next/static/chunks/4023.a19045970a9ce16a.js +0 -36
- package/apps/pwa/out/_next/static/chunks/413-24c8284bda08241d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4135.e45d6ebc29e20647.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4283.65502f5d0e6ff6c0.js +0 -287
- package/apps/pwa/out/_next/static/chunks/4305-4e28fa92a7b34a5b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4335-50efa61fd7613142.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4720.ecbd1693fa151068.js +0 -1
- package/apps/pwa/out/_next/static/chunks/483.725e4a61be922788.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4969-4b209d7307410c00.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5068-c7f53862f83c0917.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5315.bc96711c24dfefe2.js +0 -74
- package/apps/pwa/out/_next/static/chunks/5542.efbf14f16baed4dd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5622.94893f5aea7185d0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5644.8f401a25601d05fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5658.35f70eb9871a54bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5721.7bdc0e904c76ab51.js +0 -8
- package/apps/pwa/out/_next/static/chunks/5790-eef0ca20884a6bdf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5802.ef0d8598ca68fe84.js +0 -62
- package/apps/pwa/out/_next/static/chunks/5890.fb974729788dcb71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5924.5ef25cddd36d5dbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5979-e59c099ebf8d1f35.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6020.714cfeada2315e2e.js +0 -94
- package/apps/pwa/out/_next/static/chunks/6027-dfe0349d8c50f698.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6178.bc4730c86cf74536.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6273.d808b0dcb3c2b155.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6292.6ef1df64fa6a4c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/647-af8dd920ba46533c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6541-170dc5c59e135c0c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6624.981ef7caeb680560.js +0 -32
- package/apps/pwa/out/_next/static/chunks/6742-a1ad4a94ba7bc37a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6836-493dc55c39187949.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6856-53ac421c06039a18.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6908.06008c67f6380ea9.js +0 -148
- package/apps/pwa/out/_next/static/chunks/6915.096931864206f522.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6925.5e181fb7dcf90193.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6cc1d1d6.9a98dd4622f1afc6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7203-800e11037adc41eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7279.6170f591105936bb.js +0 -93
- package/apps/pwa/out/_next/static/chunks/7349-373165c7fbdcc3df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7514-a533ba0ca1cae4f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7515.1ad6efe157fa4448.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7521.d919cc1fab80c1d9.js +0 -24
- package/apps/pwa/out/_next/static/chunks/7538.90e97bbdf9178cff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7825-39203196a12579b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7869.5e1a9a12fd70d6c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8086.1ab9103c41be884f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8179-e61fffbdb70f1152.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8265.bdeb991b34db6a06.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8357.1aaa713b052a78b1.js +0 -309
- package/apps/pwa/out/_next/static/chunks/8638-e42615f2166165b5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8639-92c11d069792429e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8691.54e22831d5e8ccf8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9069.657590a84afb8fac.js +0 -41
- package/apps/pwa/out/_next/static/chunks/9125-ae588d88a0293b93.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9311.f9f641a46a508611.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9371-a6fee8378ebfa3ee.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9421-3aa9ff4fa7f63d8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9992.07ce73c10bd5e6a1.js +0 -79
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-76394dc618d322c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1b948637c37b3abd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-c63400f1bcf3e47c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-95d0957267819586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-ba957ff875a026a0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-bb6fd0714879cb87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-8f9038520d2acbef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-d4b48397e05fce2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-72d5744245dc0c27.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-37625dc8300fa43a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-fbef9d160272dbc1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-58424168df84928e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-72c12ba430ac57c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-d53f3e9ed806d7e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-4d2fbd45feee0829.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-76394dc618d322c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-8783dc0ed7590a2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-3706cb390606e930.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-9b0a86598e0111fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-4edb8df90f4537ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-47398b9ba1cc5e1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-fb86f55cfe2e681d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e22345fc8fd47b1b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-fca2e59a81744d9e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-6e96bda63100b31e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-4d17ad8e0db8cea4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-121f92766c33beaa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-43f14212c545f92d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-ccec2023e7a974ec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-b04f33f548b89a94.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-c8792823c0917021.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-8c0e32782ceef4cb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-94bd3922eb230ce8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-bca565887bafa03c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-ac1dffafe649fc6c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-52374edb5e23e455.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ad4ebca120d43ca7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-76394dc618d322c8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-718b89ce4b4ae4e4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-204eca33eeeaddec.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-e58ba9bcdb9ac0f9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-98f6dd50bafd7734.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-5a4bdd72bff27057.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-6f6ab17d44f984ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-aabb7bd15fb72da9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e4322319164a4436.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-e9efc0c8f32e2371.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-ccde99f07aaf71f7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-80464aced9614390.js +0 -1
- package/apps/pwa/out/_next/static/chunks/c696986b.5dc8bb1143af16d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/d5d3cefc-373c4eb722ed848b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/d8de0de2-dd0f4ac8e5ab1832.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-002b472a66935d31.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-49b88d47292c0476.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-fff2549cb10a133f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-32abbbba3f3b76b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-9d9cfef60c60dd7a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-cc7f483fa294034a.js +0 -1
- package/apps/pwa/out/_next/static/css/d0a48dc099ed86cf.css +0 -1
- package/scripts/ratchets/next-tui-entry-budget.ts +0 -124
- /package/apps/pwa/out/_next/static/{HjeK7zONHfR7op1hKXp4k → X1ucLw10Cds_h614NoEnh}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/** Warning-only public-copy shadow scan. It never edits source or exposes private candidate text. */
|
|
3
|
-
import {
|
|
4
|
-
import { extname, normalize, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
4
|
+
import { extname, join, normalize, relative, resolve, sep } from 'node:path';
|
|
5
5
|
import { PatentLedger } from '../src/patents/ledger.js';
|
|
6
6
|
import { checkBrandText } from './brand/check.js';
|
|
7
7
|
|
|
@@ -10,7 +10,8 @@ export type PublicCopyFinding = { source: string; line: number; rule: string };
|
|
|
10
10
|
export type PatentLineMatcher = (text: string) => number[];
|
|
11
11
|
export type PublicCopyScan = {
|
|
12
12
|
findings: PublicCopyFinding[];
|
|
13
|
-
excludedSources: Array<{ source: string; reason: 'internal-goal-document' }>;
|
|
13
|
+
excludedSources: Array<{ source: string; reason: 'internal-goal-document' | 'internal-marketing-source' | 'asset-manifest' }>;
|
|
14
|
+
documents: Array<{ source: string; findings: number; companyNameMentions: number; placeholderMentions: number }>;
|
|
14
15
|
brandRulesAvailable: boolean;
|
|
15
16
|
patentLedgerAvailable: boolean;
|
|
16
17
|
};
|
|
@@ -23,9 +24,113 @@ function normalizedLabel(label: string): string | undefined {
|
|
|
23
24
|
return normalized;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
function publicCopyExclusion(normalized: string): PublicCopyScan['excludedSources'][number]['reason'] | undefined {
|
|
28
|
+
if (normalized.startsWith('docs/goals/')) return 'internal-goal-document';
|
|
29
|
+
if (!normalized.startsWith('docs/marketing/')) return undefined;
|
|
30
|
+
const relativePath = normalized.slice('docs/marketing/'.length);
|
|
31
|
+
const basename = relativePath.split('/').at(-1) ?? '';
|
|
32
|
+
// These files are published records or release-specific source notes, not
|
|
33
|
+
// current copy candidates. Preserve their exact product names and evidence.
|
|
34
|
+
if (/^(?:BLOG-02-the-agent-drives-the-agent-|BLOG-03-it-drives-the-terminal-itself-|DRAFT-0\.2\.(?:7|8)-announcement-|DRAFT-0\.2\.7-known-issues-public-wording-|DRAFT-0\.2\.10-announcement-|PRFAQ-release-0\.2\.4-|SCRIPT-ax-demo-1008-|VO-teaser-v2-)/i.test(basename)
|
|
35
|
+
|| /^docs\/marketing\/launch\/(?:0\.2\.8-loop-final\/|0\.2\.8-rehearsal\/PRFAQ-generated\.md$|0\.2\.11-cut-)/i.test(normalized)) {
|
|
36
|
+
return 'internal-marketing-source';
|
|
37
|
+
}
|
|
38
|
+
if (normalized.startsWith('docs/marketing/') && /^(?:BRIEF-|DOCS-|EVENT-|GUIDE-|MANUAL-|MAP-|MATURITY-|PLAN-|RESEARCH-|RFC-|SCENARIO-|STORYBOARD-|STORYLINE-|STRATEGY-|SOURCES-)/i.test(basename)) {
|
|
39
|
+
return 'internal-marketing-source';
|
|
40
|
+
}
|
|
41
|
+
if (normalized.startsWith('docs/marketing/') && /^DRAFT-persona-/i.test(basename)) return 'internal-marketing-source';
|
|
42
|
+
if (/^docs\/marketing\/teaser-v\d+\/MANIFEST\.md$/i.test(normalized) || normalized === 'docs/marketing/decks/media/README.md') {
|
|
43
|
+
return 'asset-manifest';
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isRequestedPublicCopyPath(normalized: string): boolean {
|
|
49
|
+
return normalized === 'README.md' || normalized.startsWith('docs/marketing/');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function maskSpan(text: string, start: number, end: number): string {
|
|
53
|
+
return text.slice(start, end).replace(/[^\r\n]/g, ' ');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Keep line offsets while limiting brand checks to visible prose, not code, comments, metadata, or link targets. */
|
|
57
|
+
function visibleMarkdownProse(text: string): string {
|
|
58
|
+
let visible = text;
|
|
59
|
+
const masks: RegExp[] = [
|
|
60
|
+
/^---\r?\n[\s\S]*?\r?\n---\s*$/gm,
|
|
61
|
+
/<!--[\s\S]*?-->/g,
|
|
62
|
+
/^\s*(```|~~~)[^\r\n]*\r?\n[\s\S]*?^\s*\1\s*$/gm,
|
|
63
|
+
/`[^`\r\n]*`/g,
|
|
64
|
+
/\]\((?:\\.|[^)])*\)/g,
|
|
65
|
+
/^>\s*🧾\s*\*\*공개 전 할 일\*\*[\s\S]*?(?=^[^>\r\n]|$(?![\s\S]))/gm,
|
|
66
|
+
/^##\s*(?:\d+\.\s*)?(?:칸|출처 목록|sources|evidence|게시 직전 다시 잴 것|기준선|올리기 전 확인)(?:\s|$)[\s\S]*?(?=^##\s|$(?![\s\S]))/gim,
|
|
67
|
+
/^>\s*✏️\s*\*\*착지·실측된 것만\.[^\r\n]*$/gm,
|
|
68
|
+
/https?:\/\/[^\s)>]+/gi,
|
|
69
|
+
/\b(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,}(?:\/[^\s)>]*)?/g,
|
|
70
|
+
];
|
|
71
|
+
for (const pattern of masks) {
|
|
72
|
+
const matches = [...visible.matchAll(pattern)].map((match) => ({ start: match.index!, end: match.index! + match[0].length }));
|
|
73
|
+
for (const match of matches.reverse()) visible = visible.slice(0, match.start) + maskSpan(visible, match.start, match.end) + visible.slice(match.end);
|
|
74
|
+
}
|
|
75
|
+
return visible;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const PUBLIC_COMPANY_NAMES = /\b(?:OpenAI|ChatGPT|Codex|Claude(?:\s+Code)?|Anthropic|Gemini|Google|Grok|xAI|Devin|Cognition|OpenHands|Replit(?:\s+Agent(?:\s+\d+)?)?|Factory(?:\s+AI)?|Hermes|OpenClaw|Manus)\b/gi;
|
|
79
|
+
const PUBLIC_PLACEHOLDER = /\b(?:another coding agent|another agent platform)\b/gi;
|
|
80
|
+
const PUBLIC_PLACEHOLDER_LIST = /\ba coding tool\s*[,·]\s*the coding CLI\s*[,·]\s*an AI model\s*[,·]\s*a model provider\b/gi;
|
|
81
|
+
const UNSUPPORTED_COMPARISON = /\b(?:better|faster|cheaper|safer|worse|superior|inferior)\s+than\b|\b(?:vs\.?|versus|compared\s+(?:with|to))\b|(?:보다\s*(?:낫|빠르|저렴|좋|강력|안전|우수))/i;
|
|
82
|
+
|
|
83
|
+
function companyNameMentions(text: string): number {
|
|
84
|
+
return [...text.matchAll(PUBLIC_COMPANY_NAMES)].length;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function placeholderMentions(text: string): number {
|
|
88
|
+
return [...text.matchAll(PUBLIC_PLACEHOLDER)].length + [...text.matchAll(PUBLIC_PLACEHOLDER_LIST)].length;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function hasSupportedComparisonEvidence(text: string): boolean {
|
|
92
|
+
const hasSource = /\b(?:source|citation)\b|출처/i.test(text);
|
|
93
|
+
const hasMeasurement = /\b(?:measur|benchmark|tested|test conditions?)\w*\b|실측|측정|측정 조건/i.test(text);
|
|
94
|
+
return hasSource && hasMeasurement;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function measuredScopeMatchesClaim(claim: string): boolean {
|
|
98
|
+
const quantity = String.raw`(\d+(?:\.\d+)?)`;
|
|
99
|
+
const unit = String.raw`(jobs?|tasks?|workflows?|items?|건|개|작업)`;
|
|
100
|
+
const claimScope = new RegExp(String.raw`(?:completes?|processes?|handles?|완료|처리)\s*${quantity}\s*${unit}`, 'i').exec(claim);
|
|
101
|
+
const measuredScope = new RegExp(String.raw`(?:measured|measurement|실측|측정).*?(?:scope|range|범위).*?(?:limited to|only|한정|범위 내)\s*${quantity}\s*${unit}`, 'i').exec(claim);
|
|
102
|
+
if (!claimScope || !measuredScope) return false;
|
|
103
|
+
const normalizeUnit = (value: string): string => {
|
|
104
|
+
const normalized = value.toLowerCase();
|
|
105
|
+
if (normalized === 'job' || normalized === 'jobs' || normalized === 'task' || normalized === 'tasks' || normalized === 'workflow' || normalized === 'workflows' || normalized === 'item' || normalized === 'items') return normalized.replace(/s$/, '');
|
|
106
|
+
return normalized;
|
|
107
|
+
};
|
|
108
|
+
return claimScope[1] === measuredScope[1] && normalizeUnit(claimScope[2]!) === normalizeUnit(measuredScope[2]!);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function oneClickFindings(text: string, source: string): PublicCopyFinding[] {
|
|
112
|
+
const lines = text.split(/\r?\n/);
|
|
113
|
+
const findings: PublicCopyFinding[] = [];
|
|
114
|
+
for (let index = 0; index < lines.length; index++) {
|
|
115
|
+
const claims = lines[index]!.split(/(?<=[.!?。!?;;])\s*/).filter((segment) => /\bone[ -]click\b|원\s*클릭/i.test(segment));
|
|
116
|
+
for (const claim of claims) {
|
|
117
|
+
const hasLimitedClaimScope = !/(?:every|all|entire|unlimited|모든|전체|무제한)/i.test(claim)
|
|
118
|
+
&& /(?:completes?|processes?|handles?|완료|처리)/i.test(claim)
|
|
119
|
+
&& /\b\d+(?:\.\d+)?\s+(?:jobs?|tasks?|workflows?|items?)\b|\d+(?:\.\d+)?\s*(?:건|개|작업)/i.test(claim);
|
|
120
|
+
const hasMeasuredScope = hasLimitedClaimScope
|
|
121
|
+
&& measuredScopeMatchesClaim(claim)
|
|
122
|
+
&& !/(?:no|not|without|unmeasured|측정되지 않|범위 없음|범위가 없|한정되지 않)/i.test(claim);
|
|
123
|
+
if (!hasMeasuredScope) findings.push({ source, line: index + 1, rule: 'ONE-CLICK-MEASURED-SCOPE' });
|
|
124
|
+
const hasPublicationApprovalStatus = /(?:approved for publication|publication approved|publication approval pending|publication not approved|not approved for publication|unapproved|pending|게시\s*승인(?:됨|완료)?|게시\s*미승인|게시\s*승인\s*보류|게시\s*승인\s*상태\s*:\s*(?:승인|미승인|보류)|승인되지 않)/i.test(claim);
|
|
125
|
+
if (!hasPublicationApprovalStatus) findings.push({ source, line: index + 1, rule: 'ONE-CLICK-APPROVAL-STATUS' });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return findings;
|
|
129
|
+
}
|
|
130
|
+
|
|
26
131
|
function allowedNormalizedLabel(normalized: string | undefined): boolean {
|
|
27
132
|
if (!normalized) return false;
|
|
28
|
-
if (normalized === 'PR-BODY') return true;
|
|
133
|
+
if (normalized === 'PR-BODY' || normalized === 'README.md') return true;
|
|
29
134
|
if (normalized.split('/').some((part) => part === '..' || part === '')) return false;
|
|
30
135
|
if (!['.md', '.mdx', '.txt', '.html', '.htm'].includes(extname(normalized).toLowerCase())) return false;
|
|
31
136
|
return ['docs/', 'release/public/docs/', 'blog/', 'posts/', 'content/blog/', 'content/posts/']
|
|
@@ -36,30 +141,49 @@ function allowedNormalizedLabel(normalized: string | undefined): boolean {
|
|
|
36
141
|
export function scanPublicCopy(inputs: readonly PublicCopyInput[], matchPrivateCandidate: PatentLineMatcher, rulesPath?: string): PublicCopyScan {
|
|
37
142
|
const findings: PublicCopyFinding[] = [];
|
|
38
143
|
const excludedSources: PublicCopyScan['excludedSources'] = [];
|
|
144
|
+
const documents: PublicCopyScan['documents'] = [];
|
|
39
145
|
let brandRulesAvailable = true;
|
|
40
146
|
let patentLedgerAvailable = true;
|
|
41
147
|
for (const input of inputs) {
|
|
42
148
|
const label = normalizedLabel(input.label);
|
|
43
149
|
if (!allowedNormalizedLabel(label)) throw new Error(`공개 문면 입력 경로가 허용 목록 밖입니다: ${input.label}`);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (label?.startsWith('docs/goals/')) {
|
|
48
|
-
excludedSources.push({ source: input.label, reason: 'internal-goal-document' });
|
|
150
|
+
const excludedReason = label ? publicCopyExclusion(label) : undefined;
|
|
151
|
+
if (excludedReason) {
|
|
152
|
+
excludedSources.push({ source: input.label, reason: excludedReason });
|
|
49
153
|
continue;
|
|
50
154
|
}
|
|
155
|
+
const before = findings.length;
|
|
156
|
+
const prose = visibleMarkdownProse(input.text);
|
|
157
|
+
if (label && isRequestedPublicCopyPath(label)) findings.push(...oneClickFindings(prose, input.label));
|
|
51
158
|
try {
|
|
52
|
-
const brandFindings = checkBrandText(
|
|
159
|
+
const brandFindings = checkBrandText(prose, input.label, 'public-docs', rulesPath)
|
|
160
|
+
.filter((hit) => hit.id !== 'BRAND-COMPETITOR-NAME');
|
|
53
161
|
// Publication-specific promise/measurement rules apply to this explicit
|
|
54
162
|
// shadow scan without changing the existing generic brand-check scopes.
|
|
55
|
-
const publicationFindings = checkBrandText(
|
|
163
|
+
const publicationFindings = checkBrandText(prose, input.label, 'release-notes', rulesPath)
|
|
56
164
|
.filter((hit) => hit.id === 'BRAND-SOON' || hit.id === 'B4-NUMBER-SOURCE');
|
|
57
|
-
|
|
58
|
-
|
|
165
|
+
const brandLines = new Set([...brandFindings, ...publicationFindings].map((hit) => `${hit.line}:${hit.id}`));
|
|
166
|
+
for (const line of prose.split(/\r?\n/).map((text, index) => ({ text, line: index + 1 }))) {
|
|
167
|
+
if (UNSUPPORTED_COMPARISON.test(line.text) && PUBLIC_COMPANY_NAMES.test(line.text)
|
|
168
|
+
&& !hasSupportedComparisonEvidence(line.text)) {
|
|
169
|
+
brandLines.add(`${line.line}:BRAND-UNSUPPORTED-COMPARISON`);
|
|
170
|
+
}
|
|
171
|
+
PUBLIC_COMPANY_NAMES.lastIndex = 0;
|
|
172
|
+
UNSUPPORTED_COMPARISON.lastIndex = 0;
|
|
173
|
+
if (PUBLIC_PLACEHOLDER.test(line.text) || PUBLIC_PLACEHOLDER_LIST.test(line.text)) {
|
|
174
|
+
brandLines.add(`${line.line}:PUBLIC-COPY-PLACEHOLDER`);
|
|
175
|
+
}
|
|
176
|
+
PUBLIC_PLACEHOLDER.lastIndex = 0;
|
|
177
|
+
PUBLIC_PLACEHOLDER_LIST.lastIndex = 0;
|
|
178
|
+
}
|
|
179
|
+
for (const key of brandLines) {
|
|
180
|
+
const [line, rule] = key.split(':');
|
|
181
|
+
findings.push({ source: input.label, line: Number(line), rule: rule! });
|
|
59
182
|
}
|
|
60
183
|
} catch { brandRulesAvailable = false; }
|
|
61
184
|
try { for (const line of matchPrivateCandidate(input.text)) findings.push({ source: input.label, line, rule: 'PRIVATE-PATENT-CANDIDATE' }); }
|
|
62
185
|
catch { patentLedgerAvailable = false; }
|
|
186
|
+
documents.push({ source: input.label, findings: findings.length - before, companyNameMentions: companyNameMentions(prose), placeholderMentions: placeholderMentions(prose) });
|
|
63
187
|
}
|
|
64
188
|
if (inputs.length === 0 || excludedSources.length === inputs.length) {
|
|
65
189
|
// No scanner ran, so availability must not claim a successful measurement.
|
|
@@ -69,6 +193,7 @@ export function scanPublicCopy(inputs: readonly PublicCopyInput[], matchPrivateC
|
|
|
69
193
|
return {
|
|
70
194
|
findings: findings.sort((a, b) => a.source.localeCompare(b.source) || a.line - b.line || a.rule.localeCompare(b.rule)),
|
|
71
195
|
excludedSources,
|
|
196
|
+
documents,
|
|
72
197
|
brandRulesAvailable, patentLedgerAvailable,
|
|
73
198
|
};
|
|
74
199
|
}
|
|
@@ -77,6 +202,26 @@ export function lintPublicCopy(inputs: readonly PublicCopyInput[], matchPrivateC
|
|
|
77
202
|
return scanPublicCopy(inputs, matchPrivateCandidate).findings;
|
|
78
203
|
}
|
|
79
204
|
|
|
205
|
+
function collectPublicCopyFiles(paths: readonly string[], root: string): string[] {
|
|
206
|
+
const collected = new Set<string>();
|
|
207
|
+
const visit = (path: string): void => {
|
|
208
|
+
const absolute = resolve(root, path);
|
|
209
|
+
const stat = statSync(absolute);
|
|
210
|
+
if (stat.isDirectory()) {
|
|
211
|
+
for (const entry of readdirSync(absolute, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
212
|
+
if (entry.isDirectory()) visit(relative(root, join(absolute, entry.name)));
|
|
213
|
+
else if (entry.isFile() && ['.md', '.mdx', '.txt', '.html', '.htm'].includes(extname(entry.name).toLowerCase())) {
|
|
214
|
+
collected.add(relative(root, join(absolute, entry.name)));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
collected.add(relative(root, absolute));
|
|
220
|
+
};
|
|
221
|
+
for (const path of paths) visit(path);
|
|
222
|
+
return [...collected].sort();
|
|
223
|
+
}
|
|
224
|
+
|
|
80
225
|
export function run(argv: readonly string[], root = process.cwd(), ledger = new PatentLedger()): number {
|
|
81
226
|
const files: string[] = [];
|
|
82
227
|
let bodyFile: string | undefined;
|
|
@@ -97,15 +242,14 @@ export function run(argv: readonly string[], root = process.cwd(), ledger = new
|
|
|
97
242
|
} else throw new Error(`알 수 없는 옵션: ${arg}`);
|
|
98
243
|
}
|
|
99
244
|
if (!files.length && !bodyFile) throw new Error('usage: bun scripts/public-copy-lint.ts [--files <docs...>] [--pr-body-file <file> [--pr-body-label <repo-relative-label>]] [--json]');
|
|
100
|
-
const inputs: PublicCopyInput[] = files.map((file) => ({
|
|
101
|
-
label:
|
|
245
|
+
const inputs: PublicCopyInput[] = collectPublicCopyFiles(files, root).map((file) => ({
|
|
246
|
+
label: file.split(sep).join('/'), text: readFileSync(resolve(root, file), 'utf8'),
|
|
102
247
|
}));
|
|
103
248
|
if (bodyFile) inputs.push({ label: bodyLabel, text: readFileSync(resolve(root, bodyFile), 'utf8') });
|
|
104
|
-
const matchPrivateCandidate: PatentLineMatcher = (text) =>
|
|
105
|
-
|
|
106
|
-
};
|
|
249
|
+
const matchPrivateCandidate: PatentLineMatcher = (text) => ledger.unapprovedCandidateLineNumbers(text);
|
|
250
|
+
// The CLI caller uses the same scope-aware production scanner covered by focused tests.
|
|
107
251
|
const scan = scanPublicCopy(inputs, matchPrivateCandidate);
|
|
108
|
-
if (json) console.log(JSON.stringify({ mode: 'warning-only', ...scan }));
|
|
252
|
+
if (json) console.log(JSON.stringify({ mode: 'warning-only', documentsScanned: scan.documents.length, ...scan }));
|
|
109
253
|
else {
|
|
110
254
|
for (const finding of scan.findings) console.log(`${finding.source}:${finding.line}:WARN:${finding.rule}`);
|
|
111
255
|
for (const excluded of scan.excludedSources) console.log(`${excluded.source}:SKIP:${excluded.reason}`);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { acquireLockSync } from '../../src/storage/file-lock.js';
|
|
4
|
+
|
|
5
|
+
/** Deterministic recovery policy for release gate incidents. Side effects are supplied by callers. */
|
|
6
|
+
export type GateAutohealCause = 'shard-pod-death' | 'timeout' | 'host-down' | 'interference' | 'true-regression' | 'environment';
|
|
7
|
+
export type GateAutohealAction = 'reroute-shard' | 'retry-shard' | 'partial-resume' | 'host-switch' | 'hitl';
|
|
8
|
+
|
|
9
|
+
export interface GateAutohealInput {
|
|
10
|
+
cause: GateAutohealCause;
|
|
11
|
+
signature: string;
|
|
12
|
+
shardElapsedMs: number;
|
|
13
|
+
overallElapsedMs: number;
|
|
14
|
+
priorSameSignatureCount?: number;
|
|
15
|
+
shardKeys?: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface GateAutohealDecision {
|
|
19
|
+
action: GateAutohealAction;
|
|
20
|
+
cause: GateAutohealCause;
|
|
21
|
+
signature: string;
|
|
22
|
+
reason: string;
|
|
23
|
+
shardElapsedMs: number;
|
|
24
|
+
overallElapsedMs: number;
|
|
25
|
+
shardKey?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface GateResultForAutoheal {
|
|
29
|
+
outcome: 'ok' | 'regression' | 'error';
|
|
30
|
+
introduced?: readonly string[];
|
|
31
|
+
error?: string;
|
|
32
|
+
durationMs: number;
|
|
33
|
+
stalledShards?: readonly { shard: number; files: readonly string[]; reason: string; detail?: string; stall?: { sinceCheckoutMs: number } }[];
|
|
34
|
+
interferenceErrors?: readonly string[];
|
|
35
|
+
loadFlaky?: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface GateAutohealLedger {
|
|
39
|
+
schema: 1;
|
|
40
|
+
overallElapsedMs: number;
|
|
41
|
+
shards: Record<string, number>;
|
|
42
|
+
signatures: Record<string, number>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const emptyLedger = (): GateAutohealLedger => ({ schema: 1, overallElapsedMs: 0, shards: {}, signatures: {} });
|
|
46
|
+
|
|
47
|
+
/** Persist the incident budget across graph-node process restarts, keyed by release version and run id. */
|
|
48
|
+
export function decideAndRecordGateAutoheal(input: GateAutohealInput & { version: string; runId: string; shardKey: string; storeRoot: string }): GateAutohealDecision {
|
|
49
|
+
const path = join(input.storeRoot, 'release', input.version, 'gate-autoheal', `${input.runId.replace(/[^a-zA-Z0-9_.-]/g, '_')}.json`);
|
|
50
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
51
|
+
const unlock = acquireLockSync(`${path}.lock`, { breakDeadHolder: true });
|
|
52
|
+
try {
|
|
53
|
+
let ledger = emptyLedger();
|
|
54
|
+
if (existsSync(path)) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8')) as GateAutohealLedger;
|
|
57
|
+
if (parsed.schema !== 1 || !Number.isFinite(parsed.overallElapsedMs) || !parsed.shards || !parsed.signatures) throw new Error('invalid ledger');
|
|
58
|
+
ledger = parsed;
|
|
59
|
+
} catch {
|
|
60
|
+
return { action: 'hitl', cause: input.cause, signature: input.signature, reason: 'autoheal history unreadable; refusing another automatic action', shardElapsedMs: 0, overallElapsedMs: 0, shardKey: input.shardKey };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const shardKeys = [...new Set(input.shardKeys?.length ? input.shardKeys : [input.shardKey])];
|
|
64
|
+
const signatureKey = `${input.shardKey}:${input.signature}`;
|
|
65
|
+
const priorSameSignatureCount = ledger.signatures[signatureKey] ?? 0;
|
|
66
|
+
const elapsed = Math.max(0, input.shardElapsedMs);
|
|
67
|
+
const shards = { ...ledger.shards };
|
|
68
|
+
for (const key of shardKeys) shards[key] = (shards[key] ?? 0) + elapsed;
|
|
69
|
+
const shardElapsedMs = Math.max(0, ...shardKeys.map((key) => shards[key] ?? 0));
|
|
70
|
+
const overallElapsedMs = ledger.overallElapsedMs + Math.max(0, input.overallElapsedMs);
|
|
71
|
+
const decision = decideGateAutoheal({ ...input, shardElapsedMs, overallElapsedMs, priorSameSignatureCount });
|
|
72
|
+
ledger = { schema: 1, overallElapsedMs, shards,
|
|
73
|
+
signatures: { ...ledger.signatures, [signatureKey]: priorSameSignatureCount + 1 } };
|
|
74
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
75
|
+
writeFileSync(temp, `${JSON.stringify(ledger, null, 2)}\n`, { mode: 0o600 });
|
|
76
|
+
renameSync(temp, path);
|
|
77
|
+
return { ...decision, shardKey: input.shardKey };
|
|
78
|
+
} finally { unlock(); }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const GATE_AUTOHEAL_SHARD_CAP_MS = 60 * 60 * 1000;
|
|
82
|
+
export const GATE_AUTOHEAL_OVERALL_CAP_MS = 90 * 60 * 1000;
|
|
83
|
+
|
|
84
|
+
/** One signature may be acted on once; seeing it again, or crossing either timebox, stops in HITL. */
|
|
85
|
+
export function decideGateAutoheal(input: GateAutohealInput): GateAutohealDecision {
|
|
86
|
+
const shardElapsedMs = Math.max(0, input.shardElapsedMs);
|
|
87
|
+
const overallElapsedMs = Math.max(0, input.overallElapsedMs);
|
|
88
|
+
let action: GateAutohealAction;
|
|
89
|
+
let reason: string;
|
|
90
|
+
if (shardElapsedMs >= GATE_AUTOHEAL_SHARD_CAP_MS) {
|
|
91
|
+
action = 'hitl'; reason = 'per-shard timebox exceeded';
|
|
92
|
+
} else if (overallElapsedMs >= GATE_AUTOHEAL_OVERALL_CAP_MS) {
|
|
93
|
+
action = 'hitl'; reason = 'overall timebox exceeded';
|
|
94
|
+
} else if ((input.priorSameSignatureCount ?? 0) >= 1) {
|
|
95
|
+
action = 'hitl'; reason = 'same incident signature repeated';
|
|
96
|
+
} else {
|
|
97
|
+
switch (input.cause) {
|
|
98
|
+
case 'shard-pod-death': action = 'reroute-shard'; reason = 'replace failed shard Pod'; break;
|
|
99
|
+
case 'timeout': action = 'retry-shard'; reason = 'retry the timed-out shard within its budget'; break;
|
|
100
|
+
case 'host-down': action = 'host-switch'; reason = 'move the gate to a healthy host'; break;
|
|
101
|
+
case 'interference': action = 'partial-resume'; reason = 'resume only the unmeasured or affected work'; break;
|
|
102
|
+
case 'true-regression': action = 'hitl'; reason = 'measured regression requires a human decision'; break;
|
|
103
|
+
case 'environment': action = 'hitl'; reason = 'environment cause is not safe to repair automatically'; break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { action, cause: input.cause, signature: input.signature, reason, shardElapsedMs, overallElapsedMs };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function gateAutohealCause(result: GateResultForAutoheal): GateAutohealCause | undefined {
|
|
110
|
+
if (result.outcome === 'ok') return undefined;
|
|
111
|
+
if (result.outcome === 'regression' && (result.introduced?.length ?? 0) > 0) return 'true-regression';
|
|
112
|
+
if (/host.*(?:down|unreachable)|(?:down|unreachable).*host|ssh.*(?:failed|timeout)/i.test(result.error ?? '')) return 'host-down';
|
|
113
|
+
if (result.stalledShards?.some((shard) => /timeout/.test(shard.reason))) return 'timeout';
|
|
114
|
+
if ((result.interferenceErrors?.length ?? 0) > 0 || (result.loadFlaky?.length ?? 0) > 0) return 'interference';
|
|
115
|
+
if (result.stalledShards?.some((shard) => /pod.*(?:deleted|evicted|terminated|lost|not found)|(?:pod-dead|pod-evicted|pod-deleted)/i.test(`${shard.reason} ${shard.detail ?? ''}`))) return 'shard-pod-death';
|
|
116
|
+
return 'environment';
|
|
117
|
+
}
|
|
@@ -24,7 +24,7 @@ export function gateAssignableTestsAtCommit(repo: string, commit: string): strin
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface GateBaselineReceipt {
|
|
27
|
-
schema:
|
|
27
|
+
schema: 2;
|
|
28
28
|
valid: boolean;
|
|
29
29
|
reason: string;
|
|
30
30
|
version: string;
|
|
@@ -33,8 +33,35 @@ export interface GateBaselineReceipt {
|
|
|
33
33
|
recipeSha: string | null;
|
|
34
34
|
envFingerprint: string;
|
|
35
35
|
imageDigest: string | null;
|
|
36
|
+
executionIdentity: GatePodRuntimeIdentity | null;
|
|
36
37
|
files: number;
|
|
37
38
|
shards: number;
|
|
39
|
+
attributionRecheck?: { summary: number; observed: number; identified: number; baselineResult: string; targetFiles: string[] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface GatePodRuntimeIdentity {
|
|
43
|
+
bunVersion: string;
|
|
44
|
+
platform: string;
|
|
45
|
+
architecture: string;
|
|
46
|
+
envFingerprint: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const validRuntimeIdentity = (value: unknown): value is GatePodRuntimeIdentity => {
|
|
50
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
51
|
+
const row = value as Partial<GatePodRuntimeIdentity>;
|
|
52
|
+
return Object.keys(value).sort().join(',') === 'architecture,bunVersion,envFingerprint,platform'
|
|
53
|
+
&& typeof row.bunVersion === 'string' && /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(row.bunVersion)
|
|
54
|
+
&& typeof row.platform === 'string' && /^[a-z0-9_-]{1,32}$/.test(row.platform)
|
|
55
|
+
&& typeof row.architecture === 'string' && /^[a-z0-9_-]{1,32}$/.test(row.architecture)
|
|
56
|
+
&& typeof row.envFingerprint === 'string' && SHA256.test(row.envFingerprint);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Parse runtime facts written by the gate Pod before its tests start. */
|
|
60
|
+
export function parseGatePodRuntimeIdentity(text: string): GatePodRuntimeIdentity | null {
|
|
61
|
+
try {
|
|
62
|
+
const value: unknown = JSON.parse(text);
|
|
63
|
+
return validRuntimeIdentity(value) ? value : null;
|
|
64
|
+
} catch { return null; }
|
|
38
65
|
}
|
|
39
66
|
|
|
40
67
|
/** A direct hard rehearsal has no graph run state. Pin the graph and recipe bytes at its Git SHA. */
|
|
@@ -83,11 +110,18 @@ export function gateGraphPins(root: string, runId: string | undefined): { graphS
|
|
|
83
110
|
}
|
|
84
111
|
|
|
85
112
|
export function gateBaselineReceipt(input: { root: string; version: string; commit: string; fullSweep: boolean;
|
|
86
|
-
graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string
|
|
113
|
+
graphPins: { graphSha: string; recipeSha: string } | null; envFingerprint: string;
|
|
114
|
+
attributionRecheck?: { summary: number; observed: number; identified: number; baselineResult: string; targetFiles: string[] } }): GateBaselineReceipt {
|
|
87
115
|
const { root, version, commit, graphPins, envFingerprint } = input;
|
|
88
|
-
const base = { schema:
|
|
89
|
-
recipeSha: graphPins?.recipeSha ?? null, envFingerprint, imageDigest: null as string | null,
|
|
116
|
+
const base = { schema: 2 as const, version, commit, graphSha: graphPins?.graphSha ?? null,
|
|
117
|
+
recipeSha: graphPins?.recipeSha ?? null, envFingerprint, imageDigest: null as string | null,
|
|
118
|
+
executionIdentity: null as GatePodRuntimeIdentity | null, files: 0, shards: 0 };
|
|
90
119
|
const invalid = (reason: string): GateBaselineReceipt => ({ ...base, valid: false, reason });
|
|
120
|
+
if (input.attributionRecheck) {
|
|
121
|
+
const evidence = input.attributionRecheck;
|
|
122
|
+
return { ...invalid(`failure attribution unresolved after baseline recheck (${evidence.baselineResult}): summary=${evidence.summary}, observed=${evidence.observed}, identified=${evidence.identified}`),
|
|
123
|
+
attributionRecheck: { ...evidence, targetFiles: [...evidence.targetFiles].sort() } };
|
|
124
|
+
}
|
|
91
125
|
if (!input.fullSweep) return invalid('gate was not a complete clean full sweep');
|
|
92
126
|
if (!graphPins) return invalid('pinned graph or recipe SHA absent');
|
|
93
127
|
if (!SHA256.test(envFingerprint)) return invalid('gate environment fingerprint absent');
|
|
@@ -95,12 +129,14 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
|
|
|
95
129
|
const dir = join(root, 'release', version, 'gate-logs', 'cut');
|
|
96
130
|
if (!existsSync(dir)) return invalid('cut Pod logs absent');
|
|
97
131
|
const names = readdirSync(dir).filter((name) => /^pod-\d+(?:-[\w-]+)?\.json$/.test(name));
|
|
98
|
-
const roots = new Map<number, { files: string[]; shardCount: number; sweepId: string; attemptId: string }>();
|
|
99
|
-
const descendants: Array<{ name: string; commit?: string; imageDigest?: string; sweepId?: string; parentAttempt?: string; attemptId?: string }> = [];
|
|
132
|
+
const roots = new Map<number, { files: string[]; shardCount: number; sweepId: string; attemptId: string; runtimeIdentity?: GatePodRuntimeIdentity }>();
|
|
133
|
+
const descendants: Array<{ name: string; commit?: string; imageDigest?: string; sweepId?: string; parentAttempt?: string; attemptId?: string; runtimeIdentity?: GatePodRuntimeIdentity }> = [];
|
|
100
134
|
const images = new Set<string>();
|
|
135
|
+
const identities = new Map<string, GatePodRuntimeIdentity>();
|
|
101
136
|
for (const name of names) {
|
|
102
137
|
const row = JSON.parse(readFileSync(join(dir, name), 'utf8')) as
|
|
103
|
-
{ commit?: string; files?: unknown; shardCount?: number; sweepId?: string; imageDigest?: string; parentAttempt?: string; attemptId?: string };
|
|
138
|
+
{ commit?: string; files?: unknown; shardCount?: number; sweepId?: string; imageDigest?: string; parentAttempt?: string; attemptId?: string; runtimeIdentity?: unknown };
|
|
139
|
+
const runtimeIdentity = validRuntimeIdentity(row.runtimeIdentity) ? row.runtimeIdentity : undefined;
|
|
104
140
|
const match = /^pod-(\d+)\.json$/.exec(name);
|
|
105
141
|
if (match) {
|
|
106
142
|
if (row.commit !== commit) return invalid(`root cut Pod ${name} belongs to another commit`);
|
|
@@ -108,12 +144,16 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
|
|
|
108
144
|
return invalid(`root cut Pod ${name} has no file list`);
|
|
109
145
|
if (!row.attemptId) return invalid(`root cut Pod ${name} has no attempt`);
|
|
110
146
|
if (!row.sweepId) return invalid(`root cut Pod ${name} has no sweep identity`);
|
|
111
|
-
roots.set(Number(match[1]), { files: row.files as string[], shardCount: row.shardCount ?? 0,
|
|
147
|
+
roots.set(Number(match[1]), { files: row.files as string[], shardCount: row.shardCount ?? 0,
|
|
148
|
+
sweepId: row.sweepId, attemptId: row.attemptId, runtimeIdentity });
|
|
112
149
|
} else descendants.push({ name, commit: row.commit, imageDigest: row.imageDigest, sweepId: row.sweepId,
|
|
113
|
-
parentAttempt: row.parentAttempt, attemptId: row.attemptId });
|
|
150
|
+
parentAttempt: row.parentAttempt, attemptId: row.attemptId, runtimeIdentity });
|
|
114
151
|
if (match) {
|
|
115
152
|
if (!row.imageDigest || !IMAGE_DIGEST.test(row.imageDigest)) return invalid(`actual Pod image digest absent: ${name}`);
|
|
153
|
+
if (!runtimeIdentity) return invalid(`actual Pod runtime identity absent: ${name}`);
|
|
154
|
+
if (runtimeIdentity.envFingerprint !== envFingerprint) return invalid(`actual Pod environment differs: ${name}`);
|
|
116
155
|
images.add(row.imageDigest.toLowerCase());
|
|
156
|
+
identities.set(JSON.stringify(runtimeIdentity), runtimeIdentity);
|
|
117
157
|
}
|
|
118
158
|
}
|
|
119
159
|
if (!roots.size) return invalid('root cut shards absent');
|
|
@@ -137,7 +177,10 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
|
|
|
137
177
|
if (row.sweepId !== sweepId) return invalid(`cut Pod ${row.name} belongs to another sweep`);
|
|
138
178
|
if (!row.imageDigest || !IMAGE_DIGEST.test(row.imageDigest)) return invalid(`actual Pod image digest absent: ${row.name}`);
|
|
139
179
|
if (!row.attemptId) return invalid(`cut Pod ${row.name} has no attempt`);
|
|
180
|
+
if (!row.runtimeIdentity) return invalid(`actual Pod runtime identity absent: ${row.name}`);
|
|
181
|
+
if (row.runtimeIdentity.envFingerprint !== envFingerprint) return invalid(`actual Pod environment differs: ${row.name}`);
|
|
140
182
|
images.add(row.imageDigest.toLowerCase());
|
|
183
|
+
identities.set(JSON.stringify(row.runtimeIdentity), row.runtimeIdentity);
|
|
141
184
|
attempts.add(row.attemptId);
|
|
142
185
|
}
|
|
143
186
|
pending = next;
|
|
@@ -150,6 +193,8 @@ export function gateBaselineReceipt(input: { root: string; version: string; comm
|
|
|
150
193
|
if (row.commit === commit && !row.sweepId) return invalid(`cut Pod ${row.name} has no sweep identity`);
|
|
151
194
|
}
|
|
152
195
|
if (images.size !== 1) return invalid('cut Pods used different image digests');
|
|
153
|
-
|
|
196
|
+
if (identities.size !== 1) return invalid('cut Pods used different runtime identities');
|
|
197
|
+
return { ...base, valid: true, reason: 'complete clean full sweep', imageDigest: [...images][0]!,
|
|
198
|
+
executionIdentity: identities.values().next().value!, files: files.length, shards: count };
|
|
154
199
|
} catch (error) { return invalid(`cut Pod evidence unreadable: ${String(error)}`); }
|
|
155
200
|
}
|
|
@@ -4,9 +4,28 @@ export interface FailureDiff {
|
|
|
4
4
|
common: string[];
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
/** One failure identity may be printed more than once when a host retries or concatenates a test run. */
|
|
8
|
+
export function collapseGateFailureAttempts(ids: readonly string[]): { failures: string[]; observed: number; collapsed: number } {
|
|
9
|
+
const failures = [...new Set(ids)].sort();
|
|
10
|
+
return { failures, observed: ids.length, collapsed: ids.length - failures.length };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class GateFailureAttributionMismatch extends Error {
|
|
14
|
+
constructor(readonly label: string, readonly summary: number, readonly observed: number,
|
|
15
|
+
readonly identified: number, readonly collapsed: number, readonly failures: readonly string[]) {
|
|
16
|
+
super(`${label} incomplete (failure attribution mismatch: summary=${summary}, observed=${observed}, identified=${identified})`);
|
|
17
|
+
this.name = 'GateFailureAttributionMismatch';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
/** Bun's file header followed by `(fail) name` lines is the identity source. */
|
|
8
22
|
export function parseFailures(output: string): string[] {
|
|
9
|
-
|
|
23
|
+
return [...new Set(parseFailureAttempts(output))].sort();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Raw reporter rows, retaining repeated identities so gate attribution can distinguish retries from missing names. */
|
|
27
|
+
export function parseFailureAttempts(output: string): string[] {
|
|
28
|
+
const failures: string[] = [];
|
|
10
29
|
let file: string | undefined;
|
|
11
30
|
for (const raw of output.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '').split(/\r?\n/)) {
|
|
12
31
|
const line = raw.trim();
|
|
@@ -14,9 +33,9 @@ export function parseFailures(output: string): string[] {
|
|
|
14
33
|
if (header) { file = header[1]; continue; }
|
|
15
34
|
// Line-start only: a title/body `(fail)` is not a reporter row. Trailing CR is still a real Bun fail row.
|
|
16
35
|
const failed = /^\(fail\)[ \t]+(.+?)(?:[ \t]+\[[\d.]+(?:ms|s)\])?[ \t]*\r?$/.exec(raw);
|
|
17
|
-
if (failed && file) failures.
|
|
36
|
+
if (failed && file) failures.push(`${file} > ${failed[1]!.trim()}`);
|
|
18
37
|
}
|
|
19
|
-
return
|
|
38
|
+
return failures;
|
|
20
39
|
}
|
|
21
40
|
|
|
22
41
|
/** GATE-INTRO-RECHECK (0.2.19) — the failures that are bun's «timed out» shape: a `(fail) name` line whose next line is
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/** A gate run may execute only against one immutable image identity and its exact source tag. */
|
|
5
|
+
export interface GateImagePin { runId: string; digest: string; image: string; commit: string }
|
|
6
|
+
|
|
7
|
+
export type GateImagePinResolution =
|
|
8
|
+
| { ok: true; digest: string; eligiblePools: string[] }
|
|
9
|
+
| { ok: false; reason: 'saved-image-digest-invalid' | 'initial-image-digest-invalid' | 'initial-image-digest-missing' | 'pinned-image-unavailable' };
|
|
10
|
+
|
|
11
|
+
export interface ResolveGateImagePinOptions {
|
|
12
|
+
/** Persisted at the run boundary; takes precedence over any newly probed default. */
|
|
13
|
+
savedDigest?: string | null;
|
|
14
|
+
/** Used only to create the pin for a new run. */
|
|
15
|
+
initialDigest?: string | null;
|
|
16
|
+
/** Exact immutable digest selected by the gate image probe in each pool. */
|
|
17
|
+
poolImages: ReadonlyMap<string, readonly string[]>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeGateImageDigest(value: unknown): string | null {
|
|
21
|
+
if (typeof value !== 'string' || !/^sha256:[a-f0-9]{64}$/i.test(value.trim())) return null;
|
|
22
|
+
return value.trim().toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the durable run pin and fail closed unless at least one pool can prove it has that exact image.
|
|
27
|
+
* Callers must filter placement to `eligiblePools`; a mutable tag is never a substitute.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveGateImagePin(options: ResolveGateImagePinOptions): GateImagePinResolution {
|
|
30
|
+
const hasSaved = options.savedDigest !== undefined && options.savedDigest !== null;
|
|
31
|
+
const rawDigest = hasSaved ? options.savedDigest : options.initialDigest;
|
|
32
|
+
if (!hasSaved && (rawDigest === undefined || rawDigest === null || rawDigest.trim() === '')) {
|
|
33
|
+
return { ok: false, reason: 'initial-image-digest-missing' };
|
|
34
|
+
}
|
|
35
|
+
const digest = normalizeGateImageDigest(rawDigest);
|
|
36
|
+
if (!digest) return { ok: false, reason: hasSaved ? 'saved-image-digest-invalid' : 'initial-image-digest-invalid' };
|
|
37
|
+
const eligiblePools = [...options.poolImages]
|
|
38
|
+
.filter(([, images]) => images.some((image) => normalizeGateImageDigest(image) === digest))
|
|
39
|
+
.map(([pool]) => pool);
|
|
40
|
+
if (!eligiblePools.length) return { ok: false, reason: 'pinned-image-unavailable' };
|
|
41
|
+
return { ok: true, digest, eligiblePools };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The pin is immutable for one graph gate run; absence on resume is a hard refusal. */
|
|
45
|
+
export function readGateImagePin(path: string, expectedRunId: string, expectedImage: string, expectedCommit: string): GateImagePin | null {
|
|
46
|
+
if (!existsSync(path)) return null;
|
|
47
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8')) as { version?: unknown; runId?: unknown; digest?: unknown; image?: unknown; commit?: unknown };
|
|
48
|
+
const normalizedCommit = expectedCommit.toLowerCase();
|
|
49
|
+
if (parsed.version !== 2 || parsed.runId !== expectedRunId || parsed.image !== expectedImage || parsed.commit !== normalizedCommit
|
|
50
|
+
|| !normalizeGateImageDigest(parsed.digest))
|
|
51
|
+
throw new Error(`invalid gate image pin: ${path}`);
|
|
52
|
+
return { runId: expectedRunId, digest: normalizeGateImageDigest(parsed.digest)!, image: expectedImage, commit: normalizedCommit };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function writeGateImagePin(path: string, runId: string, digest: string, image: string, commit: string): void {
|
|
56
|
+
const normalized = normalizeGateImageDigest(digest);
|
|
57
|
+
if (!runId || !normalized || !image.trim() || !/^[0-9a-f]{40}$/i.test(commit)) throw new Error('invalid gate image pin identity');
|
|
58
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
59
|
+
const value = JSON.stringify({ version: 2, runId, digest: normalized, image, commit: commit.toLowerCase() });
|
|
60
|
+
if (existsSync(path)) {
|
|
61
|
+
const prior = JSON.parse(readFileSync(path, 'utf8')) as { runId?: unknown; digest?: unknown; image?: unknown; commit?: unknown };
|
|
62
|
+
if (prior.runId !== runId || normalizeGateImageDigest(prior.digest) !== normalized || prior.image !== image
|
|
63
|
+
|| prior.commit !== commit.toLowerCase())
|
|
64
|
+
throw new Error(`gate image pin is immutable: ${path}`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
try { writeFileSync(path, `${value}\n`, { mode: 0o600, flag: 'wx' }); }
|
|
68
|
+
catch (error) {
|
|
69
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
70
|
+
const prior = JSON.parse(readFileSync(path, 'utf8')) as { runId?: unknown; digest?: unknown; image?: unknown; commit?: unknown };
|
|
71
|
+
if (prior.runId === runId && normalizeGateImageDigest(prior.digest) === normalized && prior.image === image
|
|
72
|
+
&& prior.commit === commit.toLowerCase()) return;
|
|
73
|
+
throw new Error(`gate image pin is immutable: ${path}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function gateImagePinPath(root: string, version: string, runId: string): string {
|
|
78
|
+
if (!/^\d+\.\d+\.\d+(?:-rc\.\d+)?$/.test(version) || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(runId))
|
|
79
|
+
throw new Error('invalid gate image pin path identity');
|
|
80
|
+
return `${root.replace(/\/$/, '')}/release/${version}/gate-image-pin-${runId}.json`;
|
|
81
|
+
}
|