diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 41c759ac..96ffbc48 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -74,6 +74,19 @@ jobs: print('transformers', transformers.__version__); \ print('prometheus_client', __import__('importlib.metadata', fromlist=['version']).version('prometheus_client'))" + - name: Set up Node.js for proof observatory checks + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + cache-dependency-path: deploy/cloudflare-worker/package-lock.json + + - name: Validate proof observatory Worker bundle + working-directory: deploy/cloudflare-worker + run: | + npm ci + npm run check + - name: Run platform-neutral test suite with 100% coverage env: # sdks/python is the on-tree location of the Python SDK diff --git a/deploy/cloudflare-worker/package-lock.json b/deploy/cloudflare-worker/package-lock.json index 710fee4f..d5c776e7 100644 --- a/deploy/cloudflare-worker/package-lock.json +++ b/deploy/cloudflare-worker/package-lock.json @@ -6,9 +6,74 @@ "": { "name": "kakeya-inference-network-worker", "devDependencies": { + "jsdom": "^29.1.1", "wrangler": "^4.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, "node_modules/@cloudflare/kv-asset-handler": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz", @@ -133,6 +198,146 @@ "node": ">=12" } }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.0", + "@csstools/css-calc": "^3.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/@emnapi/runtime": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", @@ -586,6 +791,24 @@ "node": ">=18" } }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@img/colour": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", @@ -1153,6 +1376,16 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/blake3-wasm": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", @@ -1174,6 +1407,41 @@ "url": "https://opencollective.com/express" } }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1184,6 +1452,19 @@ "node": ">=8" } }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-stack-parser-es": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", @@ -1251,6 +1532,67 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -1261,6 +1603,23 @@ "node": ">=6" } }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/miniflare": { "version": "4.20260708.1", "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260708.1.tgz", @@ -1282,6 +1641,19 @@ "node": ">=22.0.0" } }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -1296,6 +1668,39 @@ "dev": true, "license": "MIT" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -1354,6 +1759,16 @@ "@img/sharp-win32-x64": "0.34.5" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/supports-color": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", @@ -1367,6 +1782,59 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tldts": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.9.tgz", + "integrity": "sha512-3kZ8wQQ/k5DrChD4X4FVvr2D7E5uoRgAqkPyLpSCGUvqOvqu+JEdr3mwMUaVWb+vMHZaKhF5fp2PBigKsui7hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.9" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.9.tgz", + "integrity": "sha512-DxKfPBI52p2msTEu7MPhdpdDTBhhVQg1a/8PjQckeyAvO13eMYElX545grIp6nnTGIMZlRvFZPvFhvI/WIz2Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -1395,6 +1863,54 @@ "pathe": "^2.0.3" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/workerd": { "version": "1.20260708.1", "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260708.1.tgz", @@ -1474,6 +1990,23 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/youch": { "version": "4.1.0-beta.10", "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", diff --git a/deploy/cloudflare-worker/package.json b/deploy/cloudflare-worker/package.json index 82c5c8c5..a6443394 100644 --- a/deploy/cloudflare-worker/package.json +++ b/deploy/cloudflare-worker/package.json @@ -3,9 +3,10 @@ "private": true, "scripts": { "deploy": "wrangler deploy", - "check": "wrangler deploy --dry-run" + "check": "node --check src/index.js && node --check src/page.js && node --test test_*.mjs && wrangler deploy --dry-run" }, "devDependencies": { + "jsdom": "^29.1.1", "wrangler": "^4.0.0" } } diff --git a/deploy/cloudflare-worker/src/index.js b/deploy/cloudflare-worker/src/index.js index 4e93c848..4bbcd60c 100644 --- a/deploy/cloudflare-worker/src/index.js +++ b/deploy/cloudflare-worker/src/index.js @@ -1,28 +1,68 @@ +import { PAGE } from "./page.js"; + const ORIGIN = "https://agent.kakeya.ai"; +const BUILD_ID = "2026.07.25-frontend-runtime-r2"; + +function pageResponse(request) { + return new Response(request.method === "HEAD" ? null : PAGE, { + headers: { + "Content-Type": "text/html; charset=utf-8", + "Cache-Control": "private, no-store, no-cache, must-revalidate, max-age=0", + "CDN-Cache-Control": "no-store", + "Cloudflare-CDN-Cache-Control": "no-store", + "Surrogate-Control": "no-store", + "Pragma": "no-cache", + "Expires": "0", + "Content-Security-Policy": "default-src 'self'; script-src 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; connect-src 'self'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'", + "X-Kakeya-Surface": "proof-observatory", + "X-Kakeya-Build": BUILD_ID, + "X-Content-Type-Options": "nosniff", + "Referrer-Policy": "same-origin", + }, + }); +} export default { async fetch(request) { const incoming = new URL(request.url); let path = incoming.pathname; - if (path === "/" || path === "/index.html") { - path = "/network"; + if (path === "/" || path === "/index.html" || path === "/network") { + return pageResponse(request); } else if (path === "/healthz") { path = "/v1/network/summary"; } else if ( - path !== "/network" && - !path.startsWith("/v1/network/") + !path.startsWith("/v1/network/") && + !path.startsWith("/v1/proof/") ) { - path = "/network"; + return pageResponse(request); } const target = new URL(path + incoming.search, ORIGIN); - const upstreamRequest = new Request(target, request); + const requestHeaders = new Headers(request.headers); + requestHeaders.delete("host"); + if (path.startsWith("/v1/proof/")) { + requestHeaders.set("Cache-Control", "no-cache"); + } + const upstreamRequest = new Request(target.toString(), { + method: request.method, + headers: requestHeaders, + body: ["GET", "HEAD"].includes(request.method) ? undefined : request.body, + redirect: "manual", + }); const response = await fetch(upstreamRequest); const headers = new Headers(response.headers); headers.set("X-Kakeya-Surface", "inference-network"); headers.set("X-Content-Type-Options", "nosniff"); headers.set("Referrer-Policy", "same-origin"); + if (path.startsWith("/v1/proof/")) { + headers.set("Cache-Control", "private, no-store, no-cache, must-revalidate, max-age=0"); + headers.set("CDN-Cache-Control", "no-store"); + headers.set("Cloudflare-CDN-Cache-Control", "no-store"); + headers.set("Surrogate-Control", "no-store"); + headers.set("Pragma", "no-cache"); + headers.set("Expires", "0"); + } return new Response(response.body, { status: response.status, diff --git a/deploy/cloudflare-worker/src/page.js b/deploy/cloudflare-worker/src/page.js new file mode 100644 index 00000000..3088140b --- /dev/null +++ b/deploy/cloudflare-worker/src/page.js @@ -0,0 +1,252 @@ +export function executionSnapshot(live) { + const canonical = value => String(value ?? "").trim().toLowerCase() + .replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") + .replace(/^agent_/, ""); + const state = String(live?.execution_state || live?.state || "idle").toLowerCase(); + const phase = String(live?.execution_phase || live?.phase || "").toLowerCase(); + const adapter = String(live?.adapter_status || "").toUpperCase(); + const blockedCategory = String(live?.blocked_category || "").toUpperCase(); + const blocked = Boolean(adapter || blockedCategory) || phase === "blocked_idle"; + const activeInference = live?.fresh === true && !blocked + && ["queued", "prefill", "decode", "review"].includes(state); + const resumeRole = canonical( + live?.resume_role || live?.active_role || live?.orchestration_state, + ); + return { + state, phase, adapter, blockedCategory, blocked, activeInference, resumeRole, + }; +} + +export const PAGE = String.raw` + + + + +Kakeya Proof Observatory + + + + +
+
+ ζKakeya Proof Observatory

