diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 430aa729..b3ec6793 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -44,7 +44,7 @@ body: - Connector (SQL Server) - Connector (SQLite) - Python semantic layer - - Python daemon + - KTX daemon - Docs - Other validations: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 5b2f8502..f4304878 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -24,7 +24,7 @@ body: - Connectors - Context engine - Python semantic layer - - Python daemon + - KTX daemon - Docs - Other validations: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d00a8fdd..a1b0e56b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,27 +69,6 @@ jobs: - name: Install Python dependencies run: uv sync --all-packages - - name: Prepare next prerelease branch - if: ${{ inputs.release_kind == 'rc' }} - run: | - set -euo pipefail - - source_sha="$(git rev-parse HEAD)" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - if git ls-remote --exit-code --heads origin "${KTX_PRERELEASE_BRANCH}" >/dev/null 2>&1; then - git fetch origin "${KTX_PRERELEASE_BRANCH}" - git checkout -B "${KTX_PRERELEASE_BRANCH}" "origin/${KTX_PRERELEASE_BRANCH}" - git merge --no-edit "${source_sha}" - else - git checkout -B "${KTX_PRERELEASE_BRANCH}" "${source_sha}" - fi - - git push --set-upstream origin "HEAD:${KTX_PRERELEASE_BRANCH}" - env: - KTX_PRERELEASE_BRANCH: next - - name: Prepare npm package root for release verification run: | set -euo pipefail @@ -112,36 +91,16 @@ jobs: - name: Dry-run semantic release if: ${{ !inputs.publish_live }} - run: | - set -euo pipefail - - if [ "${KTX_RELEASE_KIND}" = "rc" ]; then - export GITHUB_REF="refs/heads/${KTX_PRERELEASE_BRANCH}" - export GITHUB_REF_NAME="${KTX_PRERELEASE_BRANCH}" - export GITHUB_SHA="$(git rev-parse HEAD)" - fi - - pnpm run semantic-release:dry-run + run: pnpm run semantic-release:dry-run env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} KTX_RELEASE_KIND: ${{ inputs.release_kind }} - KTX_PRERELEASE_BRANCH: next FORCE_RELEASE: ${{ inputs.force_release }} - name: Create semantic release if: ${{ inputs.publish_live }} - run: | - set -euo pipefail - - if [ "${KTX_RELEASE_KIND}" = "rc" ]; then - export GITHUB_REF="refs/heads/${KTX_PRERELEASE_BRANCH}" - export GITHUB_REF_NAME="${KTX_PRERELEASE_BRANCH}" - export GITHUB_SHA="$(git rev-parse HEAD)" - fi - - pnpm run semantic-release + run: pnpm run semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} KTX_RELEASE_KIND: ${{ inputs.release_kind }} - KTX_PRERELEASE_BRANCH: next FORCE_RELEASE: ${{ inputs.force_release }} diff --git a/AGENTS.md b/AGENTS.md index a5447c9d..f6ce3d98 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -69,7 +69,7 @@ KTX is a pnpm + uv workspace. - LLM package: `packages/llm` - Database connectors: `packages/connector-*` - Python semantic layer: `python/ktx-sl` -- Python daemon: `python/ktx-daemon` +- KTX daemon: `python/ktx-daemon` - Examples and fixtures: `examples/` - Workspace scripts: `scripts/` - Local agent skills and internal planning docs are private overlays. Do not @@ -134,7 +134,7 @@ shared contracts or package exports are affected. test file - TypeScript dead-code tooling/config changes: `pnpm run dead-code` - Python semantic layer: `uv run pytest python/ktx-sl/tests -q` -- Python daemon: `uv run pytest python/ktx-daemon/tests -q` +- KTX daemon: `uv run pytest python/ktx-daemon/tests -q` - Python files: also run `uv run pre-commit run --files [FILES]` when pre-commit is configured diff --git a/codecov.yml b/codecov.yml index 0ff83e2f..a599cd3d 100644 --- a/codecov.yml +++ b/codecov.yml @@ -97,7 +97,7 @@ component_management: paths: - python/ktx-sl/semantic_layer/** - component_id: py_daemon - name: Python daemon + name: KTX daemon paths: - python/ktx-daemon/src/ktx_daemon/** diff --git a/docs-site/content/agents-setup.md b/docs-site/content/agents-setup.md index 24614ea8..4fcaeb3f 100644 --- a/docs-site/content/agents-setup.md +++ b/docs-site/content/agents-setup.md @@ -146,7 +146,7 @@ Success requires (canonical shape - supersedes the example in the docs): Do **not** run `--deep` ingest in this flow - that requires LLM time and is out of scope. -### Optional: directly probe the embeddings daemon +### Optional: directly probe the KTX daemon If the user asks for stronger verification that `sentence-transformers` is actually serving (not just that setup said "ok"), do all of: @@ -155,7 +155,7 @@ If the user asks for stronger verification that `sentence-transformers` is actua 3. `curl -sS http://127.0.0.1:/health` → expect HTTP 200 with `{"status":"healthy",…}`. 4. `curl -sS -X POST http://127.0.0.1:/embeddings/compute -H 'content-type: application/json' -d '{"text":"hello"}'` → expect `{"embedding": [...384 floats...]}`. -Discover the port from setup's log line `Started KTX local embeddings daemon: http://127.0.0.1:` or from the daemon's OpenAPI at `GET /openapi.json`. Note: the routes are `/health` and `/embeddings/compute` - not `/healthz` or `/embeddings`. +Discover the port from setup's log line `Started KTX daemon: http://127.0.0.1:` or from the daemon's OpenAPI at `GET /openapi.json`. Note: the routes are `/health` and `/embeddings/compute` - not `/healthz` or `/embeddings`. ## Phase 6 - Final report @@ -167,7 +167,7 @@ KTX SETUP COMPLETE Project: LLM: / Embeddings: / -Runtime: managed Python ✓ (if sentence-transformers daemon was started) +Runtime: managed Python ✓ (if the KTX daemon was started) Connections: - () status=ok schemas=[…] tables= diff --git a/docs-site/content/docs/cli-reference/ktx-admin.mdx b/docs-site/content/docs/cli-reference/ktx-admin.mdx index c7df5461..966eac5a 100644 --- a/docs-site/content/docs/cli-reference/ktx-admin.mdx +++ b/docs-site/content/docs/cli-reference/ktx-admin.mdx @@ -44,8 +44,8 @@ directory. Use it from any directory to generate editor or agent schema files. | Subcommand | Description | |-----------|-------------| | `install` | Install the bundled Python runtime wheel into the managed runtime | -| `start` | Start the KTX-managed Python HTTP daemon | -| `stop` | Stop the KTX-managed Python HTTP daemon | +| `start` | Start the KTX daemon | +| `stop` | Stop the KTX daemon | | `status` | Show managed Python runtime status and readiness checks | ## `admin runtime` Options diff --git a/docs-site/content/docs/community/contributing.mdx b/docs-site/content/docs/community/contributing.mdx index 63b08cd6..bf66f546 100644 --- a/docs-site/content/docs/community/contributing.mdx +++ b/docs-site/content/docs/community/contributing.mdx @@ -25,7 +25,7 @@ documentation, connector coverage, and examples. | Context engine | `packages/context`, including project config, ingest orchestration, and semantic search | | Connectors | `packages/connector-*`, plus connector-specific tests and integration docs | | Python semantic layer | `python/ktx-sl` for planning and SQL generation | -| Python daemon | `python/ktx-daemon` for the portable runtime API | +| KTX daemon | `python/ktx-daemon` for the portable runtime API | | Documentation | `docs-site/content/docs` for public docs and `docs-site/tests` for docs behavior | ## Development setup diff --git a/docs/release.md b/docs/release.md index 4042863c..00362c99 100644 --- a/docs/release.md +++ b/docs/release.md @@ -2,25 +2,24 @@ This runbook covers the maintainer workflow for publishing `@kaelio/ktx` to npm through GitHub Actions. The workflow uses semantic-release to choose the -next version, update release metadata, publish the package, create the GitHub -release, and commit prerelease files back to the `next` branch. +next version, update release metadata in the CI workspace, publish the +package, and create the GitHub release. No files are ever committed back to +the repository — the git tag and the published npm artifact are the source of +truth for any released version. ## Release channels -KTX has two npm release channels: +`main` is the bleeding-edge branch. Every release runs from `main`; the +dispatcher chooses the channel: -- `rc` publishes prereleases such as `0.1.0-rc.2` to the npm `next` tag. -- `stable` publishes normal releases such as `0.1.0` to the npm `latest` tag. +- `rc` publishes prereleases such as `0.3.0-rc.1` to the npm `next` tag. +- `stable` publishes normal releases such as `0.3.0` to the npm `latest` tag. -Run rc releases from the source branch you want to publish. The workflow -creates or updates the `next` prerelease branch from that source branch before -running semantic-release, because semantic-release requires a dedicated -prerelease branch in addition to the stable `main` branch. You can publish an -rc from `main` when you want to validate the current stable branch before a -stable release. +Tag history on `main` interleaves rc and stable tags +(`v0.2.0 → v0.3.0-rc.1 → v0.3.0-rc.2 → v0.3.0 → …`). semantic-release uses the +prerelease tags to graduate to the next stable cleanly. -Run stable releases only from `main`. The workflow rejects stable releases from -other branches. +The workflow rejects releases from any branch other than `main`. ## Prerequisites @@ -59,16 +58,15 @@ publishing to npm. 1. Open **Actions** in GitHub. 2. Select **KTX Release**. -3. Select the branch to release from. +3. Select `main`. 4. Set **release_kind** to `rc` or `stable`. 5. Set **publish_live** to `false`. 6. Optional: Set **force_release** to `true` when you need a patch release even if semantic-release doesn't find a releasable commit. 7. Run the workflow. -The dry-run uses the same semantic-release configuration as a live release. For -rc releases, it can create or update the `next` branch. It doesn't publish to -npm and doesn't commit release files. +The dry-run uses the same semantic-release configuration as a live release. It +doesn't publish to npm and doesn't push any tags. ## Publish an rc release @@ -77,16 +75,15 @@ promoting to `latest`. 1. Open **Actions** in GitHub. 2. Select **KTX Release**. -3. Select the source branch to release from, including `main` when needed. +3. Select `main`. 4. Set **release_kind** to `rc`. 5. Leave **publish_live** set to `true`. 6. Optional: Set **force_release** to `true`. 7. Run the workflow. -The workflow merges the selected source branch into `next`, publishes -`@kaelio/ktx` with `--access public --tag next`, runs the published package -smoke test, creates a GitHub release, and commits `CHANGELOG.md`, -`package.json`, and `release-policy.json` on `next`. +The workflow publishes `@kaelio/ktx` with `--access public --tag next`, runs +the published package smoke test, creates a GitHub release, and pushes the +`vX.Y.Z-rc.N` tag. ## Publish a stable release @@ -101,26 +98,28 @@ Publish a stable release from `main` after you have validated an rc package. 7. Run the workflow. The workflow publishes `@kaelio/ktx` with `--access public --tag latest`, runs -the published package smoke test, and creates a GitHub release. Stable releases -don't commit release metadata back to `main`, because `main` is protected and -requires changes through pull requests. +the published package smoke test, creates a GitHub release, and pushes the +`vX.Y.Z` tag. semantic-release graduates from the most recent rc tag, so the +prior rc lineage is consumed cleanly. ## Release metadata semantic-release calls `scripts/update-public-release-version.mjs` during the -prepare step before `@semantic-release/npm` publishes the package. That script -updates: +prepare step before the exec publish command runs. That script updates the +following files **inside the CI runner only**: - `package.json` with the semantic-release version. - `release-policy.json` with `publicNpmPackageVersion`, npm publish settings, and the published package smoke-test version. -The artifact packaging and readiness scripts read `publicNpmPackageVersion` -from `release-policy.json`, so manual version edits in build scripts aren't -needed for rc releases. The semantic-release npm plugin publishes the generated -`dist/public-npm-package` tree and writes the release tarball under -`dist/artifacts/npm`. Stable releases use the updated metadata during the -workflow run, but that generated metadata isn't committed back to `main`. +The artifact packaging, readiness, and smoke-test scripts read +`publicNpmPackageVersion` from `release-policy.json` within the same CI run. +Nothing reads these files at runtime — the daemon and CLI rely on the +published `package.json` (for the installed `@kaelio/ktx` package) or +`packages/cli/package.json` (for dev-tree runs from this repo, which always +report `0.0.0-private`). Because the metadata mutation never has to survive +the run, no commit is pushed back to `main`. The git tag plus the published +npm artifact carry the version forward. The bundled Python runtime wheel also derives its version from `publicNpmPackageVersion`. Stable npm versions are reused as-is, and rc diff --git a/examples/README.md b/examples/README.md index e84d9330..f18440b2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -27,7 +27,7 @@ warehouse credential. `postgres-historic/` is a manual Docker-backed smoke for Postgres query-history ingest via `pg_stat_statements`. It verifies setup, staged -query-history artifacts, managed daemon batch SQL analysis, bounded pattern +query-history artifacts, KTX daemon batch SQL analysis, bounded pattern WorkUnit shards, and no-WorkUnit idempotency for unchanged bucketed table inputs and pattern shards. diff --git a/examples/package-artifacts/README.md b/examples/package-artifacts/README.md index ed72f13f..7fa39fb3 100644 --- a/examples/package-artifacts/README.md +++ b/examples/package-artifacts/README.md @@ -14,7 +14,7 @@ generated local project. The managed Python runtime smoke requires `uv` on `PATH`, isolates `KTX_RUNTIME_ROOT`, verifies `ktx admin runtime status`, runs `ktx sl query --yes` to install the core runtime from the bundled wheel, checks `ktx admin runtime status`, -starts and reuses the managed daemon, and stops it. +starts and reuses the KTX daemon, and stops it. The artifact manifest contains the public `@kaelio/ktx` npm tarball and the bundled `kaelio-ktx` runtime wheel. The smoke does not install standalone diff --git a/package.json b/package.json index 71970b58..7d1b054a 100644 --- a/package.json +++ b/package.json @@ -48,10 +48,8 @@ }, "devDependencies": { "@biomejs/biome": "^2.4.15", - "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/exec": "^7.1.0", - "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^12.0.8", "@semantic-release/npm": "^13.1.5", "@semantic-release/release-notes-generator": "^14.1.1", diff --git a/packages/cli/src/admin-reindex.test.ts b/packages/cli/src/admin-reindex.test.ts index eb75c651..54ac5e7a 100644 --- a/packages/cli/src/admin-reindex.test.ts +++ b/packages/cli/src/admin-reindex.test.ts @@ -137,7 +137,7 @@ describe('admin reindex Commander routing', () => { force: true, json: true, output: 'plain', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', }, io.io, ); diff --git a/packages/cli/src/cli-program.test.ts b/packages/cli/src/cli-program.test.ts index 2e5333c9..91e2ed8f 100644 --- a/packages/cli/src/cli-program.test.ts +++ b/packages/cli/src/cli-program.test.ts @@ -14,8 +14,6 @@ function stubPackageInfo(): KtxCliPackageInfo { return { name: '@ktx/cli', version: '0.0.0-test', - packageVersion: '0.0.0-private', - runtimeVersion: '0.0.0-test', contextPackageName: '@ktx/context', }; } diff --git a/packages/cli/src/cli-runtime.ts b/packages/cli/src/cli-runtime.ts index a2d4765c..523945f5 100644 --- a/packages/cli/src/cli-runtime.ts +++ b/packages/cli/src/cli-runtime.ts @@ -11,7 +11,7 @@ import type { KtxSlArgs } from './sl.js'; import type { KtxSqlArgs } from './sql.js'; import { profileMark, profileSpan } from './startup-profile.js'; import type { KtxTextIngestArgs } from './text-ingest.js'; -import { resolveKtxRuntimeVersion } from './release-version.js'; +import { assertCliVersion } from './release-version.js'; profileMark('module:cli-runtime'); @@ -20,8 +20,6 @@ const requirePackageJson = createRequire(import.meta.url); export interface KtxCliPackageInfo { name: string; version: string; - packageVersion: string; - runtimeVersion: string; contextPackageName: '@ktx/context'; } @@ -66,16 +64,9 @@ export function packageInfoFromJson(packageJson: unknown): KtxCliPackageInfo { throw new Error('Invalid KTX CLI package metadata'); } - const runtimeVersion = resolveKtxRuntimeVersion({ - packageName: packageJson.name, - packageVersion: packageJson.version, - }); - return { name: packageJson.name, - version: runtimeVersion, - packageVersion: packageJson.version, - runtimeVersion, + version: assertCliVersion(packageJson.version, `${packageJson.name}/package.json`), contextPackageName: '@ktx/context', }; } diff --git a/packages/cli/src/commands/runtime-commands.ts b/packages/cli/src/commands/runtime-commands.ts index 12af875c..ef0d06cc 100644 --- a/packages/cli/src/commands/runtime-commands.ts +++ b/packages/cli/src/commands/runtime-commands.ts @@ -38,7 +38,7 @@ export function registerRuntimeCommands(program: Command, context: KtxCliCommand runtime .command('start') - .description('Start the KTX-managed Python HTTP daemon') + .description('Start the KTX daemon') .addOption(createRuntimeFeatureOption()) .option('--force', 'Restart even when a matching daemon is already running', false) .action(async (options: { feature: RuntimeFeature; force?: boolean }, command: CommandWithGlobalOptions) => { @@ -53,7 +53,7 @@ export function registerRuntimeCommands(program: Command, context: KtxCliCommand runtime .command('stop') - .description('Stop the KTX-managed Python HTTP daemon') + .description('Stop the KTX daemon') .option('--all', 'Stop all KTX daemon processes recorded or discoverable on this machine', false) .action(async (options: { all?: boolean }, command: CommandWithGlobalOptions) => { await runRuntimeArgs(context, { diff --git a/packages/cli/src/context-build-view.ts b/packages/cli/src/context-build-view.ts index 74aee915..7f47e33f 100644 --- a/packages/cli/src/context-build-view.ts +++ b/packages/cli/src/context-build-view.ts @@ -706,7 +706,7 @@ function failedStepDetail(result: KtxPublicIngestTargetResult): string | null { const INTERNAL_FAILURE_LINE_RE = /^(Report|Run|Job|Status|Adapter|Connection|Sync|Mode|Dry run|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/; const ACTIONABLE_FAILURE_LINE_RE = - /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX managed daemon|Error:|Failed\b|Could not\b|Cannot\b)/; + /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/; function trimErrorPrefix(line: string): string { return line.replace(/^Error:\s*/, ''); diff --git a/packages/cli/src/index.test.ts b/packages/cli/src/index.test.ts index 9a7fceeb..53a7b593 100644 --- a/packages/cli/src/index.test.ts +++ b/packages/cli/src/index.test.ts @@ -45,9 +45,7 @@ describe('getKtxCliPackageInfo', () => { it('identifies the CLI package and its context dependency', () => { expect(getKtxCliPackageInfo()).toEqual({ name: '@ktx/cli', - version: '0.1.0-rc.1', - packageVersion: '0.0.0-private', - runtimeVersion: '0.1.0-rc.1', + version: '0.0.0-private', contextPackageName: '@ktx/context', }); }); @@ -70,8 +68,6 @@ describe('getKtxCliPackageInfo', () => { ).toEqual({ name: '@kaelio/ktx', version: '0.1.0', - packageVersion: '0.1.0', - runtimeVersion: '0.1.0', contextPackageName: '@ktx/context', }); }); @@ -118,7 +114,7 @@ describe('runKtxCli', () => { await expect(runKtxCli(['--version'], testIo.io)).resolves.toBe(0); - expect(testIo.stdout()).toBe('@ktx/cli 0.1.0-rc.1\n'); + expect(testIo.stdout()).toBe('@ktx/cli 0.0.0-private\n'); expect(testIo.stderr()).toBe(''); }); @@ -282,7 +278,7 @@ describe('runKtxCli', () => { expect(unknownIo.stderr()).toContain("unknown option '--query'"); }); - it('routes runtime management commands with the release runtime version', async () => { + it('routes runtime management commands with the CLI package version', async () => { const runtime = vi.fn(async () => 0); const installIo = makeIo(); const startIo = makeIo(); @@ -308,7 +304,7 @@ describe('runKtxCli', () => { 1, { command: 'install', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', feature: 'local-embeddings', force: true, }, @@ -318,7 +314,7 @@ describe('runKtxCli', () => { 2, { command: 'start', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', projectDir: expect.any(String), feature: 'local-embeddings', force: true, @@ -329,7 +325,7 @@ describe('runKtxCli', () => { 3, { command: 'stop', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', projectDir: expect.any(String), all: false, }, @@ -339,7 +335,7 @@ describe('runKtxCli', () => { 4, { command: 'stop', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', projectDir: expect.any(String), all: true, }, @@ -349,7 +345,7 @@ describe('runKtxCli', () => { 5, { command: 'status', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', json: true, }, statusIo.io, @@ -422,7 +418,7 @@ describe('runKtxCli', () => { expect.objectContaining({ command: 'query', projectDir: tempDir, - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'prompt', query: expect.objectContaining({ measures: ['orders.order_count'], dimensions: [] }), }), @@ -437,7 +433,7 @@ describe('runKtxCli', () => { ).resolves.toBe(0); expect(sl).toHaveBeenLastCalledWith( expect.objectContaining({ - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'auto', }), autoIo.io, @@ -453,7 +449,7 @@ describe('runKtxCli', () => { ).resolves.toBe(0); expect(sl).toHaveBeenLastCalledWith( expect.objectContaining({ - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'never', }), noInputIo.io, @@ -589,7 +585,7 @@ describe('runKtxCli', () => { skipAgents: false, inputMode: 'auto', yes: false, - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', skipLlm: false, skipEmbeddings: false, databaseSchemas: [], @@ -719,7 +715,7 @@ describe('runKtxCli', () => { inputMode: 'disabled', depth: 'fast', queryHistory: 'default', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'never', }, testIo.io, @@ -746,7 +742,7 @@ describe('runKtxCli', () => { inputMode: 'auto', depth: 'deep', queryHistory: 'default', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'prompt', }, testIo.io, @@ -823,7 +819,7 @@ describe('runKtxCli', () => { json: false, inputMode: 'disabled', queryHistory: 'default', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'never', }, testIo.io, @@ -1128,7 +1124,7 @@ describe('runKtxCli', () => { command: 'run', projectDir: tempDir, inputMode: 'disabled', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', anthropicApiKeyEnv: 'ANTHROPIC_API_KEY', // pragma: allowlist secret llmModel: 'claude-sonnet-4-6', skipLlm: false, @@ -1167,7 +1163,7 @@ describe('runKtxCli', () => { command: 'run', projectDir: tempDir, inputMode: 'disabled', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', llmBackend: 'vertex', vertexProject: 'local-gcp-project', vertexLocation: 'us-east5', @@ -1204,7 +1200,7 @@ describe('runKtxCli', () => { command: 'run', projectDir: tempDir, inputMode: 'disabled', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', llmBackend: 'claude-code', llmModel: 'opus', skipLlm: false, @@ -1312,7 +1308,7 @@ describe('runKtxCli', () => { projectDir: '/tmp/project', inputMode: 'disabled', yes: true, - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', skipLlm: true, skipEmbeddings: true, databaseDrivers: ['postgres'], @@ -1653,7 +1649,7 @@ describe('runKtxCli', () => { queryFile: '/tmp/query.json', execute: false, format: 'json', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'auto', }, autoIo.io, @@ -1667,7 +1663,7 @@ describe('runKtxCli', () => { queryFile: '/tmp/query.json', execute: false, format: 'json', - cliVersion: '0.1.0-rc.1', + cliVersion: '0.0.0-private', runtimeInstallPolicy: 'never', }, neverIo.io, diff --git a/packages/cli/src/ingest.test.ts b/packages/cli/src/ingest.test.ts index 8dbad0e9..d3451e0c 100644 --- a/packages/cli/src/ingest.test.ts +++ b/packages/cli/src/ingest.test.ts @@ -1334,7 +1334,7 @@ describe('runKtxIngest', () => { ); }); - it('passes managed daemon options to adapters and pull-config options when no explicit daemon URL is set', async () => { + it('passes KTX daemon options to adapters and pull-config options when no explicit daemon URL is set', async () => { const projectDir = join(tempDir, 'managed-daemon-ingest-project'); await initKtxProject({ projectDir }); await writeWarehouseConfig(projectDir); diff --git a/packages/cli/src/managed-local-embeddings.test.ts b/packages/cli/src/managed-local-embeddings.test.ts index 85fa00c9..fead0655 100644 --- a/packages/cli/src/managed-local-embeddings.test.ts +++ b/packages/cli/src/managed-local-embeddings.test.ts @@ -117,7 +117,7 @@ describe('managedLocalEmbeddingProjectConfig', () => { }); describe('managedLocalEmbeddingHealthConfig', () => { - it('uses the active managed daemon URL for the immediate health check', () => { + it('uses the active KTX daemon URL for the immediate health check', () => { expect( managedLocalEmbeddingHealthConfig({ baseUrl: 'http://127.0.0.1:61234', @@ -134,7 +134,7 @@ describe('managedLocalEmbeddingHealthConfig', () => { }); describe('ensureManagedLocalEmbeddingsDaemon', () => { - it('ensures the local-embeddings feature and starts the managed daemon', async () => { + it('ensures the local-embeddings feature and starts the KTX daemon', async () => { const io = makeIo(); const ensureRuntime = vi.fn(async () => runtime()); const startDaemon = vi.fn(async () => daemonResult('started')); @@ -169,7 +169,7 @@ describe('ensureManagedLocalEmbeddingsDaemon', () => { features: ['local-embeddings'], force: false, }); - expect(io.stderr()).toContain('Started KTX local embeddings daemon: http://127.0.0.1:61234'); + expect(io.stderr()).toContain('Started KTX daemon: http://127.0.0.1:61234'); }); it('reuses an already running daemon without reporting a new start', async () => { @@ -184,6 +184,6 @@ describe('ensureManagedLocalEmbeddingsDaemon', () => { startDaemon: vi.fn(async () => daemonResult('reused')), }); - expect(io.stderr()).toContain('Using KTX local embeddings daemon: http://127.0.0.1:61234'); + expect(io.stderr()).toContain('Using KTX daemon: http://127.0.0.1:61234'); }); }); diff --git a/packages/cli/src/managed-local-embeddings.ts b/packages/cli/src/managed-local-embeddings.ts index f485a942..d9a1c309 100644 --- a/packages/cli/src/managed-local-embeddings.ts +++ b/packages/cli/src/managed-local-embeddings.ts @@ -89,7 +89,7 @@ export async function ensureManagedLocalEmbeddingsDaemon( }); const verb = daemon.status === 'started' ? 'Started' : 'Using'; - options.io.stderr.write(`${verb} KTX local embeddings daemon: ${daemon.baseUrl}\n`); + options.io.stderr.write(`${verb} KTX daemon: ${daemon.baseUrl}\n`); return { baseUrl: daemon.baseUrl, diff --git a/packages/cli/src/managed-python-daemon.test.ts b/packages/cli/src/managed-python-daemon.test.ts index 8797fb8f..d56a27b1 100644 --- a/packages/cli/src/managed-python-daemon.test.ts +++ b/packages/cli/src/managed-python-daemon.test.ts @@ -125,7 +125,7 @@ function daemonOptionsBase(root: string) { } as const; } -describe('managed Python daemon lifecycle', () => { +describe('KTX daemon lifecycle', () => { let tempDir: string; beforeEach(async () => { diff --git a/packages/cli/src/managed-python-daemon.ts b/packages/cli/src/managed-python-daemon.ts index bcf7b446..e36435c3 100644 --- a/packages/cli/src/managed-python-daemon.ts +++ b/packages/cli/src/managed-python-daemon.ts @@ -325,7 +325,7 @@ async function waitForHealth(input: { return; } lastDetail = finalHealth.detail; - throw new Error(`KTX Python daemon failed to start: ${lastDetail}. stderr: ${input.state.stderrLog}`); + throw new Error(`KTX daemon failed to start: ${lastDetail}. stderr: ${input.state.stderrLog}`); } async function removeState(layout: ManagedPythonDaemonLayout): Promise { @@ -705,7 +705,7 @@ export async function startManagedPythonDaemon( ); child.unref(); if (!child.pid) { - throw new Error(`KTX Python daemon did not report a pid. stderr: ${layout.daemonStderrPath}`); + throw new Error(`KTX daemon did not report a pid. stderr: ${layout.daemonStderrPath}`); } const state: ManagedPythonDaemonState = { schemaVersion: 1, diff --git a/packages/cli/src/managed-python-http.test.ts b/packages/cli/src/managed-python-http.test.ts index 84d70cf8..73cae844 100644 --- a/packages/cli/src/managed-python-http.test.ts +++ b/packages/cli/src/managed-python-http.test.ts @@ -57,7 +57,7 @@ describe('createManagedPythonDaemonBaseUrlResolver', () => { features: ['core'], force: false, }); - expect(testIo.stderr()).toContain('Started KTX Python daemon: http://127.0.0.1:61234'); + expect(testIo.stderr()).toContain('Started KTX daemon: http://127.0.0.1:61234'); }); it('reports daemon reuse without reinstalling after the first resolved URL', async () => { @@ -86,7 +86,7 @@ describe('createManagedPythonDaemonBaseUrlResolver', () => { expect(ensureRuntime).toHaveBeenCalledTimes(1); expect(startDaemon).toHaveBeenCalledTimes(1); - expect(testIo.stderr()).toContain('Using existing KTX Python daemon: http://127.0.0.1:61234'); + expect(testIo.stderr()).toContain('Using existing KTX daemon: http://127.0.0.1:61234'); }); }); @@ -104,8 +104,8 @@ describe('createManagedDaemonHttpJsonRunner', () => { }); }); -describe('managed daemon ingest ports', () => { - it('creates a Looker table parser backed by the managed daemon runner', async () => { +describe('KTX daemon ingest ports', () => { + it('creates a Looker table parser backed by the KTX daemon runner', async () => { const requestJson = vi.fn(async () => ({ results: { 'model.explore': { @@ -135,7 +135,7 @@ describe('managed daemon ingest ports', () => { }); }); - it('creates a SQL analysis port backed by the managed daemon runner', async () => { + it('creates a SQL analysis port backed by the KTX daemon runner', async () => { const requestJson = vi.fn(async () => ({ fingerprint: 'select-orders', normalized_sql: 'SELECT * FROM public.orders WHERE id = ?', @@ -157,7 +157,7 @@ describe('managed daemon ingest ports', () => { }); }); - it('routes SQL batch analysis through the managed daemon runner', async () => { + it('routes SQL batch analysis through the KTX daemon runner', async () => { const requestJson = vi.fn(async () => ({ results: { orders: { diff --git a/packages/cli/src/managed-python-http.ts b/packages/cli/src/managed-python-http.ts index 8496a9c9..e45caabb 100644 --- a/packages/cli/src/managed-python-http.ts +++ b/packages/cli/src/managed-python-http.ts @@ -70,7 +70,7 @@ function normalizedBaseUrl(baseUrl: string): string { function parseJsonObject(raw: string, path: string): Record { const parsed = JSON.parse(raw) as unknown; if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { - throw new Error(`KTX managed daemon HTTP ${path} returned non-object JSON`); + throw new Error(`KTX daemon HTTP ${path} returned non-object JSON`); } return parsed as Record; } @@ -101,7 +101,7 @@ export async function postManagedDaemonJson( const text = Buffer.concat(chunks).toString('utf8'); const statusCode = response.statusCode ?? 0; if (statusCode < 200 || statusCode >= 300) { - reject(new Error(`KTX managed daemon HTTP ${path} failed with ${statusCode}: ${text}`)); + reject(new Error(`KTX daemon HTTP ${path} failed with ${statusCode}: ${text}`)); return; } try { @@ -142,7 +142,7 @@ export function createManagedPythonDaemonBaseUrlResolver( force: false, }); const verb = daemon.status === 'started' ? 'Started' : 'Using existing'; - options.io.stderr.write(`${verb} KTX Python daemon: ${daemon.baseUrl}\n`); + options.io.stderr.write(`${verb} KTX daemon: ${daemon.baseUrl}\n`); cachedBaseUrl = daemon.baseUrl; return cachedBaseUrl; }; diff --git a/packages/cli/src/print-command-tree.ts b/packages/cli/src/print-command-tree.ts index 6c9de751..be643546 100644 --- a/packages/cli/src/print-command-tree.ts +++ b/packages/cli/src/print-command-tree.ts @@ -14,8 +14,6 @@ function stubPackageInfo(): KtxCliPackageInfo { return { name: '@ktx/cli', version: '0.0.0-docs', - packageVersion: '0.0.0-private', - runtimeVersion: '0.0.0-docs', contextPackageName: '@ktx/context', }; } diff --git a/packages/cli/src/public-ingest.ts b/packages/cli/src/public-ingest.ts index 2c0a2856..3b49f629 100644 --- a/packages/cli/src/public-ingest.ts +++ b/packages/cli/src/public-ingest.ts @@ -679,7 +679,7 @@ function createCapturedPublicIngestIo(): CapturedPublicIngestIo { const INTERNAL_STATUS_LINE_RE = /^(Report|Run|Job|Status|Adapter|Connection|Sync|Diff|Tasks|Work units|Failed tasks|Saved memory|Provenance rows):\s*/; const ACTIONABLE_FAILURE_LINE_RE = - /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX managed daemon|Error:|Failed\b|Could not\b|Cannot\b)/; + /^(Missing bundled Python runtime manifest|KTX Python runtime is required|KTX daemon HTTP|Error:|Failed\b|Could not\b|Cannot\b)/; const RUNTIME_BACKED_RETRY_LINE_RE = /^Then retry the runtime-backed KTX command\.?$/; function trimErrorPrefix(line: string): string { diff --git a/packages/cli/src/release-version.ts b/packages/cli/src/release-version.ts index 77bcb833..145349f0 100644 --- a/packages/cli/src/release-version.ts +++ b/packages/cli/src/release-version.ts @@ -1,55 +1,9 @@ -import { existsSync, readFileSync } from 'node:fs'; -import { dirname, join, parse } from 'node:path'; -import { fileURLToPath } from 'node:url'; - const semverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/; -function isPlainObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} - -function assertReleaseVersion(value: unknown, source: string): string { +export function assertCliVersion(value: unknown, source: string): string { if (typeof value !== 'string' || !semverPattern.test(value)) { - throw new Error(`Invalid KTX release version in ${source}`); + throw new Error(`Invalid KTX CLI version in ${source}`); } return value; } - -function findReleasePolicyPath(startDir: string): string | undefined { - let current = startDir; - const root = parse(current).root; - while (true) { - const candidate = join(current, 'release-policy.json'); - if (existsSync(candidate)) { - return candidate; - } - if (current === root) { - return undefined; - } - current = dirname(current); - } -} - -function readSourceReleaseVersion(startDir = dirname(fileURLToPath(import.meta.url))): string | undefined { - const policyPath = findReleasePolicyPath(startDir); - if (!policyPath) { - return undefined; - } - const policy = JSON.parse(readFileSync(policyPath, 'utf8')) as unknown; - if (!isPlainObject(policy)) { - throw new Error(`Invalid KTX release policy: ${policyPath}`); - } - return assertReleaseVersion(policy.publicNpmPackageVersion, policyPath); -} - -export function resolveKtxRuntimeVersion(input: { - packageName: string; - packageVersion: string; - startDir?: string; -}): string { - if (input.packageName === '@kaelio/ktx') { - return assertReleaseVersion(input.packageVersion, `${input.packageName}/package.json`); - } - return readSourceReleaseVersion(input.startDir) ?? input.packageVersion; -} diff --git a/packages/cli/src/runtime-requirements.ts b/packages/cli/src/runtime-requirements.ts index 1e35d90e..8594de7a 100644 --- a/packages/cli/src/runtime-requirements.ts +++ b/packages/cli/src/runtime-requirements.ts @@ -94,7 +94,7 @@ export function resolveProjectRuntimeRequirements( requirements.push({ feature: 'core', reason: 'database-introspection', - detail: 'Database introspection fallback uses the Python daemon.', + detail: 'Database introspection fallback uses the KTX daemon.', }); } diff --git a/packages/cli/src/runtime.test.ts b/packages/cli/src/runtime.test.ts index e4a7883f..266359e4 100644 --- a/packages/cli/src/runtime.test.ts +++ b/packages/cli/src/runtime.test.ts @@ -109,7 +109,7 @@ describe('runKtxRuntime', () => { expect(io.stderr()).toBe(''); }); - it('starts the managed Python daemon and prints the base URL', async () => { + it('starts the KTX daemon and prints the base URL', async () => { const io = makeIo(); const deps: KtxRuntimeDeps = { startDaemon: vi.fn(async (): Promise => ({ @@ -160,14 +160,14 @@ describe('runKtxRuntime', () => { features: ['local-embeddings'], force: true, }); - expect(io.stdout()).toContain('Started KTX Python daemon'); + expect(io.stdout()).toContain('Started KTX daemon'); expect(io.stdout()).toContain('url: http://127.0.0.1:61234'); expect(io.stdout()).toContain('pid: 4242'); expect(io.stdout()).toContain('features: core, local-embeddings'); expect(io.stdout()).toContain('stderr: /work/proj/.ktx/runtime/daemon.stderr.log'); }); - it('stops the managed Python daemon', async () => { + it('stops the KTX daemon', async () => { const io = makeIo(); const deps: KtxRuntimeDeps = { stopDaemon: vi.fn(async (): Promise => ({ @@ -208,11 +208,11 @@ describe('runKtxRuntime', () => { ).resolves.toBe(0); expect(deps.stopDaemon).toHaveBeenCalledWith({ cliVersion: '0.2.0', projectDir: '/work/proj' }); - expect(io.stdout()).toContain('Stopped KTX Python daemon'); + expect(io.stdout()).toContain('Stopped KTX daemon'); expect(io.stdout()).toContain('pid: 4242'); }); - it('stops all discovered Python daemons and reports the summary', async () => { + it('stops all discovered KTX daemons and reports the summary', async () => { const io = makeIo(); const deps: KtxRuntimeDeps = { stopAllDaemons: vi.fn(async (): Promise => ({ @@ -231,7 +231,7 @@ describe('runKtxRuntime', () => { ).resolves.toBe(0); expect(deps.stopAllDaemons).toHaveBeenCalledWith({ cliVersion: '0.2.0', projectDir: '/work/proj' }); - expect(io.stdout()).toContain('Stopped 2 KTX Python daemons'); + expect(io.stdout()).toContain('Stopped 2 KTX daemons'); expect(io.stdout()).toContain('pid: 4242 source: state url: http://127.0.0.1:61234'); expect(io.stdout()).toContain('pid: 5252 source: process url: http://127.0.0.1:8765'); }); @@ -259,7 +259,7 @@ describe('runKtxRuntime', () => { runKtxRuntime({ command: 'stop', cliVersion: '0.2.0', projectDir: '/work/proj', all: true }, io.io, deps), ).resolves.toBe(1); - expect(io.stderr()).toContain('Stopped 0 KTX Python daemons; failed 1'); + expect(io.stderr()).toContain('Stopped 0 KTX daemons; failed 1'); expect(io.stderr()).toContain('pid: 4242 source: state url: http://127.0.0.1:61234'); expect(io.stderr()).toContain('process scan: ps failed'); }); diff --git a/packages/cli/src/runtime.ts b/packages/cli/src/runtime.ts index 5630163f..ad978bd5 100644 --- a/packages/cli/src/runtime.ts +++ b/packages/cli/src/runtime.ts @@ -56,7 +56,7 @@ function writeInstallResult(io: KtxCliIo, result: ManagedPythonRuntimeInstallRes function writeDaemonStart(io: KtxCliIo, result: ManagedPythonDaemonStartResult): void { const verb = result.status === 'reused' ? 'Using existing' : 'Started'; - io.stdout.write(`${verb} KTX Python daemon\n`); + io.stdout.write(`${verb} KTX daemon\n`); io.stdout.write(`url: ${result.baseUrl}\n`); io.stdout.write(`pid: ${result.state.pid}\n`); io.stdout.write(`version: ${result.state.version}\n`); @@ -68,10 +68,10 @@ function writeDaemonStart(io: KtxCliIo, result: ManagedPythonDaemonStartResult): function writeDaemonStop(io: KtxCliIo, result: ManagedPythonDaemonStopResult): void { if (result.status === 'already-stopped') { - io.stdout.write('KTX Python daemon already stopped\n'); + io.stdout.write('KTX daemon already stopped\n'); return; } - io.stdout.write('Stopped KTX Python daemon\n'); + io.stdout.write('Stopped KTX daemon\n'); io.stdout.write(`pid: ${result.state?.pid ?? 'unknown'}\n`); io.stdout.write(`state: ${result.layout.daemonStatePath}\n`); } @@ -94,11 +94,11 @@ function writeDaemonStopAll(io: KtxCliIo, result: ManagedPythonDaemonStopAllResu result.failed.length === 0 && result.scanErrors.length === 0 ) { - io.stdout.write('No KTX Python daemons found\n'); + io.stdout.write('No KTX daemons found\n'); return 0; } if (failed === 0) { - io.stdout.write(`Stopped ${result.stopped.length} KTX Python daemons\n`); + io.stdout.write(`Stopped ${result.stopped.length} KTX daemons\n`); if (result.stale.length > 0) { io.stdout.write(`Cleaned ${result.stale.length} stale daemon states\n`); } @@ -111,7 +111,7 @@ function writeDaemonStopAll(io: KtxCliIo, result: ManagedPythonDaemonStopAllResu return 0; } io.stderr.write( - `Stopped ${result.stopped.length} KTX Python daemons; failed ${result.failed.length}${ + `Stopped ${result.stopped.length} KTX daemons; failed ${result.failed.length}${ result.stale.length > 0 ? `; cleaned stale ${result.stale.length}` : '' }\n`, ); diff --git a/packages/cli/src/scan.test.ts b/packages/cli/src/scan.test.ts index 8fa51130..9863be12 100644 --- a/packages/cli/src/scan.test.ts +++ b/packages/cli/src/scan.test.ts @@ -376,7 +376,7 @@ describe('runKtxScan', () => { expect(io.stdout()).not.toContain('/~'); }); - it('passes managed daemon options to local ingest adapters when no explicit daemon URL is set', async () => { + it('passes KTX daemon options to local ingest adapters when no explicit daemon URL is set', async () => { await initKtxProject({ projectDir: tempDir }); const createLocalIngestAdapters = vi.fn(() => []); const runLocalScan = vi.fn( diff --git a/packages/cli/src/setup-embeddings.test.ts b/packages/cli/src/setup-embeddings.test.ts index 36df256a..cbbb5562 100644 --- a/packages/cli/src/setup-embeddings.test.ts +++ b/packages/cli/src/setup-embeddings.test.ts @@ -361,7 +361,7 @@ describe('setup embeddings step', () => { ); expect(result.status).toBe('failed'); - expect(io.stderr()).toContain('Recent local embeddings daemon stderr:'); + expect(io.stderr()).toContain('Recent KTX daemon stderr:'); expect(io.stderr()).toContain('daemon traceback line 6'); expect(io.stderr()).toContain('daemon traceback line 45'); expect(io.stderr()).not.toContain('daemon traceback line 5'); @@ -391,7 +391,7 @@ describe('setup embeddings step', () => { ); expect(result.status).toBe('failed'); - expect(io.stderr()).not.toContain('Recent local embeddings daemon stderr:'); + expect(io.stderr()).not.toContain('Recent KTX daemon stderr:'); }); it('uses fixed OpenAI defaults and only asks for credentials when OpenAI is selected', async () => { diff --git a/packages/cli/src/setup-embeddings.ts b/packages/cli/src/setup-embeddings.ts index 442b67bc..a114d3ac 100644 --- a/packages/cli/src/setup-embeddings.ts +++ b/packages/cli/src/setup-embeddings.ts @@ -312,7 +312,7 @@ function localEmbeddingSetupMessage(message: string, stderrTail: string[] = []): 'The first run may download Python packages and the all-MiniLM-L6-v2 model.', ]; if (stderrTail.length > 0) { - lines.push('Recent local embeddings daemon stderr:', ...stderrTail); + lines.push('Recent KTX daemon stderr:', ...stderrTail); } return lines.join('\n'); } diff --git a/packages/cli/src/setup-runtime.test.ts b/packages/cli/src/setup-runtime.test.ts index 1d739423..b1e3411f 100644 --- a/packages/cli/src/setup-runtime.test.ts +++ b/packages/cli/src/setup-runtime.test.ts @@ -97,7 +97,7 @@ describe('runKtxSetupRuntimeStep', () => { expect(io.stderr()).toContain('ktx admin runtime install --yes'); }); - it('starts the managed local embeddings daemon for configured sentence-transformers embeddings', async () => { + it('starts the KTX daemon for configured sentence-transformers embeddings', async () => { const io = makeIo(); const ensureLocalEmbeddings = vi.fn(async () => ({ baseUrl: 'http://127.0.0.1:61234', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5dbc325a..39334edc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,18 +15,12 @@ importers: '@biomejs/biome': specifier: ^2.4.15 version: 2.4.15 - '@semantic-release/changelog': - specifier: ^6.0.3 - version: 6.0.3(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/commit-analyzer': specifier: ^13.0.1 version: 13.0.1(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/exec': specifier: ^7.1.0 version: 7.1.0(semantic-release@25.0.3(typescript@6.0.3)) - '@semantic-release/git': - specifier: ^10.0.1 - version: 10.0.1(semantic-release@25.0.3(typescript@6.0.3)) '@semantic-release/github': specifier: ^12.0.8 version: 12.0.8(semantic-release@25.0.3(typescript@6.0.3)) @@ -2326,22 +2320,12 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@semantic-release/changelog@6.0.3': - resolution: {integrity: sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==} - engines: {node: '>=14.17'} - peerDependencies: - semantic-release: '>=18.0.0' - '@semantic-release/commit-analyzer@13.0.1': resolution: {integrity: sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==} engines: {node: '>=20.8.1'} peerDependencies: semantic-release: '>=20.1.0' - '@semantic-release/error@3.0.0': - resolution: {integrity: sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==} - engines: {node: '>=14.17'} - '@semantic-release/error@4.0.0': resolution: {integrity: sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==} engines: {node: '>=18'} @@ -2352,12 +2336,6 @@ packages: peerDependencies: semantic-release: '>=24.1.0' - '@semantic-release/git@10.0.1': - resolution: {integrity: sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==} - engines: {node: '>=14.17'} - peerDependencies: - semantic-release: '>=18.0.0' - '@semantic-release/github@12.0.8': resolution: {integrity: sha512-tej5AAgK5X9wHRoDmYhecMXEHEkFeGOY1XsEblKxu8pIQwahzf1STYyr7iPU6Lpbg6C5I3N2w/ocXrBo+L7jhw==} engines: {node: ^22.14.0 || >= 24.10.0} @@ -3623,10 +3601,6 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -4106,10 +4080,6 @@ packages: resolution: {integrity: sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==} engines: {node: '>= 20'} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -4490,9 +4460,6 @@ packages: lodash.uniqby@4.7.0: resolution: {integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==} - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - logform@2.7.0: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} @@ -4909,10 +4876,6 @@ packages: resolution: {integrity: sha512-z9nC87iaZXXySbWWtTHfCFJyFvKaUAW6lODhikG7ILSbVgmwuFjUqkgnheHvAUcGedO29e2QGBRXMUD64aurqQ==} engines: {node: '>=20'} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5085,10 +5048,6 @@ packages: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} - p-reduce@2.1.0: - resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==} - engines: {node: '>=8'} - p-reduce@3.0.0: resolution: {integrity: sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==} engines: {node: '>=12'} @@ -5707,10 +5666,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -8558,14 +8513,6 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@semantic-release/changelog@6.0.3(semantic-release@25.0.3(typescript@6.0.3))': - dependencies: - '@semantic-release/error': 3.0.0 - aggregate-error: 3.1.0 - fs-extra: 11.3.5 - lodash: 4.18.1 - semantic-release: 25.0.3(typescript@6.0.3) - '@semantic-release/commit-analyzer@13.0.1(semantic-release@25.0.3(typescript@6.0.3))': dependencies: conventional-changelog-angular: 8.3.1 @@ -8580,8 +8527,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@semantic-release/error@3.0.0': {} - '@semantic-release/error@4.0.0': {} '@semantic-release/exec@7.1.0(semantic-release@25.0.3(typescript@6.0.3))': @@ -8596,20 +8541,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@semantic-release/git@10.0.1(semantic-release@25.0.3(typescript@6.0.3))': - dependencies: - '@semantic-release/error': 3.0.0 - aggregate-error: 3.1.0 - debug: 4.4.3 - dir-glob: 3.0.1 - execa: 5.1.1 - lodash: 4.18.1 - micromatch: 4.0.8 - p-reduce: 2.1.0 - semantic-release: 25.0.3(typescript@6.0.3) - transitivePeerDependencies: - - supports-color - '@semantic-release/github@12.0.8(semantic-release@25.0.3(typescript@6.0.3))': dependencies: '@octokit/core': 7.0.6 @@ -10034,18 +9965,6 @@ snapshots: dependencies: eventsource-parser: 3.0.8 - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -10642,8 +10561,6 @@ snapshots: transitivePeerDependencies: - supports-color - human-signals@2.1.0: {} - human-signals@5.0.0: {} human-signals@8.0.1: {} @@ -10983,8 +10900,6 @@ snapshots: lodash.uniqby@4.7.0: {} - lodash@4.18.1: {} - logform@2.7.0: dependencies: '@colors/colors': 1.6.0 @@ -11671,10 +11586,6 @@ snapshots: normalize-url@9.0.0: {} - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - npm-run-path@5.3.0: dependencies: path-key: 4.0.0 @@ -11818,8 +11729,6 @@ snapshots: p-map@7.0.4: {} - p-reduce@2.1.0: {} - p-reduce@3.0.0: {} p-timeout@6.1.4: {} @@ -12619,8 +12528,6 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@2.0.0: {} - strip-final-newline@3.0.0: {} strip-final-newline@4.0.0: {} diff --git a/scripts/local-embeddings-runtime-smoke.mjs b/scripts/local-embeddings-runtime-smoke.mjs index 6c9c6ed0..6b1787be 100644 --- a/scripts/local-embeddings-runtime-smoke.mjs +++ b/scripts/local-embeddings-runtime-smoke.mjs @@ -346,7 +346,7 @@ export async function runLocalEmbeddingsRuntimeSmoke(options = {}) { 900_000, ); validateEmbeddingResponse(embeddingResponse, 384); - process.stdout.write('KTX local embeddings daemon computed a 384-dimensional embedding\n'); + process.stdout.write('KTX daemon computed a 384-dimensional embedding\n'); const setup = await run(commands[5].command, commands[5].args, { cwd: installDir, @@ -369,7 +369,7 @@ export async function runLocalEmbeddingsRuntimeSmoke(options = {}) { }); requireSuccess(commands[6].label, stop); daemonStarted = false; - requireOutput(commands[6].label, stop, /Stopped KTX Python daemon/); + requireOutput(commands[6].label, stop, /Stopped KTX daemon/); process.stdout.write('KTX local embeddings runtime smoke verified\n'); } finally { diff --git a/scripts/local-embeddings-runtime-smoke.test.mjs b/scripts/local-embeddings-runtime-smoke.test.mjs index 1b5cbfdd..ca14e0df 100644 --- a/scripts/local-embeddings-runtime-smoke.test.mjs +++ b/scripts/local-embeddings-runtime-smoke.test.mjs @@ -129,13 +129,13 @@ describe('localEmbeddingsSmokeCommands', () => { describe('parseDaemonBaseUrl', () => { it('extracts the daemon URL from runtime start output', () => { assert.equal( - parseDaemonBaseUrl('Started KTX Python daemon\nurl: http://127.0.0.1:61234\nfeatures: local-embeddings\n'), + parseDaemonBaseUrl('Started KTX daemon\nurl: http://127.0.0.1:61234\nfeatures: local-embeddings\n'), 'http://127.0.0.1:61234', ); }); it('rejects output without a daemon URL', () => { - assert.throws(() => parseDaemonBaseUrl('Started KTX Python daemon\n'), /Daemon URL was not printed/); + assert.throws(() => parseDaemonBaseUrl('Started KTX daemon\n'), /Daemon URL was not printed/); }); }); diff --git a/scripts/package-artifacts.mjs b/scripts/package-artifacts.mjs index 96a04bf0..18a098bb 100644 --- a/scripts/package-artifacts.mjs +++ b/scripts/package-artifacts.mjs @@ -804,19 +804,19 @@ try { const runtimeStart = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'start']); requireSuccess('ktx admin runtime start', runtimeStart); daemonStarted = true; - requireOutput('ktx admin runtime start', runtimeStart, /Started KTX Python daemon/); + requireOutput('ktx admin runtime start', runtimeStart, /Started KTX daemon/); requireOutput('ktx admin runtime start', runtimeStart, /url: http:\\/\\/127\\.0\\.0\\.1:\\d+/); requireOutput('ktx admin runtime start', runtimeStart, /features: core/); const runtimeStartReuse = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'start']); requireSuccess('ktx admin runtime start reuse', runtimeStartReuse); - requireOutput('ktx admin runtime start reuse', runtimeStartReuse, /Using existing KTX Python daemon/); + requireOutput('ktx admin runtime start reuse', runtimeStartReuse, /Using existing KTX daemon/); requireOutput('ktx admin runtime start reuse', runtimeStartReuse, /features: core/); const runtimeStop = await run('pnpm', ['exec', 'ktx', 'admin', 'runtime', 'stop']); requireSuccess('ktx admin runtime stop', runtimeStop); daemonStarted = false; - requireOutput('ktx admin runtime stop', runtimeStop, /Stopped KTX Python daemon/); + requireOutput('ktx admin runtime stop', runtimeStop, /Stopped KTX daemon/); process.stdout.write('ktx admin runtime daemon lifecycle verified\\n'); const structuralScan = await run('pnpm', ['exec', 'ktx', 'ingest', 'warehouse', diff --git a/scripts/package-artifacts.test.mjs b/scripts/package-artifacts.test.mjs index 384eeb65..0104b836 100644 --- a/scripts/package-artifacts.test.mjs +++ b/scripts/package-artifacts.test.mjs @@ -502,7 +502,7 @@ describe('verification snippets', () => { assert.match(source, /status: ready/); assert.match(source, /ktx admin runtime start/); assert.match(source, /ktx admin runtime start reuse/); - assert.match(source, /Using existing KTX Python daemon/); + assert.match(source, /Using existing KTX daemon/); assert.match(source, /ktx admin runtime stop/); assert.doesNotMatch(source, /ktx admin runtime prune/); assert.doesNotMatch(source, /staleRuntimeDir/); diff --git a/scripts/release-workflow.test.mjs b/scripts/release-workflow.test.mjs index e6f8350a..20b437c7 100644 --- a/scripts/release-workflow.test.mjs +++ b/scripts/release-workflow.test.mjs @@ -21,15 +21,14 @@ describe('release workflow', () => { assert.doesNotMatch(workflow, /Prepare first stable release floor/); assert.doesNotMatch(workflow, /git tag v0\.0\.0/); assert.doesNotMatch(workflow, /KTX_STABLE_RELEASE_FLOOR_TAG/); - assert.match(workflow, /Prepare next prerelease branch/); - assert.match(workflow, /git checkout -B "\$\{KTX_PRERELEASE_BRANCH\}"/); + assert.doesNotMatch(workflow, /Prepare next prerelease branch/); + assert.doesNotMatch(workflow, /KTX_PRERELEASE_BRANCH/); + assert.doesNotMatch(workflow, /GITHUB_REF="refs\/heads\//); assert.match(workflow, /Prepare npm package root for release verification/); assert.match(workflow, /dist\/public-npm-package\/package\.json/); - assert.match(workflow, /GITHUB_REF="refs\/heads\/\$\{KTX_PRERELEASE_BRANCH\}"/); assert.match(workflow, /pnpm run semantic-release:dry-run/); assert.match(workflow, /pnpm run semantic-release$/m); assert.match(workflow, /KTX_RELEASE_KIND: \$\{\{ inputs.release_kind \}\}/); - assert.match(workflow, /KTX_PRERELEASE_BRANCH: next/); assert.match(workflow, /FORCE_RELEASE: \$\{\{ inputs.force_release \}\}/); assert.doesNotMatch(workflow, /NODE_AUTH_TOKEN/); assert.doesNotMatch(workflow, /^ push:/m); diff --git a/scripts/semantic-release-config.cjs b/scripts/semantic-release-config.cjs index fb1f959d..d1f17bbf 100644 --- a/scripts/semantic-release-config.cjs +++ b/scripts/semantic-release-config.cjs @@ -74,54 +74,22 @@ const releaseNoteTypes = [ { type: 'major', section: 'BREAKING CHANGES', hidden: false }, ]; -function currentBranch(env) { - return env.GITHUB_REF_NAME || env.INPUT_BRANCH || 'main'; -} - function releaseKind(env) { return env.KTX_RELEASE_KIND || env.INPUT_RELEASE_KIND || 'rc'; } -function prereleaseBranch(env) { - return env.KTX_PRERELEASE_BRANCH || env.INPUT_PRERELEASE_BRANCH || 'next'; -} - function releaseTag(kind) { return kind === 'rc' ? 'next' : 'latest'; } -function releaseChangelogPlugins(kind) { - return kind === 'rc' ? ['@semantic-release/changelog'] : []; -} - -function releaseGitPlugins(kind) { - if (kind !== 'rc') { - return []; - } - - return [ - [ - '@semantic-release/git', - { - assets: ['CHANGELOG.md', 'package.json', 'release-policy.json'], - message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}', - }, - ], - ]; -} - function releaseBranches(env = process.env) { - const branch = currentBranch(env); const kind = releaseKind(env); if (kind === 'rc') { - return ['main', { name: prereleaseBranch(env), prerelease: 'rc', channel: 'next' }]; + return [{ name: 'main', prerelease: 'rc', channel: 'next' }]; } if (kind === 'stable') { - if (branch !== 'main') { - throw new Error(`Stable KTX releases must run from main, got ${branch}`); - } return ['main']; } @@ -157,7 +125,6 @@ function createReleaseConfig(env = process.env) { }, }, ], - ...releaseChangelogPlugins(kind), [ '@semantic-release/exec', { @@ -172,7 +139,6 @@ function createReleaseConfig(env = process.env) { ].join(' && '), }, ], - ...releaseGitPlugins(kind), [ '@semantic-release/github', { @@ -188,7 +154,6 @@ function createReleaseConfig(env = process.env) { module.exports = { createReleaseConfig, - prereleaseBranch, releaseBranches, releaseKind, releaseTag, diff --git a/scripts/semantic-release-config.test.mjs b/scripts/semantic-release-config.test.mjs index c62f0fdf..be4dcc38 100644 --- a/scripts/semantic-release-config.test.mjs +++ b/scripts/semantic-release-config.test.mjs @@ -9,21 +9,16 @@ function releaseExecOptions(config) { return config.plugins.find((plugin) => Array.isArray(plugin) && plugin[0] === '@semantic-release/exec' && plugin[1].prepareCmd)[1]; } -function releaseExecIndex(config) { - return config.plugins.findIndex((plugin) => Array.isArray(plugin) && plugin[0] === '@semantic-release/exec' && plugin[1].prepareCmd); -} - function pluginNames(config) { return config.plugins.map((plugin) => (Array.isArray(plugin) ? plugin[0] : plugin)); } describe('semantic-release config', () => { - it('configures rc releases on a dedicated next prerelease branch', () => { + it('configures rc releases as a prerelease on main', () => { assert.equal(releaseKind({ KTX_RELEASE_KIND: 'rc' }), 'rc'); assert.equal(releaseTag('rc'), 'next'); assert.deepEqual(releaseBranches({ KTX_RELEASE_KIND: 'rc', GITHUB_REF_NAME: 'main' }), [ - 'main', - { name: 'next', prerelease: 'rc', channel: 'next' }, + { name: 'main', prerelease: 'rc', channel: 'next' }, ]); const config = createReleaseConfig({ KTX_RELEASE_KIND: 'rc', GITHUB_REF_NAME: 'main' }); @@ -42,14 +37,6 @@ describe('semantic-release config', () => { ); assert.match(releaseExecOptions(config).publishCmd, /pnpm run release:published-smoke/); assert.doesNotMatch(JSON.stringify(config.plugins), /release:npm-publish/); - const releaseFilePluginNames = pluginNames(config).filter( - (plugin) => plugin === '@semantic-release/changelog' || plugin === '@semantic-release/git', - ); - assert.deepEqual(releaseFilePluginNames, ['@semantic-release/changelog', '@semantic-release/git']); - - const names = pluginNames(config); - assert.ok(names.indexOf('@semantic-release/changelog') < releaseExecIndex(config)); - assert.ok(names.indexOf('@semantic-release/git') > releaseExecIndex(config)); }); it('configures stable releases only from main with latest tag', () => { @@ -69,18 +56,22 @@ describe('semantic-release config', () => { assert.equal(config.plugins.includes('./scripts/semantic-release-version-policy.cjs'), false); }); - it('does not commit release files back to protected main during stable releases', () => { - const config = createReleaseConfig({ KTX_RELEASE_KIND: 'stable', GITHUB_REF_NAME: 'main' }); - - assert.equal(pluginNames(config).includes('@semantic-release/git'), false); - assert.equal(pluginNames(config).includes('@semantic-release/changelog'), false); + it('never commits release files back to the repo', () => { + for (const kind of ['rc', 'stable']) { + const config = createReleaseConfig({ KTX_RELEASE_KIND: kind, GITHUB_REF_NAME: 'main' }); + assert.equal(pluginNames(config).includes('@semantic-release/git'), false, `${kind}: @semantic-release/git`); + assert.equal(pluginNames(config).includes('@semantic-release/changelog'), false, `${kind}: @semantic-release/changelog`); + } }); - it('rejects stable releases from non-main branches', () => { - assert.throws( - () => releaseBranches({ KTX_RELEASE_KIND: 'stable', GITHUB_REF_NAME: 'feature/release-test' }), - /Stable KTX releases must run from main, got feature\/release-test/, - ); + it('produces a loadable config regardless of GITHUB_REF_NAME', () => { + // Knip and other tooling load .releaserc.cjs on PR runners where + // GITHUB_REF_NAME is the merge ref. semantic-release itself enforces the + // main-only rule by refusing to publish when the current branch does not + // match a configured release branch, so the config must not throw at load. + for (const kind of ['rc', 'stable']) { + assert.doesNotThrow(() => releaseBranches({ KTX_RELEASE_KIND: kind, GITHUB_REF_NAME: '180/merge' })); + } }); it('keeps the force-release patch escape hatch', () => {