diff --git a/docs/superpowers/specs/2026-05-11-npm-managed-python-runtime-design.md b/docs/superpowers/specs/2026-05-11-npm-managed-python-runtime-design.md new file mode 100644 index 00000000..bc089765 --- /dev/null +++ b/docs/superpowers/specs/2026-05-11-npm-managed-python-runtime-design.md @@ -0,0 +1,234 @@ +# npm-managed Python runtime design + +This spec defines how KTX ships as one visible npm package while still using +Python for sqlglot, semantic-layer planning, database-agent compute, and local +embeddings. The goal is a user experience where users install or run only +`@kaelio/ktx`, and KTX manages its Python runtime automatically when a command +needs it. + +## Goals + +KTX must be usable through the npm package `@kaelio/ktx` with a `ktx` binary. +Users can run KTX without learning about the Python packages that power parts of +the system. + +The first release must support these invocation modes: + +- `npx @kaelio/ktx setup demo` +- `npx @kaelio/ktx sl query ...` +- `npm install @kaelio/ktx`, followed by `npx ktx ...` +- `npm install -g @kaelio/ktx`, followed by `ktx ...` + +KTX-owned Python code must ship inside the npm package as a bundled wheel. KTX +doesn't need to publish its own Python code to PyPI for this release. + +## Non-goals + +This release does not need to provide a public TypeScript SDK split across +multiple npm packages. The internal workspace package layout can remain useful +for development, but the public npm surface is a single package. + +This release does not need a fully offline install. KTX's own Python wheel is +bundled, but third-party Python dependencies can come from PyPI through `uv`. + +This release does not install local embedding dependencies by default. Local +embeddings remain lazy because `sentence-transformers`, `torch`, and model +downloads are large. + +## Package model + +KTX publishes one public npm package: + +```text +@kaelio/ktx +``` + +That package exposes one binary: + +```json +{ + "bin": { + "ktx": "./dist/bin.js" + } +} +``` + +The npm package includes these assets: + +- Bundled JavaScript CLI output. +- Packaged demo assets. +- One KTX-owned Python wheel, for example + `python/kaelio_ktx-0.1.0-py3-none-any.whl`. +- A wheel checksum or runtime manifest that lets the CLI verify the bundled + Python payload before installation. + +The Python wheel contains the current `semantic_layer` and `ktx_daemon` +modules. It exposes at least the `ktx-daemon` console script. + +## Runtime installation + +KTX creates a managed Python runtime only when a command needs Python-backed +behavior. The runtime lives outside the npm cache so it survives `npx` runs. + +The runtime root is platform-specific: + +- macOS: `~/Library/Application Support/kaelio/ktx/runtime` +- Linux: `${XDG_DATA_HOME:-~/.local/share}/kaelio/ktx/runtime` +- Windows: `%LOCALAPPDATA%/Kaelio/KTX/runtime` + +The runtime is versioned by the npm package version. A versioned runtime avoids +mixing JavaScript and Python code from incompatible releases. + +The installer performs these steps: + +1. Locate `uv`. +2. Create a virtual environment under the versioned runtime directory. +3. Install the bundled KTX wheel into that environment. +4. Write a runtime manifest with the CLI version, wheel checksum, Python + executable, daemon executable, and installed feature set. + +For lightweight Python support, the install command uses the bundled wheel's +default dependency set. For local embeddings, the installer adds the embeddings +extra only when selected: + +```bash +uv pip install "/path/to/kaelio_ktx-0.1.0-py3-none-any.whl" +uv pip install "/path/to/kaelio_ktx-0.1.0-py3-none-any.whl[local-embeddings]" +``` + +## Feature installation levels + +KTX manages Python runtime features in levels so first use stays fast. + +`core` includes: + +- `sqlglot` +- `pydantic` +- `pyyaml` +- `fastapi` +- `uvicorn` +- lightweight daemon dependencies + +`local-embeddings` adds: + +- `sentence-transformers` +- `torch` +- model download support for `all-MiniLM-L6-v2` + +Commands that only need semantic-layer SQL generation require `core`. +Commands that need local embeddings require `local-embeddings`. + +## Command behavior + +Pure TypeScript commands run without the managed Python runtime. + +Python-backed one-shot operations use the managed `ktx-daemon` executable +directly. Examples include semantic query compilation, semantic validation, +semantic source generation, and sqlglot-backed table identifier parsing. + +Repeated or expensive operations use a managed HTTP daemon. Local embeddings use +the daemon because loading the model for every one-shot process is too slow. + +KTX provides runtime management commands: + +```bash +ktx runtime install +ktx runtime status +ktx runtime start +ktx runtime stop +ktx runtime doctor +ktx runtime prune +``` + +Normal commands can install the runtime lazily. Runtime commands make that +behavior inspectable and debuggable. + +## Daemon lifecycle + +The daemon binds to `127.0.0.1` on an available random port. KTX writes daemon +state to the runtime manifest or an adjacent state file: + +```json +{ + "pid": 12345, + "port": 58731, + "version": "0.1.0", + "features": ["core", "local-embeddings"], + "startedAt": "2026-05-11T00:00:00Z" +} +``` + +Before reusing a daemon, KTX checks that the process is alive, the port responds +to `/health`, and the daemon version matches the CLI version. If any check +fails, KTX treats the daemon as stale and starts a new one. + +KTX uses one-shot Python for short operations by default. It starts the daemon +only when a command benefits from process reuse. + +## Interactive and CI behavior + +In an interactive terminal, KTX prompts before installing the managed runtime +for the first time. The prompt states that Python dependencies will be +downloaded. + +With `--yes`, KTX installs the required runtime features without prompting. + +With `--no-input`, KTX fails if a required runtime feature is missing and no +explicit auto-install flag is present. The error prints the exact command to +prepare the runtime. + +For local embeddings, KTX prompts separately because the dependency and model +downloads are larger than the core runtime. + +## Error handling + +If `uv` is missing, KTX prints a focused error that explains how to install it +and how to retry. A later release can add a bundled or downloaded `uv` strategy. + +If Python runtime installation fails, KTX preserves install logs in the runtime +directory and prints the log path. + +If the daemon fails to start, KTX prints the captured daemon stdout and stderr +path. It falls back to one-shot mode only when the requested operation supports +one-shot execution. + +If JavaScript and Python versions don't match, KTX reinstalls the managed +runtime for the current npm package version. + +## Release flow + +The release builds the Python wheel before packing npm artifacts. The npm pack +step includes the wheel as an asset. + +Release checks must cover: + +1. Clean install of the packed npm package. +2. `npx` execution of the packed package. +3. First-run managed runtime install from the bundled wheel. +4. One-shot semantic-layer query through the managed runtime. +5. Runtime status and doctor output. +6. Daemon start, health check, reuse, and stop. +7. Optional local embeddings smoke in a separate job or opt-in check. + +## Open decisions + +KTX still needs a final decision on whether `uv` is a hard prerequisite or a +bootstrap dependency that KTX downloads automatically. + +KTX also needs the final Python distribution name. This spec uses +`kaelio-ktx` as the distribution name and `kaelio_ktx` in wheel filenames. + +## Success criteria + +Users can run `npx @kaelio/ktx ...` and complete Python-backed KTX operations +without manually installing a KTX Python package. + +Users who install `@kaelio/ktx` locally can run `npx ktx ...` through the local +project's npm binary resolution. + +The first Python-backed command installs only the core runtime. Local embedding +dependencies install only after the user selects local embeddings or explicitly +requests the `local-embeddings` runtime feature. + +KTX can diagnose and repair stale or mismatched managed runtimes without asking +users to delete directories manually.