Riemann Hypothesis · proof operations

+ +
+
+
+
BackendCHECKING
Connecting…
Waiting for authoritative telemetry
+
Exact role / phase
No active execution
Run and worker unavailable
+
Obligation under attack
Loading active obligation…
+
Latest completed change
Waiting for completed history
+
+
+
Role pipeline0 / 0 · no active iteration
+
+
+
+ +
+
+
+
Decomposition graph · active chain emphasizedLoading graph…
+
+
+
active chainselectedrejected
+
+
+ +
+
+
+
Completed-run reasoning timeline

What changed, and why it stopped

Newest first

Waiting for completed proof runs.

+
+
+
+
LEDGER VERSION
PROOF TASKS
OPEN LEAVES
ACTIVE CHAIN DEPTH
PROVED
ALLENS PREFILL TOK/S
+
Telemetry provenance and review health
Waiting for telemetry.
+
+
+ +
+
+ +`; diff --git a/deploy/cloudflare-worker/test_execution_state.mjs b/deploy/cloudflare-worker/test_execution_state.mjs new file mode 100644 index 00000000..b49666f3 --- /dev/null +++ b/deploy/cloudflare-worker/test_execution_state.mjs @@ -0,0 +1,63 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { PAGE, executionSnapshot } from "./src/page.js"; + + +test("blocked idle overrides retained resumable role", () => { + const state = executionSnapshot({ + fresh: true, + state: "idle", + phase: "blocked_idle", + adapter_status: "ADAPTER_BLOCKED", + orchestration_state: "DEFINITION_AUDITOR", + active_role: "definition_auditor", + resume_role: "definition_auditor", + }); + assert.equal(state.blocked, true); + assert.equal(state.activeInference, false); + assert.equal(state.resumeRole, "definition_auditor"); + assert.match(PAGE, /Blocked \/ idle/); + assert.match(PAGE, /Resume role:/); +}); + + +test("only fresh inference heartbeat marks a role active", () => { + const fresh = executionSnapshot({ + fresh: true, + state: "decode", + phase: "definition_auditor_decode", + active_role: "definition_auditor", + }); + const stale = executionSnapshot({ + fresh: false, + state: "decode", + phase: "definition_auditor_decode", + active_role: "definition_auditor", + }); + assert.equal(fresh.activeInference, true); + assert.equal(stale.activeInference, false); +}); + + +test("same-run state changes produce a new render snapshot", () => { + const run = {fresh: true, run_id: "same", sequence: 1}; + const active = executionSnapshot({ + ...run, + state: "prefill", + phase: "definition_auditor_prefill", + }); + const blocked = executionSnapshot({ + ...run, + sequence: 2, + state: "idle", + phase: "blocked_idle", + adapter_status: "ADAPTER_BLOCKED", + resume_role: "definition_auditor", + }); + assert.equal(active.activeInference, true); + assert.equal(blocked.activeInference, false); + assert.equal(blocked.blocked, true); + assert.match(PAGE, /setInterval\(load,1800\)/); +} +); diff --git a/deploy/cloudflare-worker/test_frontend_runtime.mjs b/deploy/cloudflare-worker/test_frontend_runtime.mjs new file mode 100644 index 00000000..aaea6a3d --- /dev/null +++ b/deploy/cloudflare-worker/test_frontend_runtime.mjs @@ -0,0 +1,168 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { JSDOM, VirtualConsole } from "jsdom"; + +import { PAGE } from "./src/page.js"; + +const proofBase = { + ledger_version: 91, + nodes: [ + { + id: "root", + parent_id: "", + statement: "Root proof obligation", + status: "UNRESOLVED", + formal_status: "UNFORMALIZED", + }, + { + id: "leaf", + parent_id: "root", + statement: "Active graph obligation", + status: "UNRESOLVED", + formal_status: "TYPECHECKED", + }, + ], + longest_chain: ["root", "leaf"], + active_leaf_id: "leaf", + active_obligation: { id: "leaf", statement: "Active graph obligation" }, + unresolved_leaves: 1, + status_counts: { PROVED: 4 }, + reasoning_runs: [ + { + run_id: "completed-1", + outcome: "host_gate_rejected", + target_statement: "Previous obligation", + mathematical_summary: "The latest completed reasoning result.", + updated_at: 1784969000, + }, + ], + collaboration: { chain_participants: [], authoritative_gates: [], open_slots: 3 }, + review_health: { status: "healthy", latest_errors: [] }, +}; + +const networkNodes = [ + { + id: "allens-mini", + prefill_worker: { tokens_per_second: 2.75 }, + }, +]; + +function inlineScripts(html) { + return [...html.matchAll(/]*)?>([\s\S]*?)<\/script>/gi)] + .map(match => match[1]); +} + +function assertStandaloneBrowserScripts(html) { + const scripts = inlineScripts(html); + assert.ok(scripts.length, "served HTML must contain an inline application script"); + for (const script of scripts) { + assert.doesNotMatch(script, /\b__name\b/); + } +} + +async function render(liveExecution) { + const runtimeErrors = []; + const virtualConsole = new VirtualConsole(); + virtualConsole.on("error", error => runtimeErrors.push(error)); + virtualConsole.on("jsdomError", error => runtimeErrors.push(error)); + + const dom = new JSDOM(PAGE, { + runScripts: "dangerously", + url: "https://kakeya.ai/", + virtualConsole, + beforeParse(window) { + window.matchMedia = () => ({ + matches: false, + addEventListener() {}, + removeEventListener() {}, + }); + window.requestAnimationFrame = callback => { + callback(0); + return 1; + }; + window.fetch = async input => { + const path = new URL(String(input), window.location.href).pathname; + const body = path === "/v1/proof/progress" + ? { ...proofBase, live_execution: liveExecution } + : path === "/v1/network/nodes" + ? networkNodes + : null; + if (body === null) return { ok: false, status: 404, json: async () => ({}) }; + return { ok: true, status: 200, json: async () => structuredClone(body) }; + }; + window.addEventListener("error", event => { + runtimeErrors.push(event.error || event.message); + }); + window.addEventListener("unhandledrejection", event => { + runtimeErrors.push(event.reason); + }); + }, + }); + + await new Promise(resolve => setTimeout(resolve, 30)); + return { + dom, + document: dom.window.document, + runtimeErrors, + }; +} + +test("static scan rejects the exact Worker helper regression fixture", () => { + assertStandaloneBrowserScripts(PAGE); + const brokenFixture = ""; + assert.throws( + () => assertStandaloneBrowserScripts(brokenFixture), + /did not match|__name/, + ); +}); + +test("served inline script renders active telemetry, role pipeline, graph, and metrics", async t => { + const result = await render({ + fresh: true, + state: "prefill", + phase: "decomposer_prefill", + execution_state: "prefill", + execution_phase: "decomposer_prefill", + orchestration_state: "DECOMPOSER", + active_role: "decomposer", + run_id: "active-run", + worker: "allens", + sequence: 12, + progress: { current: 5, total: 10 }, + }); + t.after(() => result.dom.window.close()); + + assert.deepEqual(result.runtimeErrors, []); + assert.equal(result.document.querySelector("#backendAnswer").textContent, "Backend live"); + assert.match(result.document.querySelector("#roleAnswer").textContent, /Decomposer/); + assert.match(result.document.querySelector("#progressLabel").textContent, /5 \/ 10/); + assert.equal(result.document.querySelectorAll(".node").length, 2); + assert.equal(result.document.querySelector("#graphActiveTitle").textContent, "Active graph obligation"); + assert.equal(result.document.querySelector("#ledgerVersion").textContent, "v91"); + assert.equal(result.document.querySelector("#prefillRate").textContent, "2.75"); +}); + +test("served inline script renders blocked idle without claiming active execution", async t => { + const result = await render({ + fresh: true, + state: "idle", + phase: "blocked_idle", + adapter_status: "ADAPTER_BLOCKED", + blocked_category: "DEFINITION_REQUIRED", + blocked_reason: "Typed definition required", + orchestration_state: "DEFINITION_AUDITOR", + active_role: "definition_auditor", + resume_role: "definition_auditor", + progress: { current: 8, total: 10 }, + }); + t.after(() => result.dom.window.close()); + + assert.deepEqual(result.runtimeErrors, []); + assert.equal(result.document.querySelector("#backendAnswer").textContent, "Blocked / idle"); + assert.equal(result.document.querySelector("#roleAnswer").textContent, "Blocked / idle"); + assert.match(result.document.querySelector("#roleDetail").textContent, /Resume role: Definition Auditor/); + assert.equal(result.document.querySelector("#progressLabel").textContent, "0 / 0 · no active iteration"); + assert.equal(result.document.querySelectorAll(".pipeline-step.current").length, 0); + assert.equal(result.document.querySelectorAll(".node").length, 2); +}); diff --git a/inference_engine/network/api.py b/inference_engine/network/api.py index 3899ca34..8b5f8473 100644 --- a/inference_engine/network/api.py +++ b/inference_engine/network/api.py @@ -50,6 +50,7 @@ class BenchmarkUpdateRequest(BaseModel): stages: list[dict] = Field(default_factory=list) status: Optional[str] = None finished_at: Optional[float] = None + provenance: Optional[dict] = None def create_network_app( diff --git a/inference_engine/network/state.py b/inference_engine/network/state.py index d4fc74b4..9f87710e 100644 --- a/inference_engine/network/state.py +++ b/inference_engine/network/state.py @@ -132,13 +132,18 @@ def update_benchmark( stages: list[dict[str, Any]] | None = None, status: str | None = None, finished_at: float | None = None, + provenance: dict[str, Any] | None = None, ) -> dict[str, Any]: if status not in (None, "running", "completed", "failed"): raise ValueError("invalid benchmark status") + if provenance is not None: + assert_public_safe(provenance) with self._lock: run = self._benchmark_locked(run_id) if stages: run["stages"].extend(normalize_stage(stage) for stage in stages) + if provenance is not None: + run["provenance"] = dict(provenance) if status is not None: run["status"] = status if finished_at is not None: diff --git a/scripts/check_typed_role_contracts.py b/scripts/check_typed_role_contracts.py new file mode 100644 index 00000000..e65d945c --- /dev/null +++ b/scripts/check_typed_role_contracts.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +"""Mandatory static architecture gate for model/host ownership.""" +from __future__ import annotations + +import ast +import inspect +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from autoresearch.prefill.orchestration_state import ( + ALLOWED_TRANSITIONS, + ARCHITECTURE_VERSION, + OrchestrationCheckpoint, + ProofState, +) +from autoresearch.prefill.typed_transport import ( + ROLE_TRANSPORT_REGISTRY, + transport_prompt, +) +from scripts import agent_gan_repl + + +EXPECTED_ROLES = { + "strategy_tournament_selector", + "strategy_critic_selector", + "synthesis", + "premise_suspicion", + "premise_auditor", + "premise_proponent", + "definition_auditor", + "counterexample_worker", + "decomposer", + "math_ir_translator", + "proof_action_selector", + "adversarial_proponent", + "judge", +} +FORBIDDEN_ACTIVE_PROMPT_TEXT = ( + "json", + "artifact:", + "schema_version", + "```", + ":= by", + "lean source", + "latex", +) +FORBIDDEN_MODEL_FIELD_FRAGMENTS = ( + "claim", + "proposition", + "statement", + "assumption", + "conclusion", + "source", + "lean", + "json", + "text", +) +ALLOWED_REFERENCE_FIELDS = {"parent_claim_ref", "target_proposition_ref"} + + +def _fail(message: str) -> None: + raise SystemExit(f"typed role contract violation: {message}") + + +def main() -> None: + if set(ROLE_TRANSPORT_REGISTRY) != EXPECTED_ROLES: + _fail("typed transport role registry is incomplete") + for role in sorted(EXPECTED_ROLES): + contract = ROLE_TRANSPORT_REGISTRY[role] + for field in contract.fields: + if ( + any( + fragment in field.name + for fragment in FORBIDDEN_MODEL_FIELD_FRAGMENTS + ) + and field.name not in ALLOWED_REFERENCE_FIELDS + ): + _fail(f"{role} has model-owned escape field {field.name}") + if field.value_kind not in { + "enum", "identifier", "reference", "proof_step", + }: + _fail(f"{role}.{field.name} is not field-typed") + prompt = transport_prompt(role).casefold() + for forbidden in FORBIDDEN_ACTIVE_PROMPT_TEXT: + if forbidden in prompt: + _fail(f"{role} prompt requests forbidden syntax: {forbidden}") + active_source = inspect.getsource(agent_gan_repl._typed_role_messages) + active_tree = ast.parse(active_source) + forbidden_calls = [] + for node in ast.walk(active_tree): + if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute): + owner = getattr(node.func.value, "id", "") + if owner == "json" and node.func.attr in {"loads", "dumps"}: + forbidden_calls.append(f"json.{node.func.attr}") + if forbidden_calls: + _fail("active role prompt serializes JSON") + for role in ("decomposer", "math_ir_translator"): + fields = {field.name for field in ROLE_TRANSPORT_REGISTRY[role].fields} + if "move_id" not in fields or "typed_ir_step" in fields: + _fail(f"{role} must use only the Host constrained move selector") + synthesis_fields = { + field.name for field in ROLE_TRANSPORT_REGISTRY["synthesis"].fields + } + if synthesis_fields != {"choice_code", "reason_code"}: + _fail("synthesis must expose only one short choice and one reason code") + definition_fields = { + field.name + for field in ROLE_TRANSPORT_REGISTRY["definition_auditor"].fields + } + if definition_fields != { + "target_ref", + "symbol_id", + "domain_id", + "topology_id", + "definition_id", + "missing_definition_id", + "audit_outcome", + }: + _fail("Definition Auditor must expose only constrained registry IDs") + synthesis_prompt = transport_prompt( + "synthesis", + registered_choices={"choice_code": tuple("ABCDEFGHI")}, + ) + if "candidate_id" in synthesis_prompt or "C1_" in synthesis_prompt: + _fail("synthesis transport exposes a long candidate ID") + state_fields = set(OrchestrationCheckpoint.__dataclass_fields__) + removed_field = "protocol_" + "attempt" + if removed_field in state_fields: + _fail("adapter attempts remain in mathematical checkpoint") + source = ( + Path(agent_gan_repl.__file__).read_text(encoding="utf-8") + + Path(inspect.getfile(OrchestrationCheckpoint)).read_text(encoding="utf-8") + ) + migration_marker = "typed_ir_host_constrained_selector_v2" + if migration_marker not in source: + _fail("production v2 migration path is absent") + if "creative_decomposition_synthesis_moves_v3" not in source: + _fail("production creative decomposition v3 migration path is absent") + dispatch_source = inspect.getsource( + agent_gan_repl.run_certified_decomposition, + ) + if ".migration_event" in dispatch_source: + _fail("migration_event is used as an executable feature flag") + required_dispatch = ( + "require_typed_dispatch(orchestration_checkpoint)", + "return _run_typed_ir_v2(", + ) + if any(marker not in dispatch_source for marker in required_dispatch): + _fail("typed dispatch is not fail-closed") + if ARCHITECTURE_VERSION < 7: + _fail("strategy tournament replacement requires architecture 7") + for legacy in ( + ProofState.NEEDS_STRATEGY, + ProofState.GENERATOR, + ProofState.CRITIC, + ): + if ALLOWED_TRANSITIONS[legacy]: + _fail(f"legacy state has executable edges: {legacy.value}") + proof_fields = { + field.name + for field in ROLE_TRANSPORT_REGISTRY["proof_action_selector"].fields + } + if proof_fields != { + "goal_id", "action_id", "operand_id", "substitution_id", + }: + _fail("proof selector must expose short registered IDs only") + typed_source = inspect.getsource(agent_gan_repl._run_typed_ir_v2) + for legacy_call in ( + "_decomposer_repair_messages(", + "_decomposer_payload_result(", + "parse_certified_artifact(", + "repair_json_backslashes(", + "parse_proof_plan(", + "render_proof_plan(", + 'run_role(\n "proof_search"', + ): + if legacy_call in typed_source: + _fail(f"typed path reaches legacy call {legacy_call}") + print( + f"typed role contracts passed: {len(EXPECTED_ROLES)} roles, " + f"{len(state_fields)} checkpoint fields", + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/run_local_ci.sh b/scripts/run_local_ci.sh index ed338f8f..1a01d61f 100755 --- a/scripts/run_local_ci.sh +++ b/scripts/run_local_ci.sh @@ -10,6 +10,34 @@ export PYTHONPATH="${PYTHONPATH:-.:sdks/python}" # This script is the canonical local CI subset. GitHub Actions invokes this # exact file, then adds Docker, package, proto, TypeScript, and Mac integration # jobs around it. +"$PYTHON" -m py_compile \ + autoresearch/prefill/architecture_v7.py \ + autoresearch/prefill/atomic_definition.py \ + autoresearch/prefill/creative_decomposition.py \ + autoresearch/prefill/definition_registry.py \ + autoresearch/prefill/definition_resolution.py \ + autoresearch/prefill/evidence_planner.py \ + autoresearch/prefill/host_compiler.py \ + autoresearch/prefill/lean_gate.py \ + autoresearch/prefill/live_status.py \ + autoresearch/prefill/math_ir.py \ + autoresearch/prefill/orchestration_state.py \ + autoresearch/prefill/research_contract.py \ + autoresearch/prefill/semantic_decompose.py \ + autoresearch/prefill/stepwise_proof.py \ + autoresearch/prefill/strategy_tournament.py \ + autoresearch/prefill/supervisor.py \ + autoresearch/prefill/theorem_cards.py \ + autoresearch/prefill/typed_transport.py \ + scripts/agent_gan_inference_demo.py \ + scripts/agent_gan_repl.py \ + scripts/check_typed_role_contracts.py + +"$PYTHON" scripts/check_typed_role_contracts.py +node --check deploy/cloudflare-worker/src/index.js +node --check deploy/cloudflare-worker/src/page.js +node --test deploy/cloudflare-worker/test_execution_state.mjs + lake build "$PYTHON" -m coverage erase diff --git a/tests/inference_engine/network/test_network_state.py b/tests/inference_engine/network/test_network_state.py index 70ee5c4b..04618dfb 100644 --- a/tests/inference_engine/network/test_network_state.py +++ b/tests/inference_engine/network/test_network_state.py @@ -154,8 +154,10 @@ def test_benchmark_lifecycle_persistence_and_retention(tmp_path): stages=[stage], status="completed", finished_at=20, + provenance={"source": "unit_test", "candidate_count": 3}, ) assert completed["summary"]["decode_tok_s_p50"] == 5 + assert completed["provenance"]["candidate_count"] == 3 assert state.live_benchmark() is None assert state.list_benchmarks(limit=1)[0]["id"] == run["id"] assert state.list_benchmarks(status="completed")[0]["status"] == "completed"