ktx/docs/superpowers/plans/2026-05-11-managed-python-runtime-release-smoke.md
Andrey Avtomonov 9dad936ac7
feat: npm-managed Python runtime for @kaelio/ktx (#7)
* docs: add npm managed python runtime design

* build: add bundled python runtime wheel builder

* build: make local embedding dependencies optional

* build: bundle python runtime wheel in cli artifacts

* build: track bundled python runtime release artifact

* test: verify bundled python runtime wheel

* docs: add plan for bundled python runtime wheel

* test: cover managed python runtime lifecycle

* feat: add managed python runtime installer

* feat: add runtime command runner

* feat: expose runtime management commands

* test: verify managed python runtime commands

* docs: add plan for managed python runtime installer

* feat: add managed python command helper

* feat: use managed runtime for sl query compute

* feat: route sl query managed runtime policy

* docs: add plan for managed runtime sl query integration

* feat: add managed runtime daemon metadata

* feat: manage python daemon lifecycle

* feat: add runtime daemon start stop commands

* fix: verify managed runtime daemon lifecycle

* docs: add plan for managed runtime daemon lifecycle

* feat: add managed local embeddings config marker

* feat: add managed local embeddings daemon helper

* feat: use managed runtime for local embedding setup

* feat: pass managed runtime policy through setup

* docs: add plan for managed local embeddings runtime

* feat: read CLI package metadata dynamically

* feat: assemble public kaelio ktx npm package

* feat: release one public kaelio ktx npm artifact

* test: cover public kaelio ktx package invocations

* chore: verify public kaelio ktx package artifacts

* docs: add plan for public kaelio ktx npm package

* test: verify managed runtime in public package smoke

* test: finalize managed runtime release smoke

* docs: add plan for managed runtime release smoke

* test: specify local embeddings release smoke

* feat: add local embeddings runtime smoke

* chore: register local embeddings smoke

* fix: verify local embeddings smoke

* fix: restore artifact smoke python env helper

* docs: add plan for managed local embeddings release smoke

* refactor: share managed runtime install policy parsing

* feat: use managed runtime for agent semantic queries

* feat: use managed runtime for MCP semantic compute

* docs: add plan for managed agent and MCP semantic runtime

* feat(cli): add managed daemon HTTP helpers

* feat(cli): route local adapters through managed daemon

* feat(cli): use managed daemon for ingest helpers

* feat(cli): pass managed daemon options to scan

* feat(context): pass MCP ingest pull config options

* feat(cli): pass managed daemon options to serve ingest

* test: verify managed local ingest daemon runtime

* docs: add plan for managed local ingest daemon runtime

* docs: align managed runtime examples

* docs: add plan for managed runtime docs cleanup

* test: cover published package runtime smoke commands

* test: validate published package smoke outputs

* docs: add plan for published package runtime smoke

* build: stamp public npm package version

* release: add npm public release policy

* release: add guarded npm publish script

* release: document public npm release handoff

* docs: add plan for public npm release handoff

* test: cover managed runtime prune in package smoke

* docs: document managed runtime prune

* docs: add plan for managed runtime prune smoke and docs

* chore: encode uv runtime prerequisite policy

* fix: clarify missing uv runtime error

* docs: document uv runtime prerequisite

* docs: add plan for uv runtime prerequisite contract

* refactor: limit release artifacts to public package runtime

* chore: align release policy with bundled runtime wheel

* docs: describe single public runtime artifact surface

* test: verify single public runtime artifact contract

* docs: add plan for single public runtime artifact cleanup

* fix: align local embeddings smoke with public version

* docs: add plan for local embeddings smoke public version

* release: soft-launch as @kaelio/ktx@0.1.0-rc.0 on next tag

Publish target moves to the pre-release version 0.1.0-rc.0 under the next
dist-tag so npm install @kaelio/ktx (which resolves to latest) does not
pick up the soft-launch build. Users opt in via @kaelio/ktx@next.

* Fix release script boundary checks

* Remove PostHog from public package bundle
2026-05-11 15:50:34 +02:00

20 KiB

Managed Python Runtime Release Smoke 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: Make the public @kaelio/ktx artifact smoke prove that the npm package installs and uses its own managed Python runtime without an externally prepared Python environment.

Architecture: Keep the release smoke black-box: install the packed public npm tarball into a clean project, isolate KTX_RUNTIME_ROOT, and exercise the installed ktx binary. The first ktx sl query --yes performs the lazy core runtime install from bundled package assets, then the smoke verifies runtime status, runtime doctor, daemon start/reuse, and daemon stop.

Tech Stack: Node 22 ESM scripts, node:test, pnpm, uv, KTX CLI managed Python runtime assets.


Existing status

This plan is based on docs/superpowers/specs/2026-05-11-npm-managed-python-runtime-design.md.

Existing plans based on the spec:

  • 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

All six are implemented in this worktree. Evidence found before writing this plan includes:

  • scripts/build-python-runtime-wheel.mjs and scripts/build-python-runtime-wheel.test.mjs.
  • packages/cli/assets/python/kaelio_ktx-0.1.0-py3-none-any.whl and packages/cli/assets/python/manifest.json.
  • packages/cli/src/managed-python-runtime.ts, packages/cli/src/runtime.ts, and packages/cli/src/commands/runtime-commands.ts.
  • packages/cli/src/managed-python-command.ts and ktx sl query runtime install policy flags.
  • packages/cli/src/managed-python-daemon.ts, daemon state paths, and ktx runtime start / ktx runtime stop.
  • packages/cli/src/managed-local-embeddings.ts, packages/context/src/llm/local-config.ts managed marker constants, and setup wiring in packages/cli/src/setup-embeddings.ts.
  • scripts/build-public-npm-package.mjs, scripts/build-public-npm-package.test.mjs, release-policy.json listing @kaelio/ktx, and published smoke command construction for the required @kaelio/ktx invocation modes.

The remaining release-smoke gap is in scripts/package-artifacts.mjs:

  • verifyNpmArtifacts() creates a smoke .venv, installs the built Python runtime wheel into it, and runs installed CLI smoke scripts with that venv at the front of PATH.
  • The installed CLI smoke does run ktx sl query --yes, but it does not isolate KTX_RUNTIME_ROOT, does not assert that the first query installed the managed runtime from bundled npm assets, and does not exercise ktx runtime status, doctor, start, reuse, and stop.

This plan closes that release-flow gap without changing the separate Python artifact smoke. verifyPythonArtifacts() must continue to install the built Python wheel directly because it verifies the Python artifact itself.

File structure

  • Modify scripts/package-artifacts.test.mjs: remove the npm-smoke venv test, add a source-level guard that npm artifact verification does not prepare an external Python venv, and assert that the installed CLI smoke exercises the managed runtime lifecycle.
  • Modify scripts/package-artifacts.mjs: remove npm-smoke Python venv PATH setup, isolate KTX_RUNTIME_ROOT inside npmRuntimeSmokeSource(), assert first-run lazy install, and add runtime status/doctor/start/reuse/stop smoke commands.

Task 1: Add failing release-smoke tests

Files:

  • Modify: scripts/package-artifacts.test.mjs

  • Test: scripts/package-artifacts.test.mjs

  • Step 1: Remove the stale npm-smoke venv import

In scripts/package-artifacts.test.mjs, delete npmSmokePythonEnv from the import list. The surrounding import block must contain this sequence after the edit:

  npmDemoSmokeSource,
  npmRuntimeSmokeSource,
  npmSmokePackageJson,
  npmVerifySource,
  • Step 2: Replace the npm-smoke venv test with a source guard

Delete this entire test block:

describe('npmSmokePythonEnv', () => {
  it('prepends the npm smoke virtualenv bin directory to PATH', () => {
    const env = npmSmokePythonEnv('/tmp/ktx-npm-smoke', { PATH: '/usr/bin' });

    assert.match(env.PATH, /^\/tmp\/ktx-npm-smoke\/\.venv\/(bin|Scripts)/);
    assert.match(env.PATH, /\/usr\/bin$/);
  });
});

Insert this block in the same location:

describe('verifyNpmArtifacts', () => {
  it('does not prepare an external Python environment for the npm smoke', async () => {
    const source = await readFile(new URL('./package-artifacts.mjs', import.meta.url), 'utf8');
    const start = source.indexOf('async function verifyNpmArtifacts');
    const end = source.indexOf('async function verifyNpmDemoArtifacts');
    assert.ok(start > 0, 'verifyNpmArtifacts function must exist');
    assert.ok(end > start, 'verifyNpmDemoArtifacts must follow verifyNpmArtifacts');

    const body = source.slice(start, end);
    assert.doesNotMatch(body, /uv', \['venv', '\.venv'\]/);
    assert.doesNotMatch(body, /pythonArtifactInstallArgs/);
    assert.doesNotMatch(body, /npmSmokePythonEnv/);
  });
});
  • Step 3: Extend the installed CLI smoke assertions

In the it('runs installed CLI commands through the public package runtime', ...) test, add these assertions after the existing assert.match(source, /ktx sl query sqlite execute/); assertion:

    assert.match(source, /import Database from 'better-sqlite3'/);
    assert.doesNotMatch(source, /run\('python'/);
    assert.match(source, /KTX_RUNTIME_ROOT/);
    assert.match(source, /managed-runtime/);
    assert.match(source, /ktx runtime status missing/);
    assert.match(source, /runtimeStatusBefore\.kind, 'missing'/);
    assert.match(source, /Installing KTX Python runtime \(core\) with uv/);
    assert.match(source, /KTX Python runtime ready:/);
    assert.match(source, /ktx runtime status ready/);
    assert.match(source, /runtimeStatusAfter\.kind, 'ready'/);
    assert.match(source, /runtimeStatusAfter\.manifest\.features/);
    assert.match(source, /ktx runtime doctor/);
    assert.match(source, /PASS Managed Python runtime/);
    assert.match(source, /ktx runtime start/);
    assert.match(source, /ktx runtime start reuse/);
    assert.match(source, /Using existing KTX Python daemon/);
    assert.match(source, /ktx runtime stop/);
  • Step 4: Run the failing package artifact tests

Run:

node --test scripts/package-artifacts.test.mjs

Expected: FAIL. The guard fails because verifyNpmArtifacts() still creates the npm-smoke .venv, and the installed CLI smoke assertions fail because npmRuntimeSmokeSource() does not yet isolate or verify the managed runtime.

Task 2: Make the npm smoke use only the managed runtime

Files:

  • Modify: scripts/package-artifacts.mjs

  • Modify: scripts/package-artifacts.test.mjs

  • Test: scripts/package-artifacts.test.mjs

  • Step 1: Remove the npm-smoke PATH helper

In scripts/package-artifacts.mjs, change the path import from:

import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';

to:

import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';

Then delete this exported helper:

export function npmSmokePythonEnv(projectDir, baseEnv = process.env) {
  const binDir = process.platform === 'win32' ? join(projectDir, '.venv', 'Scripts') : join(projectDir, '.venv', 'bin');
  const existingPath = baseEnv.PATH ?? '';

  return Object.assign({}, baseEnv, {
    PATH: existingPath ? `${binDir}${delimiter}${existingPath}` : binDir,
  });
}
  • Step 2: Add runtime-smoke helpers to npmRuntimeSmokeSource()

Inside the template string returned by npmRuntimeSmokeSource(), add this helper immediately after requireSuccess():

function requireSuccessWithStderr(label, result, stderrPattern) {
  assert.equal(
    result.code,
    0,
    label + ' failed with code ' + result.code + '\\nstdout:\\n' + result.stdout + '\\nstderr:\\n' + result.stderr,
  );
  assert.match(result.stderr, stderrPattern, label + ' stderr did not match ' + stderrPattern);
}

Then replace the smoke root setup:

const root = await mkdtemp(join(tmpdir(), 'ktx-installed-cli-smoke-'));
try {
  const projectDir = join(root, 'project');
  const sourceDir = join(root, 'source');

with:

const root = await mkdtemp(join(tmpdir(), 'ktx-installed-cli-smoke-'));
const previousRuntimeRoot = process.env.KTX_RUNTIME_ROOT;
process.env.KTX_RUNTIME_ROOT = join(root, 'managed-runtime');
let daemonStarted = false;
try {
  const projectDir = join(root, 'project');
  const sourceDir = join(root, 'source');

Finally replace the existing finally block at the end of npmRuntimeSmokeSource():

} finally {
  await rm(root, { recursive: true, force: true });
}

with:

} finally {
  if (daemonStarted) {
    await run('pnpm', ['exec', 'ktx', 'runtime', 'stop']);
  }
  if (previousRuntimeRoot === undefined) {
    delete process.env.KTX_RUNTIME_ROOT;
  } else {
    process.env.KTX_RUNTIME_ROOT = previousRuntimeRoot;
  }
  await rm(root, { recursive: true, force: true });
}
  • Step 3: Create the sqlite smoke warehouse without Python

Inside the template string returned by npmRuntimeSmokeSource(), add this import after the assert import:

import Database from 'better-sqlite3';

Then replace the current writeSqliteWarehouse() function:

async function writeSqliteWarehouse(projectDir) {
  const createDb = await run('python', [
    '-c',
    [
      'import sqlite3',
      'import sys',
      'db_path = sys.argv[1]',
      'conn = sqlite3.connect(db_path)',
      'conn.executescript("""',
      'DROP TABLE IF EXISTS orders;',
      'CREATE TABLE orders (',
      '  id INTEGER PRIMARY KEY,',
      '  status TEXT NOT NULL,',
      '  amount INTEGER NOT NULL',
      ');',
      "INSERT INTO orders (status, amount) VALUES ('paid', 20), ('paid', 30), ('open', 10);",
      '""")',
      'conn.close()',
    ].join('\\n'),
    join(projectDir, 'warehouse.db'),
  ]);
  requireSuccess('create sqlite warehouse', createDb);
}

with:

async function writeSqliteWarehouse(projectDir) {
  const database = new Database(join(projectDir, 'warehouse.db'));
  try {
    database.exec(`
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  status TEXT NOT NULL,
  amount INTEGER NOT NULL
);
INSERT INTO orders (status, amount) VALUES ('paid', 20), ('paid', 30), ('open', 10);
`);
  } finally {
    database.close();
  }
}
  • Step 4: Assert the isolated runtime is initially missing

In npmRuntimeSmokeSource(), insert this block immediately after the public package version assertion:

  const runtimeStatusBefore = parseJsonResult(
    'ktx runtime status missing',
    await run('pnpm', ['exec', 'ktx', 'runtime', 'status', '--json']),
  );
  assert.equal(runtimeStatusBefore.kind, 'missing');
  assert.equal(runtimeStatusBefore.layout.runtimeRoot, process.env.KTX_RUNTIME_ROOT);
  process.stdout.write('ktx managed runtime starts missing in isolated root\\n');
  • Step 5: Assert first sl query --yes performs lazy managed install

In npmRuntimeSmokeSource(), replace the current slQuery verification block:

  const slQuery = await run('pnpm', ['exec', 'ktx', 'sl', 'query',
    '--connection-id',
    'warehouse',
    '--measure',
    'orders.order_count',
    '--format',
    'json',
    '--yes',
    '--project-dir',
    projectDir,
  ]);
  requireSuccess('ktx sl query', slQuery);
  requireOutput('ktx sl query', slQuery, /"mode": "compile_only"/);
  requireOutput('ktx sl query', slQuery, /orders/);

with:

  const slQuery = await run('pnpm', ['exec', 'ktx', 'sl', 'query',
    '--connection-id',
    'warehouse',
    '--measure',
    'orders.order_count',
    '--format',
    'json',
    '--yes',
    '--project-dir',
    projectDir,
  ]);
  requireSuccessWithStderr(
    'ktx sl query first managed runtime install',
    slQuery,
    /Installing KTX Python runtime \(core\) with uv[\s\S]*KTX Python runtime ready:/,
  );
  requireOutput('ktx sl query first managed runtime install', slQuery, /"mode": "compile_only"/);
  requireOutput('ktx sl query first managed runtime install', slQuery, /orders/);

  const runtimeStatusAfter = parseJsonResult(
    'ktx runtime status ready',
    await run('pnpm', ['exec', 'ktx', 'runtime', 'status', '--json']),
  );
  assert.equal(runtimeStatusAfter.kind, 'ready');
  assert.deepEqual(runtimeStatusAfter.manifest.features, ['core']);
  assert.equal(runtimeStatusAfter.layout.runtimeRoot, process.env.KTX_RUNTIME_ROOT);
  process.stdout.write('ktx managed runtime lazy install verified\\n');
  • Step 6: Add runtime doctor and daemon lifecycle smoke

In npmRuntimeSmokeSource(), insert this block immediately after the sqliteSlQuery verification block:

  const runtimeDoctor = await run('pnpm', ['exec', 'ktx', 'runtime', 'doctor']);
  requireSuccess('ktx runtime doctor', runtimeDoctor);
  requireOutput('ktx runtime doctor', runtimeDoctor, /PASS uv/);
  requireOutput('ktx runtime doctor', runtimeDoctor, /PASS Bundled Python wheel/);
  requireOutput('ktx runtime doctor', runtimeDoctor, /PASS Managed Python runtime/);
  process.stdout.write('ktx runtime doctor verified\\n');

  const runtimeStart = await run('pnpm', ['exec', 'ktx', 'runtime', 'start']);
  requireSuccess('ktx runtime start', runtimeStart);
  daemonStarted = true;
  requireOutput('ktx runtime start', runtimeStart, /Started KTX Python daemon/);
  requireOutput('ktx runtime start', runtimeStart, /url: http:\/\/127\.0\.0\.1:\d+/);
  requireOutput('ktx runtime start', runtimeStart, /features: core/);

  const runtimeStartReuse = await run('pnpm', ['exec', 'ktx', 'runtime', 'start']);
  requireSuccess('ktx runtime start reuse', runtimeStartReuse);
  requireOutput('ktx runtime start reuse', runtimeStartReuse, /Using existing KTX Python daemon/);
  requireOutput('ktx runtime start reuse', runtimeStartReuse, /features: core/);

  const runtimeStop = await run('pnpm', ['exec', 'ktx', 'runtime', 'stop']);
  requireSuccess('ktx runtime stop', runtimeStop);
  daemonStarted = false;
  requireOutput('ktx runtime stop', runtimeStop, /Stopped KTX Python daemon/);
  process.stdout.write('ktx runtime daemon lifecycle verified\\n');
  • Step 7: Remove npm-smoke Python preparation from artifact verification

In scripts/package-artifacts.mjs, replace verifyNpmArtifacts() with this implementation:

async function verifyNpmArtifacts(layout, tmpRoot) {
  for (const packageInfo of NPM_ARTIFACT_PACKAGES) {
    await assertPathExists(layout.npmTarballs[packageInfo.name], `${packageInfo.name} tarball`);
  }

  const projectDir = join(tmpRoot, 'npm-clean-install');
  await mkdir(projectDir, { recursive: true });
  await writeFile(
    join(projectDir, 'package.json'),
    `${JSON.stringify(npmSmokePackageJson(layout), null, 2)}\n`,
  );
  await writeFile(join(projectDir, 'verify-npm.mjs'), npmVerifySource());
  await writeFile(join(projectDir, 'verify-installed-cli.mjs'), npmRuntimeSmokeSource());
  await writeFile(join(projectDir, 'verify-installed-demo.mjs'), npmDemoSmokeSource());

  await runCommand('pnpm', ['install'], { cwd: projectDir });
  await runCommand('pnpm', ['rebuild', 'better-sqlite3'], { cwd: projectDir });
  await runCommand('node', ['verify-npm.mjs'], { cwd: projectDir });
  await runCommand('pnpm', ['exec', 'ktx', '--version'], { cwd: projectDir });
  await runCommand('node', ['verify-installed-cli.mjs'], { cwd: projectDir });
  await runCommand('node', ['verify-installed-demo.mjs'], { cwd: projectDir });
}
  • Step 8: Run the focused package artifact tests

Run:

node --test scripts/package-artifacts.test.mjs

Expected: PASS.

  • Step 9: Commit the release-smoke implementation

Run:

git add scripts/package-artifacts.mjs scripts/package-artifacts.test.mjs
git commit -m "test: verify managed runtime in public package smoke"

Task 3: Verify the release-smoke surface

Files:

  • Test: scripts/package-artifacts.test.mjs

  • Test: scripts/package-artifacts.mjs

  • Step 1: Run script unit tests that cover artifact packaging

Run:

node --test scripts/build-python-runtime-wheel.test.mjs scripts/build-public-npm-package.test.mjs scripts/package-artifacts.test.mjs scripts/published-package-smoke.test.mjs scripts/release-readiness.test.mjs

Expected: PASS.

  • Step 2: Run the public package artifact smoke

Run:

pnpm run artifacts:verify

Expected: PASS. The verify-installed-cli.mjs output must include:

ktx managed runtime starts missing in isolated root
ktx managed runtime lazy install verified
ktx runtime doctor verified
ktx runtime daemon lifecycle verified
  • Step 3: Run release readiness

Run:

pnpm run release:readiness

Expected: PASS. The report must still list @kaelio/ktx as the only npm package and must still report registry publishing as disabled by release-policy.json.

  • Step 4: Run pre-commit for changed files

Run:

if [ -d .venv ]; then source .venv/bin/activate; fi
uv run pre-commit run --files scripts/package-artifacts.mjs scripts/package-artifacts.test.mjs

Expected: PASS. If pre-commit cannot run because the local environment lacks a compatible hook version, record the exact failure and keep the passing node --test and artifact smoke results.

  • Step 5: Commit verification fixes if needed

If Step 1, Step 2, Step 3, or Step 4 required edits, run:

git add scripts/package-artifacts.mjs scripts/package-artifacts.test.mjs
git commit -m "test: finalize managed runtime release smoke"

If no files changed after Task 2, do not create an empty commit.

Acceptance criteria

  • verifyNpmArtifacts() no longer creates a Python .venv, no longer calls pythonArtifactInstallArgs(), and no longer runs npm smoke scripts with a custom Python venv at the front of PATH.
  • The installed public npm smoke creates its sqlite warehouse with better-sqlite3 and does not shell out to python.
  • The installed public npm smoke sets an isolated KTX_RUNTIME_ROOT and confirms that ktx runtime status --json starts as missing.
  • The first installed ktx sl query --yes installs the core managed Python runtime from bundled npm package assets and still returns compile-only SQL.
  • A second semantic query executes against sqlite using the installed managed runtime.
  • ktx runtime doctor passes after lazy install.
  • ktx runtime start starts a core daemon, a second ktx runtime start reuses the daemon, and ktx runtime stop stops it.
  • The separate Python artifact verification still installs and tests the Python wheel directly.
  • Focused script tests, pnpm run artifacts:verify, release readiness, and pre-commit pass or have explicitly recorded environment blockers.

Self-review

  • Spec coverage: the previous six plans cover the bundled wheel, runtime installer, sl query command integration, daemon lifecycle, local embeddings, and public npm package surface. This plan covers release-flow checks for clean install of the packed npm package, first-run managed runtime install from the bundled wheel, one-shot semantic-layer query through the managed runtime, runtime status and doctor output, and daemon start/reuse/stop.
  • Remaining intentional gap: optional local-embeddings smoke remains outside the default release artifact smoke because the spec permits it in a separate job or opt-in check and the dependency downloads are large.
  • Placeholder scan: no steps contain placeholder implementation language.
  • Type consistency: runtime feature names remain core and local-embeddings; the public npm package name remains @kaelio/ktx; the runtime root environment variable is KTX_RUNTIME_ROOT.