* 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
31 KiB
Single Public Runtime Artifact Cleanup 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 release artifacts match the npm-managed Python runtime design:
one public @kaelio/ktx npm tarball plus one bundled kaelio-ktx runtime
wheel, with no standalone ktx-sl or ktx-daemon release artifacts.
Architecture: Keep python/ktx-sl and python/ktx-daemon as source
packages used to assemble the bundled runtime wheel. Remove direct standalone
Python wheel and source-distribution builds from the release artifact path,
manifest, readiness policy, and artifact smoke docs. The packed npm package
remains the only user-visible package; Python-backed verification continues
through the managed runtime installed from the bundled wheel.
Tech Stack: Node 22 ESM scripts, node:test, pnpm, uv-built bundled
runtime wheel, JSON release policy, Markdown.
Current state
This plan follows
docs/superpowers/specs/2026-05-11-npm-managed-python-runtime-design.md.
The following plan files are based on that spec and are implemented in the current tree:
docs/superpowers/plans/2026-05-11-bundled-python-runtime-wheel.mddocs/superpowers/plans/2026-05-11-managed-python-runtime-installer.mddocs/superpowers/plans/2026-05-11-managed-python-runtime-command-integration.mddocs/superpowers/plans/2026-05-11-managed-python-runtime-daemon-lifecycle.mddocs/superpowers/plans/2026-05-11-managed-local-embeddings-runtime.mddocs/superpowers/plans/2026-05-11-public-kaelio-ktx-npm-package.mddocs/superpowers/plans/2026-05-11-managed-python-runtime-release-smoke.mddocs/superpowers/plans/2026-05-11-managed-local-embeddings-release-smoke.mddocs/superpowers/plans/2026-05-11-managed-agent-mcp-semantic-runtime.mddocs/superpowers/plans/2026-05-11-managed-local-ingest-daemon-runtime.mddocs/superpowers/plans/2026-05-11-managed-runtime-docs-and-postgres-smoke-cleanup.mddocs/superpowers/plans/2026-05-11-published-package-managed-runtime-smoke.mddocs/superpowers/plans/2026-05-11-public-npm-release-handoff.mddocs/superpowers/plans/2026-05-11-managed-runtime-prune-smoke-and-docs.mddocs/superpowers/plans/2026-05-11-managed-runtime-uv-prerequisite-contract.md
Implementation evidence found before writing this plan includes:
packages/cli/assets/python/manifest.jsonandpackages/cli/assets/python/kaelio_ktx-0.1.0-py3-none-any.whl.packages/cli/src/managed-python-runtime.ts,packages/cli/src/managed-python-command.ts,packages/cli/src/managed-python-daemon.ts,packages/cli/src/managed-local-embeddings.ts,packages/cli/src/managed-python-http.ts, andpackages/cli/src/runtime.ts.scripts/build-public-npm-package.mjs,scripts/package-artifacts.mjs,scripts/published-package-smoke.mjs,scripts/local-embeddings-runtime-smoke.mjs,scripts/publish-public-npm-package.mjs, and.github/workflows/release.yml.release-policy.jsonis innpm-public-release-readymode, publishes@kaelio/ktx, disables Python package publishing, and encodes the harduvprerequisite.README.mdandexamples/package-artifacts/README.mddocument public npm usage, managed runtime commands,runtime prune, and theuvprerequisite.
The remaining mismatch is in the artifact release surface:
scripts/package-artifacts.mjsstill runsuv build --package ktx-slanduv build --package ktx-daemon.scripts/package-artifacts.mjsstill addsktx-slandktx-daemonwheel and source-distribution files to the artifact manifest.scripts/package-artifacts.mjsstill runs a direct Python clean-install smoke, even though the npm artifact smoke already proves Python-backed commands through the managed runtime.release-policy.jsonstill listsktx-slandktx-daemonunderpython.packages.examples/package-artifacts/README.mdsays the Python smoke installs standalone Python artifacts directly.
This plan removes those release artifacts. It does not delete the Python source
packages because the bundled runtime wheel builder still copies from
python/ktx-sl/semantic_layer and python/ktx-daemon/src/ktx_daemon.
File structure
- Modify
scripts/package-artifacts.test.mjs: make artifact tests expect only@kaelio/ktxplus thekaelio-ktxbundled runtime wheel, and add a guard that direct standalone Python artifact smoke code is gone. - Modify
scripts/package-artifacts.mjs: stop building standalone Python artifacts, stop looking for their wheel and source-distribution files, remove their release metadata, and remove the direct Python artifact verification path. - Modify
scripts/release-readiness.test.mjs: update release policy fixtures and readiness reports so the only Python release metadata iskaelio-ktx. - Modify
release-policy.json: setpython.packagesto["kaelio-ktx"]. - Modify
scripts/examples-docs.test.mjs: require docs to describe the single npm tarball plus runtime wheel artifact shape and reject the old direct Python-artifact smoke wording. - Modify
README.md: clarify thatpython/ktx-slandpython/ktx-daemonare source packages, not release artifacts for the first npm release. - Modify
examples/package-artifacts/README.md: replace the stale standalone Python smoke paragraph with the managed-runtime artifact contract.
Task 1: Make package artifact tests expect one runtime wheel
Files:
-
Modify:
scripts/package-artifacts.test.mjs -
Test:
scripts/package-artifacts.test.mjs -
Step 1: Update package artifact imports
In scripts/package-artifacts.test.mjs, replace the import from
./package-artifacts.mjs with this import:
import {
CLI_PYTHON_ASSET_MANIFEST,
INTERNAL_NPM_WORKSPACE_PACKAGES,
RUNTIME_WHEEL_DISTRIBUTION_NAME,
RUNTIME_WHEEL_NORMALIZED_NAME,
RUNTIME_WHEEL_PACKAGE_VERSION,
artifactManifestPath,
buildArtifactCommands,
copyRuntimeWheelAssets,
findPythonArtifacts,
NPM_ARTIFACT_PACKAGES,
npmDemoSmokeSource,
npmRuntimeSmokeSource,
npmSmokePackageJson,
npmVerifySource,
packageArtifactLayout,
packageReleaseMetadata,
verifyArtifactManifest,
writeArtifactManifest,
} from './package-artifacts.mjs';
- Step 2: Remove standalone Python fixture setup
In scripts/package-artifacts.test.mjs, replace writeReleaseMetadataInputs
with this function:
async function writeReleaseMetadataInputs(root) {
for (const packageInfo of INTERNAL_NPM_WORKSPACE_PACKAGES) {
await mkdir(join(root, packageInfo.packageRoot), { recursive: true });
await writeJson(join(root, packageInfo.packageRoot, 'package.json'), {
name: packageInfo.name,
version: '0.0.0-private',
private: true,
});
}
}
Replace writeUploadableArtifactFixtures with this function:
async function writeUploadableArtifactFixtures(layout) {
await mkdir(layout.npmDir, { recursive: true });
await mkdir(layout.pythonDir, { recursive: true });
const fileContents = new Map([
...NPM_ARTIFACT_PACKAGES.map((packageInfo) => [
layout.npmTarballs[packageInfo.name],
`${packageInfo.name}-tarball`,
]),
[
join(layout.pythonDir, 'kaelio_ktx-0.1.0-py3-none-any.whl'),
'kaelio-ktx-runtime-wheel',
],
]);
for (const [path, contents] of fileContents) {
await writeFile(path, contents);
}
}
- Step 3: Change build command expectations
In the buildArtifactCommands test, replace the body with this code:
it('builds TypeScript packages and the runtime wheel before packing npm artifacts', () => {
const layout = packageArtifactLayout('/repo/ktx');
const commands = buildArtifactCommands(layout);
assert.deepEqual(
commands.slice(0, NPM_BUILD_PACKAGE_ORDER.length).map((command) => [command.command, command.args]),
NPM_BUILD_PACKAGE_ORDER.map((packageName) => ['pnpm', ['--filter', packageName, 'run', 'build']]),
);
assert.deepEqual(
commands.slice(NPM_BUILD_PACKAGE_ORDER.length, NPM_BUILD_PACKAGE_ORDER.length + 1).map((command) => [
command.command,
command.args,
]),
[[process.execPath, ['scripts/build-python-runtime-wheel.mjs']]],
);
assert.deepEqual(
commands.slice(NPM_BUILD_PACKAGE_ORDER.length + 1).map((command) => [command.command, command.args]),
[[process.execPath, ['scripts/build-public-npm-package.mjs']]],
);
});
- Step 4: Change release metadata expectations
In the packageReleaseMetadata test, replace the expected array with this
array:
assert.deepEqual(await packageReleaseMetadata(root), [
{
ecosystem: 'npm',
packageName: '@kaelio/ktx',
packageRoot: 'packages/cli',
packageVersion: '0.1.0',
private: false,
releaseMode: 'ci-artifact-only',
},
{
ecosystem: 'python',
packageName: 'kaelio-ktx',
packageRoot: 'python/runtime-wheel',
packageVersion: '0.1.0',
private: false,
releaseMode: 'ci-artifact-only',
},
]);
- Step 5: Change Python artifact discovery expectations
Replace the findPythonArtifacts success test with this test:
it('finds the bundled runtime wheel only', async () => {
const root = await mkdtemp(join(tmpdir(), 'ktx-artifacts-test-'));
try {
await writeFile(join(root, 'kaelio_ktx-0.1.0-py3-none-any.whl'), '');
assert.deepEqual(await findPythonArtifacts(root), {
runtimeWheel: join(root, 'kaelio_ktx-0.1.0-py3-none-any.whl'),
});
} finally {
await rm(root, { recursive: true, force: true });
}
});
- Step 6: Change artifact manifest expectations
Inside the artifact manifest test, replace the Python package assertion with:
assert.deepEqual(
manifest.packages.filter((entry) => entry.ecosystem === 'python'),
[
{
ecosystem: 'python',
packageName: 'kaelio-ktx',
packageRoot: 'python/runtime-wheel',
packageVersion: '0.1.0',
private: false,
releaseMode: 'ci-artifact-only',
},
],
);
Replace the Python file assertion with:
assert.deepEqual(
manifest.files
.filter((file) => file.ecosystem === 'python')
.map((file) => ({
artifactKind: file.artifactKind,
ecosystem: file.ecosystem,
packageName: file.packageName,
packageVersion: file.packageVersion,
path: file.path,
})),
[
{
artifactKind: 'wheel',
ecosystem: 'python',
packageName: 'kaelio-ktx',
packageVersion: '0.1.0',
path: 'python/kaelio_ktx-0.1.0-py3-none-any.whl',
},
],
);
In the verifyArtifactManifest success test, replace the file-count assertion
with:
assert.equal(manifest.files.length, NPM_ARTIFACT_PACKAGES.length + 1);
- Step 7: Replace direct Python smoke tests with a dead-code guard
Remove the whole describe('pythonArtifactInstallArgs', ...) block.
In describe('verification snippets', ...), remove the test named
asserts the Python modules that clean installs must expose.
Add this test after the verifyNpmArtifacts test:
describe('standalone Python artifact cleanup', () => {
it('does not build or verify standalone Python package artifacts', async () => {
const source = await readFile(new URL('./package-artifacts.mjs', import.meta.url), 'utf8');
assert.doesNotMatch(source, /uv', \['build', '--package', 'ktx-sl'/);
assert.doesNotMatch(source, /uv', \['build', '--package', 'ktx-daemon'/);
assert.doesNotMatch(source, /async function verifyPythonArtifacts/);
assert.doesNotMatch(source, /pythonArtifactInstallArgs/);
assert.doesNotMatch(source, /pythonVerifySource/);
assert.doesNotMatch(source, /ktx_sl-0\.1\.0/);
assert.doesNotMatch(source, /ktx_daemon-0\.1\.0/);
});
});
- Step 8: Run package artifact tests and verify failure
Run:
node --test scripts/package-artifacts.test.mjs
Expected: FAIL. The failures mention the extra ktx-sl and ktx-daemon
artifact commands, metadata entries, manifest files, or direct Python smoke
helpers.
Task 2: Remove standalone Python artifacts from package artifacts
Files:
-
Modify:
scripts/package-artifacts.mjs -
Test:
scripts/package-artifacts.test.mjs -
Step 1: Remove dead constants and imports
In scripts/package-artifacts.mjs, replace the node:path import with this
import:
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
Remove these constants:
const PACKAGE_VERSION = '0.0.0-private';
const PYTHON_PACKAGE_VERSION = '0.1.0';
Remove the whole ordersSource constant block.
- Step 2: Make npm artifact names public-package only
Replace npmPackageTarballName with this function:
function npmPackageTarballName(packageName) {
if (packageName !== PUBLIC_NPM_PACKAGE_NAME) {
throw new Error(`Unsupported npm artifact package: ${packageName}`);
}
return publicNpmPackageTarballName(PUBLIC_NPM_PACKAGE_VERSION);
}
- Step 3: Remove standalone Python build commands
Replace buildArtifactCommands with this function:
export function buildArtifactCommands(layout) {
const packagesByName = new Map(INTERNAL_NPM_WORKSPACE_PACKAGES.map((packageInfo) => [packageInfo.name, packageInfo]));
const npmBuildCommands = NPM_ARTIFACT_BUILD_ORDER.map((packageName) => {
const packageInfo = packagesByName.get(packageName);
if (!packageInfo) {
throw new Error(`Unknown npm artifact build package: ${packageName}`);
}
return {
command: 'pnpm',
args: ['--filter', packageInfo.name, 'run', 'build'],
cwd: layout.rootDir,
};
});
const publicPackageCommand = {
command: process.execPath,
args: ['scripts/build-public-npm-package.mjs'],
cwd: layout.rootDir,
};
return [
...npmBuildCommands,
{
command: process.execPath,
args: ['scripts/build-python-runtime-wheel.mjs'],
cwd: layout.rootDir,
},
publicPackageCommand,
];
}
- Step 4: Discover only the bundled runtime wheel
Replace findOne and findPythonArtifacts with these functions:
function findOne(files, distributionName, suffix, label, pythonDir, version) {
const normalized = normalizePythonDistributionName(distributionName);
const found = files.find((file) => file.startsWith(`${normalized}-${version}`) && file.endsWith(suffix));
if (!found) {
throw new Error(`Missing Python artifact: ${label}`);
}
return join(pythonDir, found);
}
export async function findPythonArtifacts(pythonDir) {
const files = await readdir(pythonDir);
return {
runtimeWheel: findOne(
files,
RUNTIME_WHEEL_DISTRIBUTION_NAME,
'.whl',
'kaelio-ktx runtime wheel',
pythonDir,
RUNTIME_WHEEL_PACKAGE_VERSION,
),
};
}
- Step 5: Emit release metadata only for npm and runtime wheel
Replace packageReleaseMetadata with this function:
export async function packageReleaseMetadata(rootDir = scriptRootDir()) {
const npmPackages = await Promise.all(
NPM_ARTIFACT_PACKAGES.map((packageInfo) => readNpmPackageMetadata(rootDir, packageInfo)),
);
return [
...npmPackages,
releaseMetadataEntry({
ecosystem: 'python',
packageName: RUNTIME_WHEEL_DISTRIBUTION_NAME,
packageRoot: 'python/runtime-wheel',
packageVersion: RUNTIME_WHEEL_PACKAGE_VERSION,
privatePackage: false,
}),
];
}
- Step 6: Remove dead TOML metadata helpers
Delete these helper functions from scripts/package-artifacts.mjs because
release metadata no longer reads standalone Python pyproject.toml files:
function readProjectBlock(toml, sourcePath) {
const lines = toml.split(/\r?\n/);
const block = [];
let inProject = false;
for (const line of lines) {
if (/^\[project\]\s*$/.test(line)) {
inProject = true;
continue;
}
if (inProject && /^\[.*\]\s*$/.test(line)) {
break;
}
if (inProject) {
block.push(line);
}
}
if (!inProject) {
throw new Error(`Missing [project] table in ${sourcePath}`);
}
return block.join('\n');
}
function readTomlStringField(projectBlock, fieldName, sourcePath) {
const match = projectBlock.match(new RegExp(`^${fieldName}\\s*=\\s*"([^"]+)"\\s*$`, 'm'));
if (!match) {
throw new Error(`Missing project.${fieldName} in ${sourcePath}`);
}
return match[1];
}
async function readPyprojectMetadata(path) {
const toml = await readFile(path, 'utf-8');
const projectBlock = readProjectBlock(toml, path);
return {
name: readTomlStringField(projectBlock, 'name', path),
version: readTomlStringField(projectBlock, 'version', path),
};
}
- Step 7: Emit manifest records only for npm and runtime wheel
Replace artifactPackageRecords with this function:
function artifactPackageRecords(layout, pythonArtifacts, packages) {
const packagesByName = packageMetadataByName(packages);
const npmRecords = NPM_ARTIFACT_PACKAGES.map((packageInfo) => ({
artifactKind: 'tarball',
artifactPath: layout.npmTarballs[packageInfo.name],
metadata: requirePackageMetadata(packagesByName, packageInfo.name),
}));
return [
...npmRecords,
{
artifactKind: 'wheel',
artifactPath: pythonArtifacts.runtimeWheel,
metadata: requirePackageMetadata(packagesByName, RUNTIME_WHEEL_DISTRIBUTION_NAME),
},
];
}
- Step 8: Remove direct Python artifact verification helpers
Delete these exports and functions from scripts/package-artifacts.mjs:
export function pythonArtifactInstallArgs(python, pythonArtifacts) {
return ['pip', 'install', '--python', python, pythonArtifacts.runtimeWheel];
}
export function pythonVerifySource() {
return `
import importlib.metadata
import semantic_layer
import ktx_daemon
assert importlib.metadata.version("kaelio-ktx") == "0.1.0"
assert semantic_layer is not None
assert ktx_daemon.PACKAGE_NAME == "ktx-daemon"
`;
}
function pythonExecutable(projectDir) {
if (process.platform === 'win32') {
return join(projectDir, '.venv', 'Scripts', 'python.exe');
}
return join(projectDir, '.venv', 'bin', 'python');
}
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 {
...baseEnv,
PATH: existingPath ? `${binDir}${delimiter}${existingPath}` : binDir,
};
}
async function verifyPythonArtifacts(layout, tmpRoot) {
const pythonArtifacts = await findPythonArtifacts(layout.pythonDir);
const projectDir = join(tmpRoot, 'python-clean-install');
await mkdir(projectDir, { recursive: true });
const python = pythonExecutable(projectDir);
await writeFile(join(projectDir, 'verify_python.py'), pythonVerifySource());
await runCommand('uv', ['venv', '.venv'], { cwd: projectDir });
await runCommand('uv', pythonArtifactInstallArgs(python, pythonArtifacts), {
cwd: projectDir,
});
await runCommand(python, ['verify_python.py'], { cwd: projectDir });
await runCommand(python, ['-m', 'ktx_daemon', 'semantic-validate'], {
cwd: projectDir,
input: `${JSON.stringify({ sources: [ordersSource], dialect: 'postgres' })}\n`,
});
}
- Step 9: Verify artifacts through npm only
Replace verifyArtifacts with this function:
async function verifyArtifacts(layout) {
await verifyArtifactManifest(layout);
const tmpRoot = await mkdtemp(join(tmpdir(), 'ktx-artifacts-'));
try {
await verifyNpmArtifacts(layout, tmpRoot);
} finally {
await rm(tmpRoot, { recursive: true, force: true });
}
}
- Step 10: Run package artifact tests and verify pass
Run:
node --test scripts/package-artifacts.test.mjs
Expected: PASS. The output includes # fail 0.
- Step 11: Commit package artifact cleanup
Run:
git add scripts/package-artifacts.mjs scripts/package-artifacts.test.mjs
git commit -m "refactor: limit release artifacts to public package runtime"
Task 3: Align release policy and readiness reports
Files:
-
Modify:
release-policy.json -
Modify:
scripts/release-readiness.test.mjs -
Test:
scripts/release-readiness.test.mjs -
Step 1: Update release readiness fixtures
In scripts/release-readiness.test.mjs, replace
writeReleaseMetadataInputs with:
async function writeReleaseMetadataInputs(root) {
for (const packageInfo of INTERNAL_NPM_WORKSPACE_PACKAGES) {
await mkdir(join(root, packageInfo.packageRoot), { recursive: true });
await writeJson(join(root, packageInfo.packageRoot, 'package.json'), {
name: packageInfo.name,
version: '0.0.0-private',
private: true,
});
}
}
Replace writeUploadableArtifactFixtures with:
async function writeUploadableArtifactFixtures(layout) {
await mkdir(layout.npmDir, { recursive: true });
await mkdir(layout.pythonDir, { recursive: true });
const fileContents = new Map([
...NPM_ARTIFACT_PACKAGES.map((packageInfo) => [
layout.npmTarballs[packageInfo.name],
`${packageInfo.name}-tarball`,
]),
[join(layout.pythonDir, 'kaelio_ktx-0.1.0-py3-none-any.whl'), 'kaelio-ktx-runtime-wheel'],
]);
for (const [path, contents] of fileContents) {
await writeFile(path, contents);
}
}
In releasePolicy, replace the python object with:
python: {
publish: false,
repository: null,
packages: ['kaelio-ktx'],
...pythonOverrides,
},
- Step 2: Update readiness report expectations
In scripts/release-readiness.test.mjs, replace every expected
packageNames array with:
packageNames: ['@kaelio/ktx', 'kaelio-ktx'],
There are three report assertions to update:
-
accepts the current ci-artifact-only policy, package metadata, and artifact manifest -
reports required published package smoke when release mode requires it -
accepts the npm public release ready policy -
Step 3: Update checked release policy
In release-policy.json, replace the python.packages value with:
"packages": ["kaelio-ktx"]
- Step 4: Run readiness tests and verify pass
Run:
node --test scripts/release-readiness.test.mjs
Expected: PASS. The output includes # fail 0.
- Step 5: Commit release policy cleanup
Run:
git add release-policy.json scripts/release-readiness.test.mjs
git commit -m "chore: align release policy with bundled runtime wheel"
Task 4: Document the single release artifact surface
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 the public package artifact smoke shape', ...), add these
assertions after the existing assert.match(readme, /managed Python runtime/);
line:
assert.match(readme, /public `@kaelio\/ktx` npm tarball and the bundled `kaelio-ktx` runtime wheel/);
assert.match(readme, /does not install standalone Python packages directly/);
assert.doesNotMatch(readme, /standalone Python distributions/);
assert.doesNotMatch(readme, /installs the Python artifacts directly/);
In it('documents public npm and managed runtime usage in the README', ...),
add these assertions after the existing uv assertions:
assert.match(rootReadme, /release artifact manifest contains the public npm tarball and the bundled `kaelio-ktx` runtime wheel/);
assert.match(rootReadme, /source packages for development, not public release artifacts/);
- Step 2: Run docs tests and verify failure
Run:
node --test scripts/examples-docs.test.mjs
Expected: FAIL. The failure mentions the missing single-artifact wording in
README.md or examples/package-artifacts/README.md.
- Step 3: Update the package artifact example README
In examples/package-artifacts/README.md, replace:
The Python smoke project still installs the Python artifacts directly because
it verifies the standalone Python distributions that feed the bundled runtime
wheel.
with:
The artifact manifest contains the public `@kaelio/ktx` npm tarball and the
bundled `kaelio-ktx` runtime wheel. The smoke does not install standalone
Python packages directly; Python-backed behavior is verified through the
managed runtime installed from the npm package.
- Step 4: Update the root README release status
In README.md, in the ## Release status section, replace this paragraph:
This repository builds one public npm artifact named `@kaelio/ktx`. The first
public npm handoff is policy-gated through `release-policy.json`, which keeps
Python package publishing disabled because KTX-owned Python code ships inside
the npm package as a bundled wheel.
with:
This repository builds one public npm artifact named `@kaelio/ktx`. The release
artifact manifest contains the public npm tarball and the bundled `kaelio-ktx`
runtime wheel. The first public npm handoff is policy-gated through
`release-policy.json`, which keeps Python package publishing disabled because
KTX-owned Python code ships inside the npm package as a bundled wheel. The
`python/ktx-sl` and `python/ktx-daemon` directories remain source packages for
development, not public release artifacts.
- Step 5: Run docs tests and verify pass
Run:
node --test scripts/examples-docs.test.mjs
Expected: PASS. The output includes # fail 0.
- Step 6: Commit docs cleanup
Run:
git add README.md examples/package-artifacts/README.md scripts/examples-docs.test.mjs
git commit -m "docs: describe single public runtime artifact surface"
Task 5: Verify the cleaned release artifact contract
Files:
-
Verify:
scripts/package-artifacts.mjs -
Verify:
scripts/package-artifacts.test.mjs -
Verify:
scripts/release-readiness.test.mjs -
Verify:
scripts/examples-docs.test.mjs -
Verify:
release-policy.json -
Verify:
README.md -
Verify:
examples/package-artifacts/README.md -
Step 1: Run focused tests
Run:
node --test scripts/package-artifacts.test.mjs scripts/release-readiness.test.mjs scripts/examples-docs.test.mjs
Expected: PASS. The output includes # fail 0.
- Step 2: Verify stale artifact strings are gone from production/docs files
Run (scans only production and docs files, not test files — test files keep guard assertions that reference the removed strings):
rg -n "uv', \\['build', '--package', 'ktx-sl'|uv', \\['build', '--package', 'ktx-daemon'|ktx_sl-0\\.1\\.0|ktx_daemon-0\\.1\\.0|pythonArtifactInstallArgs|pythonVerifySource|verifyPythonArtifacts|standalone Python distributions|installs the Python artifacts directly" scripts/package-artifacts.mjs scripts/release-readiness.mjs README.md examples/package-artifacts/README.md release-policy.json
Expected: no matches.
- Step 3: Verify release readiness against the current artifact manifest
Run:
pnpm run release:readiness -- --json
Expected: PASS when dist/artifacts/manifest.json has been rebuilt after this
change. The JSON output contains:
{
"releaseMode": "npm-public-release-ready",
"packageNames": ["@kaelio/ktx", "kaelio-ktx"],
"pythonPublishEnabled": false
}
If this command fails because the local artifact manifest was generated before the cleanup, run:
pnpm run artifacts:check
pnpm run release:readiness -- --json
Expected: both commands pass. The rebuilt manifest contains only
npm/kaelio-ktx-0.1.0.tgz and
python/kaelio_ktx-0.1.0-py3-none-any.whl under files.
- Step 4: Run pre-commit on changed files when configured
Run:
uv run pre-commit run --files scripts/package-artifacts.mjs scripts/package-artifacts.test.mjs scripts/release-readiness.test.mjs scripts/examples-docs.test.mjs release-policy.json README.md examples/package-artifacts/README.md
Expected: PASS. If pre-commit is not installed or no pre-commit config exists, record the exact error and keep the focused Node test output from Step 1.
- Step 5: Commit final verification fixes if needed
If Step 1, Step 2, Step 3, or Step 4 required code or docs fixes, commit them:
git add scripts/package-artifacts.mjs scripts/package-artifacts.test.mjs scripts/release-readiness.test.mjs scripts/examples-docs.test.mjs release-policy.json README.md examples/package-artifacts/README.md
git commit -m "test: verify single public runtime artifact contract"
If no fixes were required after the previous commits, do not create an empty commit.
Acceptance criteria
scripts/package-artifacts.mjsbuilds TypeScript packages, builds the bundledkaelio-ktxruntime wheel, copies it into CLI assets, and packs the public@kaelio/ktxnpm tarball.scripts/package-artifacts.mjsno longer buildsktx-slorktx-daemonstandalone wheel or source-distribution artifacts.- Artifact manifests contain release metadata for
@kaelio/ktxandkaelio-ktxonly. release-policy.jsonlists only@kaelio/ktxundernpm.packagesand onlykaelio-ktxunderpython.packages.- The artifact smoke verifies Python-backed behavior through the installed public npm package and managed runtime, not by installing standalone Python artifacts directly.
- Public docs state that
python/ktx-slandpython/ktx-daemonremain source packages for development, not public release artifacts.
Self-review
Spec coverage:
- The plan preserves the single public npm package requirement.
- The plan preserves the bundled KTX-owned Python wheel requirement.
- The plan keeps Python package publishing disabled.
- The plan removes the only remaining artifact path that treated KTX-owned Python source packages as standalone release artifacts.
Placeholder scan:
- No steps contain placeholder implementation text.
- Every code-changing step names exact files and provides concrete replacement snippets.
Type and name consistency:
- Public npm package name remains
@kaelio/ktx. - Bundled runtime distribution name remains
kaelio-ktx. - Runtime wheel filename remains
kaelio_ktx-0.1.0-py3-none-any.whl. - Removed standalone Python artifact names are consistently
ktx-slandktx-daemon.