@xgent-ai/skills 0.7.4 → 0.7.6
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/.claude/hooks/xgent-statusline.js +32 -18
- package/README.md +65 -37
- package/bin/phoenix-diagram.js +14 -0
- package/bin/xgent-skills.js +259 -243
- package/dist/phoenix-diagram/LICENSE +21 -0
- package/dist/phoenix-diagram/NOTICE.md +34 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/005fc765ddc5115da796cca915baa9557abae13ff35e0a47c47affc56f6c414d.txt +272 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/010492e490b92ec85db01daad2a290ea5959f29314a1c4ff25e7a172e223abf9.txt +18 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt +3 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0218327e7a480793ffdd4eb792379a9709e5c135c7ba267f709d6f6d4d70af0a.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/02bb8c3b4e70190e3986c0404ad2fd8d639b4f534252d82379cc1b502b6d1812.txt +191 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0444c6991eead6822f7b9102e654448d51624431119546492e8b231db42c48bb.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0552c898248eadfc130df0c0e8f6f82776fcb3f1dd5a5af2fa991b5baff9367a.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0ab4d106b6faac07fb6a051815fd1b4d862d730895e2d7d7358c2f13565e7a38.txt +20 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0d3f7c086c6b6cf3ea4aac714e9aa3e1fb02e355ce35bd21c56acf8d04390885.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/117829c3ca21efb132d81a44b55363d395ab8eea18526873bc828da4c0e5f038.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/1a2f5c12ddc934d58956aa5dbdd3255fe55fd957633ab7d0d39e4f0daa73f7df.txt +26 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5.txt +373 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/1f485f0d6125658861351e3d6084ea90fefe78f89450be9915044b7c56d5a75a.txt +15 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b.txt +26 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/20c7855c364d57ea4c97889a5e8d98470a9952dade37bd9248b9a54431670e5e.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/20fe7b00e904ed690e3b9fd6073784d3fc428141dbd10b81c01fd143d0797f58.txt +176 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/219920e865eee70b7dcfc948a86b099e7f4fe2de01bcca2ca9a20c0a033f2b59.txt +7 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d.txt +7 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt +23 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5.txt +220 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/28a9529c7d0bb4dc51f4bf5c116a3d16ef247a052f7591466768ddf563fd1cf5.txt +428 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/29e9fe5074bd27e0e5d5d110394fbbcd841baee2651a3c4b4560a632702cede4.txt +26 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652.txt +23 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/3521672491a3479422d5fe1aca6645dd2984090f85da6e5205abfb18fb7a6897.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab.txt +176 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/36516aefdc84c5d5a1e7485425913a22dbda69eb1930c5e84d6ae4972b5194b9.txt +23 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/375bdbf54d6584debb4bcc5bce1c9dcfdd581f977c9a8469113dbdf208f4c927.txt +131 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/391a5396cec6230bfabd4ef4eb2350eb895bc5efce377a2218f5702ed020d3e3.txt +20 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/39712155836e5c10ef1b07037b606e4ad2d633e72205d162fdd7e2290aa54f15.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/3bb1e548250b3acc43febde65a5cdba9f2e0a3288018aa41f009a3c59ffab51a.txt +390 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/3bc70e239e91272782006c638fc0452a714d384224f11f0923036b7be07cf9b5.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/3f3d9e0024b1921b067d6f7f88deb4a60cbe7a78e76c64e3f1d7fc3b779b9d04.txt +373 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/3f941b3b89cf7b8370ceb83cc76d2120d471b58735d8ca60238a751a48d7f72f.txt +359 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/42fa16951ce7f24b5a467a40e5b449a1d41e662f97ca779864f053f39e097737.txt +26 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/451167c55c0fa447cc2d5632714f5e3c567fe4f1e1badefab2c1333852198aca.txt +542 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/45f522cacecb1023856e46df79ca625dfc550c94910078bd8aec6e02880b3d42.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/45fd05c4865e7c350b98ad7ac50e1b15462d49af4a91e9b0c9dd933dc9a69742.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/4789210f4df8abc03fce1cddd9c2ba517f184840687f2815d741de282c1aa93a.txt +228 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/4cada0bd02ea3692eee6f16400d86c6508bbd3bafb2b65fed0419f36d4f83e8f.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/516b24e051bf5630880ebbd55c40a25ce9552ebaf8970a53e8976eb70e522406.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583.txt +13 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/5757a1f28eff735a8b5e7425478f367812d71e974530f5bedd01219480965f4a.txt +51 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/5b698ca13897be3afdb7174256fa1574f8c6892b8bea1a66dd6469d3fe27885a.txt +19 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/5e05b024f653a5ce199e77cbbbd42fb5553562ec714b819421ed0c3e552a75d7.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/60a7062291b01ba068f300612cdbdc20382ac1d4934f07bcdd7167c15299f309.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt +176 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/65f94e99ddaf4f5d1782a6dae23f35d4293a9a01444a13135a6887017d353cee.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51.txt +187 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/709e3175b4212f7b13aa93971c9f62ff8c69ec45ad8c6532a7e0c41d7a7d6f8c.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/71674605ec4c087fe9eb534e3e4f9e26eb2e4aabcd76a29fd156c6a844d44b3d.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7abd9b6960dcf7d4d0a48606a5b71bfe37d472db68d70637f3a58a56785f1621.txt +13 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7ca6700600dfa9c9497bf5556365067daa802c871ea78239f129309c7a2048f7.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7cfafc877eccc46c0e346ccbaa5c51bb6b894d2b818e617d970211e232785ad4.txt +15 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c.txt +24 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7e5886959f67f8c75063a9d55cd3cb08c5a8cbea9a944fa1df2d4ae038f53721.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/7fea0ee51a4ca5d5cea7464135fd55e8b09caf3a61da3d451ac8a22af95c033f.txt +20 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/83c1763356e822adde0a2cae748d938a73fdc263849ccff6b27776dff213bd32.txt +24 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/86dd7f026f916daf7511e39951ad8ea8cf55a8db67ae64060dacf829761c18f3.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/8a35369f3ca263b3c62fbb5032947e53b6bfebc6c8a4d1bb982de1c069f6fba5.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/8b43ce8accd61e9d370b5ca9e9c4f953279b5c239926c62315b40e24df51b726.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/8bb1b50b0e5c9399ae33bd35fab2769010fa6c14e8860c729a52295d84896b7a.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/8c7516d4b27b1e495be5e38b612298b63de48d05f49cdac94f70f3cd70f8864b.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9046848b63a5c92bff14e4accca80bd987e0623b74adf9226ce5198d312b79d5.txt +30 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5.txt +12 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9117d922e667125508dde62b02c1f57ed22f5ad21eb536aa2e2d99e1c796e639.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9387c2d197e2370a740ca457dae1662c56de56b811ee4862024343961307f9bf.txt +19 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/946c9835d8034d24404f8cfec5f4654cee5dad17e944afc3d06d742cf2882831.txt +240 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/95bd3988beee069fa2848f648dab43cc6e0b2add2ad6bcb17360caf749802bcc.txt +176 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9ba9550ad48438d0836ddab3da480b3b69ffa0aac7b7878b5a0039e7ab429411.txt +396 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9d185ac6703c4b0453974c0d85e9eee43e6941009296bb1f5eb0b54e2329e9f3.txt +202 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9e0dfd2dd4173a530e238cb6adb37aa78c34c6bc7444e0e10c1ab5d8881f63ba.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9f2ae19ef94ba82887729a4a1617ff25eede56611dd21a4b2c6f797df30fca33.txt +2013 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/9fa38f610a00115dd1f612bdd3559e3b381ea579538b6ff25a5899da250a5fce.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/a00511478c83ebb464ef19d840f061c7444adc4636bb561900e75c5808cfc6cc.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499.txt +121 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/a5dea80c1f383cb5f80a6bb0da5e55a2beb9f24adb123ce6300af2cbaaa3bf65.txt +19 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/a825bd853ab71619a4923d7b4311221427848070ff44d990da39b0b274c1683f.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/ae9baa7beea910273c2f384c2a6b721fb7bd02bda3436074a1072e4ee689f985.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744.txt +19 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/b38f11f6096706e6de553dabe2a7ed142d59b6fa8c97e290c67496154745cdd5.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/b3d734001a94efff3579978d953391aa7115f877657d25eb54037a43875d078a.txt +9 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/b4eb00df6e2a4d22518fcaa6a2b4646f249b3a3c9814509b22bd2091f1392ff1.txt +27 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/b68ad1a3367b825447089e1f8d6829b97f47a89eb78d2f4ebaef4672f5606186.txt +22 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/b7e650f3fce5c53249d1cdc608b54df156a97edd636cf9d23498d0cfe7aec63e.txt +19 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c09aae9d3c77b531f56351a9947bc7446511d6b025b3255312d3e3442a9a7583.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt +21 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c30152c94a6d75e021adbc52b3a52470366a46edb917e17deae3259251af244c.txt +42 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c394e3bda1f3b9378f30a7cab11242887eeeafc73c7bef36a0fe13e09691e18b.txt +17 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt +201 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c962ee4d1d05ddc138b202b2540219ebc57893fcf97b364852094a9a94ce1365.txt +7 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt +202 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/d1fc1bc0d155df60b2e7705b6b2ae02a05c96f948e1cec6e2fb86360b09f346b.txt +29 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/d5c22aa3118d240e877ad41c5d9fa232f9c77d757d4aac0c2f943afc0a95e0ef.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/d5fca2653c88ad02d197faead77e22b80c70636b5bec6e02f2805f389a21b86d.txt +10 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/db11fec9946737df39ca3898d9cd8c10ec6f6c3a884a6802b0ad0b81b4e8f23a.txt +1 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/dc91f8200e4b2a1f9261035d4c18c33c246911a6c0f7b543d75347e61b249cff.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/ddea4e94a1c35ed9826e54e13a5b50b8f81b21665a00ca07c8861f1859e56b6b.txt +14 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/e271993808fec50ab29350b39539cdec611a9103f827e0aa26d61da70e2d33f8.txt +61 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/e66c269d4819aaab34b49ef5220c4ddab6756f21bb5180761a4eb8561f2b7bbd.txt +438 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/efcfee7981ff72431fffb06925cad00a23dce079ed4354f61030ad5abdb78829.txt +22 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/f025ccfb7dfb6bdfedc75ca0f67acc69e6fb4998143d834f7c2f38a29989680f.txt +13 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt +46 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/f7715d38a3fa1b4ac97c5729740752505a39cb92ee83ab5b102aeb5eaa7cdea4.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1.txt +39 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/f9c4c77baa3828004ee54b8a4f2db2e88ed44a6237a493965bf551fac0fcb62d.txt +17 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85.txt +373 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2.txt +25 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/ff92bed461f50338dd703a9ba9aee496a425957873df1d91192776e4bdf5dda7.txt +74 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/COPYRIGHT-library.html +29357 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/Apache-2.0.txt +73 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/BSD-2-Clause.txt +9 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/CC-BY-SA-4.0.txt +427 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/GCC-exception-3.1.txt +30 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/GPL-2.0-only.txt +133 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/GPL-3.0-or-later.txt +202 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/ISC.txt +7 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/LLVM-exception.txt +15 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/MIT.txt +9 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/NCSA.txt +29 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/OFL-1.1.txt +43 -0
- package/dist/phoenix-diagram/THIRD-PARTY-LICENSES/rust-std-1.99.1/licenses/Unicode-3.0.txt +39 -0
- package/dist/phoenix-diagram/THIRD-PARTY-NOTICES.md +210 -0
- package/dist/phoenix-diagram/THIRD-PARTY.json +5459 -0
- package/dist/phoenix-diagram/VERSION.json +39 -0
- package/dist/phoenix-diagram/bundle/ENGINE.json +39 -0
- package/dist/phoenix-diagram/bundle/SKILL.md +109 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.architecture.json +11 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.class.json +23 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.dataflow.json +11 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.erd.json +25 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.lifecycle.json +11 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.sequence.json +13 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.timeline.json +15 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.tree.json +13 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.waterfall.json +12 -0
- package/dist/phoenix-diagram/bundle/examples/minimal.workflow.json +12 -0
- package/dist/phoenix-diagram/bundle/manifest.json +253 -0
- package/dist/phoenix-diagram/bundle/references/architecture-compare.md +111 -0
- package/dist/phoenix-diagram/bundle/references/authoring-defaults.md +42 -0
- package/dist/phoenix-diagram/bundle/references/brand-marks.md +52 -0
- package/dist/phoenix-diagram/bundle/references/compatibility.md +22 -0
- package/dist/phoenix-diagram/bundle/references/delivery-contract.md +127 -0
- package/dist/phoenix-diagram/bundle/references/extending-types.md +14 -0
- package/dist/phoenix-diagram/bundle/references/migration.md +57 -0
- package/dist/phoenix-diagram/bundle/references/preview.md +45 -0
- package/dist/phoenix-diagram/bundle/references/repository-authoring.md +29 -0
- package/dist/phoenix-diagram/bundle/references/viewer-runtime.md +27 -0
- package/dist/phoenix-diagram/bundle/schemas/README.md +23 -0
- package/dist/phoenix-diagram/bundle/schemas/architecture.schema.json +329 -0
- package/dist/phoenix-diagram/bundle/schemas/class.schema.json +348 -0
- package/dist/phoenix-diagram/bundle/schemas/common.schema.json +358 -0
- package/{skills/archify → dist/phoenix-diagram/bundle}/schemas/dataflow.schema.json +36 -8
- package/dist/phoenix-diagram/bundle/schemas/erd.schema.json +384 -0
- package/dist/phoenix-diagram/bundle/schemas/lifecycle.schema.json +202 -0
- package/{skills/archify → dist/phoenix-diagram/bundle}/schemas/sequence.schema.json +41 -10
- package/dist/phoenix-diagram/bundle/schemas/timeline.schema.json +170 -0
- package/dist/phoenix-diagram/bundle/schemas/tree.schema.json +144 -0
- package/dist/phoenix-diagram/bundle/schemas/waterfall.schema.json +150 -0
- package/{skills/archify → dist/phoenix-diagram/bundle}/schemas/workflow.schema.json +42 -10
- package/dist/phoenix-diagram/bundle/scripts/phoenix-bootstrap.cjs +589 -0
- package/dist/phoenix-diagram/bundle/scripts/phoenix-diagram +6 -0
- package/dist/phoenix-diagram/bundle/scripts/phoenix-launcher.cjs +10 -0
- package/dist/phoenix-ui/THIRD-PARTY.json +164 -158
- package/dist/phoenix-ui/VERSION.json +17 -17
- package/dist/phoenix-ui/bundle/blobs/00727f7b333a7be8ac7740aa257a3a3f691db8ae545a6afab2b787acab5e88df +46 -0
- package/dist/phoenix-ui/bundle/blobs/{8b1723fb51e6aad9bfc0bc5ba8e09b5a373b0e33c251331f6ca14ff6b1c261ef → 00f4a6a630f31eb05fbdd5725e4a294233d24574bd70f4f32f4597d8256a7c1c} +2 -5
- package/dist/phoenix-ui/bundle/blobs/{41400e182e2180a567923d58bfa935d2799a54f2a747b2c5813c3884c84f768b → 04057592e18ea9dd3c4edcac14cde3fa5b75239c2552d4459db8e047068f61a8} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{34571f7d46632205d518d15cb008457494dfad745c6b483bb11d63ca3a22ac48 → 0cb3bf26e1980b0e530373fb32edbe5864a171492a04e2abe1952ffe78bff231} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{3133bec6998506bf92baeeabba39438c5ee27aa3330c2e6245f9df216fbe7b2d → 0e684572eb98d24b39cd70fa23e66c163178f5a11756dcf8a98fe2335636b33f} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{d17b3a454146e74ef4ecf69d7b72a8fa45c4fe1c695dffb0d36690b653cd2d8f → 10553c45c7bf38e527a57a281c4365152a768b4dd7ef597ff34ae841e9ff7d41} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{b544da38492c15f5295053b64189a9ec5ebe446c77138ac52ca7bd9da9c0e842 → 125bc7030d7f2ba789d7b1d5f474af34f8902b764e7bc351b98f1f6f55779929} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{74dbcbbad6c3831e95abb38775dd488c59ae9f1d44a95f5fd4bc0051577488fa → 13cf6c054ed424e2eb639ea12d20a4aa2b1c85eeb00afa8cbd8a30c6c22aadd8} +1 -1
- package/dist/phoenix-ui/bundle/blobs/1832ff9624cebd30370eb0e806665c656ac879215f93d33bcc0750baa47459dc +46 -0
- package/dist/phoenix-ui/bundle/blobs/{8e7de5ca347036a07f5f1e9cab942d8ff378d029bc101fa6d126406f3298c9db → 1864a10c29e7062cca5e49d1790dc7d9d8ea777bfa2ab6fd941635965cd0084d} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{784722b72224a7005a80cdcd6408dd2b25b23fd9619d0f6c8788f7ac681fad4c → 1ecc81de39d28b85b95e797bcc338d4213a50231c3315a741d14571fd22c844f} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{b8c614dc7c2eb30ee0f864fec116dbcafb39e5a199dce66d0c1a88b13f76265a → 2079e4146039a8dadf19a39237cbdc0b8b54ea1fa16d320716c2cbf58161c835} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{dba6e4a09c5d752e5ddebe7c2c1aefc9c4b2a84e01a3178599579778ed2d09e9 → 2813aa68c0804a7fe061d88201d8a684cac9987b48511e1e2cbfe0a869a67a82} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{2889c2817b5079abff254bb6d13f92dbde0fabc38be4acc7d20e613eef5e5829 → 2873e340be947c83c3d03520139b73524fd166a34063d2693d3b711021ea5b87} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{64c4aa2d1c24ae5028e987498ac544020d48600a51d9654b2df562681ba480f2 → 298ef41899acefa056eccf2345378078832db6d868d2377f471f896d5c1599f9} +6 -14
- package/dist/phoenix-ui/bundle/blobs/{1b7174a6f3f49a8efc8f7fa74ff84cf8960e2646b9bc976e4129a7c8ad608faa → 2b5cef2a14a5b1256ec6de9172a4f57f227948ced1921ef86e899f48524d7fb8} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{f148f630f43f3c91cf10feeda5a3c21448ca0683ce82d0fe763c1b43dee584d6 → 2d376cbbf7ec1e262614aac4f1b200e8f60e86e940c7137a2a26788bc857e509} +6 -7
- package/dist/phoenix-ui/bundle/blobs/2e5985a6eb339d70cfa25f6d2b420a0ef286caa8467f0226eb841702ec438d0b +46 -0
- package/dist/phoenix-ui/bundle/blobs/{7e199833376de5d2b6e088ee2965b448f94da842f28ccbd707e1cc3a295147c8 → 3322d691bf7b578b38723576df6b266993b2afb9e567b0cd1ded898ffe1dc17e} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{6530364f99c98b9ae9a8f9f3d34866a6393a75e75db48d8c0cbb49dd93a69f81 → 3625c5c2750f117021232ce81c1e39ac5ef7c21445729327b88cf54dfc937df0} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{335ccd78685b679dc0c880fc46139d0fa94aebcfa6e5ff848fd0599b187d25c4 → 36310febe3e7bd4c9b21407603f2a04afefdc87b7821758bb47098b1b2876765} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{24d92a176573768bfffaf8f9c6483e58bceb5bf2a095d1cdf3d3a2c0bcda0707 → 3a9c2daf4ef284ca83b0e7f6fef3a5300e273c32b20769e090463cb8a8b0b06f} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{153e377c44b162cf0074698a61716db7f664db78f5635efa926bed484de4f54c → 3ce1a44efe39b93e7a39307c6a5e5f514e938fbae818e358986b4db1b05883ca} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{ad8ad0b7acf6c88ef9ce0ee1a640c2200da77821f0d97d605d4f1d4a10e5897a → 403fcde498d75abec7844ee31434201152ac229f7675772164d69563f0572da4} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{e8ee6c08e94b80324173d89d6f4cdc5b4829ea6858b3a11fc186588a490cada4 → 45d0e7eff0923c40296e971318756617944c4f1c97f03126cd74c0f8c629f8a4} +3 -1
- package/dist/phoenix-ui/bundle/blobs/{e23dabf908842be08751fc4e7f3b586e8c6b1ca0fc30303ec5317c77f83dcf26 → 4c0f396bfa85171f61972d3f3765733318769bb53fe06d2cdcb5d7f64578b5b8} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{a5517d32c1539c388c47c00879a0d807ed2989647bfa8c0844ef22328f75f5be → 4f6563876e0e0cc51c38a06767b8800eb3238611e9fdd11e5ff9dd487c742e74} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{a9d2203acd45ca33a13d5c68b02b23ed512425ac15f0e8438318ed124b43729d → 4fcada229d0c689c1a493d13184145a81ff40c498e882142ae390bda8e5b2492} +15 -0
- package/dist/phoenix-ui/bundle/blobs/5293adc55cf66068b6df20468ad9e91433fd1c48e75d66c86f874aca951a8fb9 +46 -0
- package/dist/phoenix-ui/bundle/blobs/{96e2e6bd4fcf9a2c6da65fb029f96d9176308fae2efd9d8653d3b8d838960e07 → 5436f0301ec258f3badcc6842019ee29a9b870d2727dc425fbc9cc0f4f88a284} +3 -1
- package/dist/phoenix-ui/bundle/blobs/{d0f4939e86a62b0ca437d51a997ceba7e77d64566b7fa8ca2747487e31ca819d → 54a09a4afdffca3cc135e5007716ea684dc1ed7a78349de859225c55a2fed810} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{d9f1d8e01b40a8d7310e367f7e3da7d1fac5850313082feb853093b7eb36d2bd → 55277fa15eb4bd2f09bf7bb24c4e79df417b06654b1e10f502c7928b192fd7cc} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{3759780bd25526f7eaa3793c010356bccb850d40db931e0c6d47d35e81e96f96 → 567c3f42394f9aee9b048a001016a7ba9d8773d78027f8c995c379886634e719} +6 -3
- package/dist/phoenix-ui/bundle/blobs/57c8f33a7401f918566b8a4883871a69ef2223438c51f158c67f69e3250be465 +46 -0
- package/dist/phoenix-ui/bundle/blobs/{5f5fc6bb04605feb7ac6f6b19e35ff2f8d39e11a89fdd260159c6829f27b4a4d → 61450dbef64859e3136cd17ae3e997134d5df5721d96985c4e07367f38522129} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{e3c1e925be0b2deb0e3765b83ab94b86560292364f275e98bb23a8bdf2c2e049 → 656bbfda3e72b40d53b5832293dc8700ad8a784e804a6246271bf0ea23527822} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{b49cdd7f125d9cb5ce10de7d39c5e3daff7d6649b9ac1c5ffd36a0aebdc4e7ca → 68b454a99ba72c3b9b93ed96c4267847297169f6a5bf321b20e60d637c271068} +6 -3
- package/dist/phoenix-ui/bundle/blobs/68e44832c82b873b0aeb2da56def7b1de5f612c019445e5e28238c690124cbbe +46 -0
- package/dist/phoenix-ui/bundle/blobs/{ec8968ee6a8e42b952e5ece2369ba69f40185c62a135ed54e930df3170421d9c → 6b8d4c71355429a0b16bf367163fea46103a0a99b4e34f87608a39d85b1b980a} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{264815d0080e4862715a4f82542e66241c6563f07259d32aed4a17bbc329b9cc → 6cef874722442de7c60ea96f4221dc54cc4250a270cd05a1e818b58c5b43e232} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{2028a857c0476324a4256faf66693e2a869275e53a88d2b182a28b62c2a17893 → 7181f1f4c452c4345695f9100f6deb610f54f4c55f126043433a65b8491fc680} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{ccb76aabba47938d518a5d46a09d7e70c4a9222bc391e163763f2a7f12d30437 → 75850a8c9d2a8259f71ef1f86dd5d1325415227c1887e81a3467ae1ee400979f} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{4b03cdb5b17d534b5c7e84b079c066f59e8b60b9e454671a453867c4aeb9920d → 77bcc6c2d6ac00a8bfead3b6c6b5fc69f4b60bf086ebbc89271e1b23003168c9} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{9520e25a381c82af6f46e175b7c347d1dc513d933dd6cf8c52570c72abb4ca7f → 792552563745d208594cf4ade19110f2c811a0e1a2661ae1ef43a185ccff5a69} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{f7c8af35acf71a96056afb6afcff6a3ea42f65561f02652d04e11ad9ece9b8e9 → 7acd9b6f2f61b509c6a518508b8b52c2f0de67cdea4fd3f3d383b3ce8b38cf5e} +6 -7
- package/dist/phoenix-ui/bundle/blobs/7bacc1e3da7e8b7127ca3f8f69a2422098b9b2d23fbedefc56dfe76117c1c0e3 +46 -0
- package/dist/phoenix-ui/bundle/blobs/{3077452231f1ce3c48ae5d8dd912b14ab30de55a270a031d07065357bc1cca94 → 8078b804bea630ed9c4ea07ec2f8ac92aba4128725860a66599eda90390d98f6} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{99f49df49ade2ca21deffcc752fc8a76c9a6645f5416b9314ea3ab1bd010f34c → 88b150cb804bc141b80a5687a091f61b99627155c465a27adb88546ef615af92} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{a64a45a4e082a0d05daf711080cb7c3195f15fb6b3f3dce0521f4816b64452f3 → 90f8b07ec12b78e633e306322cda58415aea70d8e0f9ff85bd439361c01dabe1} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{486145071d3445e82774f1b39b2bb15986ce52277e798a31ef7e9c27afb42af3 → 95b2944686e8258a655c3b5e6db68593e24e5199354e46aac553918ea2c0f55a} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{a1998e37084c11f496d4970f86a85d5a6375bbeaa4797b9f42bbac2d44048d23 → 979639f0e8859d600a8db57e733566d9ad37ff8258c3e05a6f9c0f3008e92373} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{3173293607055ea6e1a153ec6f51fb8154782157c8a61abf411da1f41be61728 → 9eaaa00049844060f842fe7fb73cc8edc4be5225cf38a96f33bda3ffc381456c} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{69086f7cb235b35ccc230e1f2da41cbb10cea8f0636b6d4514672113a3adc2cb → a420bbda622399399a6b5c7c3614dcaebc6c62faceaf7b3c83c97db53d26b165} +1 -1
- package/dist/phoenix-ui/bundle/blobs/a63054b8920905045753facece83f154bf806158c6f46ef380036e7a82d4fde5 +46 -0
- package/dist/phoenix-ui/bundle/blobs/{e802e4f7bdc89050a9c0f2ca506e1d493a2316e6e810c0336c57aa073717ef3e → a63788c764b0e4fdcbc029d46093016310edcbcf702d62688e5aaac7ddeee6fd} +3 -1
- package/dist/phoenix-ui/bundle/blobs/{827a211ea03d2cb44b4ea1bc81ba1a6dc51a87fa16d6692997c4e4751241c815 → acfc4c26b135d5b27fc0930738486c9d43d379704102fab45564320180942e4b} +4 -2
- package/dist/phoenix-ui/bundle/blobs/{8da14f3eabac53c990a66e721387367b46cb676cc4204e8c67d6f88c95a6217f → ad243ca615349127aa422bccc1a6604ad17149735659087ca5bab3c5fae97f4e} +6 -7
- package/dist/phoenix-ui/bundle/blobs/b63931066e2b2623d012e1a3c1a0769cc43ed6640460c3899669b45539d6a20d +46 -0
- package/dist/phoenix-ui/bundle/blobs/{bb65af58ecc3e0c0093dc9a705a7ab643035e4e592c3adb93583c82d1aef1f51 → bde793ab5ad776f7e6f6d25ce2b838c1b5022054a1dad5502a873423eb0ad917} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{48b7e6acf597d4f0320834db1393003451d1bd7c0d024dd62f7577a398b2e320 → c0a70ab2889e5c16e1f09249aefad948ce352c42cfe2b5afb758a4524e1b21b6} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{60b2914df394800688c4e20f4eb34e117cd430df9954cf4d48574734507723f4 → c9ee9006ae6c0194a5d39a9f0cb275c52d74a71ea3f6dc2b4c1c7bd89f48a2c0} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{d21ccafc83495872c80ec9873ad4fbfb4223837a747b524aa03b6d819f7c08e3 → cadc2daefcf238aab981fa56388381d3d5ab9c0686bb9dd8187035c9933dfcee} +6 -3
- package/dist/phoenix-ui/bundle/blobs/{d7b744fcc95164d96d9f811a93e2e8a374887146eb762ef775f3d3c7f27abc8d → cbea5728b90f58ff047be6ec4f14eed9d63c7148f5db99b5f147d2f17b472317} +1 -1
- package/dist/phoenix-ui/bundle/blobs/d408a4fea71b656aa8a5f6b5c037815a6e4229df336629ebddf4b5c85ed82fac +46 -0
- package/dist/phoenix-ui/bundle/blobs/{6ff950a5729ad9ea480ddf8c1b489b1c84189bbf7d6034b3b5a98e7c11a28cec → d9ae0c782d8a77787561536c0d7bc3b61175d59303f3cb8c20796f85140c6984} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{1d53e2226465b1047faec7cda2e743e61540ea69e073a3a4057e8d86b277405f → e2932d63eb7f52a82960a19ba79bc800ef4f212164e098f96dee7a4326302f38} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{fa576f6655b235bdea535198647305b7a3855e810180cbdf5d4c246c03c7d190 → e381bcfec42ac541d574011460600cf0818aa0e3362e8c6d205b4157890bf821} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{8fbce5b4859e6011afa83b1c07c4436589cd7181f9cb997538f4c27f196d0d34 → e415657d8725d9b60ece42cc18581da177c42fc47cb11e501db722a5f524ecf7} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{1ba392d45294b3e90cc7f3964f134ae685118bbbcc84ca944953a4270ec67a54 → e46d63e5f8a6ead1d44c8b6302a0c7247af8585b975ab5715518381777622ede} +6 -3
- package/dist/phoenix-ui/bundle/blobs/ee63b3f9b354d69443d05ea4a338af56546897335ace7533546e10be56ea2207 +46 -0
- package/dist/phoenix-ui/bundle/blobs/{c23617601e9c4d97b775c175a5ce1642c40459b157d0821609fe1d253b83ced8 → ef7d57633252f4604749447608850e299c277f7dd1a32776685df0724b1051c8} +6 -7
- package/dist/phoenix-ui/bundle/blobs/{0581c8428246f43c5374393878a29e38f2b1865df87e6f13f2309b8eae54e76f → f0652c6ce41e6949133360178f192ea4ee9f6129a018c7a8b6652870c2330392} +1 -1
- package/dist/phoenix-ui/bundle/blobs/{cfac9eaf96481a8874391e5cecc6149abea1f1f19622f77f1ebb9e1876720e91 → f06c9d797ca2ac8c6a49d3dcd2e99d1b4df8bc9637a49c7e80f3161f49d06b1b} +4 -2
- package/dist/phoenix-ui/bundle/blobs/{882bfe4d8ffd9bc217b0c1bdd5e3cf659cd4b46598d631c4649aaaf0e13333ce → f077545caeb3ecb9f51a3f703a09736ca924869f4b4345c1b1ffbd9bf927a287} +6 -3
- package/dist/phoenix-ui/bundle/blobs/f781c5874bcd79ebe1b3b012275b9af7eae6c4867e9a16e29bb3d3ed3dc1940b +46 -0
- package/dist/phoenix-ui/bundle/blobs/fa10c0c926997387c89b7c1070e25b1b2b53ee891741e3c450a89ec6c6dcaf7d +46 -0
- package/dist/phoenix-ui/bundle/blobs/fb1a36ab7116b0c6709b89720cdded29d80982114c2f87cad614e01e126270dd +46 -0
- package/dist/phoenix-ui/bundle/blobs/fbd3214fc12ba70d9c69a995742ba73dc5365823af63afdc93c0c8a8f0c484af +46 -0
- package/dist/phoenix-ui/bundle/blobs/{77682e9d63b484f94a699f2c0b393e4540797d311362c384a3d643aec11d6985 → fea19b8381ec10ff8ed1923a336332974bbb552227ed215e7ddaaf0ec0112c1d} +1 -1
- package/dist/phoenix-ui/bundle/manifest.json +126 -126
- package/docs/phoenix-diagram.md +173 -0
- package/docs/phoenix-ui.md +57 -16
- package/package.json +14 -10
- package/skills/agi-mode/SKILL.md +3 -1
- package/skills/agi-mode/assets/sdlc/event.md +4 -5
- package/skills/agi-mode/assets/sdlc/object-status.md +1 -1
- package/skills/agi-mode/assets/sdlc/protocol.md +4 -2
- package/skills/agi-mode/evals/scenarios.md +108 -3
- package/skills/agi-mode/playbooks/authoring-a-skill.md +13 -2
- package/skills/agi-mode/playbooks/eval.md +19 -3
- package/skills/agi-mode/playbooks/hillclimb.md +4 -2
- package/skills/agi-mode/playbooks/reflection.md +1 -1
- package/skills/agi-mode/playbooks/sdlc.md +10 -6
- package/skills/agi-mode/playbooks/what-next.md +6 -2
- package/skills/agi-mode/references/eval-protocol.md +13 -4
- package/skills/agi-mode/references/interface-design.md +1 -0
- package/skills/agi-mode/references/remote-evolution.md +178 -0
- package/skills/agi-mode/references/skill-evolution.md +2 -0
- package/skills/agi-mode/scripts/evolution/compare.py +569 -0
- package/skills/agi-mode/scripts/evolution/evolution.py +2886 -0
- package/skills/agi-mode/scripts/evolution/examples/public-smoke.json +128 -0
- package/skills/agi-mode/scripts/evolution/examples/quality-calibration.json +128 -0
- package/skills/agi-mode/scripts/evolution/examples/quality-rubric.json +133 -0
- package/skills/agi-mode/scripts/evolution/protocol.py +1586 -0
- package/skills/agi-mode/scripts/evolution/pyproject.toml +16 -0
- package/skills/agi-mode/scripts/evolution/quality.py +689 -0
- package/skills/agi-mode/scripts/evolution/remote.py +2303 -0
- package/skills/agi-mode/scripts/evolution/tests/support.py +201 -0
- package/skills/agi-mode/scripts/evolution/tests/test_apply.py +643 -0
- package/skills/agi-mode/scripts/evolution/tests/test_bounded_exploration.py +236 -0
- package/skills/agi-mode/scripts/evolution/tests/test_bundles.py +414 -0
- package/skills/agi-mode/scripts/evolution/tests/test_calibration.py +460 -0
- package/skills/agi-mode/scripts/evolution/tests/test_compare.py +377 -0
- package/skills/agi-mode/scripts/evolution/tests/test_protocol.py +357 -0
- package/skills/agi-mode/scripts/evolution/tests/test_quality.py +647 -0
- package/skills/agi-mode/scripts/evolution/tests/test_remote.py +857 -0
- package/skills/agi-mode/scripts/evolution/tests/test_search.py +1109 -0
- package/skills/agi-mode/scripts/evolution/tests/test_typesafe.py +624 -0
- package/skills/agi-mode/scripts/evolution/tests/test_verifier_context.py +132 -0
- package/skills/agi-mode/scripts/evolution/typesafe_worker.py +145 -0
- package/skills/agi-mode/scripts/evolution/uv.lock +2548 -0
- package/skills/apply-code-review/SKILL.md +1 -0
- package/skills/apply-code-review/agents/openai.yaml +2 -0
- package/skills/apply-doc-review/SKILL.md +1 -0
- package/skills/apply-doc-review/agents/openai.yaml +2 -0
- package/skills/apply-ui-review/SKILL.md +1 -0
- package/skills/apply-ui-review/agents/openai.yaml +2 -0
- package/skills/autoresearch/SKILL.md +1 -0
- package/skills/clean-artifacts/SKILL.md +52 -0
- package/skills/clean-artifacts/agents/openai.yaml +4 -0
- package/skills/clean-artifacts/evals/scenarios.md +13 -0
- package/skills/clean-artifacts/references/history-cleanup.md +41 -0
- package/skills/crd/SKILL.md +100 -65
- package/skills/crd/assets/cr-template.md +15 -0
- package/skills/crd-respond/SKILL.md +17 -5
- package/skills/crd-respond/agents/openai.yaml +2 -2
- package/skills/crd-respond/assets/response-template.md +7 -0
- package/skills/crd-respond/evals/scenarios.md +7 -0
- package/skills/create-verification-skill/SKILL.md +1 -0
- package/skills/debugging/SKILL.md +1 -0
- package/skills/design-md/SKILL.md +1 -0
- package/skills/design-reference/SKILL.md +6 -2
- package/skills/design-reference/evals/scenarios.md +4 -0
- package/skills/dev-plan/SKILL.md +11 -6
- package/skills/dev-plan/references/execution-workflow.md +1 -1
- package/skills/dev-plan/references/plan-skeleton.md +234 -69
- package/skills/dev-plan/scripts/check_plan.py +1 -1
- package/skills/documentation-writer/SKILL.md +19 -14
- package/skills/documentation-writer/references/architecture.md +6 -2
- package/skills/documentation-writer/references/user-manual.md +2 -2
- package/skills/elintp/SKILL.md +12 -6
- package/skills/experience-capture/SKILL.md +1 -0
- package/skills/gdpr-compliant/SKILL.md +1 -0
- package/skills/kpi-dashboard-design/SKILL.md +1 -0
- package/skills/landing-page-conversion-audit/SKILL.md +1 -0
- package/skills/mcp-release-qa/SKILL.md +2 -1
- package/skills/meeting-minutes/SKILL.md +1 -0
- package/skills/optimize-agents-md/SKILL.md +128 -0
- package/skills/optimize-agents-md/assets/coding-principles.md +118 -0
- package/skills/phoenix-diagram/ENGINE.json +39 -0
- package/skills/phoenix-diagram/SKILL.md +109 -0
- package/skills/phoenix-diagram/examples/legacy.agent-tool-call.workflow.json +94 -0
- package/skills/phoenix-diagram/examples/legacy.async-job-roundtrip.sequence.json +61 -0
- package/skills/phoenix-diagram/examples/legacy.deployment-release.lifecycle.json +49 -0
- package/skills/phoenix-diagram/examples/legacy.product-analytics.dataflow.json +76 -0
- package/skills/{archify/test/fixtures/v1-baseline/web-app.architecture.json → phoenix-diagram/examples/legacy.web-app.architecture.json} +16 -11
- package/skills/phoenix-diagram/examples/minimal.architecture.json +11 -0
- package/skills/phoenix-diagram/examples/minimal.class.json +23 -0
- package/skills/phoenix-diagram/examples/minimal.dataflow.json +11 -0
- package/skills/phoenix-diagram/examples/minimal.erd.json +25 -0
- package/skills/phoenix-diagram/examples/minimal.lifecycle.json +11 -0
- package/skills/phoenix-diagram/examples/minimal.sequence.json +13 -0
- package/skills/phoenix-diagram/examples/minimal.timeline.json +15 -0
- package/skills/phoenix-diagram/examples/minimal.tree.json +13 -0
- package/skills/phoenix-diagram/examples/minimal.waterfall.json +12 -0
- package/skills/phoenix-diagram/examples/minimal.workflow.json +12 -0
- package/skills/phoenix-diagram/references/architecture-compare.md +111 -0
- package/skills/phoenix-diagram/references/authoring-defaults.md +42 -0
- package/skills/phoenix-diagram/references/brand-marks.md +52 -0
- package/skills/phoenix-diagram/references/compatibility.md +22 -0
- package/skills/phoenix-diagram/references/delivery-contract.md +127 -0
- package/skills/phoenix-diagram/references/extending-types.md +14 -0
- package/skills/phoenix-diagram/references/migration.md +57 -0
- package/skills/phoenix-diagram/references/preview.md +45 -0
- package/skills/phoenix-diagram/references/repository-authoring.md +29 -0
- package/skills/phoenix-diagram/references/viewer-runtime.md +27 -0
- package/skills/phoenix-diagram/schemas/README.md +23 -0
- package/skills/phoenix-diagram/schemas/architecture.schema.json +329 -0
- package/skills/phoenix-diagram/schemas/class.schema.json +348 -0
- package/skills/phoenix-diagram/schemas/common.schema.json +358 -0
- package/skills/{archify/schemas/lifecycle.schema.json → phoenix-diagram/schemas/dataflow.schema.json} +63 -58
- package/skills/phoenix-diagram/schemas/erd.schema.json +384 -0
- package/skills/phoenix-diagram/schemas/lifecycle.schema.json +202 -0
- package/skills/phoenix-diagram/schemas/sequence.schema.json +254 -0
- package/skills/phoenix-diagram/schemas/timeline.schema.json +170 -0
- package/skills/phoenix-diagram/schemas/tree.schema.json +144 -0
- package/skills/phoenix-diagram/schemas/waterfall.schema.json +150 -0
- package/skills/phoenix-diagram/schemas/workflow.schema.json +460 -0
- package/skills/phoenix-diagram/scripts/phoenix-bootstrap.cjs +589 -0
- package/skills/phoenix-diagram/scripts/phoenix-diagram +6 -0
- package/skills/phoenix-diagram/scripts/phoenix-launcher.cjs +10 -0
- package/skills/phoenix-ui/GENERATED.json +255 -240
- package/skills/phoenix-ui/SKILL.md +7 -4
- package/skills/phoenix-ui/evals/scenarios.md +8 -1
- package/skills/phoenix-ui/reference/craft-floor.md +3 -1
- package/skills/phoenix-ui/reference/critique.md +6 -14
- package/skills/phoenix-ui/reference/init.md +1 -1
- package/skills/phoenix-ui/reference/live.md +1 -1
- package/skills/phoenix-ui/reference/operate.md +15 -0
- package/skills/phoenix-ui/scripts/ENGINE.json +17 -17
- package/skills/phoenix-ui/scripts/phoenix-bootstrap.cjs +2 -5
- package/skills/portal-app-exchange/SKILL.md +1 -0
- package/skills/portal-dev-setup/SKILL.md +71 -12
- package/skills/portal-dev-setup/scripts/onebox.sh +69 -6
- package/skills/portal-external-app/SKILL.md +55 -16
- package/skills/portal-external-app/references/config-placement.md +108 -2
- package/skills/portal-external-app/references/registration-and-onebox.md +56 -2
- package/skills/portal-logging/SKILL.md +1 -0
- package/skills/portal-micro-app/SKILL.md +2 -1
- package/skills/portal-micro-app/references/sdk-reference.md +3 -1
- package/skills/prd/SKILL.md +238 -159
- package/skills/prd/evals/scenarios.md +13 -0
- package/skills/prd/references/prd-skeleton.md +14 -4
- package/skills/react/SKILL.md +1 -0
- package/skills/resolve-blocked/SKILL.md +46 -19
- package/skills/resolve-blocked/agents/openai.yaml +2 -2
- package/skills/resolve-blocked/evals/scenarios.md +22 -0
- package/skills/resolve-blocked/references/decisions-contract.md +7 -4
- package/skills/review-code/SKILL.md +308 -46
- package/skills/review-code/agents/openai.yaml +2 -0
- package/skills/review-dev-plan/SKILL.md +88 -33
- package/skills/review-dev-plan/agents/openai.yaml +2 -0
- package/skills/review-dev-plan/references/report-contract.md +53 -19
- package/skills/review-dev-plan/references/review-lenses.md +2 -1
- package/skills/review-prd/SKILL.md +90 -34
- package/skills/review-prd/agents/openai.yaml +2 -0
- package/skills/review-prd/references/report-contract.md +54 -18
- package/skills/review-prd-dev-gaps/SKILL.md +1 -0
- package/skills/review-prd-dev-gaps/agents/openai.yaml +2 -0
- package/skills/review-ui/SKILL.md +369 -71
- package/skills/review-ui/agents/openai.yaml +2 -0
- package/skills/review-ui/evals/scenarios.md +2 -0
- package/skills/rust/SKILL.md +76 -84
- package/skills/rust/references/api-design.md +97 -0
- package/skills/rust/references/{chapter_10.md → async-tokio.md} +42 -18
- package/skills/rust/references/behavioral-patterns.md +49 -0
- package/skills/rust/references/bevy.md +95 -0
- package/skills/rust/references/{chapter_02.md → clippy-and-linting.md} +26 -15
- package/skills/rust/references/{chapter_01.md → coding-idioms.md} +58 -7
- package/skills/rust/references/crates-and-dependencies.md +66 -0
- package/skills/rust/references/{chapter_08.md → documentation.md} +30 -3
- package/skills/rust/references/{chapter_04.md → error-handling.md} +32 -6
- package/skills/rust/references/{chapter_06.md → generics-and-dispatch.md} +28 -11
- package/skills/rust/references/macros.md +45 -0
- package/skills/rust/references/{chapter_03.md → performance.md} +25 -2
- package/skills/rust/references/{chapter_09.md → pointers-and-ownership.md} +37 -17
- package/skills/rust/references/{chapter_05.md → testing.md} +18 -2
- package/skills/rust/references/{chapter_07.md → type-state.md} +5 -1
- package/skills/rust/references/unsafe-and-ffi.md +56 -0
- package/skills/rust-port/SKILL.md +144 -0
- package/skills/rust-review/SKILL.md +177 -0
- package/skills/security-audit/SKILL.md +1 -0
- package/skills/spec-human-decision/SKILL.md +1 -0
- package/skills/sql-optimization/SKILL.md +1 -0
- package/skills/story-points/SKILL.md +1 -0
- package/skills/swarm/SKILL.md +1 -0
- package/skills/test-plan/SKILL.md +1 -0
- package/skills/ui-loop/SKILL.md +39 -0
- package/skills/ui-loop/agents/openai.yaml +3 -0
- package/skills/ui-pattern-research/SKILL.md +1 -0
- package/skills/use-llm-gateway/SKILL.md +58 -0
- package/skills/use-llm-gateway/agents/openai.yaml +4 -0
- package/skills/use-llm-gateway/references/api.md +129 -0
- package/skills/use-llm-gateway/references/recovery.md +86 -0
- package/skills/use-prompt-dsl/SKILL.md +165 -0
- package/skills/use-prompt-dsl/references/examples.md +726 -0
- package/skills/use-prompt-dsl/references/language.md +938 -0
- package/skills/use-rustify-docs/SKILL.md +60 -0
- package/skills/use-rustify-docs/agents/openai.yaml +4 -0
- package/skills/use-rustify-docs/references/rustify-docs.md +138 -0
- package/skills/use-task-gateway/SKILL.md +60 -0
- package/skills/use-task-gateway/agents/openai.yaml +3 -0
- package/skills/use-task-gateway/references/authentication.md +75 -0
- package/skills/use-task-gateway/references/task-api.md +121 -0
- package/skills/use-task-gateway/references/workers.md +85 -0
- package/skills/use-task-gateway/references/workflow-actions.md +66 -0
- package/skills/use-workflow-service/SKILL.md +57 -0
- package/skills/use-workflow-service/agents/openai.yaml +3 -0
- package/skills/use-workflow-service/references/action-provider.md +84 -0
- package/skills/use-workflow-service/references/authentication.md +76 -0
- package/skills/use-workflow-service/references/authoring.md +62 -0
- package/skills/use-workflow-service/references/mcp.md +47 -0
- package/skills/use-workflow-service/references/open-api.md +88 -0
- package/skills/use-workflow-service/references/orchestration-choice.md +63 -0
- package/skills/working-with-mbx/SKILL.md +1 -0
- package/skills/working-with-mise/SKILL.md +1 -0
- package/skills/xgent-app-release/SKILL.md +114 -16
- package/skills/xgent-app-release/references/build-and-package.md +2 -1
- package/skills/xgent-app-release/references/publish-api.md +6 -1
- package/skills/xgent-app-release/references/troubleshooting.md +2 -1
- package/skills/xgent-app-release/references/verify-preflight.mjs +21 -1
- package/skills/xgent-app-release/scripts/preflight.mjs +42 -3
- package/skills/xgent-image-push/SKILL.md +1 -0
- package/skills/xgent-init/SKILL.md +1 -0
- package/docs/claude-context-goal-guard.md +0 -124
- package/docs/codex-context-goal-guard.md +0 -109
- package/hooks/claude/context-goal-guard.js +0 -139
- package/hooks/claude/settings.example.json +0 -26
- package/hooks/codex/context-goal-guard.py +0 -189
- package/hooks/codex/hooks.example.json +0 -26
- package/skills/archify/SKILL.md +0 -137
- package/skills/archify/assets/template.html +0 -14937
- package/skills/archify/bin/archify.mjs +0 -2139
- package/skills/archify/bin/open-artifact.mjs +0 -86
- package/skills/archify/bin/preview.mjs +0 -653
- package/skills/archify/bin/visual-check.mjs +0 -829
- package/skills/archify/brand-marks/README.md +0 -31
- package/skills/archify/brand-marks/catalog.json +0 -131
- package/skills/archify/delta/architecture-delta.mjs +0 -1232
- package/skills/archify/examples/agent-run.lifecycle.json +0 -60
- package/skills/archify/examples/brand-aware-delivery.architecture.json +0 -47
- package/skills/archify/examples/cache-miss-request.sequence.json +0 -82
- package/skills/archify/examples/checkout-platform.base.architecture.json +0 -31
- package/skills/archify/examples/checkout-platform.head.architecture.json +0 -31
- package/skills/archify/examples/dataflow-product-analytics.html +0 -15048
- package/skills/archify/examples/event-stream.dataflow.json +0 -57
- package/skills/archify/examples/incident-response.workflow.json +0 -64
- package/skills/archify/examples/lifecycle-agent-run.html +0 -14983
- package/skills/archify/examples/production-deployment.architecture.json +0 -71
- package/skills/archify/examples/release-delivery.workflow.json +0 -62
- package/skills/archify/examples/sequence-cache-miss-request.html +0 -15063
- package/skills/archify/examples/web-app-rendered.html +0 -15012
- package/skills/archify/examples/workflow-agent-tool-call-rendered.html +0 -15054
- package/skills/archify/migrations/workflow-v2.mjs +0 -279
- package/skills/archify/package-lock.json +0 -149
- package/skills/archify/package.json +0 -39
- package/skills/archify/recipes/scenarios.mjs +0 -391
- package/skills/archify/references/authoring-contract.md +0 -247
- package/skills/archify/references/brand-marks.md +0 -65
- package/skills/archify/references/delivery-contract.md +0 -138
- package/skills/archify/references/viewer-runtime.md +0 -45
- package/skills/archify/renderers/architecture/grid.mjs +0 -62
- package/skills/archify/renderers/architecture/render-architecture.mjs +0 -1078
- package/skills/archify/renderers/dataflow/README.md +0 -104
- package/skills/archify/renderers/dataflow/render-dataflow.mjs +0 -483
- package/skills/archify/renderers/lifecycle/README.md +0 -115
- package/skills/archify/renderers/lifecycle/render-lifecycle.mjs +0 -561
- package/skills/archify/renderers/sequence/README.md +0 -114
- package/skills/archify/renderers/sequence/render-sequence.mjs +0 -464
- package/skills/archify/renderers/shared/brand-marks.mjs +0 -563
- package/skills/archify/renderers/shared/cli.mjs +0 -218
- package/skills/archify/renderers/shared/desktop-readability.mjs +0 -26
- package/skills/archify/renderers/shared/diagnostics.mjs +0 -160
- package/skills/archify/renderers/shared/engineering-profiles.mjs +0 -157
- package/skills/archify/renderers/shared/generated-brand-marks.mjs +0 -2003
- package/skills/archify/renderers/shared/generated-validators.mjs +0 -13
- package/skills/archify/renderers/shared/geometry.mjs +0 -1423
- package/skills/archify/renderers/shared/i18n.mjs +0 -595
- package/skills/archify/renderers/shared/layout-report.mjs +0 -40
- package/skills/archify/renderers/shared/legend.mjs +0 -217
- package/skills/archify/renderers/shared/output-path.mjs +0 -340
- package/skills/archify/renderers/shared/repository-evidence.mjs +0 -239
- package/skills/archify/renderers/shared/repository-location.mjs +0 -58
- package/skills/archify/renderers/shared/text-fit.mjs +0 -49
- package/skills/archify/renderers/shared/utils.mjs +0 -232
- package/skills/archify/renderers/shared/validator.mjs +0 -86
- package/skills/archify/renderers/workflow/README.md +0 -223
- package/skills/archify/renderers/workflow/render-workflow.mjs +0 -35
- package/skills/archify/renderers/workflow/workflow-compiler.mjs +0 -4400
- package/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +0 -144
- package/skills/archify/schemas/README.md +0 -211
- package/skills/archify/schemas/architecture.schema.json +0 -178
- package/skills/archify/schemas/common.schema.json +0 -115
- package/skills/archify/scripts/check-render-output.mjs +0 -916
- package/skills/archify/scripts/check-update.mjs +0 -1667
- package/skills/archify/scripts/generate-brand-marks.mjs +0 -141
- package/skills/archify/scripts/generate-validators.mjs +0 -66
- package/skills/archify/scripts/render-examples.mjs +0 -26
- package/skills/archify/scripts/update-contract.mjs +0 -182
- package/skills/archify/skill-release.json +0 -10
- package/skills/archify/test/adaptive-reader-layout.test.mjs +0 -86
- package/skills/archify/test/animation.test.mjs +0 -179
- package/skills/archify/test/architecture-compare-recovery.test.mjs +0 -157
- package/skills/archify/test/architecture-delta-markers.test.mjs +0 -137
- package/skills/archify/test/architecture-delta.test.mjs +0 -705
- package/skills/archify/test/artifact-receipt-flush.test.mjs +0 -126
- package/skills/archify/test/authored-reachability.test.mjs +0 -95
- package/skills/archify/test/authoring-safety-contract.test.mjs +0 -54
- package/skills/archify/test/automatic-port-spread.test.mjs +0 -376
- package/skills/archify/test/base-input-compatibility.test.mjs +0 -42
- package/skills/archify/test/brand-marks.test.mjs +0 -678
- package/skills/archify/test/chapter-delta-preview.test.mjs +0 -157
- package/skills/archify/test/chapter-handoff.test.mjs +0 -97
- package/skills/archify/test/chapter-rail.test.mjs +0 -86
- package/skills/archify/test/checkout-line-endings.test.mjs +0 -72
- package/skills/archify/test/ci-scope.test.mjs +0 -59
- package/skills/archify/test/clean-skill-staging.test.mjs +0 -440
- package/skills/archify/test/cli-output-types.test.mjs +0 -201
- package/skills/archify/test/cli.test.mjs +0 -986
- package/skills/archify/test/community-proof-intake.test.mjs +0 -125
- package/skills/archify/test/cursor-onboarding.test.mjs +0 -74
- package/skills/archify/test/degraded.test.mjs +0 -161
- package/skills/archify/test/delivery-contract.test.mjs +0 -52
- package/skills/archify/test/desktop-reader-browser.test.mjs +0 -80
- package/skills/archify/test/diagram-guide.test.mjs +0 -92
- package/skills/archify/test/engineering-profile.test.mjs +0 -209
- package/skills/archify/test/export-browser.test.mjs +0 -305
- package/skills/archify/test/export-cleanup-browser.test.mjs +0 -294
- package/skills/archify/test/finder-browser.test.mjs +0 -274
- package/skills/archify/test/finder.test.mjs +0 -104
- package/skills/archify/test/fixtures/automatic-routing-node-border-clearance.workflow.json +0 -40
- package/skills/archify/test/fixtures/fail-migration-cleanup.mjs +0 -17
- package/skills/archify/test/fixtures/issue-126/custom-widths.workflow.json +0 -18
- package/skills/archify/test/fixtures/issue-126/explicit-route.workflow.json +0 -28
- package/skills/archify/test/fixtures/issue-126/explicit-viewbox.workflow.json +0 -19
- package/skills/archify/test/fixtures/issue-126/labels.workflow.json +0 -18
- package/skills/archify/test/fixtures/v1-baseline/agent-run.lifecycle.json +0 -64
- package/skills/archify/test/fixtures/v1-baseline/agent-tool-call.workflow.json +0 -88
- package/skills/archify/test/fixtures/v1-baseline/cache-miss-request.sequence.json +0 -75
- package/skills/archify/test/fixtures/v1-baseline/event-stream.dataflow.json +0 -57
- package/skills/archify/test/fixtures/v1-baseline/product-analytics.dataflow.json +0 -70
- package/skills/archify/test/fixtures/v1-baseline/production-deployment.architecture.json +0 -71
- package/skills/archify/test/fixtures/v1-workflow-700x400.workflow.json +0 -22
- package/skills/archify/test/fixtures/v1-workflow-explicit-coordinates.workflow.json +0 -47
- package/skills/archify/test/focus-browser.test.mjs +0 -226
- package/skills/archify/test/gallery.test.mjs +0 -130
- package/skills/archify/test/generate-validators.test.mjs +0 -59
- package/skills/archify/test/generate-viewer.test.mjs +0 -165
- package/skills/archify/test/generated-artifact-xml.test.mjs +0 -88
- package/skills/archify/test/geometry.test.mjs +0 -739
- package/skills/archify/test/golden.mjs +0 -207
- package/skills/archify/test/grid.test.mjs +0 -47
- package/skills/archify/test/guide-page.test.mjs +0 -85
- package/skills/archify/test/guide.test.mjs +0 -81
- package/skills/archify/test/guided-views-browser.test.mjs +0 -460
- package/skills/archify/test/guided-views.test.mjs +0 -144
- package/skills/archify/test/helpers/desktop-browser.mjs +0 -48
- package/skills/archify/test/helpers/offline-fonts.mjs +0 -76
- package/skills/archify/test/helpers/viewer-click.mjs +0 -72
- package/skills/archify/test/helpers/xml.mjs +0 -37
- package/skills/archify/test/i18n.test.mjs +0 -422
- package/skills/archify/test/intent-trace-browser.test.mjs +0 -284
- package/skills/archify/test/intent-trace.test.mjs +0 -111
- package/skills/archify/test/landing.test.mjs +0 -97
- package/skills/archify/test/layout-rules.test.mjs +0 -1498
- package/skills/archify/test/legend-contract.test.mjs +0 -404
- package/skills/archify/test/motion-governor-browser.test.mjs +0 -342
- package/skills/archify/test/motion-governor.test.mjs +0 -111
- package/skills/archify/test/offline-font-browser.test.mjs +0 -210
- package/skills/archify/test/offline-self-containment.test.mjs +0 -80
- package/skills/archify/test/open-artifact.test.mjs +0 -118
- package/skills/archify/test/ordinary-model-floor.test.mjs +0 -1052
- package/skills/archify/test/output-path.test.mjs +0 -587
- package/skills/archify/test/presentation.test.mjs +0 -71
- package/skills/archify/test/preset-tryon.test.mjs +0 -113
- package/skills/archify/test/preview-contract.test.mjs +0 -40
- package/skills/archify/test/preview.test.mjs +0 -530
- package/skills/archify/test/proof-aperture.test.mjs +0 -78
- package/skills/archify/test/reach-share-card.test.mjs +0 -149
- package/skills/archify/test/reader-layout-browser.test.mjs +0 -320
- package/skills/archify/test/readme-showcase.test.mjs +0 -319
- package/skills/archify/test/real-repository-proof.test.mjs +0 -209
- package/skills/archify/test/relationship-direct-explorer.test.mjs +0 -115
- package/skills/archify/test/relationship-lens.test.mjs +0 -128
- package/skills/archify/test/relationship-permalink.test.mjs +0 -130
- package/skills/archify/test/relationship-pulse.test.mjs +0 -122
- package/skills/archify/test/release-identity.test.mjs +0 -476
- package/skills/archify/test/release-package-gates.test.mjs +0 -750
- package/skills/archify/test/render-output-checks.test.mjs +0 -588
- package/skills/archify/test/renderer-diagnostic-boundary.test.mjs +0 -107
- package/skills/archify/test/repair-receipt.test.mjs +0 -181
- package/skills/archify/test/repository-evidence-replacement.test.mjs +0 -136
- package/skills/archify/test/repository-evidence.test.mjs +0 -605
- package/skills/archify/test/repository-language-metadata.test.mjs +0 -67
- package/skills/archify/test/route-journey.test.mjs +0 -125
- package/skills/archify/test/route-probe-browser.test.mjs +0 -363
- package/skills/archify/test/route-probe.test.mjs +0 -114
- package/skills/archify/test/route-share-card.test.mjs +0 -163
- package/skills/archify/test/semantic-camera.test.mjs +0 -88
- package/skills/archify/test/semantic-flow.test.mjs +0 -90
- package/skills/archify/test/semantic-legend-gateway.test.mjs +0 -156
- package/skills/archify/test/semantic-lens-browser.test.mjs +0 -372
- package/skills/archify/test/semantic-lens.test.mjs +0 -103
- package/skills/archify/test/semantic-passport.test.mjs +0 -99
- package/skills/archify/test/semantic-radar.test.mjs +0 -735
- package/skills/archify/test/semantic-zoom.test.mjs +0 -86
- package/skills/archify/test/sequence-column-fit.test.mjs +0 -137
- package/skills/archify/test/settled-flow.test.mjs +0 -97
- package/skills/archify/test/share-card-export.test.mjs +0 -132
- package/skills/archify/test/site-language-continuity.test.mjs +0 -569
- package/skills/archify/test/site-language-integration.mjs +0 -9
- package/skills/archify/test/skill-metadata.test.mjs +0 -84
- package/skills/archify/test/stable-update-manifest.test.mjs +0 -167
- package/skills/archify/test/start-page.test.mjs +0 -312
- package/skills/archify/test/story-beat-navigator.test.mjs +0 -103
- package/skills/archify/test/story-carrier.test.mjs +0 -90
- package/skills/archify/test/story-director-strip.test.mjs +0 -91
- package/skills/archify/test/story-follow-camera.test.mjs +0 -103
- package/skills/archify/test/story-horizon.test.mjs +0 -91
- package/skills/archify/test/story-moment-link.test.mjs +0 -93
- package/skills/archify/test/story-shelf.test.mjs +0 -84
- package/skills/archify/test/story-trail.test.mjs +0 -81
- package/skills/archify/test/toolbar-polish.test.mjs +0 -57
- package/skills/archify/test/update-contract.test.mjs +0 -133
- package/skills/archify/test/update-notifier.test.mjs +0 -3317
- package/skills/archify/test/v1-compatibility.test.mjs +0 -329
- package/skills/archify/test/vertical-edge.test.mjs +0 -68
- package/skills/archify/test/viewer-camera-browser.test.mjs +0 -395
- package/skills/archify/test/viewer-chrome-layout.test.mjs +0 -1104
- package/skills/archify/test/viewer-identifiers-browser.test.mjs +0 -135
- package/skills/archify/test/visual-check.test.mjs +0 -400
- package/skills/archify/test/webm-artifact.smoke.mjs +0 -1855
- package/skills/archify/test/workflow-compiler-hard-contract.test.mjs +0 -2136
- package/skills/archify/test/workflow-compiler.test.mjs +0 -1163
- package/skills/archify/test/workflow-migration.test.mjs +0 -620
- package/skills/archify/test/workflow-semantic-contract.test.mjs +0 -131
- /package/{skills/archify/THIRD_PARTY_NOTICES.md → dist/phoenix-diagram/THIRD-PARTY-LICENSES/0b7fb59365dc21c93b0f62526d74ee913aeec9ae532c112bc76afa7edcd56e14.txt} +0 -0
- /package/{skills/archify/LICENSE → dist/phoenix-diagram/THIRD-PARTY-LICENSES/b799ab081703e7821ae5096d2c1abdf14bbdc75e8ea1c4045998c36ed6db9706.txt} +0 -0
- /package/{skills/archify/assets/JetBrainsMono-OFL.txt → dist/phoenix-diagram/THIRD-PARTY-LICENSES/c1ab7c666206842a02b35b30770dac0d7a10156ed401c9defc3f02a754d89e90.txt} +0 -0
- /package/{skills/archify/examples/agent-tool-call.workflow.json → dist/phoenix-diagram/bundle/examples/legacy.agent-tool-call.workflow.json} +0 -0
- /package/{skills/archify/examples/async-job-roundtrip.sequence.json → dist/phoenix-diagram/bundle/examples/legacy.async-job-roundtrip.sequence.json} +0 -0
- /package/{skills/archify/examples/deployment-release.lifecycle.json → dist/phoenix-diagram/bundle/examples/legacy.deployment-release.lifecycle.json} +0 -0
- /package/{skills/archify/examples/product-analytics.dataflow.json → dist/phoenix-diagram/bundle/examples/legacy.product-analytics.dataflow.json} +0 -0
- /package/{skills/archify/examples/web-app.architecture.json → dist/phoenix-diagram/bundle/examples/legacy.web-app.architecture.json} +0 -0
|
@@ -0,0 +1,1586 @@
|
|
|
1
|
+
"""Validate and freeze experiment inputs without importing a remote runtime."""
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
import errno
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
import math
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path, PurePosixPath
|
|
12
|
+
import re
|
|
13
|
+
import stat
|
|
14
|
+
import subprocess
|
|
15
|
+
import tempfile
|
|
16
|
+
import uuid
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
VERSIONS = {"harbor_version": "0.24.0", "codex_version": "0.162.1"}
|
|
20
|
+
PRO_V2_REVISION = "66f92766bba642462d4bbe5479e83f91f9211862"
|
|
21
|
+
PHASES = {"prepared", "starting", "running", "capturing", "captured"}
|
|
22
|
+
ROLES = {"optimizer", "trial", "grader", "probe"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ProtocolError(ValueError):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def canonical(value):
|
|
30
|
+
return json.dumps(
|
|
31
|
+
value,
|
|
32
|
+
ensure_ascii=False,
|
|
33
|
+
sort_keys=True,
|
|
34
|
+
separators=(",", ":"),
|
|
35
|
+
allow_nan=False,
|
|
36
|
+
).encode("utf-8")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def sha256(content):
|
|
40
|
+
return hashlib.sha256(content).hexdigest()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def task_contract(case):
|
|
44
|
+
return ("\n\n".join(case["observable_requirements"]) + "\n").encode("utf-8")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def parse_json(content):
|
|
48
|
+
def pairs(items):
|
|
49
|
+
result = {}
|
|
50
|
+
for key, value in items:
|
|
51
|
+
if key in result:
|
|
52
|
+
raise ProtocolError(f"duplicate JSON field: {key}")
|
|
53
|
+
result[key] = value
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
def invalid_constant(value):
|
|
57
|
+
raise ProtocolError(f"nonfinite JSON number: {value}")
|
|
58
|
+
|
|
59
|
+
return json.loads(
|
|
60
|
+
content.decode("utf-8") if isinstance(content, bytes) else content,
|
|
61
|
+
object_pairs_hook=pairs,
|
|
62
|
+
parse_constant=invalid_constant,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def load_json(path):
|
|
67
|
+
return parse_json(Path(path).read_text(encoding="utf-8"))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def fields(value, label, required, optional=()):
|
|
71
|
+
if not isinstance(value, dict):
|
|
72
|
+
raise ProtocolError(f"{label}: expected object")
|
|
73
|
+
missing = set(required) - value.keys()
|
|
74
|
+
unknown = value.keys() - set(required) - set(optional)
|
|
75
|
+
if missing or unknown:
|
|
76
|
+
raise ProtocolError(
|
|
77
|
+
f"{label}: missing fields {sorted(missing)}, unknown fields {sorted(unknown)}"
|
|
78
|
+
)
|
|
79
|
+
return value
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def text(value, label):
|
|
83
|
+
if not isinstance(value, str) or not value.strip():
|
|
84
|
+
raise ProtocolError(f"{label}: expected nonempty string")
|
|
85
|
+
return value
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def number(value, label, minimum=0, maximum=None, integer=False):
|
|
89
|
+
if type(value) not in (int, float) or not math.isfinite(value) or value < minimum:
|
|
90
|
+
raise ProtocolError(f"{label}: expected finite number >= {minimum}")
|
|
91
|
+
if integer and type(value) is not int:
|
|
92
|
+
raise ProtocolError(f"{label}: expected integer")
|
|
93
|
+
if maximum is not None and value > maximum:
|
|
94
|
+
raise ProtocolError(f"{label}: exceeds {maximum}")
|
|
95
|
+
return value
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def boolean(value, label):
|
|
99
|
+
if type(value) is not bool:
|
|
100
|
+
raise ProtocolError(f"{label}: expected boolean")
|
|
101
|
+
return value
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def choice(value, options, label):
|
|
105
|
+
if not isinstance(value, str) or value not in options:
|
|
106
|
+
raise ProtocolError(f"{label}: expected one of {sorted(options)}")
|
|
107
|
+
return value
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def rows(value, label, empty=False):
|
|
111
|
+
if not isinstance(value, list) or (not empty and not value):
|
|
112
|
+
raise ProtocolError(
|
|
113
|
+
f"{label}: expected {'possibly empty ' if empty else 'nonempty '}list"
|
|
114
|
+
)
|
|
115
|
+
return value
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def fingerprint(value, label, length=64):
|
|
119
|
+
if not isinstance(value, str) or not re.fullmatch(f"[0-9a-f]{{{length}}}", value):
|
|
120
|
+
raise ProtocolError(f"{label}: expected {length}-character content hash")
|
|
121
|
+
return value
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def relative_path(name):
|
|
125
|
+
text(name, "path")
|
|
126
|
+
path = PurePosixPath(name)
|
|
127
|
+
if (
|
|
128
|
+
path.is_absolute()
|
|
129
|
+
or ".." in path.parts
|
|
130
|
+
or "\\" in name
|
|
131
|
+
or path.as_posix() != name
|
|
132
|
+
or name == "."
|
|
133
|
+
):
|
|
134
|
+
raise ProtocolError(f"path must be canonical and relative: {name}")
|
|
135
|
+
if "\x00" in name:
|
|
136
|
+
raise ProtocolError("path contains NUL")
|
|
137
|
+
return name
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def remote_path(name):
|
|
141
|
+
text(name, "remote path")
|
|
142
|
+
path = PurePosixPath(name)
|
|
143
|
+
if (
|
|
144
|
+
not path.is_absolute()
|
|
145
|
+
or ".." in path.parts
|
|
146
|
+
or path.as_posix() != name
|
|
147
|
+
or "\x00" in name
|
|
148
|
+
or name == "/"
|
|
149
|
+
):
|
|
150
|
+
raise ProtocolError(
|
|
151
|
+
"remote paths must be canonical, absolute and below the root"
|
|
152
|
+
)
|
|
153
|
+
return name
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def export_path(name):
|
|
157
|
+
relative_path(name)
|
|
158
|
+
parts = PurePosixPath(name).parts
|
|
159
|
+
for index, part in enumerate(parts):
|
|
160
|
+
if (
|
|
161
|
+
part == ".git"
|
|
162
|
+
or part == ".env"
|
|
163
|
+
or part.startswith(".env.")
|
|
164
|
+
or part in {".mcp.json", "auth.json", "credentials.json"}
|
|
165
|
+
):
|
|
166
|
+
raise ProtocolError(
|
|
167
|
+
f"host configuration or credential path cannot be exported: {name}"
|
|
168
|
+
)
|
|
169
|
+
if part in {".codex", ".claude", ".agents"} and parts[
|
|
170
|
+
index + 1 : index + 2
|
|
171
|
+
] != ("skills",):
|
|
172
|
+
raise ProtocolError(f"host configuration cannot be exported: {name}")
|
|
173
|
+
return name
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def path_list(value, label, empty=False):
|
|
177
|
+
names = [export_path(name) for name in rows(value, label, empty)]
|
|
178
|
+
if len(names) != len(set(names)):
|
|
179
|
+
raise ProtocolError(f"{label}: duplicate paths")
|
|
180
|
+
return names
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def covered(name, allowed):
|
|
184
|
+
return any(name == prefix or name.startswith(prefix + "/") for prefix in allowed)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def directory(name, base):
|
|
188
|
+
path = Path(text(name, "directory"))
|
|
189
|
+
if not path.is_absolute():
|
|
190
|
+
path = base / path
|
|
191
|
+
path = path.resolve(strict=True)
|
|
192
|
+
if not path.is_dir():
|
|
193
|
+
raise ProtocolError("expected directory")
|
|
194
|
+
return path
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def read_file(root, name):
|
|
198
|
+
export_path(name)
|
|
199
|
+
# O_NONBLOCK avoids opening a FIFO before checking its type; O_NOFOLLOW
|
|
200
|
+
# and directory descriptors keep every lookup inside the authorized root.
|
|
201
|
+
directory_fd = os.open(root, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
|
|
202
|
+
try:
|
|
203
|
+
parts = PurePosixPath(name).parts
|
|
204
|
+
for part in parts[:-1]:
|
|
205
|
+
child_fd = os.open(
|
|
206
|
+
part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=directory_fd
|
|
207
|
+
)
|
|
208
|
+
os.close(directory_fd)
|
|
209
|
+
directory_fd = child_fd
|
|
210
|
+
descriptor = os.open(
|
|
211
|
+
parts[-1], os.O_RDONLY | os.O_NONBLOCK | os.O_NOFOLLOW, dir_fd=directory_fd
|
|
212
|
+
)
|
|
213
|
+
except OSError as error:
|
|
214
|
+
if error.errno in {errno.ELOOP, errno.ENOTDIR}:
|
|
215
|
+
raise ProtocolError(
|
|
216
|
+
f"symlink or invalid directory cannot be exported: {name}"
|
|
217
|
+
) from None
|
|
218
|
+
raise
|
|
219
|
+
finally:
|
|
220
|
+
os.close(directory_fd)
|
|
221
|
+
with os.fdopen(descriptor, "rb") as stream:
|
|
222
|
+
info = os.fstat(stream.fileno())
|
|
223
|
+
if not stat.S_ISREG(info.st_mode):
|
|
224
|
+
raise ProtocolError(f"only regular files can be exported: {name}")
|
|
225
|
+
mode = stat.S_IMODE(info.st_mode)
|
|
226
|
+
if mode & 0o7000:
|
|
227
|
+
raise ProtocolError(f"special permission bits are not supported: {name}")
|
|
228
|
+
content = stream.read()
|
|
229
|
+
return {"content": content, "sha256": sha256(content), "mode": mode}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def manifest(files):
|
|
233
|
+
return {
|
|
234
|
+
name: {
|
|
235
|
+
"sha256": row["sha256"],
|
|
236
|
+
"mode": row["mode"],
|
|
237
|
+
"size": len(row["content"]),
|
|
238
|
+
}
|
|
239
|
+
for name, row in sorted(files.items())
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def bundle_hash(files):
|
|
244
|
+
return sha256(canonical(manifest(files)))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def git(root, *args):
|
|
248
|
+
result = subprocess.run(["git", "-C", str(root), *args], capture_output=True)
|
|
249
|
+
if result.returncode:
|
|
250
|
+
# Git diagnostics may include credential-bearing remote URLs.
|
|
251
|
+
raise ProtocolError("repository or base commit could not be read")
|
|
252
|
+
return result.stdout
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def repository_files(spec):
|
|
256
|
+
root = Path(spec["target"]["repo"])
|
|
257
|
+
base = spec["target"]["base_commit"]
|
|
258
|
+
top = Path(os.fsdecode(git(root, "rev-parse", "--show-toplevel")).strip()).resolve()
|
|
259
|
+
if top != root:
|
|
260
|
+
raise ProtocolError("target.repo must be the repository root")
|
|
261
|
+
if git(root, "rev-parse", base + "^{commit}").decode().strip() != base:
|
|
262
|
+
raise ProtocolError("base_commit must identify an existing commit")
|
|
263
|
+
if git(root, "rev-parse", "HEAD").decode().strip() != base:
|
|
264
|
+
raise ProtocolError(
|
|
265
|
+
"checkout HEAD must match base_commit before including dirty files"
|
|
266
|
+
)
|
|
267
|
+
entries = git(root, "ls-tree", "-rz", "--full-tree", base).split(b"\x00")
|
|
268
|
+
tracked = {}
|
|
269
|
+
for entry in entries:
|
|
270
|
+
if not entry:
|
|
271
|
+
continue
|
|
272
|
+
meta, raw_name = entry.split(b"\t", 1)
|
|
273
|
+
mode, kind, _ = meta.decode().split()
|
|
274
|
+
tracked[os.fsdecode(raw_name)] = (mode, kind)
|
|
275
|
+
selected = set()
|
|
276
|
+
for prefix in spec["source"]["paths"]:
|
|
277
|
+
matches = {name for name in tracked if covered(name, [prefix])}
|
|
278
|
+
if not matches:
|
|
279
|
+
raise ProtocolError(
|
|
280
|
+
f"source path has no files at the base commit: {prefix}"
|
|
281
|
+
)
|
|
282
|
+
selected.update(matches)
|
|
283
|
+
for name in spec["source"]["new_paths"]:
|
|
284
|
+
if name in tracked:
|
|
285
|
+
raise ProtocolError(f"new_path already exists at base commit: {name}")
|
|
286
|
+
selected.add(name)
|
|
287
|
+
result, deleted = {}, []
|
|
288
|
+
for name in sorted(selected):
|
|
289
|
+
export_path(name)
|
|
290
|
+
if not covered(name, spec["authority"]["export_paths"]):
|
|
291
|
+
raise ProtocolError(f"file outside export authority: {name}")
|
|
292
|
+
if name in tracked and tracked[name] not in {
|
|
293
|
+
("100644", "blob"),
|
|
294
|
+
("100755", "blob"),
|
|
295
|
+
}:
|
|
296
|
+
raise ProtocolError(f"symlinks and submodules cannot be exported: {name}")
|
|
297
|
+
try:
|
|
298
|
+
result[name] = read_file(root, name)
|
|
299
|
+
except FileNotFoundError:
|
|
300
|
+
if name not in tracked:
|
|
301
|
+
raise ProtocolError(f"missing authorized new file: {name}") from None
|
|
302
|
+
deleted.append(name)
|
|
303
|
+
for name in spec["target"]["allowed_paths"]:
|
|
304
|
+
if name in tracked and name not in selected:
|
|
305
|
+
raise ProtocolError(f"target file is missing from source allowlist: {name}")
|
|
306
|
+
return result, deleted
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def validate_target(spec, base):
|
|
310
|
+
target = fields(
|
|
311
|
+
spec["target"],
|
|
312
|
+
"target",
|
|
313
|
+
{
|
|
314
|
+
"kind",
|
|
315
|
+
"repo",
|
|
316
|
+
"repo_id",
|
|
317
|
+
"base_commit",
|
|
318
|
+
"allowed_paths",
|
|
319
|
+
"surfaces",
|
|
320
|
+
"protected_rules",
|
|
321
|
+
},
|
|
322
|
+
{"compatible_bases"},
|
|
323
|
+
)
|
|
324
|
+
choice(target["kind"], {"skill", "agents"}, "target.kind")
|
|
325
|
+
target["repo"] = str(directory(target["repo"], base))
|
|
326
|
+
text(target["repo_id"], "target.repo_id")
|
|
327
|
+
fingerprint(target["base_commit"], "target.base_commit", 40)
|
|
328
|
+
for revision in rows(
|
|
329
|
+
target.get("compatible_bases", []), "compatible_bases", empty=True
|
|
330
|
+
):
|
|
331
|
+
fingerprint(revision, "compatible base", 40)
|
|
332
|
+
git(
|
|
333
|
+
Path(target["repo"]),
|
|
334
|
+
"merge-base",
|
|
335
|
+
"--is-ancestor",
|
|
336
|
+
revision,
|
|
337
|
+
target["base_commit"],
|
|
338
|
+
)
|
|
339
|
+
allowed = path_list(target["allowed_paths"], "target.allowed_paths")
|
|
340
|
+
surfaces = fields(target["surfaces"], "target.surfaces", {"skill", "agents"})
|
|
341
|
+
for surface in surfaces:
|
|
342
|
+
path_list(surfaces[surface], f"surfaces.{surface}", empty=True)
|
|
343
|
+
all_paths = surfaces["skill"] + surfaces["agents"]
|
|
344
|
+
if set(all_paths) != set(allowed) or len(set(all_paths)) != len(all_paths):
|
|
345
|
+
raise ProtocolError("instruction surfaces must partition allowed_paths")
|
|
346
|
+
if not surfaces[target["kind"]]:
|
|
347
|
+
raise ProtocolError("target kind must have a nonempty instruction surface")
|
|
348
|
+
for rule in rows(target["protected_rules"], "protected_rules", empty=True):
|
|
349
|
+
fields(rule, "protected_rule", {"path", "text", "kind", "verification"})
|
|
350
|
+
if relative_path(rule["path"]) not in allowed:
|
|
351
|
+
raise ProtocolError("protected rule is outside the target")
|
|
352
|
+
text(rule["text"], "protected_rule.text")
|
|
353
|
+
choice(
|
|
354
|
+
rule["kind"],
|
|
355
|
+
{"principle", "authority", "scope", "fact"},
|
|
356
|
+
"protected_rule.kind",
|
|
357
|
+
)
|
|
358
|
+
choice(
|
|
359
|
+
rule["verification"],
|
|
360
|
+
{"exact", "independent"},
|
|
361
|
+
"protected_rule.verification",
|
|
362
|
+
)
|
|
363
|
+
authority = fields(
|
|
364
|
+
spec["authority"],
|
|
365
|
+
"authority",
|
|
366
|
+
{
|
|
367
|
+
"repo",
|
|
368
|
+
"export_paths",
|
|
369
|
+
"write_paths",
|
|
370
|
+
"remote_host",
|
|
371
|
+
"remote_root",
|
|
372
|
+
"auth_path",
|
|
373
|
+
"allow_remote",
|
|
374
|
+
"allow_apply",
|
|
375
|
+
"allow_unverified",
|
|
376
|
+
},
|
|
377
|
+
{"evaluation_root", "allow_typesafe_auxiliary"} if spec["schema_version"] == 2 else (),
|
|
378
|
+
)
|
|
379
|
+
authority["repo"] = str(directory(authority["repo"], base))
|
|
380
|
+
if authority["repo"] != target["repo"]:
|
|
381
|
+
raise ProtocolError("authority.repo does not match target.repo")
|
|
382
|
+
for name in ["export_paths", "write_paths"]:
|
|
383
|
+
path_list(authority[name], "authority." + name, empty=name == "write_paths")
|
|
384
|
+
if any(name not in allowed for name in authority["write_paths"]):
|
|
385
|
+
raise ProtocolError("write authority must enumerate target files exactly")
|
|
386
|
+
for name in ["allow_remote", "allow_apply", "allow_unverified"]:
|
|
387
|
+
boolean(authority[name], "authority." + name)
|
|
388
|
+
if "allow_typesafe_auxiliary" in authority:
|
|
389
|
+
boolean(authority["allow_typesafe_auxiliary"], "authority.allow_typesafe_auxiliary")
|
|
390
|
+
if not authority["allow_remote"]:
|
|
391
|
+
raise ProtocolError("remote export and execution have not been authorized")
|
|
392
|
+
source = fields(spec["source"], "source", {"paths", "new_paths"}, {"fixed_skill_roots"})
|
|
393
|
+
path_list(source["paths"], "source.paths")
|
|
394
|
+
path_list(source["new_paths"], "source.new_paths", empty=True)
|
|
395
|
+
if "fixed_skill_roots" in source:
|
|
396
|
+
path_list(source["fixed_skill_roots"], "source.fixed_skill_roots", empty=True)
|
|
397
|
+
if any(
|
|
398
|
+
not covered(name, authority["export_paths"])
|
|
399
|
+
for name in source["paths"] + source["new_paths"]
|
|
400
|
+
):
|
|
401
|
+
raise ProtocolError("source allowlist exceeds export authority")
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def validate_runtime(spec):
|
|
405
|
+
runtime = fields(
|
|
406
|
+
spec["runtime"],
|
|
407
|
+
"runtime",
|
|
408
|
+
{
|
|
409
|
+
"provider",
|
|
410
|
+
"ssh_target",
|
|
411
|
+
"remote_root",
|
|
412
|
+
"docker_binary",
|
|
413
|
+
"docker_version",
|
|
414
|
+
"codex_architecture",
|
|
415
|
+
"authentication",
|
|
416
|
+
*VERSIONS,
|
|
417
|
+
},
|
|
418
|
+
{"model", "effort", "tools", "sampling", "apparmor_profile", "model_proxy"},
|
|
419
|
+
)
|
|
420
|
+
choice(runtime["provider"], {"ssh"}, "runtime.provider")
|
|
421
|
+
if "model_proxy" in runtime and not re.fullmatch(
|
|
422
|
+
r"https?://[A-Za-z0-9][A-Za-z0-9.-]*:[0-9]{1,5}",
|
|
423
|
+
text(runtime["model_proxy"], "model_proxy"),
|
|
424
|
+
):
|
|
425
|
+
raise ProtocolError(
|
|
426
|
+
"model_proxy must name an existing HTTP proxy without credentials or paths"
|
|
427
|
+
)
|
|
428
|
+
if "apparmor_profile" in runtime and not re.fullmatch(
|
|
429
|
+
r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}",
|
|
430
|
+
text(runtime["apparmor_profile"], "apparmor_profile"),
|
|
431
|
+
):
|
|
432
|
+
raise ProtocolError(
|
|
433
|
+
"apparmor_profile must name an existing profile without options"
|
|
434
|
+
)
|
|
435
|
+
choice(
|
|
436
|
+
runtime["codex_architecture"],
|
|
437
|
+
{"aarch64", "x86_64"},
|
|
438
|
+
"runtime.codex_architecture",
|
|
439
|
+
)
|
|
440
|
+
if not re.fullmatch(
|
|
441
|
+
r"(?:[A-Za-z0-9._-]+@)?[A-Za-z0-9][A-Za-z0-9._-]*",
|
|
442
|
+
text(runtime["ssh_target"], "ssh_target"),
|
|
443
|
+
):
|
|
444
|
+
raise ProtocolError(
|
|
445
|
+
"ssh_target must be a host or user@host without SSH options"
|
|
446
|
+
)
|
|
447
|
+
for key in ["remote_root", "docker_binary"]:
|
|
448
|
+
remote_path(runtime[key])
|
|
449
|
+
if not re.fullmatch(
|
|
450
|
+
r"[0-9]+\.[0-9]+\.[0-9]+", text(runtime["docker_version"], "docker_version")
|
|
451
|
+
):
|
|
452
|
+
raise ProtocolError("docker_version must be explicit")
|
|
453
|
+
authority = spec["authority"]
|
|
454
|
+
if (authority["remote_host"], authority["remote_root"]) != (
|
|
455
|
+
runtime["ssh_target"],
|
|
456
|
+
runtime["remote_root"],
|
|
457
|
+
):
|
|
458
|
+
raise ProtocolError("remote host or output root is outside authority")
|
|
459
|
+
authentication = runtime["authentication"]
|
|
460
|
+
if authentication is not None:
|
|
461
|
+
fields(authentication, "authentication", {"mode", "path"})
|
|
462
|
+
choice(authentication["mode"], {"remote_codex_login"}, "authentication.mode")
|
|
463
|
+
remote_path(authentication["path"])
|
|
464
|
+
auth_path = None if authentication is None else authentication["path"]
|
|
465
|
+
if authority["auth_path"] != auth_path:
|
|
466
|
+
raise ProtocolError("authentication source is outside authority")
|
|
467
|
+
for key, version in VERSIONS.items():
|
|
468
|
+
if runtime[key] != version:
|
|
469
|
+
raise ProtocolError(f"runtime.{key}: expected {version}")
|
|
470
|
+
if spec["mode"] != "smoke" or "model" in runtime:
|
|
471
|
+
if authentication is None:
|
|
472
|
+
raise ProtocolError(
|
|
473
|
+
"model execution requires explicitly authorized remote Codex login"
|
|
474
|
+
)
|
|
475
|
+
for key in ["model", "effort", "tools", "sampling"]:
|
|
476
|
+
if key not in runtime:
|
|
477
|
+
raise ProtocolError(f"runtime.{key}: required for model execution")
|
|
478
|
+
text(runtime["model"], "runtime.model")
|
|
479
|
+
choice(
|
|
480
|
+
runtime["effort"],
|
|
481
|
+
{"minimal", "low", "medium", "high", "xhigh"},
|
|
482
|
+
"runtime.effort",
|
|
483
|
+
)
|
|
484
|
+
tools = rows(runtime["tools"], "runtime.tools")
|
|
485
|
+
if any(tool != "shell" for tool in tools) or len(tools) != len(set(tools)):
|
|
486
|
+
raise ProtocolError("only the explicit shell tool is supported")
|
|
487
|
+
sampling = fields(runtime["sampling"], "runtime.sampling", {"seed"})
|
|
488
|
+
if sampling["seed"] is not None:
|
|
489
|
+
raise ProtocolError(
|
|
490
|
+
"this Codex runtime has no controllable sampling seed; record null"
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def validate_measurement(spec):
|
|
495
|
+
budget = fields(
|
|
496
|
+
spec["budget"],
|
|
497
|
+
"budget",
|
|
498
|
+
{
|
|
499
|
+
"max_candidates",
|
|
500
|
+
"max_trials",
|
|
501
|
+
"max_parallel_trials",
|
|
502
|
+
"per_trial_timeout_sec",
|
|
503
|
+
"total_deadline_sec",
|
|
504
|
+
"recovery_window_sec",
|
|
505
|
+
"max_retries",
|
|
506
|
+
},
|
|
507
|
+
{"max_auxiliary_calls"} if spec["schema_version"] == 2 else (),
|
|
508
|
+
)
|
|
509
|
+
for key, value in budget.items():
|
|
510
|
+
number(
|
|
511
|
+
value,
|
|
512
|
+
"budget." + key,
|
|
513
|
+
minimum=0 if key in {"max_retries", "max_auxiliary_calls"} else 1,
|
|
514
|
+
integer=key.startswith("max_"),
|
|
515
|
+
)
|
|
516
|
+
if budget["max_parallel_trials"] > budget["max_trials"]:
|
|
517
|
+
raise ProtocolError("parallel trials exceed total trials")
|
|
518
|
+
if (
|
|
519
|
+
budget["per_trial_timeout_sec"] + budget["recovery_window_sec"]
|
|
520
|
+
> budget["total_deadline_sec"]
|
|
521
|
+
):
|
|
522
|
+
raise ProtocolError("trial timeout and recovery window exceed total deadline")
|
|
523
|
+
measurement = fields(
|
|
524
|
+
spec["measurement"],
|
|
525
|
+
"measurement",
|
|
526
|
+
{
|
|
527
|
+
"objective",
|
|
528
|
+
"primary_metric",
|
|
529
|
+
"minimum_effect",
|
|
530
|
+
"confidence_level",
|
|
531
|
+
"minimum_groups",
|
|
532
|
+
"repeats",
|
|
533
|
+
"seed",
|
|
534
|
+
"checkpoint",
|
|
535
|
+
"confirmation",
|
|
536
|
+
"weights",
|
|
537
|
+
},
|
|
538
|
+
{"quality_loss_limit", "minimum_cost_effect"}
|
|
539
|
+
| ({"minimum_effect_reason", "search_policy"} if spec["schema_version"] == 2 else set()),
|
|
540
|
+
)
|
|
541
|
+
choice(
|
|
542
|
+
measurement["objective"],
|
|
543
|
+
{"quality", "cost_at_quality"},
|
|
544
|
+
"measurement.objective",
|
|
545
|
+
)
|
|
546
|
+
choice(
|
|
547
|
+
measurement["primary_metric"],
|
|
548
|
+
{"task_quality"} if spec["schema_version"] == 2 else {"task_success"},
|
|
549
|
+
"measurement.primary_metric",
|
|
550
|
+
)
|
|
551
|
+
if spec["schema_version"] == 2:
|
|
552
|
+
if measurement["objective"] != "quality":
|
|
553
|
+
raise ProtocolError("v2 supports the frozen quality objective only")
|
|
554
|
+
text(measurement.get("minimum_effect_reason"), "minimum_effect_reason")
|
|
555
|
+
if spec["runtime"]["model"] != "gpt-6.1-sol" or spec["runtime"]["effort"] != "high":
|
|
556
|
+
raise ProtocolError("v2 quality experiments require gpt-6.1-sol/high")
|
|
557
|
+
if "search_policy" in measurement:
|
|
558
|
+
text(measurement["search_policy"], "measurement.search_policy")
|
|
559
|
+
choice(measurement["search_policy"], {"strict", "bounded_exploration_v1"}, "measurement.search_policy")
|
|
560
|
+
if measurement.get("search_policy") == "bounded_exploration_v1":
|
|
561
|
+
if spec["mode"] != "search" or budget["max_candidates"] != 1:
|
|
562
|
+
raise ProtocolError("bounded exploration requires search with exactly one candidate limit")
|
|
563
|
+
if spec["authority"]["allow_apply"] is not False or spec["authority"]["write_paths"]:
|
|
564
|
+
raise ProtocolError("bounded exploration cannot have apply or write authority")
|
|
565
|
+
number(measurement["minimum_effect"], "minimum_effect", maximum=1)
|
|
566
|
+
number(
|
|
567
|
+
measurement["confidence_level"], "confidence_level", minimum=0.5, maximum=0.999
|
|
568
|
+
)
|
|
569
|
+
number(measurement["minimum_groups"], "minimum_groups", minimum=2, integer=True)
|
|
570
|
+
number(measurement["repeats"], "repeats", minimum=1, integer=True)
|
|
571
|
+
number(measurement["seed"], "seed", integer=True)
|
|
572
|
+
choice(measurement["checkpoint"], {"fixed"}, "measurement.checkpoint")
|
|
573
|
+
choice(measurement["confirmation"], {"once"}, "measurement.confirmation")
|
|
574
|
+
if measurement["objective"] == "cost_at_quality":
|
|
575
|
+
for key in ["quality_loss_limit", "minimum_cost_effect"]:
|
|
576
|
+
if key not in measurement:
|
|
577
|
+
raise ProtocolError(f"measurement.{key}: required for cost objective")
|
|
578
|
+
number(
|
|
579
|
+
measurement[key],
|
|
580
|
+
key,
|
|
581
|
+
maximum=1 if key == "quality_loss_limit" else None,
|
|
582
|
+
)
|
|
583
|
+
else:
|
|
584
|
+
for key in ["quality_loss_limit", "minimum_cost_effect"]:
|
|
585
|
+
if key in measurement:
|
|
586
|
+
number(
|
|
587
|
+
measurement[key],
|
|
588
|
+
key,
|
|
589
|
+
maximum=1 if key == "quality_loss_limit" else None,
|
|
590
|
+
)
|
|
591
|
+
for guardrail in rows(spec["guardrails"], "guardrails"):
|
|
592
|
+
fields(guardrail, "guardrail", {"name", "minimum"})
|
|
593
|
+
text(guardrail["name"], "guardrail.name")
|
|
594
|
+
number(guardrail["minimum"], "guardrail.minimum", maximum=1)
|
|
595
|
+
names = [item["name"] for item in spec["guardrails"]]
|
|
596
|
+
if len(names) != len(set(names)):
|
|
597
|
+
raise ProtocolError("duplicate guardrail name")
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def verify_public_source(case, root, views):
|
|
601
|
+
if case["source"]["kind"] != "pro_v2":
|
|
602
|
+
return
|
|
603
|
+
top = Path(os.fsdecode(git(root, "rev-parse", "--show-toplevel")).strip()).resolve()
|
|
604
|
+
if git(top, "rev-parse", "HEAD").decode().strip() != PRO_V2_REVISION:
|
|
605
|
+
raise ProtocolError("public task checkout is not at the fixed source revision")
|
|
606
|
+
prefix = root.relative_to(top).as_posix()
|
|
607
|
+
if len(PurePosixPath(prefix).parts) != 3 or not prefix.startswith("v2/tasks/"):
|
|
608
|
+
raise ProtocolError(
|
|
609
|
+
"public task must come from the fixed source task directory"
|
|
610
|
+
)
|
|
611
|
+
files = {name: row for view in views.values() for name, row in view.items()}
|
|
612
|
+
tracked = {}
|
|
613
|
+
for entry in git(
|
|
614
|
+
top, "ls-tree", "-rz", "--full-tree", PRO_V2_REVISION, "--", prefix
|
|
615
|
+
).split(b"\x00"):
|
|
616
|
+
if not entry:
|
|
617
|
+
continue
|
|
618
|
+
metadata, name = entry.split(b"\t", 1)
|
|
619
|
+
mode, kind, digest = metadata.decode().split()
|
|
620
|
+
relative = PurePosixPath(os.fsdecode(name)).relative_to(prefix).as_posix()
|
|
621
|
+
tracked[relative] = (mode, kind, digest)
|
|
622
|
+
if set(files) != set(tracked):
|
|
623
|
+
raise ProtocolError("public task views must account for every frozen task file")
|
|
624
|
+
for name, row in files.items():
|
|
625
|
+
mode, kind, digest = tracked[name]
|
|
626
|
+
blob_id = hashlib.sha1(
|
|
627
|
+
b"blob " + str(len(row["content"])).encode() + b"\x00" + row["content"]
|
|
628
|
+
).hexdigest()
|
|
629
|
+
expected_mode = "100755" if row["mode"] & 0o111 else "100644"
|
|
630
|
+
if kind != "blob" or mode != expected_mode or blob_id != digest:
|
|
631
|
+
raise ProtocolError(
|
|
632
|
+
"public task file differs from the fixed source revision"
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def task_files(spec, base):
|
|
637
|
+
case_files, ids, split_groups, split_families, split_tasks = {}, set(), {}, {}, {}
|
|
638
|
+
family_groups, task_groups = {}, {}
|
|
639
|
+
for case in rows(spec["cases"], "cases"):
|
|
640
|
+
fields(
|
|
641
|
+
case,
|
|
642
|
+
"case",
|
|
643
|
+
{
|
|
644
|
+
"case_id",
|
|
645
|
+
"group_id",
|
|
646
|
+
"repo_id",
|
|
647
|
+
"base_commit",
|
|
648
|
+
"split",
|
|
649
|
+
"task_path",
|
|
650
|
+
"source",
|
|
651
|
+
"public_paths",
|
|
652
|
+
"grader_paths",
|
|
653
|
+
"oracle_paths",
|
|
654
|
+
"image",
|
|
655
|
+
"observable_requirements",
|
|
656
|
+
"information_cutoff",
|
|
657
|
+
},
|
|
658
|
+
{"grader_ref", "platform", "repo_path"},
|
|
659
|
+
)
|
|
660
|
+
for key in ["case_id", "group_id", "repo_id"]:
|
|
661
|
+
text(case[key], "case." + key)
|
|
662
|
+
if case["case_id"] in ids:
|
|
663
|
+
raise ProtocolError("duplicate case_id")
|
|
664
|
+
ids.add(case["case_id"])
|
|
665
|
+
fingerprint(case["base_commit"], "case.base_commit", 40)
|
|
666
|
+
choice(case["split"], {"search", "validation", "confirmation"}, "case.split")
|
|
667
|
+
source = fields(case["source"], "case.source", {"kind", "revision", "family"})
|
|
668
|
+
choice(source["kind"], {"repo", "pro_v2"}, "case.source.kind")
|
|
669
|
+
text(source["family"], "case.source.family")
|
|
670
|
+
fingerprint(source["revision"], "case.source.revision", 40)
|
|
671
|
+
expected_revision = (
|
|
672
|
+
PRO_V2_REVISION if source["kind"] == "pro_v2" else case["base_commit"]
|
|
673
|
+
)
|
|
674
|
+
if source["revision"] != expected_revision:
|
|
675
|
+
raise ProtocolError("task source revision does not match the frozen source")
|
|
676
|
+
target = spec["target"]
|
|
677
|
+
if target["kind"] == "agents" and (
|
|
678
|
+
case["repo_id"] != target["repo_id"]
|
|
679
|
+
or case["base_commit"]
|
|
680
|
+
not in [target["base_commit"], *target.get("compatible_bases", [])]
|
|
681
|
+
):
|
|
682
|
+
raise ProtocolError(
|
|
683
|
+
"AGENTS task must match the target repository and an explicitly compatible base"
|
|
684
|
+
)
|
|
685
|
+
if not re.fullmatch(
|
|
686
|
+
r"[^\s]+@sha256:[0-9a-f]{64}", text(case["image"], "case.image")
|
|
687
|
+
):
|
|
688
|
+
raise ProtocolError("task image must be pinned by digest")
|
|
689
|
+
case.setdefault("platform", "linux/amd64")
|
|
690
|
+
platforms = {"linux/amd64"}
|
|
691
|
+
if spec["schema_version"] == 2 and source["kind"] == "repo":
|
|
692
|
+
platforms.add("linux/arm64")
|
|
693
|
+
choice(case["platform"], platforms, "case.platform")
|
|
694
|
+
for item in rows(case["observable_requirements"], "observable_requirements"):
|
|
695
|
+
text(item, "observable requirement")
|
|
696
|
+
try:
|
|
697
|
+
cutoff = datetime.fromisoformat(
|
|
698
|
+
text(case["information_cutoff"], "information_cutoff").replace(
|
|
699
|
+
"Z", "+00:00"
|
|
700
|
+
)
|
|
701
|
+
)
|
|
702
|
+
except ValueError:
|
|
703
|
+
raise ProtocolError("information_cutoff must be an ISO timestamp") from None
|
|
704
|
+
if cutoff.tzinfo is None:
|
|
705
|
+
raise ProtocolError("information_cutoff must include timezone")
|
|
706
|
+
root = directory(case["task_path"], base)
|
|
707
|
+
case["task_path"] = str(root)
|
|
708
|
+
views = {}
|
|
709
|
+
for key in ["public_paths", "grader_paths", "oracle_paths"]:
|
|
710
|
+
names = path_list(case[key], "case." + key)
|
|
711
|
+
views[key] = {name: read_file(root, name) for name in names}
|
|
712
|
+
if "instruction.md" not in views["public_paths"]:
|
|
713
|
+
raise ProtocolError("public task view must contain instruction.md")
|
|
714
|
+
if "repo_path" not in case:
|
|
715
|
+
match = re.search(
|
|
716
|
+
r"A code repository is available in the `([^`]+)` directory",
|
|
717
|
+
views["public_paths"]["instruction.md"]["content"].decode("utf-8"),
|
|
718
|
+
)
|
|
719
|
+
if match is None:
|
|
720
|
+
raise ProtocolError(
|
|
721
|
+
"case.repo_path is required when the public task does not declare it"
|
|
722
|
+
)
|
|
723
|
+
case["repo_path"] = match[1]
|
|
724
|
+
remote_path(case["repo_path"])
|
|
725
|
+
if any(
|
|
726
|
+
covered(case["repo_path"], [name])
|
|
727
|
+
for name in [
|
|
728
|
+
"/evolution-control",
|
|
729
|
+
"/evolution-input",
|
|
730
|
+
"/home/evolution",
|
|
731
|
+
"/tests",
|
|
732
|
+
"/solution",
|
|
733
|
+
]
|
|
734
|
+
):
|
|
735
|
+
raise ProtocolError(
|
|
736
|
+
"task repository overlaps the runtime control namespace"
|
|
737
|
+
)
|
|
738
|
+
public = set(views["public_paths"])
|
|
739
|
+
private = set(views["grader_paths"]) | set(views["oracle_paths"])
|
|
740
|
+
if public & private or any(
|
|
741
|
+
PurePosixPath(name).parts[0] in {"tests", "solution", "answers"}
|
|
742
|
+
for name in public
|
|
743
|
+
):
|
|
744
|
+
raise ProtocolError("public task view exposes grader or oracle")
|
|
745
|
+
public_inodes = {(root / name).stat().st_ino for name in public}
|
|
746
|
+
private_inodes = {(root / name).stat().st_ino for name in private}
|
|
747
|
+
if public_inodes & private_inodes:
|
|
748
|
+
raise ProtocolError("public task view aliases private material")
|
|
749
|
+
verify_public_source(case, root, views)
|
|
750
|
+
grader_ref = bundle_hash(views["grader_paths"])
|
|
751
|
+
if "grader_ref" in case and case["grader_ref"] != grader_ref:
|
|
752
|
+
raise ProtocolError("grader_ref does not match the actual grading files")
|
|
753
|
+
case["grader_ref"] = grader_ref
|
|
754
|
+
identity = bundle_hash(views["public_paths"])
|
|
755
|
+
for mapping, key in [
|
|
756
|
+
(family_groups, (case["repo_id"], source["family"])),
|
|
757
|
+
(task_groups, (case["repo_id"], identity)),
|
|
758
|
+
]:
|
|
759
|
+
if mapping.setdefault(key, case["group_id"]) != case["group_id"]:
|
|
760
|
+
raise ProtocolError(
|
|
761
|
+
"related task sources cannot count as independent groups"
|
|
762
|
+
)
|
|
763
|
+
for mapping, key in [
|
|
764
|
+
(split_groups, (case["repo_id"], case["group_id"])),
|
|
765
|
+
(split_families, (case["repo_id"], source["family"])),
|
|
766
|
+
(split_tasks, (case["repo_id"], identity)),
|
|
767
|
+
]:
|
|
768
|
+
if key in mapping and mapping[key] != case["split"]:
|
|
769
|
+
raise ProtocolError(
|
|
770
|
+
"related tasks cannot cross search/validation/confirmation splits"
|
|
771
|
+
)
|
|
772
|
+
mapping[key] = case["split"]
|
|
773
|
+
case_files[case["case_id"]] = views
|
|
774
|
+
splits = {case["split"] for case in spec["cases"]}
|
|
775
|
+
bounded_exploration = (spec["schema_version"] == 2 and spec["mode"] == "search"
|
|
776
|
+
and spec["measurement"].get("search_policy") == "bounded_exploration_v1")
|
|
777
|
+
if "search" not in splits or (
|
|
778
|
+
spec["mode"] not in {"smoke", "calibrate"} and not bounded_exploration and "confirmation" not in splits
|
|
779
|
+
):
|
|
780
|
+
raise ProtocolError(
|
|
781
|
+
"mode requires search and, for comparisons, heldout confirmation tasks"
|
|
782
|
+
)
|
|
783
|
+
if spec["mode"] == "calibrate" and splits != {"search"}:
|
|
784
|
+
raise ProtocolError("calibrate does not expose confirmation or validation tasks")
|
|
785
|
+
weights = spec["measurement"]["weights"]
|
|
786
|
+
if not isinstance(weights, dict) or set(weights) != ids:
|
|
787
|
+
raise ProtocolError("measurement.weights must name every case exactly once")
|
|
788
|
+
for weight in weights.values():
|
|
789
|
+
number(weight, "case weight", minimum=0.000001)
|
|
790
|
+
return case_files
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
def skill_name(content):
|
|
794
|
+
header = re.match(rb"\A---\r?\n(.*?)\r?\n---(?:\r?\n|\Z)", content, re.DOTALL)
|
|
795
|
+
if header is None:
|
|
796
|
+
return None
|
|
797
|
+
name = re.search(
|
|
798
|
+
rb"(?m)^name:[ \t]*['\"]?([a-z0-9][a-z0-9-]*)['\"]?[ \t]*(?:#.*)?$", header[1]
|
|
799
|
+
)
|
|
800
|
+
return name[1].decode() if name else None
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
def condition_bundles(spec, baseline):
|
|
804
|
+
target = spec["target"]
|
|
805
|
+
surfaces = target["surfaces"]
|
|
806
|
+
skill_roots = {
|
|
807
|
+
str(PurePosixPath(path).parent)
|
|
808
|
+
for path in surfaces["skill"]
|
|
809
|
+
if PurePosixPath(path).name == "SKILL.md"
|
|
810
|
+
}
|
|
811
|
+
for root in skill_roots:
|
|
812
|
+
if any(
|
|
813
|
+
covered(path, [root]) and path not in surfaces["skill"] for path in baseline
|
|
814
|
+
):
|
|
815
|
+
raise ProtocolError(
|
|
816
|
+
"skill surface omits files belonging to the target skill"
|
|
817
|
+
)
|
|
818
|
+
target_skills = [
|
|
819
|
+
baseline[path]["content"]
|
|
820
|
+
for path in surfaces["skill"]
|
|
821
|
+
if path in baseline and PurePosixPath(path).name == "SKILL.md"
|
|
822
|
+
]
|
|
823
|
+
target_names = {skill_name(content) for content in target_skills}
|
|
824
|
+
if surfaces["skill"] and (not target_skills or None in target_names):
|
|
825
|
+
raise ProtocolError(
|
|
826
|
+
"target skill requires SKILL.md with a canonical name field"
|
|
827
|
+
)
|
|
828
|
+
for path, row in baseline.items():
|
|
829
|
+
if (
|
|
830
|
+
path not in surfaces["skill"]
|
|
831
|
+
and PurePosixPath(path).name == "SKILL.md"
|
|
832
|
+
and (
|
|
833
|
+
row["content"] in target_skills
|
|
834
|
+
or skill_name(row["content"]) in target_names
|
|
835
|
+
)
|
|
836
|
+
):
|
|
837
|
+
raise ProtocolError(
|
|
838
|
+
"target skill has another readable copy outside its surface"
|
|
839
|
+
)
|
|
840
|
+
bundles, levels, differences = {}, {"skill": {}, "agents": {}}, {}
|
|
841
|
+
for condition in rows(spec["conditions"], "conditions"):
|
|
842
|
+
fields(condition, "condition", {"condition_id", "skill", "agents", "overrides"})
|
|
843
|
+
name = text(condition["condition_id"], "condition_id")
|
|
844
|
+
if name in bundles:
|
|
845
|
+
raise ProtocolError("duplicate condition_id")
|
|
846
|
+
choice(
|
|
847
|
+
condition["skill"],
|
|
848
|
+
{"absent", "baseline", "candidate", "fixed"},
|
|
849
|
+
"condition.skill",
|
|
850
|
+
)
|
|
851
|
+
choice(
|
|
852
|
+
condition["agents"],
|
|
853
|
+
{"minimal", "baseline", "candidate", "fixed"},
|
|
854
|
+
"condition.agents",
|
|
855
|
+
)
|
|
856
|
+
if not isinstance(condition["overrides"], dict):
|
|
857
|
+
raise ProtocolError("condition.overrides must be a path-to-content object")
|
|
858
|
+
files = deepcopy(baseline)
|
|
859
|
+
changed = []
|
|
860
|
+
for path, content in condition["overrides"].items():
|
|
861
|
+
export_path(path)
|
|
862
|
+
if path not in target["allowed_paths"]:
|
|
863
|
+
raise ProtocolError(f"condition changes an undeclared surface: {path}")
|
|
864
|
+
if content is None:
|
|
865
|
+
files.pop(path, None)
|
|
866
|
+
elif isinstance(content, str):
|
|
867
|
+
raw = content.encode("utf-8")
|
|
868
|
+
files[path] = {
|
|
869
|
+
"content": raw,
|
|
870
|
+
"sha256": sha256(raw),
|
|
871
|
+
"mode": baseline.get(path, {}).get("mode", 0o644),
|
|
872
|
+
}
|
|
873
|
+
else:
|
|
874
|
+
raise ProtocolError("condition content must be text or null")
|
|
875
|
+
if baseline.get(path) != files.get(path):
|
|
876
|
+
changed.append(path)
|
|
877
|
+
for rule in target["protected_rules"]:
|
|
878
|
+
if condition["skill"] == "absent" and rule["path"] in surfaces["skill"]:
|
|
879
|
+
continue
|
|
880
|
+
if rule["verification"] == "exact" and rule[
|
|
881
|
+
"text"
|
|
882
|
+
].encode() not in files.get(rule["path"], {}).get("content", b""):
|
|
883
|
+
raise ProtocolError(
|
|
884
|
+
f"condition removes a protected rule from {rule['path']}"
|
|
885
|
+
)
|
|
886
|
+
for surface, paths in surfaces.items():
|
|
887
|
+
level = condition[surface]
|
|
888
|
+
content_hash = bundle_hash(
|
|
889
|
+
{path: files[path] for path in paths if path in files}
|
|
890
|
+
)
|
|
891
|
+
prior = levels[surface]
|
|
892
|
+
if level in prior and prior[level] != content_hash:
|
|
893
|
+
raise ProtocolError(f"same {surface} level has different bytes")
|
|
894
|
+
if level not in prior and content_hash in prior.values():
|
|
895
|
+
raise ProtocolError(f"different {surface} levels have identical bytes")
|
|
896
|
+
prior[level] = content_hash
|
|
897
|
+
if not paths and level != "fixed":
|
|
898
|
+
raise ProtocolError(f"{surface} factor is declared without a surface")
|
|
899
|
+
if level == "absent" and any(path in files for path in paths):
|
|
900
|
+
raise ProtocolError(
|
|
901
|
+
"absent skill condition still contains target skill files"
|
|
902
|
+
)
|
|
903
|
+
if level in {"baseline", "fixed"} and any(
|
|
904
|
+
files.get(path) != baseline.get(path) for path in paths
|
|
905
|
+
):
|
|
906
|
+
raise ProtocolError(
|
|
907
|
+
f"{surface} baseline/fixed condition changes actual bytes"
|
|
908
|
+
)
|
|
909
|
+
bundles[name] = files
|
|
910
|
+
differences[name] = sorted(changed)
|
|
911
|
+
initial = text(spec["baseline_condition"], "baseline_condition")
|
|
912
|
+
if initial not in bundles or bundles[initial] != baseline:
|
|
913
|
+
raise ProtocolError(
|
|
914
|
+
"baseline_condition must reproduce the actual initial bytes"
|
|
915
|
+
)
|
|
916
|
+
if spec["mode"] not in {"smoke", "calibrate"}:
|
|
917
|
+
selected = spec.get("candidate_condition")
|
|
918
|
+
if selected is not None:
|
|
919
|
+
text(selected, "candidate_condition")
|
|
920
|
+
if spec["mode"] == "compare" and (
|
|
921
|
+
selected not in bundles or selected == initial
|
|
922
|
+
):
|
|
923
|
+
raise ProtocolError(
|
|
924
|
+
"compare requires a distinct frozen candidate_condition"
|
|
925
|
+
)
|
|
926
|
+
if target["kind"] == "skill" and "absent" not in levels["skill"]:
|
|
927
|
+
raise ProtocolError("skill comparison requires a no-target-skill control")
|
|
928
|
+
return bundles, differences
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def candidate_from_patch(job, parent, proposal, patch, *, search_records=None):
|
|
932
|
+
fields(
|
|
933
|
+
proposal,
|
|
934
|
+
"proposal",
|
|
935
|
+
{
|
|
936
|
+
"parent_hash",
|
|
937
|
+
"hypothesis",
|
|
938
|
+
"expected_effect",
|
|
939
|
+
"counterexample",
|
|
940
|
+
"changed_paths",
|
|
941
|
+
} | ({"mechanism"} if job.spec["schema_version"] == 2 else set()),
|
|
942
|
+
)
|
|
943
|
+
if proposal["parent_hash"] != bundle_hash(parent):
|
|
944
|
+
raise ProtocolError("proposal does not name the frozen parent")
|
|
945
|
+
for key in ["hypothesis", "expected_effect", "counterexample"]:
|
|
946
|
+
text(proposal[key], "proposal." + key)
|
|
947
|
+
if job.spec["schema_version"] == 2:
|
|
948
|
+
from quality import continuous_item
|
|
949
|
+
|
|
950
|
+
mechanism = fields(proposal["mechanism"], "one mechanism", {
|
|
951
|
+
"criterion_id", "patch_sha256", "cause", "expected_criteria", "regression_risk"})
|
|
952
|
+
for key in ("criterion_id", "cause", "regression_risk"):
|
|
953
|
+
text(mechanism[key], "mechanism." + key)
|
|
954
|
+
fingerprint(mechanism["patch_sha256"], "mechanism artifact")
|
|
955
|
+
rubrics = {c["case_id"]: parse_json(job.evaluation_files[job.spec["evaluation"]["rubrics"][c["case_id"]]]["content"])
|
|
956
|
+
for c in job.spec["cases"] if c["split"] == "search"}
|
|
957
|
+
known = {c["criterion_id"] for r in rubrics.values() for c in r["criteria"]}
|
|
958
|
+
expected = rows(mechanism["expected_criteria"], "expected quality criteria")
|
|
959
|
+
if any(not isinstance(name, str) or name not in known for name in expected) or len(set(expected)) != len(expected) or mechanism["criterion_id"] not in expected:
|
|
960
|
+
raise ProtocolError("mechanism must target the observed public quality criterion")
|
|
961
|
+
supported = False
|
|
962
|
+
for record in search_records or []:
|
|
963
|
+
if not (record.get("schema_version") == 2 and record.get("purpose") == "task"
|
|
964
|
+
and record.get("split") == "search" and record.get("status") in {"ok", "behavior_failure"}
|
|
965
|
+
and record.get("candidate_hash") == proposal["parent_hash"]
|
|
966
|
+
and record.get("patch_sha256") == mechanism["patch_sha256"]
|
|
967
|
+
and type(record.get("task_success")) is int and record["task_success"] in (0, 1)
|
|
968
|
+
and record.get("quality_status") == "valid"):
|
|
969
|
+
continue
|
|
970
|
+
criterion = next((c for c in rubrics.get(record.get("case_id"), {}).get("criteria", [])
|
|
971
|
+
if c["criterion_id"] == mechanism["criterion_id"]), None)
|
|
972
|
+
component = record.get("quality_components", {}).get(mechanism["criterion_id"], {})
|
|
973
|
+
if (criterion is not None
|
|
974
|
+
and (not criterion["gate"] or mechanism["criterion_id"] in record.get("failed_gates", []))
|
|
975
|
+
and component.get("status") == "valid" and type(component.get("level")) in (int, float)
|
|
976
|
+
and (0 <= component["level"] < 1 if continuous_item(rubrics[record["case_id"]], criterion)
|
|
977
|
+
else component["level"] in (0, 0.5))):
|
|
978
|
+
supported = True
|
|
979
|
+
break
|
|
980
|
+
if not supported:
|
|
981
|
+
raise ProtocolError("mechanism lacks a measured current-parent search weakness")
|
|
982
|
+
declared = path_list(proposal["changed_paths"], "proposal.changed_paths")
|
|
983
|
+
allowed = job.spec["target"]["allowed_paths"]
|
|
984
|
+
if any(name not in allowed for name in declared):
|
|
985
|
+
raise ProtocolError("proposal changes paths outside the target surface")
|
|
986
|
+
with tempfile.TemporaryDirectory(prefix="instruction-candidate-") as temporary:
|
|
987
|
+
root = Path(temporary)
|
|
988
|
+
for name, row in parent.items():
|
|
989
|
+
destination = root / name
|
|
990
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
991
|
+
destination.write_bytes(row["content"])
|
|
992
|
+
destination.chmod(row["mode"])
|
|
993
|
+
applied = subprocess.run(
|
|
994
|
+
[
|
|
995
|
+
"git",
|
|
996
|
+
"-c",
|
|
997
|
+
"core.hooksPath=/dev/null",
|
|
998
|
+
"-c",
|
|
999
|
+
"core.fsmonitor=false",
|
|
1000
|
+
"apply",
|
|
1001
|
+
"--binary",
|
|
1002
|
+
"-",
|
|
1003
|
+
],
|
|
1004
|
+
cwd=root,
|
|
1005
|
+
input=patch,
|
|
1006
|
+
capture_output=True,
|
|
1007
|
+
)
|
|
1008
|
+
if applied.returncode:
|
|
1009
|
+
raise ProtocolError(
|
|
1010
|
+
"candidate patch cannot be applied safely to its parent"
|
|
1011
|
+
)
|
|
1012
|
+
candidate = {}
|
|
1013
|
+
for path in sorted(root.rglob("*")):
|
|
1014
|
+
if path.is_dir() and not path.is_symlink():
|
|
1015
|
+
continue
|
|
1016
|
+
name = path.relative_to(root).as_posix()
|
|
1017
|
+
candidate[name] = read_file(root, name)
|
|
1018
|
+
changed = sorted(
|
|
1019
|
+
name
|
|
1020
|
+
for name in parent.keys() | candidate.keys()
|
|
1021
|
+
if parent.get(name) != candidate.get(name)
|
|
1022
|
+
)
|
|
1023
|
+
if changed != sorted(declared):
|
|
1024
|
+
raise ProtocolError("proposal changed_paths differs from the actual patch")
|
|
1025
|
+
for rule in job.spec["target"]["protected_rules"]:
|
|
1026
|
+
if rule["verification"] == "exact" and rule[
|
|
1027
|
+
"text"
|
|
1028
|
+
].encode() not in candidate.get(rule["path"], {}).get("content", b""):
|
|
1029
|
+
raise ProtocolError("candidate removes a protected text block")
|
|
1030
|
+
references = set()
|
|
1031
|
+
for views in job.case_files.values():
|
|
1032
|
+
for row in views["oracle_paths"].values():
|
|
1033
|
+
content = row["content"].strip()
|
|
1034
|
+
if len(content) >= 16:
|
|
1035
|
+
references.add(content)
|
|
1036
|
+
references.update(
|
|
1037
|
+
line[1:].strip()
|
|
1038
|
+
for line in content.splitlines()
|
|
1039
|
+
if line.startswith(b"+")
|
|
1040
|
+
and not line.startswith(b"+++")
|
|
1041
|
+
and len(line.strip()) >= 33
|
|
1042
|
+
)
|
|
1043
|
+
for name in changed:
|
|
1044
|
+
raw = candidate.get(name, {}).get("content", b"")
|
|
1045
|
+
original = parent.get(name, {}).get("content", b"")
|
|
1046
|
+
if any(
|
|
1047
|
+
reference in raw and reference not in original for reference in references
|
|
1048
|
+
):
|
|
1049
|
+
raise ProtocolError("candidate contains known private reference content")
|
|
1050
|
+
for case in job.spec["cases"]:
|
|
1051
|
+
if len(case["case_id"]) >= 8 and case["case_id"].encode() in raw:
|
|
1052
|
+
raise ProtocolError("candidate contains restricted task identifiers")
|
|
1053
|
+
if Path(name).name == "SKILL.md" and name in parent:
|
|
1054
|
+
if skill_name(raw) != skill_name(parent[name]["content"]):
|
|
1055
|
+
raise ProtocolError("candidate changes the skill invocation name")
|
|
1056
|
+
return candidate
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
@dataclass
|
|
1060
|
+
class PreparedJob:
|
|
1061
|
+
spec: dict
|
|
1062
|
+
bundles: dict
|
|
1063
|
+
case_files: dict
|
|
1064
|
+
method_files: dict
|
|
1065
|
+
deleted_paths: list
|
|
1066
|
+
differences: dict
|
|
1067
|
+
minimum_calls: int
|
|
1068
|
+
confirmation_reserve: int
|
|
1069
|
+
evaluation_files: dict = field(default_factory=dict)
|
|
1070
|
+
worker_files: dict = field(default_factory=dict)
|
|
1071
|
+
call_plan: dict = field(default_factory=dict)
|
|
1072
|
+
|
|
1073
|
+
@property
|
|
1074
|
+
def bundle_hashes(self):
|
|
1075
|
+
return {name: bundle_hash(files) for name, files in self.bundles.items()}
|
|
1076
|
+
|
|
1077
|
+
def inputs(self):
|
|
1078
|
+
result = {
|
|
1079
|
+
"bundles": {name: manifest(files) for name, files in self.bundles.items()},
|
|
1080
|
+
"cases": {
|
|
1081
|
+
name: {view: manifest(files) for view, files in views.items()}
|
|
1082
|
+
for name, views in self.case_files.items()
|
|
1083
|
+
},
|
|
1084
|
+
"method": manifest(self.method_files),
|
|
1085
|
+
"deleted_paths": self.deleted_paths,
|
|
1086
|
+
}
|
|
1087
|
+
if self.spec["schema_version"] == 2:
|
|
1088
|
+
result.update(evaluation=manifest(self.evaluation_files), workers=manifest(self.worker_files))
|
|
1089
|
+
return result
|
|
1090
|
+
|
|
1091
|
+
@property
|
|
1092
|
+
def protocol_hash(self):
|
|
1093
|
+
return sha256(canonical({"spec": self.spec, "inputs": self.inputs()}))
|
|
1094
|
+
|
|
1095
|
+
def summary(self):
|
|
1096
|
+
return {
|
|
1097
|
+
"schema_version": self.spec["schema_version"],
|
|
1098
|
+
"protocol_id": self.spec["protocol_id"],
|
|
1099
|
+
"protocol_hash": self.protocol_hash,
|
|
1100
|
+
"mode": self.spec["mode"],
|
|
1101
|
+
"configuration": deepcopy(self.spec),
|
|
1102
|
+
"export_files": sorted(self.bundles[self.spec["baseline_condition"]]),
|
|
1103
|
+
"condition_differences": self.differences,
|
|
1104
|
+
"bundle_hashes": self.bundle_hashes,
|
|
1105
|
+
"minimum_calls": self.minimum_calls,
|
|
1106
|
+
"confirmation_reserve": self.confirmation_reserve,
|
|
1107
|
+
"call_upper_bound": min(
|
|
1108
|
+
self.spec["budget"]["max_trials"],
|
|
1109
|
+
self.minimum_calls * (1 + self.spec["budget"]["max_retries"]),
|
|
1110
|
+
)
|
|
1111
|
+
if self.spec["mode"] == "smoke"
|
|
1112
|
+
else self.spec["budget"]["max_trials"],
|
|
1113
|
+
"effect_eligible": self.spec["mode"] not in {"smoke", "calibrate"},
|
|
1114
|
+
**({"call_plan": self.call_plan} if self.spec["schema_version"] == 2 else {}),
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
def optimizer_view(self):
|
|
1118
|
+
cases = []
|
|
1119
|
+
for case in self.spec["cases"]:
|
|
1120
|
+
if case["split"] != "search":
|
|
1121
|
+
continue
|
|
1122
|
+
cases.append(
|
|
1123
|
+
{
|
|
1124
|
+
"case_id": case["case_id"],
|
|
1125
|
+
"group_id": case["group_id"],
|
|
1126
|
+
"repo_id": case["repo_id"],
|
|
1127
|
+
"observable_requirements": case["observable_requirements"],
|
|
1128
|
+
"files": {
|
|
1129
|
+
name: row["content"].decode("utf-8")
|
|
1130
|
+
for name, row in self.case_files[case["case_id"]][
|
|
1131
|
+
"public_paths"
|
|
1132
|
+
].items()
|
|
1133
|
+
},
|
|
1134
|
+
}
|
|
1135
|
+
)
|
|
1136
|
+
return {"schema_version": 1, "cases": cases}
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
def evaluation_files(spec, base):
|
|
1140
|
+
from quality import CONTINUOUS_SCORING, validate_calibration, validate_rubric, validate_auxiliary_rubric
|
|
1141
|
+
|
|
1142
|
+
evaluation = fields(spec.get("evaluation"), "evaluation", {
|
|
1143
|
+
"root", "paths", "rubrics", "calibration",
|
|
1144
|
+
}, {"auxiliary", "scoring"})
|
|
1145
|
+
if "scoring" in evaluation:
|
|
1146
|
+
choice(evaluation["scoring"], {CONTINUOUS_SCORING}, "evaluation.scoring")
|
|
1147
|
+
root = directory(evaluation["root"], base)
|
|
1148
|
+
authority_root = directory(spec["authority"].get("evaluation_root"), base)
|
|
1149
|
+
if root != authority_root:
|
|
1150
|
+
raise ProtocolError("evaluation root is outside explicit authority")
|
|
1151
|
+
evaluation["root"] = spec["authority"]["evaluation_root"] = str(root)
|
|
1152
|
+
files = {name: read_file(root, name) for name in path_list(evaluation["paths"], "evaluation.paths")}
|
|
1153
|
+
|
|
1154
|
+
def document(name):
|
|
1155
|
+
relative_path(name)
|
|
1156
|
+
if name not in files:
|
|
1157
|
+
raise ProtocolError("evaluation file is outside its allowlist")
|
|
1158
|
+
return parse_json(files[name]["content"])
|
|
1159
|
+
|
|
1160
|
+
ids = {case["case_id"] for case in spec["cases"]}
|
|
1161
|
+
if not isinstance(evaluation["rubrics"], dict) or evaluation["rubrics"].keys() != ids:
|
|
1162
|
+
raise ProtocolError("evaluation.rubrics must name every case exactly once")
|
|
1163
|
+
rubrics = {case_id: validate_rubric(document(name), case_id=case_id)
|
|
1164
|
+
for case_id, name in evaluation["rubrics"].items()}
|
|
1165
|
+
scoring = {r.get("scoring") for r in rubrics.values()}
|
|
1166
|
+
if len(scoring) != 1 or ("scoring" in evaluation and evaluation["scoring"] not in scoring):
|
|
1167
|
+
raise ProtocolError("quality rubrics use different scoring contracts")
|
|
1168
|
+
status_formats = {r.get("derive_assessment_status", False) for r in rubrics.values()
|
|
1169
|
+
if any(c["checker_kind"] == "judge" for c in r["criteria"])}
|
|
1170
|
+
if len(status_formats) > 1:
|
|
1171
|
+
raise ProtocolError("quality rubrics use different assessment status formats")
|
|
1172
|
+
if next(iter(scoring)) is not None:
|
|
1173
|
+
evaluation["scoring"] = next(iter(scoring))
|
|
1174
|
+
for case in spec["cases"]:
|
|
1175
|
+
public = {name: read_file(Path(case["task_path"]), name)["content"].decode("utf-8")
|
|
1176
|
+
for name in case["public_paths"]}
|
|
1177
|
+
if evaluation.get("scoring") == CONTINUOUS_SCORING:
|
|
1178
|
+
if "task-contract.md" in public:
|
|
1179
|
+
raise ProtocolError("task-contract.md is reserved for declared observable requirements")
|
|
1180
|
+
public["task-contract.md"] = task_contract(case).decode("utf-8")
|
|
1181
|
+
for criterion in rubrics[case["case_id"]]["criteria"]:
|
|
1182
|
+
name, separator, quote = criterion["public_requirement_ref"].partition(": ")
|
|
1183
|
+
if not separator or name not in public or not quote or quote not in public[name]:
|
|
1184
|
+
raise ProtocolError("quality criterion must reference an actual public requirement")
|
|
1185
|
+
calibration = validate_calibration(document(evaluation["calibration"]), files, rubrics)
|
|
1186
|
+
if any(next(c for c in spec["cases"] if c["case_id"] == a["case_id"])["split"] != "search"
|
|
1187
|
+
for a in calibration["artifacts"]):
|
|
1188
|
+
raise ProtocolError("calibration cannot consume heldout tasks")
|
|
1189
|
+
workers = {name: read_file(Path(__file__).parent, name) for name in ("quality.py", "remote.py")}
|
|
1190
|
+
if evaluation.get("scoring") == CONTINUOUS_SCORING:
|
|
1191
|
+
workers.update({name: read_file(Path(__file__).parent, name) for name in ("protocol.py", "evolution.py", "compare.py")})
|
|
1192
|
+
auxiliary = evaluation.get("auxiliary")
|
|
1193
|
+
if auxiliary is not None:
|
|
1194
|
+
fields(auxiliary, "evaluation.auxiliary", {
|
|
1195
|
+
"provider", "model", "criterion_ids", "rubric_path", "calibration_path", "authentication",
|
|
1196
|
+
}, {"long_context"})
|
|
1197
|
+
if "long_context" in auxiliary:
|
|
1198
|
+
boolean(auxiliary["long_context"], "auxiliary.long_context")
|
|
1199
|
+
choice(auxiliary["provider"], {"typesafe"}, "auxiliary provider")
|
|
1200
|
+
choice(auxiliary["model"], {"jev-1.14.0"}, "auxiliary model")
|
|
1201
|
+
authentication = fields(auxiliary["authentication"], "auxiliary authentication", {"source", "name"})
|
|
1202
|
+
if authentication != {"source": "controller_env", "name": "TYPESAFE_API_KEY"}:
|
|
1203
|
+
raise ProtocolError("auxiliary credential must come from TYPESAFE_API_KEY in the controller environment")
|
|
1204
|
+
if spec["authority"].get("allow_typesafe_auxiliary") is not True:
|
|
1205
|
+
raise ProtocolError("TypeSafe evidence export is not explicitly authorized")
|
|
1206
|
+
if "max_auxiliary_calls" not in spec["budget"]:
|
|
1207
|
+
raise ProtocolError("auxiliary scoring requires a finite sub-budget")
|
|
1208
|
+
if spec["budget"]["max_auxiliary_calls"] > spec["budget"]["max_trials"]:
|
|
1209
|
+
raise ProtocolError("auxiliary sub-budget exceeds the total call budget")
|
|
1210
|
+
validate_auxiliary_rubric(document(auxiliary["rubric_path"]), rubrics, auxiliary["criterion_ids"])
|
|
1211
|
+
auxiliary_calibration = validate_calibration(document(auxiliary["calibration_path"]), files, rubrics,
|
|
1212
|
+
auxiliary=True, criterion_ids=auxiliary["criterion_ids"])
|
|
1213
|
+
artifacts = {a["artifact_id"]: a for a in calibration["artifacts"]}
|
|
1214
|
+
for artifact in auxiliary_calibration["artifacts"]:
|
|
1215
|
+
if next(c for c in spec["cases"] if c["case_id"] == artifact["case_id"])["split"] != "search":
|
|
1216
|
+
raise ProtocolError("auxiliary calibration cannot consume heldout tasks")
|
|
1217
|
+
prior = artifacts.get(artifact["artifact_id"])
|
|
1218
|
+
if prior is not None and prior != artifact:
|
|
1219
|
+
raise ProtocolError("calibration artifact id aliases different frozen inputs")
|
|
1220
|
+
if any(artifact["expected_levels"][name] not in (0, 0.5, 1)
|
|
1221
|
+
for name in auxiliary["criterion_ids"] if name in artifact["expected_levels"]):
|
|
1222
|
+
raise ProtocolError("auxiliary calibration requires its own three semantic reference levels")
|
|
1223
|
+
workers["typesafe_worker.py"] = read_file(Path(__file__).parent, "typesafe_worker.py")
|
|
1224
|
+
elif spec["budget"].get("max_auxiliary_calls", 0):
|
|
1225
|
+
raise ProtocolError("auxiliary budget requires auxiliary configuration")
|
|
1226
|
+
return files, workers, rubrics, calibration
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
def quality_case_calls(criteria):
|
|
1230
|
+
return 2 + sum(c["checker_kind"] == "regression_replay" for c in criteria) + 2 * any(
|
|
1231
|
+
c["checker_kind"] == "judge" for c in criteria)
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
def quality_call_plan(spec, files, rubrics, calibration, bundles):
|
|
1235
|
+
def per_case(case):
|
|
1236
|
+
criteria = rubrics[case["case_id"]]["criteria"]
|
|
1237
|
+
return quality_case_calls(criteria)
|
|
1238
|
+
|
|
1239
|
+
repeats = spec["measurement"]["repeats"]
|
|
1240
|
+
search = sum(per_case(c) for c in spec["cases"] if c["split"] == "search") * repeats
|
|
1241
|
+
validation = sum(per_case(c) for c in spec["cases"] if c["split"] == "validation") * repeats
|
|
1242
|
+
confirmation = 2 * sum(per_case(c) for c in spec["cases"] if c["split"] == "confirmation") * repeats
|
|
1243
|
+
calibration_calls = 1 + 2 * len(spec["cases"])
|
|
1244
|
+
for artifact in calibration["artifacts"]:
|
|
1245
|
+
calibration_calls += per_case({"case_id": artifact["case_id"]}) - 1
|
|
1246
|
+
auxiliary = spec["evaluation"].get("auxiliary")
|
|
1247
|
+
if auxiliary is not None:
|
|
1248
|
+
primary_ids = {a["artifact_id"] for a in calibration["artifacts"]}
|
|
1249
|
+
auxiliary_calibration = parse_json(files[auxiliary["calibration_path"]]["content"])
|
|
1250
|
+
for artifact in auxiliary_calibration["artifacts"]:
|
|
1251
|
+
if artifact["artifact_id"] not in primary_ids:
|
|
1252
|
+
criteria = rubrics[artifact["case_id"]]["criteria"]
|
|
1253
|
+
calibration_calls += 1 + sum(c["checker_kind"] == "regression_replay" for c in criteria)
|
|
1254
|
+
plan = {"runtime_and_quality_calibration": calibration_calls, "baseline_aa": 2 * search,
|
|
1255
|
+
"initial_conditions": 0 if spec["mode"] == "calibrate" else len(bundles) * (search + validation),
|
|
1256
|
+
"optimizer_and_policy": 2 * spec["budget"]["max_candidates"] if spec["mode"] == "search" else 1 if spec["mode"] == "compare" else 0,
|
|
1257
|
+
"candidate_pairs": 2 * (search + validation) * spec["budget"]["max_candidates"] if spec["mode"] == "search" else 0,
|
|
1258
|
+
"confirmation_reserve": confirmation,
|
|
1259
|
+
"auxiliary_maximum": spec["budget"].get("max_auxiliary_calls", 0)}
|
|
1260
|
+
planned = sum(plan.values())
|
|
1261
|
+
optional_rounds = max(0, spec["budget"]["max_candidates"] - 1) if spec["mode"] == "search" else 0
|
|
1262
|
+
plan["minimum_calls"] = planned - optional_rounds * (2 + 2 * (search + validation))
|
|
1263
|
+
plan["finite_retry_allowance"] = (planned - plan["auxiliary_maximum"]) * spec["budget"]["max_retries"]
|
|
1264
|
+
plan["maximum_planned_calls"] = planned + plan["finite_retry_allowance"]
|
|
1265
|
+
return plan
|
|
1266
|
+
|
|
1267
|
+
|
|
1268
|
+
def prepare(path):
|
|
1269
|
+
path = Path(path).resolve(strict=True)
|
|
1270
|
+
spec = load_json(path)
|
|
1271
|
+
fields(
|
|
1272
|
+
spec,
|
|
1273
|
+
"JobSpec",
|
|
1274
|
+
{
|
|
1275
|
+
"schema_version",
|
|
1276
|
+
"protocol_id",
|
|
1277
|
+
"question",
|
|
1278
|
+
"mode",
|
|
1279
|
+
"target",
|
|
1280
|
+
"source",
|
|
1281
|
+
"runtime",
|
|
1282
|
+
"authority",
|
|
1283
|
+
"budget",
|
|
1284
|
+
"measurement",
|
|
1285
|
+
"guardrails",
|
|
1286
|
+
"cases",
|
|
1287
|
+
"conditions",
|
|
1288
|
+
"baseline_condition",
|
|
1289
|
+
},
|
|
1290
|
+
{"optimizer", "candidate_condition", "attribution", "evaluation"},
|
|
1291
|
+
)
|
|
1292
|
+
if type(spec["schema_version"]) is not int or spec["schema_version"] not in {1, 2}:
|
|
1293
|
+
raise ProtocolError("unsupported schema_version")
|
|
1294
|
+
text(spec["protocol_id"], "protocol_id")
|
|
1295
|
+
text(spec["question"], "question")
|
|
1296
|
+
choice(spec["mode"], {"calibrate", "compare", "search"} if spec["schema_version"] == 2 else {"smoke", "compare", "search"}, "mode")
|
|
1297
|
+
if spec["schema_version"] == 1 and "evaluation" in spec:
|
|
1298
|
+
raise ProtocolError("quality evaluation requires schema_version=2")
|
|
1299
|
+
validate_target(spec, path.parent)
|
|
1300
|
+
validate_runtime(spec)
|
|
1301
|
+
validate_measurement(spec)
|
|
1302
|
+
baseline, deleted = repository_files(spec)
|
|
1303
|
+
cases = task_files(spec, path.parent)
|
|
1304
|
+
evaluation, workers, rubrics, calibration = {}, {}, {}, None
|
|
1305
|
+
if spec["schema_version"] == 2:
|
|
1306
|
+
evaluation, workers, rubrics, calibration = evaluation_files(spec, path.parent)
|
|
1307
|
+
private_files = set()
|
|
1308
|
+
for case in spec["cases"]:
|
|
1309
|
+
root = Path(case["task_path"])
|
|
1310
|
+
for name in case["grader_paths"] + case["oracle_paths"]:
|
|
1311
|
+
info = (root / name).stat()
|
|
1312
|
+
private_files.add((info.st_dev, info.st_ino))
|
|
1313
|
+
for name in baseline:
|
|
1314
|
+
info = (Path(spec["target"]["repo"]) / name).stat()
|
|
1315
|
+
if (info.st_dev, info.st_ino) in private_files:
|
|
1316
|
+
raise ProtocolError(
|
|
1317
|
+
"repository export contains private grading or oracle material"
|
|
1318
|
+
)
|
|
1319
|
+
fixed_roots = spec["source"].get("fixed_skill_roots", [])
|
|
1320
|
+
target_roots = {str(PurePosixPath(path).parent) for path in spec["target"]["surfaces"]["skill"]
|
|
1321
|
+
if PurePosixPath(path).name == "SKILL.md"}
|
|
1322
|
+
skill_names = {skill_name(baseline[path]["content"]) for path in spec["target"]["surfaces"]["skill"]
|
|
1323
|
+
if PurePosixPath(path).name == "SKILL.md" and path in baseline}
|
|
1324
|
+
for root in fixed_roots:
|
|
1325
|
+
descriptor = root + "/SKILL.md"
|
|
1326
|
+
if descriptor not in baseline:
|
|
1327
|
+
raise ProtocolError("fixed skill descriptor must be in the source export")
|
|
1328
|
+
if any(covered(root, [target]) or covered(target, [root]) for target in target_roots) or any(
|
|
1329
|
+
covered(path, [root]) for path in spec["target"]["allowed_paths"]
|
|
1330
|
+
):
|
|
1331
|
+
raise ProtocolError("fixed skill cannot overlap a candidate surface")
|
|
1332
|
+
name = skill_name(baseline[descriptor]["content"])
|
|
1333
|
+
if name is None or name in skill_names:
|
|
1334
|
+
raise ProtocolError("fixed skill identity is missing or duplicated")
|
|
1335
|
+
skill_names.add(name)
|
|
1336
|
+
bundles, differences = condition_bundles(spec, baseline)
|
|
1337
|
+
method = {}
|
|
1338
|
+
if spec["mode"] == "search":
|
|
1339
|
+
if "optimizer" not in spec:
|
|
1340
|
+
raise ProtocolError("search requires an optimizer")
|
|
1341
|
+
optimizer = fields(
|
|
1342
|
+
spec["optimizer"],
|
|
1343
|
+
"optimizer",
|
|
1344
|
+
{"model", "effort", "method_path", "method_sha256"},
|
|
1345
|
+
)
|
|
1346
|
+
text(optimizer["model"], "optimizer.model")
|
|
1347
|
+
choice(
|
|
1348
|
+
optimizer["effort"],
|
|
1349
|
+
{"minimal", "low", "medium", "high", "xhigh"},
|
|
1350
|
+
"optimizer.effort",
|
|
1351
|
+
)
|
|
1352
|
+
fingerprint(optimizer["method_sha256"], "optimizer.method_sha256")
|
|
1353
|
+
method_path = Path(optimizer["method_path"])
|
|
1354
|
+
if not method_path.is_absolute():
|
|
1355
|
+
method_path = path.parent / method_path
|
|
1356
|
+
method = {
|
|
1357
|
+
"method.md": read_file(
|
|
1358
|
+
method_path.parent.resolve(strict=True), method_path.name
|
|
1359
|
+
)
|
|
1360
|
+
}
|
|
1361
|
+
optimizer["method_path"] = str(method_path.absolute())
|
|
1362
|
+
if method["method.md"]["sha256"] != optimizer["method_sha256"]:
|
|
1363
|
+
raise ProtocolError("optimizer method hash mismatch")
|
|
1364
|
+
if spec["schema_version"] == 2 and (optimizer["model"], optimizer["effort"]) != ("gpt-6.1-sol", "high"):
|
|
1365
|
+
raise ProtocolError("v2 optimizer requires gpt-6.1-sol/high")
|
|
1366
|
+
elif "optimizer" in spec:
|
|
1367
|
+
raise ProtocolError("optimizer is only valid in search mode")
|
|
1368
|
+
if "attribution" in spec:
|
|
1369
|
+
choice(
|
|
1370
|
+
spec["attribution"],
|
|
1371
|
+
{"skill", "agents", "joint", "interaction"},
|
|
1372
|
+
"attribution",
|
|
1373
|
+
)
|
|
1374
|
+
if spec["attribution"] == "interaction":
|
|
1375
|
+
combinations = {(c["skill"], c["agents"]) for c in spec["conditions"]}
|
|
1376
|
+
required = {
|
|
1377
|
+
(s, a)
|
|
1378
|
+
for s in ["baseline", "candidate"]
|
|
1379
|
+
for a in ["baseline", "candidate"]
|
|
1380
|
+
}
|
|
1381
|
+
if not required.issubset(combinations):
|
|
1382
|
+
raise ProtocolError(
|
|
1383
|
+
"interaction attribution requires a frozen crossed design"
|
|
1384
|
+
)
|
|
1385
|
+
elif spec["attribution"] in {"skill", "agents"}:
|
|
1386
|
+
background = "agents" if spec["attribution"] == "skill" else "skill"
|
|
1387
|
+
if len({c[background] for c in spec["conditions"]}) != 1:
|
|
1388
|
+
raise ProtocolError(
|
|
1389
|
+
"single-factor attribution changes the other factor"
|
|
1390
|
+
)
|
|
1391
|
+
repeats = spec["measurement"]["repeats"]
|
|
1392
|
+
search_count = sum(case["split"] == "search" for case in spec["cases"])
|
|
1393
|
+
validation_count = sum(case["split"] == "validation" for case in spec["cases"])
|
|
1394
|
+
confirmation_count = sum(case["split"] == "confirmation" for case in spec["cases"])
|
|
1395
|
+
reserve = 0 if spec["mode"] == "smoke" else 4 * confirmation_count * repeats
|
|
1396
|
+
minimum = 1 + 2 * len(cases)
|
|
1397
|
+
if spec["mode"] != "smoke":
|
|
1398
|
+
minimum += (
|
|
1399
|
+
4 * search_count * repeats
|
|
1400
|
+
+ 2 * len(bundles) * (search_count + validation_count) * repeats
|
|
1401
|
+
+ reserve
|
|
1402
|
+
)
|
|
1403
|
+
if spec["mode"] == "search":
|
|
1404
|
+
minimum += 2 + 4 * (search_count + validation_count) * repeats
|
|
1405
|
+
else:
|
|
1406
|
+
minimum += 1
|
|
1407
|
+
call_plan = {}
|
|
1408
|
+
if spec["schema_version"] == 2:
|
|
1409
|
+
call_plan = quality_call_plan(spec, evaluation, rubrics, calibration, bundles)
|
|
1410
|
+
minimum, reserve = call_plan["minimum_calls"], call_plan["confirmation_reserve"]
|
|
1411
|
+
if minimum > spec["budget"]["max_trials"]:
|
|
1412
|
+
raise ProtocolError(
|
|
1413
|
+
f"budget cannot fund calibration, baseline and final confirmation: requires {minimum}"
|
|
1414
|
+
)
|
|
1415
|
+
return PreparedJob(
|
|
1416
|
+
spec, bundles, cases, method, deleted, differences, minimum, reserve,
|
|
1417
|
+
evaluation, workers, call_plan,
|
|
1418
|
+
)
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
def new_execution(
|
|
1422
|
+
job, role, idempotency_key, command_hash, runner_hash, *, now, run_deadline
|
|
1423
|
+
):
|
|
1424
|
+
choice(role, ROLES, "role")
|
|
1425
|
+
budget = job.spec["budget"]
|
|
1426
|
+
command_deadline = min(
|
|
1427
|
+
now + budget["per_trial_timeout_sec"],
|
|
1428
|
+
run_deadline - budget["recovery_window_sec"],
|
|
1429
|
+
)
|
|
1430
|
+
if command_deadline <= now:
|
|
1431
|
+
raise ProtocolError(
|
|
1432
|
+
"insufficient time for command and reserved recovery window"
|
|
1433
|
+
)
|
|
1434
|
+
task_id = str(uuid.uuid4())
|
|
1435
|
+
result = {
|
|
1436
|
+
"schema_version": 1,
|
|
1437
|
+
"remote_task_id": task_id,
|
|
1438
|
+
"role": role,
|
|
1439
|
+
"idempotency_key": text(idempotency_key, "idempotency_key"),
|
|
1440
|
+
"input_hash": job.protocol_hash,
|
|
1441
|
+
"command_hash": command_hash,
|
|
1442
|
+
"runner_hash": runner_hash,
|
|
1443
|
+
"runner_version": 1,
|
|
1444
|
+
"ssh_target": job.spec["runtime"]["ssh_target"],
|
|
1445
|
+
"remote_root": job.spec["runtime"]["remote_root"],
|
|
1446
|
+
"sandbox_name": "evolve-" + task_id,
|
|
1447
|
+
"sandbox_id": None,
|
|
1448
|
+
"engine_id": None,
|
|
1449
|
+
"receipt_path": job.spec["runtime"]["remote_root"]
|
|
1450
|
+
+ "/executions/"
|
|
1451
|
+
+ task_id
|
|
1452
|
+
+ "/control/receipt.json",
|
|
1453
|
+
"phase": "prepared",
|
|
1454
|
+
"submission_status": "not_submitted",
|
|
1455
|
+
"process": None,
|
|
1456
|
+
"exit_code": None,
|
|
1457
|
+
"termination_reason": None,
|
|
1458
|
+
"artifact_manifest": [],
|
|
1459
|
+
"export_status": "not_exported",
|
|
1460
|
+
"cleanup_status": "not_cleaned",
|
|
1461
|
+
"cancel_reason": None,
|
|
1462
|
+
"command_deadline": command_deadline,
|
|
1463
|
+
"resource_deadline": run_deadline,
|
|
1464
|
+
}
|
|
1465
|
+
validate_execution(result)
|
|
1466
|
+
return result
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
def validate_execution(value):
|
|
1470
|
+
fields(
|
|
1471
|
+
value,
|
|
1472
|
+
"RemoteExecution",
|
|
1473
|
+
{
|
|
1474
|
+
"schema_version",
|
|
1475
|
+
"remote_task_id",
|
|
1476
|
+
"role",
|
|
1477
|
+
"idempotency_key",
|
|
1478
|
+
"input_hash",
|
|
1479
|
+
"command_hash",
|
|
1480
|
+
"runner_hash",
|
|
1481
|
+
"runner_version",
|
|
1482
|
+
"ssh_target",
|
|
1483
|
+
"remote_root",
|
|
1484
|
+
"sandbox_name",
|
|
1485
|
+
"sandbox_id",
|
|
1486
|
+
"engine_id",
|
|
1487
|
+
"receipt_path",
|
|
1488
|
+
"phase",
|
|
1489
|
+
"submission_status",
|
|
1490
|
+
"process",
|
|
1491
|
+
"exit_code",
|
|
1492
|
+
"termination_reason",
|
|
1493
|
+
"artifact_manifest",
|
|
1494
|
+
"export_status",
|
|
1495
|
+
"cleanup_status",
|
|
1496
|
+
"cancel_reason",
|
|
1497
|
+
"command_deadline",
|
|
1498
|
+
"resource_deadline",
|
|
1499
|
+
},
|
|
1500
|
+
)
|
|
1501
|
+
if (
|
|
1502
|
+
type(value["schema_version"]) is not int
|
|
1503
|
+
or value["schema_version"] != 1
|
|
1504
|
+
or type(value["runner_version"]) is not int
|
|
1505
|
+
or value["runner_version"] != 1
|
|
1506
|
+
):
|
|
1507
|
+
raise ProtocolError("unsupported execution or runner schema")
|
|
1508
|
+
try:
|
|
1509
|
+
uuid.UUID(value["remote_task_id"])
|
|
1510
|
+
except (ValueError, TypeError, AttributeError):
|
|
1511
|
+
raise ProtocolError("invalid remote_task_id") from None
|
|
1512
|
+
choice(value["role"], ROLES, "role")
|
|
1513
|
+
choice(value["phase"], PHASES, "phase")
|
|
1514
|
+
for key in ["input_hash", "command_hash", "runner_hash"]:
|
|
1515
|
+
fingerprint(value[key], key)
|
|
1516
|
+
for key in [
|
|
1517
|
+
"ssh_target",
|
|
1518
|
+
"remote_root",
|
|
1519
|
+
"idempotency_key",
|
|
1520
|
+
"sandbox_name",
|
|
1521
|
+
"receipt_path",
|
|
1522
|
+
]:
|
|
1523
|
+
text(value[key], key)
|
|
1524
|
+
expected_receipt = (
|
|
1525
|
+
remote_path(value["remote_root"])
|
|
1526
|
+
+ "/executions/"
|
|
1527
|
+
+ value["remote_task_id"]
|
|
1528
|
+
+ "/control/receipt.json"
|
|
1529
|
+
)
|
|
1530
|
+
if (
|
|
1531
|
+
value["sandbox_name"] != "evolve-" + value["remote_task_id"]
|
|
1532
|
+
or value["receipt_path"] != expected_receipt
|
|
1533
|
+
):
|
|
1534
|
+
raise ProtocolError("remote execution resource or receipt identity has drifted")
|
|
1535
|
+
if value["sandbox_id"] is not None:
|
|
1536
|
+
text(value["sandbox_id"], "sandbox_id")
|
|
1537
|
+
if value["engine_id"] is not None:
|
|
1538
|
+
text(value["engine_id"], "engine_id")
|
|
1539
|
+
choice(
|
|
1540
|
+
value["submission_status"],
|
|
1541
|
+
{
|
|
1542
|
+
"not_submitted",
|
|
1543
|
+
"intent_saved",
|
|
1544
|
+
"resource_created",
|
|
1545
|
+
"launch_intent_saved",
|
|
1546
|
+
"runner_submitted",
|
|
1547
|
+
"unresolved",
|
|
1548
|
+
},
|
|
1549
|
+
"submission_status",
|
|
1550
|
+
)
|
|
1551
|
+
choice(
|
|
1552
|
+
value["export_status"],
|
|
1553
|
+
{"not_exported", "partial", "exported", "unresolved"},
|
|
1554
|
+
"export_status",
|
|
1555
|
+
)
|
|
1556
|
+
choice(
|
|
1557
|
+
value["cleanup_status"],
|
|
1558
|
+
{"not_cleaned", "pending", "terminated", "unresolved"},
|
|
1559
|
+
"cleanup_status",
|
|
1560
|
+
)
|
|
1561
|
+
for key in ["command_deadline", "resource_deadline"]:
|
|
1562
|
+
number(value[key], key, minimum=1)
|
|
1563
|
+
if value["command_deadline"] >= value["resource_deadline"]:
|
|
1564
|
+
raise ProtocolError("command deadline must leave a recovery window")
|
|
1565
|
+
if value["exit_code"] is not None and type(value["exit_code"]) is not int:
|
|
1566
|
+
raise ProtocolError("exit_code must be integer or null")
|
|
1567
|
+
for key in ["termination_reason", "cancel_reason"]:
|
|
1568
|
+
if value[key] is not None:
|
|
1569
|
+
text(value[key], key)
|
|
1570
|
+
if value["phase"] == "running" and value["process"] is None:
|
|
1571
|
+
raise ProtocolError("running receipt must identify the original process")
|
|
1572
|
+
if value["process"] is not None:
|
|
1573
|
+
fields(value["process"], "process", {"pid", "pgid", "start_identity"})
|
|
1574
|
+
for key in ["pid", "pgid"]:
|
|
1575
|
+
number(value["process"][key], key, minimum=1, integer=True)
|
|
1576
|
+
text(value["process"]["start_identity"], "process.start_identity")
|
|
1577
|
+
paths = set()
|
|
1578
|
+
for artifact in rows(value["artifact_manifest"], "artifact_manifest", empty=True):
|
|
1579
|
+
fields(artifact, "artifact", {"path", "size", "sha256"})
|
|
1580
|
+
name = relative_path(artifact["path"])
|
|
1581
|
+
if name in paths:
|
|
1582
|
+
raise ProtocolError("duplicate artifact path")
|
|
1583
|
+
paths.add(name)
|
|
1584
|
+
fingerprint(artifact["sha256"], "artifact.sha256")
|
|
1585
|
+
number(artifact["size"], "artifact.size", integer=True)
|
|
1586
|
+
return value
|