diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 4e5ee43..63ed19d 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -274,7 +274,7 @@ Supported notarization inputs: - The supported arm64 release-candidate path includes its application runtime, core Python pack, and FFmpeg/FFprobe; creators do not install Python, pip, FFmpeg, or a virtual environment for that path. - Baseline transcription is included and the verified Whisper base model downloads into app-managed storage on first transcription. Once installed, baseline transcription can run without model-network access. - Optional capabilities may not be included. Do not claim all AI is offline or that all capabilities are bundled. -- Candidate metadata records that transitive runtime wheel hashes are not fully locked; 3B.5A does not claim fully reproducible builds. +- The macOS arm64 runtime package inputs are version- and SHA-256-locked. Source-built Whisper uses the pinned portable Python toolchain without isolated build downloads. Generated wheels and the final DMG are still not claimed byte-for-byte reproducible. - `npm run release:ffmpeg` verifies that FFmpeg/FFprobe execute from the release bundle and packages non-system macOS dylibs. Do not manually copy host FFmpeg executables into a release. - The bundle manifest records whether the selected FFmpeg supports ASS burn-in captions. Releases without that filter use the tested sidecar `.srt` fallback and must state that in their notes. - Parakeet TDT v3 requires optional NVIDIA NeMo ASR dependencies. diff --git a/runtime/core-darwin-arm64-py311.lock.txt b/runtime/core-darwin-arm64-py311.lock.txt new file mode 100644 index 0000000..fcdd526 --- /dev/null +++ b/runtime/core-darwin-arm64-py311.lock.txt @@ -0,0 +1,39 @@ +# Hash-locked runtime artifacts for native macOS arm64 / Python 3.11.15. +# Generated from the reviewed constraints graph. openai-whisper is intentionally +# locked to its source archive and built with the hash-pinned portable Python +# toolchain using --no-build-isolation. +annotated-doc==0.0.5 --hash=sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101 +annotated-types==0.8.0 --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 +anyio==4.15.1 --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 +certifi==2026.7.22 --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 +charset-normalizer==3.5.2 --hash=sha256:3d21b8b13c7592db2ac5e544a6d83187b995257472b0c9e8351b6d507ae37ed6 +click==8.5.0 --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 +fastapi==0.138.1 --hash=sha256:b994cae7ba8b82c976a728b544244de31333fa5f7d261f9a1dffe526444cae23 +filelock==4.1.1 --hash=sha256:3f4a557945a7b0f95efeb1f432267affe5d45ac8ddde2aed1b97ebb62382c089 +fsspec==2026.9.0 --hash=sha256:8dd6e646e99ea382bd85f97a45e6b526a442d79423a7dc673f1e2756d05fcb5f +h11==0.16.0 --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 +idna==3.20 --hash=sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c +jinja2==3.1.6 --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 +llvmlite==0.50.0 --hash=sha256:818b3d4845ac8e126e23cb500867570d0602a42a43e67b14acec31f046e03130 +markupsafe==3.0.4 --hash=sha256:7d3391b2188d18737cb2fa147028b1096236eaa7e156446c650a489fa2cadc91 +more-itertools==11.2.0 --hash=sha256:e4c522352a24664c974d0be8382944107ae638281a06391a5a538366b5707fe5 +mpmath==1.3.0 --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c +networkx==3.6.1 --hash=sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762 +numba==0.68.0 --hash=sha256:50399af9d3799a4677044294861169c614bd7e1d8bbfc9479f78a67ab28ff427 +numpy==1.26.4 --hash=sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef +openai-whisper==20250625 --hash=sha256:37a91a3921809d9f44748ffc73c0a55c9f366c85a3ef5c2ae0cc09540432eb96 +pydantic==2.13.4 --hash=sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba +pydantic-core==2.46.4 --hash=sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c +python-multipart==0.0.32 --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23 +regex==2026.9.29 --hash=sha256:066d0e3dbfdd739bce2bf8c2a41dd16f73e3d8adc2eb06dd803a36a307f56075 +requests==2.34.2 --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 +setuptools==81.0.0 --hash=sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6 +starlette==1.8.0 --hash=sha256:dfdd6b29c26483288088d990eee59631dedadd66ce20d203402a7ca8e3c4656f +sympy==1.14.0 --hash=sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5 +tiktoken==0.14.0 --hash=sha256:cd8ca1305c1c902fe42c486165f2e4808d9997625c98ffb05b9e0366d99d3948 +torch==2.12.1 --hash=sha256:ef81f503912effea2ce3d9b12a2e3a6ed488943e91271c90c7a829f60baf6aa2 +tqdm==4.70.1 --hash=sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73 +typing-extensions==4.16.0 --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 +typing-inspection==0.4.4 --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147 +urllib3==2.8.0 --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 +uvicorn==0.49.0 --hash=sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f diff --git a/scripts/check-packaged-runtime.js b/scripts/check-packaged-runtime.js index 5c19927..6c482a9 100644 --- a/scripts/check-packaged-runtime.js +++ b/scripts/check-packaged-runtime.js @@ -165,6 +165,24 @@ function inspectPackage(appPath) { } if (!fs.existsSync(constraintsPath)) fail(`runtime constraints source is missing: ${constraints.source}`); if (sha256(constraintsPath) !== constraints.sha256) fail('runtime constraints SHA-256 does not match the committed source'); + const dependencyLock = manifest.packs?.lock; + if ( + !dependencyLock + || typeof dependencyLock.source !== 'string' + || !/^[a-f0-9]{64}$/.test(dependencyLock.sha256 || '') + || dependencyLock.requireHashes !== true + || dependencyLock.buildIsolation !== false + ) { + fail('runtime manifest is missing valid hash-locked dependency provenance'); + } + if (path.isAbsolute(dependencyLock.source)) fail('runtime dependency lock source must be repository-relative'); + const dependencyLockPath = path.resolve(root, dependencyLock.source); + const relativeDependencyLockPath = path.relative(root, dependencyLockPath); + if (!relativeDependencyLockPath || relativeDependencyLockPath.startsWith('..') || path.isAbsolute(relativeDependencyLockPath)) { + fail('runtime dependency lock source escapes the repository root'); + } + if (!fs.existsSync(dependencyLockPath)) fail(`runtime dependency lock source is missing: ${dependencyLock.source}`); + if (sha256(dependencyLockPath) !== dependencyLock.sha256) fail('runtime dependency lock SHA-256 does not match the committed source'); let modelManifest; try { modelManifest = readModelManifest(modelManifestPath); @@ -232,6 +250,7 @@ function inspectPackage(appPath) { console.log(`FFmpeg/bin: ${binSize} bytes`); console.log(`Manifest: ${normalized.schema}, ${normalized.target.id}, Python ${normalized.python.version}`); console.log(`Core constraints: ${constraints.source}, SHA-256 ${constraints.sha256}`); + console.log(`Core dependency lock: ${dependencyLock.source}, SHA-256 ${dependencyLock.sha256}, requireHashes=true, buildIsolation=false`); console.log(`Model manifest: ${modelManifest.id}, ${modelManifest.revision}, ${modelManifest.expectedBytes} bytes`); console.log('Core imports: fastapi, uvicorn, pydantic, requests, torch, whisper==20250625'); console.log('Optional packages bundled: No'); diff --git a/scripts/check-public-release.js b/scripts/check-public-release.js index b3219fe..69d23a4 100644 --- a/scripts/check-public-release.js +++ b/scripts/check-public-release.js @@ -92,6 +92,13 @@ function validateManifest(manifest, { allowPendingAttestation = false } = {}) { assert(manifest.codeSignature?.hardenedRuntime === false, 'public ad-hoc candidate must not claim Hardened Runtime'); assert(manifest.runtime?.mode === 'packaged-bundled' && manifest.runtime?.pythonSource === 'bundled', 'runtime must be packaged-bundled'); assert(manifest.runtime?.target?.platform === 'darwin' && manifest.runtime?.target?.arch === 'arm64', 'runtime target must be darwin arm64'); + assert(typeof manifest.runtime?.dependencyLock?.source === 'string' && manifest.runtime.dependencyLock.source.length > 0, 'runtime dependency lock source is missing'); + assert(!path.isAbsolute(manifest.runtime.dependencyLock.source), 'runtime dependency lock source must be repository-relative'); + assert(/^[0-9a-f]{64}$/.test(manifest.runtime.dependencyLock.sha256 || ''), 'runtime dependency lock SHA-256 is missing'); + assert(manifest.runtime.dependencyLock.requireHashes === true, 'runtime dependency lock must require hashes'); + assert(manifest.runtime.dependencyLock.buildIsolation === false, 'runtime dependency lock must disable isolated source builds'); + assert(manifest.reproducible === false, 'public alpha must not claim byte-for-byte reproducibility'); + assert(/hash-locked/i.test(manifest.reproducibilityNote || '') && /not claimed byte-for-byte reproducible/i.test(manifest.reproducibilityNote || ''), 'runtime reproducibility note is inaccurate'); assert(manifest.model?.embedded === false, 'model weights must not be embedded'); if (manifest.bundleSize) { assert(manifest.bundleSize.schema === 'scriptcut.bundle-size.v1', 'bundle-size summary schema is invalid'); diff --git a/scripts/prepare-public-release.js b/scripts/prepare-public-release.js index 1b8363c..5700378 100644 --- a/scripts/prepare-public-release.js +++ b/scripts/prepare-public-release.js @@ -121,6 +121,7 @@ function publicManifest({ pkg, tag, commit, artifact, candidate, dmgAttestation pythonVersion: candidate.runtime.pythonVersion, pythonBuild: candidate.runtime.pythonBuild, manifestSha256: candidate.runtime.manifestSha256, + dependencyLock: candidate.runtime.dependencyLock, }, coreInventorySha256: candidate.coreInventorySha256, ffmpeg: candidate.ffmpeg, @@ -141,7 +142,7 @@ function publicManifest({ pkg, tag, commit, artifact, candidate, dmgAttestation dmgAttestation, }, reproducible: false, - reproducibilityNote: 'Transitive runtime wheel hashes are not fully locked in this phase.', + reproducibilityNote: 'Runtime package source/wheel inputs are hash-locked; generated wheels and the final DMG are not claimed byte-for-byte reproducible.', }; } @@ -187,6 +188,7 @@ macOS Apple Silicon (arm64) only. Intel, Windows, Linux, and browser builds are ## What's included - A self-contained Electron application with portable Python ${manifest.runtime.pythonVersion} and the pinned core runtime. +- A hash-locked macOS arm64 runtime package set; source-built Whisper uses the pinned portable Python toolchain without isolated build downloads. - Bundled FFmpeg and FFprobe for local export. - Baseline Whisper transcription code and a trusted model manifest. - Optional capabilities may be absent from this build. diff --git a/scripts/prepare-python-runtime.js b/scripts/prepare-python-runtime.js index 9e17af1..ba6eec9 100644 --- a/scripts/prepare-python-runtime.js +++ b/scripts/prepare-python-runtime.js @@ -25,6 +25,7 @@ const modelInputPath = path.join(root, 'runtime', 'models', 'whisper-base.json') const generatedModelManifestPath = path.join(buildRoot, 'manifests', 'model-manifest.json'); const coreInputPath = path.join(root, 'runtime', 'core-darwin-arm64-py311.txt'); const coreConstraintsPath = path.join(root, 'runtime', 'core-darwin-arm64-py311.constraints.txt'); +const coreLockPath = path.join(root, 'runtime', 'core-darwin-arm64-py311.lock.txt'); // Torch imports torch.testing through its normal autograd import path. const RUNTIME_REQUIRED_TEST_DIRECTORIES = new Set(['torch/testing']); @@ -211,6 +212,7 @@ function pythonVersion(interpreterPath, environment) { function installCorePack(interpreterPath) { if (!fs.existsSync(coreInputPath)) fail(`missing committed core input: ${path.relative(root, coreInputPath)}`); if (!fs.existsSync(coreConstraintsPath)) fail(`missing committed core constraints: ${path.relative(root, coreConstraintsPath)}`); + if (!fs.existsSync(coreLockPath)) fail(`missing committed core artifact lock: ${path.relative(root, coreLockPath)}`); fs.rmSync(corePackRoot, { recursive: true, force: true }); fs.mkdirSync(corePackRoot, { recursive: true }); const environment = isolatedEnvironment({ @@ -222,9 +224,10 @@ function installCorePack(interpreterPath) { '-m', 'pip', 'install', '--disable-pip-version-check', '--no-compile', + '--no-build-isolation', + '--require-hashes', '--target', corePackRoot, - '--constraint', coreConstraintsPath, - '--requirement', coreInputPath, + '--requirement', coreLockPath, ], { env: environment, inherit: true }); } @@ -285,6 +288,12 @@ function writeManifest(relativeInterpreter, version) { source: path.relative(root, coreConstraintsPath).split(path.sep).join('/'), sha256: sha256(coreConstraintsPath), }, + lock: { + source: path.relative(root, coreLockPath).split(path.sep).join('/'), + sha256: sha256(coreLockPath), + requireHashes: true, + buildIsolation: false, + }, }, security: { tokenRequired: true }, }; diff --git a/scripts/release-alpha.js b/scripts/release-alpha.js index 778665f..c667887 100644 --- a/scripts/release-alpha.js +++ b/scripts/release-alpha.js @@ -139,6 +139,15 @@ function readProvenance() { } async function createReleaseManifest(pkg, artifact, checksums, provenance, bundleSize) { + const dependencyLock = provenance.runtime.packs?.lock; + if ( + !dependencyLock + || !/^[a-f0-9]{64}$/.test(dependencyLock.sha256 || '') + || dependencyLock.requireHashes !== true + || dependencyLock.buildIsolation !== false + ) { + throw new Error('Runtime manifest is missing hash-locked dependency provenance.'); + } const manifest = { schema: 'scriptcut.release.v1', productName: pkg.build?.productName || pkg.name, @@ -164,6 +173,12 @@ async function createReleaseManifest(pkg, artifact, checksums, provenance, bundl pythonVersion: provenance.runtime.python.version, pythonBuild: provenance.runtime.python.build, manifestSha256: await checksumFile(provenance.runtimeManifestPath), + dependencyLock: { + source: dependencyLock.source, + sha256: dependencyLock.sha256, + requireHashes: true, + buildIsolation: false, + }, }, coreInventorySha256: await checksumFile(provenance.coreInventoryPath), ffmpeg: { @@ -197,7 +212,7 @@ async function createReleaseManifest(pkg, artifact, checksums, provenance, bundl signed: false, notarized: false, reproducible: false, - reproducibilityNote: 'Transitive runtime wheel hashes are not fully locked in this phase.', + reproducibilityNote: 'Runtime package source/wheel inputs are hash-locked; generated wheels and the final DMG are not claimed byte-for-byte reproducible.', }; const manifestPath = path.join(releaseMetadataDir, 'release-manifest.json'); fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8'); diff --git a/scripts/smoke-public-release.js b/scripts/smoke-public-release.js index 6c1eae1..4adb7fe 100644 --- a/scripts/smoke-public-release.js +++ b/scripts/smoke-public-release.js @@ -53,6 +53,12 @@ function writeFixture(rootDir) { pythonVersion: '3.11.15', pythonBuild: '20260807', manifestSha256: '1'.repeat(64), + dependencyLock: { + source: 'runtime/core-darwin-arm64-py311.lock.txt', + sha256: '6'.repeat(64), + requireHashes: true, + buildIsolation: false, + }, }, coreInventorySha256: '2'.repeat(64), codeSignature: { type: 'ad-hoc', structurallyValid: true, hardenedRuntime: false }, @@ -86,6 +92,7 @@ async function main() { const manifest = JSON.parse(fs.readFileSync(result.manifestPath, 'utf8')); const notes = fs.readFileSync(path.join(fixture.outputDir, 'RELEASE_NOTES.md'), 'utf8'); validateManifest(manifest); + assert(manifest.runtime.dependencyLock.sha256 === '6'.repeat(64), 'runtime dependency lock provenance was not propagated'); assert(manifest.bundleSize?.schema === 'scriptcut.bundle-size.v1', 'bundle-size summary was not propagated to public manifest'); validateNotes(notes); assert(/--signer-workflow[\s\S]*--source-digest/.test(notes), 'generated RELEASE_NOTES omit constrained attestation verification'); diff --git a/scripts/smoke-release-metadata.js b/scripts/smoke-release-metadata.js index 6db479e..70a588e 100644 --- a/scripts/smoke-release-metadata.js +++ b/scripts/smoke-release-metadata.js @@ -55,6 +55,12 @@ function createSourceFixture() { pythonSource: 'bundled', target: { platform: 'darwin', arch: 'arm64' }, manifestSha256: '2'.repeat(64), + dependencyLock: { + source: 'runtime/core-darwin-arm64-py311.lock.txt', + sha256: '5'.repeat(64), + requireHashes: true, + buildIsolation: false, + }, }, coreInventorySha256: '3'.repeat(64), ffmpeg: { manifestSha256: '4'.repeat(64) }, @@ -123,10 +129,13 @@ async function validateReleaseDirectory(releaseDir) { assert(manifest.runtime?.mode === 'packaged-bundled', 'runtime mode must be packaged-bundled'); assert(manifest.runtime?.pythonSource === 'bundled', 'Python source must be bundled'); assert(manifest.runtime?.target?.platform === 'darwin' && manifest.runtime?.target?.arch === 'arm64', 'runtime target must be darwin-arm64'); + assert(manifest.runtime?.dependencyLock?.source === 'runtime/core-darwin-arm64-py311.lock.txt', 'runtime dependency lock source is missing'); + assert(/^[0-9a-f]{64}$/.test(manifest.runtime?.dependencyLock?.sha256 || ''), 'runtime dependency lock SHA-256 is missing'); + assert(manifest.runtime?.dependencyLock?.requireHashes === true && manifest.runtime?.dependencyLock?.buildIsolation === false, 'runtime dependency lock policy is missing'); assert(manifest.artifact?.filename && path.basename(manifest.artifact.filename) === manifest.artifact.filename, 'artifact filename must be relative'); assert(Number.isInteger(manifest.artifact.bytes) && manifest.artifact.bytes > 0, 'artifact bytes must be present'); assert(/^[0-9a-f]{64}$/.test(manifest.artifact.sha256), 'artifact SHA-256 must be present'); - for (const field of [manifest.runtime.manifestSha256, manifest.coreInventorySha256, manifest.ffmpeg.manifestSha256, manifest.model.manifestSha256]) { + for (const field of [manifest.runtime.manifestSha256, manifest.runtime.dependencyLock.sha256, manifest.coreInventorySha256, manifest.ffmpeg.manifestSha256, manifest.model.manifestSha256]) { assert(/^[0-9a-f]{64}$/.test(field), 'provenance SHA-256 must be present'); } walkValues(manifest, (value) => { diff --git a/scripts/smoke-release-notes.js b/scripts/smoke-release-notes.js index dcb1448..acc35c6 100644 --- a/scripts/smoke-release-notes.js +++ b/scripts/smoke-release-notes.js @@ -62,6 +62,12 @@ function writeCandidate(directory) { pythonVersion: '3.11.15', pythonBuild: '20260807', manifestSha256: '1'.repeat(64), + dependencyLock: { + source: 'runtime/core-darwin-arm64-py311.lock.txt', + sha256: '6'.repeat(64), + requireHashes: true, + buildIsolation: false, + }, }, coreInventorySha256: '2'.repeat(64), codeSignature: { type: 'ad-hoc', structurallyValid: true, hardenedRuntime: false }, diff --git a/scripts/smoke-runtime-artifacts.js b/scripts/smoke-runtime-artifacts.js index c172dc2..49c6161 100644 --- a/scripts/smoke-runtime-artifacts.js +++ b/scripts/smoke-runtime-artifacts.js @@ -18,18 +18,29 @@ assert.strictEqual(commonModelMappings.length, 1); assert.ok(!(packageJson.build?.extraResources || []).some((entry) => entry.from === 'build/manifests')); const coreRequirements = fs.readFileSync(path.join(__dirname, '..', 'runtime', 'core-darwin-arm64-py311.txt'), 'utf8'); const coreConstraints = fs.readFileSync(path.join(__dirname, '..', 'runtime', 'core-darwin-arm64-py311.constraints.txt'), 'utf8'); +const coreLock = fs.readFileSync(path.join(__dirname, '..', 'runtime', 'core-darwin-arm64-py311.lock.txt'), 'utf8'); assert.ok(!coreRequirements.includes('model-manifest')); assert.ok(!coreRequirements.includes('base.pt')); +const constraintPins = coreConstraints.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith('#')); +const lockLines = coreLock.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith('#')); +assert.strictEqual(lockLines.length, constraintPins.length); +for (const line of lockLines) assert.match(line, /^[a-z0-9.-]+==[^\s]+ --hash=sha256:[a-f0-9]{64}$/); for (const requirement of coreRequirements.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith('#'))) { assert.ok(coreConstraints.split(/\r?\n/).includes(requirement), `core constraints are missing direct pin: ${requirement}`); } +for (const constraint of constraintPins) { + const normalized = constraint.toLowerCase().replace(/_/g, '-'); + assert.ok(lockLines.some((line) => line.startsWith(`${normalized} --hash=sha256:`)), `core lock is missing artifact hash for: ${constraint}`); +} const runtimePreparation = fs.readFileSync(path.join(__dirname, 'prepare-python-runtime.js'), 'utf8'); assert.match(runtimePreparation, /function\s+pruneNonRuntimeCoreArtifacts\s*\(/); assert.match(runtimePreparation, /function\s+prunePythonBytecode\s*\(/); assert.match(runtimePreparation, /function\s+prunePackageTestArtifacts\s*\(/); assert.match(runtimePreparation, /RUNTIME_REQUIRED_TEST_DIRECTORIES\s*=\s*new Set\(\[['"]torch\/testing['"]\]\)/); assert.match(runtimePreparation, /['"]--no-compile['"]/); -assert.match(runtimePreparation, /['"]--constraint['"],\s*coreConstraintsPath/); +assert.match(runtimePreparation, /['"]--no-build-isolation['"]/); +assert.match(runtimePreparation, /['"]--require-hashes['"]/); +assert.match(runtimePreparation, /['"]--requirement['"],\s*coreLockPath/); assert.match(runtimePreparation, /PYTHONDONTWRITEBYTECODE\s*=\s*['"]1['"]/); assert.match(runtimePreparation, /path\.join\(\s*corePackRoot\s*,\s*['"]torch['"]\s*,\s*['"]include['"]\s*\)/); assert.match(runtimePreparation, /fs\.rmSync\(\s*torchIncludePath\s*,\s*\{[\s\S]*?recursive:\s*true/);