# Managed Runtime uv Prerequisite Contract Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use > superpowers:subagent-driven-development (recommended) or > superpowers:executing-plans to implement this plan task-by-task. Steps use > checkbox (`- [ ]`) syntax for tracking. **Goal:** Close the remaining npm-managed Python runtime open decision by making `uv` a documented, release-policy-checked prerequisite. **Architecture:** Keep the runtime installer behavior simple: the CLI locates `uv` on `PATH` and prints a focused error when it is missing. Encode that decision in `release-policy.json`, validate it during release readiness, use one shared runtime error message, and document the prerequisite in public docs. **Tech Stack:** Node 22 ESM scripts, `node:test`, TypeScript, Vitest, JSON release policy, Markdown. --- ## Existing status This plan is based on `docs/superpowers/specs/2026-05-11-npm-managed-python-runtime-design.md`. The following plan files are based on that spec and are already implemented in this worktree: - `docs/superpowers/plans/2026-05-11-bundled-python-runtime-wheel.md` - `docs/superpowers/plans/2026-05-11-managed-python-runtime-installer.md` - `docs/superpowers/plans/2026-05-11-managed-python-runtime-command-integration.md` - `docs/superpowers/plans/2026-05-11-managed-python-runtime-daemon-lifecycle.md` - `docs/superpowers/plans/2026-05-11-managed-local-embeddings-runtime.md` - `docs/superpowers/plans/2026-05-11-public-kaelio-ktx-npm-package.md` - `docs/superpowers/plans/2026-05-11-managed-python-runtime-release-smoke.md` - `docs/superpowers/plans/2026-05-11-managed-local-embeddings-release-smoke.md` - `docs/superpowers/plans/2026-05-11-managed-agent-mcp-semantic-runtime.md` - `docs/superpowers/plans/2026-05-11-managed-local-ingest-daemon-runtime.md` - `docs/superpowers/plans/2026-05-11-managed-runtime-docs-and-postgres-smoke-cleanup.md` - `docs/superpowers/plans/2026-05-11-published-package-managed-runtime-smoke.md` - `docs/superpowers/plans/2026-05-11-public-npm-release-handoff.md` - `docs/superpowers/plans/2026-05-11-managed-runtime-prune-smoke-and-docs.md` Implementation evidence found before writing this plan includes: - `packages/cli/assets/python/manifest.json` and the bundled `kaelio_ktx-0.1.0-py3-none-any.whl`. - `packages/cli/src/managed-python-runtime.ts`, including runtime roots, bundled wheel verification, install, status, doctor, and prune behavior. - `packages/cli/src/managed-python-command.ts`, `packages/cli/src/managed-python-daemon.ts`, `packages/cli/src/managed-local-embeddings.ts`, and `packages/cli/src/managed-python-http.ts`. - `scripts/build-public-npm-package.mjs`, `scripts/package-artifacts.mjs`, `scripts/published-package-smoke.mjs`, `scripts/local-embeddings-runtime-smoke.mjs`, and `scripts/publish-public-npm-package.mjs`. - `release-policy.json` is already in `npm-public-release-ready` mode for `@kaelio/ktx` `0.1.0` and keeps Python package publishing disabled. - `README.md` and `examples/package-artifacts/README.md` document the managed runtime command family, including `runtime prune`. The remaining spec gap is the open decision in `docs/superpowers/specs/2026-05-11-npm-managed-python-runtime-design.md`: ```text KTX still needs a final decision on whether uv is a hard prerequisite or a bootstrap dependency that KTX downloads automatically. ``` This plan chooses the hard-prerequisite path for the first public release. KTX will not download `uv` automatically in this release. ## File structure - Modify `release-policy.json`: add a `runtimeInstaller` policy section that records the hard `uv` prerequisite decision. - Modify `scripts/release-readiness.mjs`: validate the runtime installer policy, include it in readiness reports, and print it in text output. - Modify `scripts/release-readiness.test.mjs`: cover the accepted policy and rejection paths for missing or bootstrap-style `uv` policies. - Modify `packages/cli/src/managed-python-runtime.ts`: export one shared missing-`uv` message and use it for install and doctor output. - Modify `packages/cli/src/managed-python-runtime.test.ts`: cover install and doctor behavior when `uv` is missing. - Modify `scripts/examples-docs.test.mjs`: require public docs to state the hard `uv` prerequisite. - Modify `README.md`: document that `uv` must be on `PATH` and KTX does not download it automatically. - Modify `examples/package-artifacts/README.md`: document the artifact smoke `uv` prerequisite. ### Task 1: Encode the runtime installer policy **Files:** - Modify: `release-policy.json` - Modify: `scripts/release-readiness.test.mjs` - Modify: `scripts/release-readiness.mjs` - Test: `scripts/release-readiness.test.mjs` - [ ] **Step 1: Add failing release policy tests** In `scripts/release-readiness.test.mjs`, inside the `releasePolicy()` helper return value, add the `runtimeInstaller` object immediately after `publishedPackageSmoke`: ```javascript runtimeInstaller: { uvStrategy: 'path-prerequisite', bootstrapUv: false, missingUvBehavior: 'focused-error', }, ``` In the three `assert.deepEqual(report, { ... })` expectations, add this field immediately after `publishedPackageSmokeGate`: ```javascript runtimeInstaller: { uvStrategy: 'path-prerequisite', bootstrapUv: false, missingUvBehavior: 'focused-error', }, ``` Add these tests immediately after the `it('accepts the npm public release ready policy', async () => { ... })` block: ```javascript it('rejects npm public release ready mode without a runtime installer policy', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-runtime-policy-missing-test-')); try { await writeReadyFixture(root, { policy: releasePolicy({ releaseMode: 'npm-public-release-ready', npm: { publish: true, registry: null, access: 'public', tag: 'latest', }, publishedPackageSmoke: { packageName: '@kaelio/ktx', version: PUBLIC_NPM_PACKAGE_VERSION, registry: null, }, runtimeInstaller: undefined, requiredBeforePublishing: [], }), }); await assert.rejects( () => releaseReadinessReport(root), /Release policy runtimeInstaller must be a JSON object/, ); } finally { await rm(root, { recursive: true, force: true }); } }); it('rejects uv bootstrap download policy for the first public npm release', async () => { const root = await mkdtemp(join(tmpdir(), 'ktx-runtime-policy-bootstrap-test-')); try { await writeReadyFixture(root, { policy: releasePolicy({ releaseMode: 'npm-public-release-ready', npm: { publish: true, registry: null, access: 'public', tag: 'latest', }, publishedPackageSmoke: { packageName: '@kaelio/ktx', version: PUBLIC_NPM_PACKAGE_VERSION, registry: null, }, runtimeInstaller: { uvStrategy: 'bootstrap-download', bootstrapUv: true, missingUvBehavior: 'download', }, requiredBeforePublishing: [], }), }); await assert.rejects( () => releaseReadinessReport(root), /Release policy runtimeInstaller\.uvStrategy must be path-prerequisite/, ); } finally { await rm(root, { recursive: true, force: true }); } }); ``` - [ ] **Step 2: Run the release readiness tests and verify failure** Run: ```bash node --test scripts/release-readiness.test.mjs ``` Expected: FAIL because `releaseReadinessReport()` does not include `runtimeInstaller`, and `validateReleasePolicy()` does not validate the new policy section. - [ ] **Step 3: Validate the runtime installer policy** In `scripts/release-readiness.mjs`, add this function immediately after the `assertRequiredBeforePublishing(policy)` function definition: ```javascript function assertRuntimeInstallerPolicy(policy) { assertPlainObject(policy.runtimeInstaller, 'Release policy runtimeInstaller'); assertString(policy.runtimeInstaller.uvStrategy, 'Release policy runtimeInstaller.uvStrategy'); assertBoolean(policy.runtimeInstaller.bootstrapUv, 'Release policy runtimeInstaller.bootstrapUv'); assertString( policy.runtimeInstaller.missingUvBehavior, 'Release policy runtimeInstaller.missingUvBehavior', ); if (policy.runtimeInstaller.uvStrategy !== 'path-prerequisite') { throw new Error('Release policy runtimeInstaller.uvStrategy must be path-prerequisite'); } if (policy.runtimeInstaller.bootstrapUv !== false) { throw new Error('Release policy runtimeInstaller.bootstrapUv must be false'); } if (policy.runtimeInstaller.missingUvBehavior !== 'focused-error') { throw new Error('Release policy runtimeInstaller.missingUvBehavior must be focused-error'); } } ``` In `validateReleasePolicy(policy)`, add this call immediately after `assertRequiredBeforePublishing(policy);`: ```javascript assertRuntimeInstallerPolicy(policy); ``` In `releaseReadinessReport(rootDir = scriptRootDir())`, add `runtimeInstaller` to the returned object immediately after `publishedPackageSmokeGate`: ```javascript runtimeInstaller: policy.runtimeInstaller, ``` In `main()`, add these lines immediately after the published package smoke registry line: ```javascript process.stdout.write(`Runtime uv strategy: ${report.runtimeInstaller.uvStrategy}\n`); process.stdout.write( `Runtime uv bootstrap: ${report.runtimeInstaller.bootstrapUv ? 'enabled' : 'disabled'}\n`, ); ``` - [ ] **Step 4: Encode the policy in `release-policy.json`** Replace `release-policy.json` with this exact content: ```json { "schemaVersion": 1, "releaseMode": "npm-public-release-ready", "npm": { "publish": true, "registry": null, "access": "public", "tag": "latest", "packages": ["@kaelio/ktx"] }, "python": { "publish": false, "repository": null, "packages": ["ktx-sl", "ktx-daemon", "kaelio-ktx"] }, "publishedPackageSmoke": { "packageName": "@kaelio/ktx", "version": "0.1.0", "registry": null }, "runtimeInstaller": { "uvStrategy": "path-prerequisite", "bootstrapUv": false, "missingUvBehavior": "focused-error" }, "requiredBeforePublishing": [] } ``` - [ ] **Step 5: Run the release readiness tests and verify success** Run: ```bash node --test scripts/release-readiness.test.mjs ``` Expected: PASS. - [ ] **Step 6: Commit the release policy contract** ```bash git add release-policy.json scripts/release-readiness.mjs scripts/release-readiness.test.mjs git commit -m "chore: encode uv runtime prerequisite policy" ``` ### Task 2: Centralize missing-uv runtime output **Files:** - Modify: `packages/cli/src/managed-python-runtime.test.ts` - Modify: `packages/cli/src/managed-python-runtime.ts` - Test: `packages/cli/src/managed-python-runtime.test.ts` - [ ] **Step 1: Add failing missing-uv runtime tests** In `packages/cli/src/managed-python-runtime.test.ts`, add `MISSING_UV_RUNTIME_INSTALL_MESSAGE` to the import from `./managed-python-runtime.js`: ```typescript import { MISSING_UV_RUNTIME_INSTALL_MESSAGE, doctorManagedPythonRuntime, installManagedPythonRuntime, managedPythonRuntimeLayout, pruneManagedPythonRuntimes, readManagedPythonRuntimeStatus, verifyRuntimeAsset, type ManagedPythonRuntimeExec, } from './managed-python-runtime.js'; ``` Inside `describe('installManagedPythonRuntime', () => { ... })`, add this test after the local embeddings test: ```typescript it('fails with the hard-prerequisite message when uv is missing', async () => { const { assetDir } = await writeAsset(tempDir, 'core-wheel'); const commands: Array<{ command: string; args: string[] }> = []; const exec: ManagedPythonRuntimeExec = vi.fn(async (command, args) => { commands.push({ command, args }); throw new Error('spawn uv ENOENT'); }); await expect( installManagedPythonRuntime({ cliVersion: '0.2.0', runtimeRoot: join(tempDir, 'runtime'), assetDir, features: ['core'], exec, }), ).rejects.toThrow(MISSING_UV_RUNTIME_INSTALL_MESSAGE); expect(commands).toEqual([{ command: 'uv', args: ['--version'] }]); }); ``` Inside `describe('doctorManagedPythonRuntime', () => { ... })`, add this test after the existing doctor test: ```typescript it('reports uv as a hard prerequisite when uv is missing', async () => { const { assetDir } = await writeAsset(tempDir, 'core-wheel'); const exec: ManagedPythonRuntimeExec = vi.fn(async () => { throw new Error('spawn uv ENOENT'); }); const checks = await doctorManagedPythonRuntime({ cliVersion: '0.2.0', runtimeRoot: join(tempDir, 'runtime'), assetDir, exec, }); expect(checks[0]).toEqual({ id: 'uv', label: 'uv', status: 'fail', detail: MISSING_UV_RUNTIME_INSTALL_MESSAGE, fix: 'Install uv, make sure it is on PATH, and run: ktx runtime install --yes', }); }); ``` - [ ] **Step 2: Run the runtime tests and verify failure** Run: ```bash pnpm --filter @ktx/cli run test -- src/managed-python-runtime.test.ts ``` Expected: FAIL because the shared message constant does not exist and the doctor fix text still uses the older message. - [ ] **Step 3: Add the shared missing-uv message** In `packages/cli/src/managed-python-runtime.ts`, add this export immediately after the `ManagedPythonRuntimePruneResult` interface: ```typescript export const MISSING_UV_RUNTIME_INSTALL_MESSAGE = 'uv is required to install the KTX Python runtime. KTX does not download uv automatically. Install uv, make sure it is on PATH, and retry: ktx runtime install --yes'; ``` Replace the body of the `catch` block in `ensureUv()` with: ```typescript throw new Error(MISSING_UV_RUNTIME_INSTALL_MESSAGE); ``` In `doctorManagedPythonRuntime()`, replace the `fix` value for the `uv` check with: ```typescript fix: 'Install uv, make sure it is on PATH, and run: ktx runtime install --yes', ``` - [ ] **Step 4: Run the runtime tests and verify success** Run: ```bash pnpm --filter @ktx/cli run test -- src/managed-python-runtime.test.ts ``` Expected: PASS. - [ ] **Step 5: Commit the runtime output contract** ```bash git add packages/cli/src/managed-python-runtime.ts packages/cli/src/managed-python-runtime.test.ts git commit -m "fix: clarify missing uv runtime error" ``` ### Task 3: Document the hard uv prerequisite **Files:** - Modify: `scripts/examples-docs.test.mjs` - Modify: `README.md` - Modify: `examples/package-artifacts/README.md` - Test: `scripts/examples-docs.test.mjs` - [ ] **Step 1: Add failing docs assertions** In `scripts/examples-docs.test.mjs`, inside `it('documents public npm and managed runtime usage in the README', ... )`, add these assertions immediately after the existing `ktx runtime prune --yes` assertion: ```javascript assert.match(rootReadme, /KTX requires `uv` on `PATH`/); assert.match(rootReadme, /KTX doesn't download `uv` automatically/); ``` Inside `it('documents the public package artifact smoke shape', ... )`, add this assertion immediately after the `managed Python runtime` assertion: ```javascript assert.match(readme, /requires `uv` on `PATH`/); ``` - [ ] **Step 2: Run the docs test and verify failure** Run: ```bash node --test scripts/examples-docs.test.mjs ``` Expected: FAIL because the README files do not state the hard `uv` prerequisite. - [ ] **Step 3: Update the root README runtime section** In `README.md`, in the `## Managed Python runtime` section, replace this paragraph: ```markdown KTX installs its Python runtime only when a Python-backed command needs it. The runtime lives outside the npm cache, is versioned by the installed CLI version, and is managed by `ktx runtime` commands: ``` With: ```markdown KTX installs its Python runtime only when a Python-backed command needs it. The runtime lives outside the npm cache, is versioned by the installed CLI version, and is managed by `ktx runtime` commands. KTX requires `uv` on `PATH` to create the managed runtime. Install `uv` with your system package manager or the official installer before running Python- backed KTX commands. KTX doesn't download `uv` automatically; run `ktx runtime doctor` if runtime installation fails: ``` - [ ] **Step 4: Update the package artifact smoke README** In `examples/package-artifacts/README.md`, replace this paragraph: ```markdown The managed Python runtime smoke isolates `KTX_RUNTIME_ROOT`, verifies `ktx runtime status`, runs `ktx sl query --yes` to install the core runtime from the bundled wheel, checks `ktx runtime doctor`, starts and reuses the managed daemon, stops it, previews a stale runtime with `ktx runtime prune --dry-run`, verifies confirmation is required, and removes the stale runtime with `ktx runtime prune --yes`. ``` With: ```markdown The managed Python runtime smoke requires `uv` on `PATH`, isolates `KTX_RUNTIME_ROOT`, verifies `ktx runtime status`, runs `ktx sl query --yes` to install the core runtime from the bundled wheel, checks `ktx runtime doctor`, starts and reuses the managed daemon, stops it, previews a stale runtime with `ktx runtime prune --dry-run`, verifies confirmation is required, and removes the stale runtime with `ktx runtime prune --yes`. ``` - [ ] **Step 5: Run the docs test and verify success** Run: ```bash node --test scripts/examples-docs.test.mjs ``` Expected: PASS. - [ ] **Step 6: Commit the public docs update** ```bash git add README.md examples/package-artifacts/README.md scripts/examples-docs.test.mjs git commit -m "docs: document uv runtime prerequisite" ``` ### Task 4: Verify the completed contract **Files:** - Verify: `release-policy.json` - Verify: `scripts/release-readiness.mjs` - Verify: `scripts/release-readiness.test.mjs` - Verify: `packages/cli/src/managed-python-runtime.ts` - Verify: `packages/cli/src/managed-python-runtime.test.ts` - Verify: `scripts/examples-docs.test.mjs` - Verify: `README.md` - Verify: `examples/package-artifacts/README.md` - [ ] **Step 1: Run focused verification** Run: ```bash node --test scripts/release-readiness.test.mjs scripts/examples-docs.test.mjs pnpm --filter @ktx/cli run test -- src/managed-python-runtime.test.ts ``` Expected: PASS. - [ ] **Step 2: Verify release readiness text output** Run: ```bash pnpm run release:readiness ``` Expected output includes: ```text KTX release mode: npm-public-release-ready Runtime uv strategy: path-prerequisite Runtime uv bootstrap: disabled NPM publish target: @kaelio/ktx@0.1.0 (latest) ``` - [ ] **Step 3: Verify no pre-commit config is required** Run: ```bash rg --files -g '.pre-commit-config.yaml' -g 'pre-commit-config.yaml' ``` Expected: no output and exit code 1. No Python files changed, so the repository Python pre-commit requirement does not apply. - [ ] **Step 4: Review the final diff** Run: ```bash git diff --stat git diff -- release-policy.json scripts/release-readiness.mjs scripts/release-readiness.test.mjs packages/cli/src/managed-python-runtime.ts packages/cli/src/managed-python-runtime.test.ts scripts/examples-docs.test.mjs README.md examples/package-artifacts/README.md ``` Expected: only the runtime installer policy, missing-`uv` message/tests, and public docs changed. - [ ] **Step 5: Commit final verification notes if needed** If Task 4 produces only verification output and no file changes, skip this step. If a correction was made during verification, commit it: ```bash git add release-policy.json scripts/release-readiness.mjs scripts/release-readiness.test.mjs packages/cli/src/managed-python-runtime.ts packages/cli/src/managed-python-runtime.test.ts scripts/examples-docs.test.mjs README.md examples/package-artifacts/README.md git commit -m "chore: finish uv prerequisite release contract" ``` ## Self-review Spec coverage: - The earlier implemented plans cover the single public npm package, bundled Python wheel, managed runtime installer, runtime commands, daemon lifecycle, local embeddings, Python-backed command integration, release smoke, published smoke, docs cleanup, release handoff, and prune coverage. - This plan closes the spec's remaining `uv` open decision by choosing `path-prerequisite`, recording that decision in release policy, validating it in release readiness, using one CLI error message, and documenting it. - The plan keeps Python package publication disabled and keeps KTX-owned Python code bundled in the npm package. Placeholder scan: - No task contains deferred implementation markers. - Each code-changing step names exact files and includes the concrete code to add or replace. Type consistency: - The release policy field is consistently named `runtimeInstaller`. - The chosen strategy is consistently `path-prerequisite`. - The shared CLI message constant is consistently `MISSING_UV_RUNTIME_INSTALL_MESSAGE`.