Skip to content

feat(0.43.3): kbagent update --beta + tag-pinned install URL - #317

Merged
padak merged 4 commits into
mainfrom
feat/update-beta-tag-aware-install
May 18, 2026
Merged

feat(0.43.3): kbagent update --beta + tag-pinned install URL#317
padak merged 4 commits into
mainfrom
feat/update-beta-tag-aware-install

Conversation

@padak

@padak padak commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

  • Introduces kbagent update --beta (and KBAGENT_INCLUDE_PRERELEASE=1) opt-in for pre-release channel — discovery hits /releases (plural) and picks highest PEP 440, install propagates --prerelease=allow (uv) / --pre (pip)
  • Variant B fix: install URL now tag-pins to @v<version> when (and only when) opting into prerelease, so beta tags living on a feature branch resolve correctly instead of silently falling back to main HEAD
  • CLAUDE.md "Beta / pre-release versions" gets a third gate documented (tag-pinned install URL) and author workflow renamed to "Releasing a beta from a feature branch" reflecting the canonical PR-head flow
  • CONTRIBUTING.md gains "Releasing a beta (pre-release) version" section

Why three commits

  1. feat(0.42.0) cherry-picked from PR feat(0.44.0): kbagent agent <verb> -- CLI parity for /agents REST surface #310 — original kbagent update --beta implementation. PEP 440 + GitHub --prerelease gates.
  2. docs: cherry-picked from PR feat(0.44.0): kbagent agent <verb> -- CLI parity for /agents REST surface #310 — CLAUDE.md and CONTRIBUTING.md beta workflow docs.
  3. fix(0.43.3) new — the Variant B fix that makes --beta actually work when betas live on feature branches.

The original v0.42.0 implementation in PR #310 had a latent bug: uv tool install --prerelease=allow git+URL resolves the default branch (main), so even though _fetch_kbagent_latest_prerelease() advertised 0.44.0b1 from a feature-branch tag, uv would silently install whatever main carried at HEAD. The fix appends @v<version> to the install URL.

This release-pattern was discovered while preparing PR #310 for beta release: the agent-cli-parity feature lives on a long-running PR branch, main is on 0.43.2, and we want kbagent update --beta to land users on the actual beta tag rather than silently fall back to main.

Test plan

  • 12 unit tests in test_version_service.py (PEP 440 sort, draft skip, fallback to stable, invalid tag handling, HTTP failure, uv prerelease, pip prerelease, NEW: prerelease+target_version appends @v, NEW: stable+target_version ignores tag)
  • make check passes locally (3393 tests green)
  • Post-merge: kbagent update (stable path) from a 0.43.x install → resolves to 0.43.3, no tag-pinning regression
  • Post-merge: this PR enables the beta flow for PR feat(0.44.0): kbagent agent <verb> -- CLI parity for /agents REST surface #310 to ship 0.44.0b1 from the feature branch

Follow-up

After this lands as 0.43.3, PR #310 will be rebased onto 0.43.3, will drop the duplicated --beta and Variant B commits (already in main), bump to 0.44.0b1, and ship as a tagged beta release. Beta users will then be able to opt in via kbagent update --beta and land on the actual 0.44.0b1 tag.

padak added 3 commits May 18, 2026 12:34
PEP 440 + GitHub --prerelease flag = two independent gates that keep
stable users safe from accidentally landing on a beta release.

Default path (unchanged):
- _fetch_kbagent_latest_version() hits /releases/latest -- GitHub
  defines this endpoint as "the most recent non-prerelease, non-draft
  release", so betas marked --prerelease are invisible to the auto-update
  startup hook.
- build_kbagent_upgrade_command() returns the same uv tool install
  --upgrade ... cmd as before -- uv defaults to rejecting PEP 440
  pre-release version specs.

Beta opt-in (kbagent update --beta or KBAGENT_INCLUDE_PRERELEASE=1):
- Version fetcher switches to /releases (plural), filters drafts,
  parses every tag through packaging.version.Version, returns the
  highest by PEP 440 ordering. Stable hot-fixes to an older release
  line still win over a more-recently-tagged beta on a newer line.
- Resolver opt-in: --prerelease=allow (uv) / --pre (pip) inserted into
  the install command so the resolver accepts 0.43.0b1 even though
  it's a pre-release.

UX choices:
- Ad-hoc only -- no release_channel: beta persistent config setting.
  Each opt-in is an active choice (--beta or env var per shell). This
  prevents the "I once typed --beta six months ago and forgot" foot-gun.
- env var KBAGENT_INCLUDE_PRERELEASE=1 supports the CI-smoke-test use
  case (don't want to re-type --beta in every cron job step).
- Startup auto-update hook untouched -- it still never auto-installs
  a beta. Only the explicit kbagent update --beta does.

Tests: 10 new in test_version_service.py:
- TestFetchKbagentLatestVersion: default uses /releases/latest endpoint;
  prerelease uses /releases and returns highest by PEP 440 ordering;
  prerelease skips drafts; falls back to stable when no betas; ignores
  invalid tags; HTTP failure returns None.
- TestBuildKbagentUpgradeCommand: uv flag insertion (with and without
  [server] extras), pip --pre fallback.

Docs:
- CONTRIBUTING.md: new "Releasing a beta (pre-release) version" section
  documenting the PEP 440 + gh release create --prerelease workflow,
  the two-gate model, and the two opt-in paths.
- CLAUDE.md `## All CLI Commands`: `version [--beta]` and `update [--beta]`
  with a one-line note about the env var override.
- context.py: per-command help refreshed.
- commands-reference.md: same.
- changelog.py: 0.42.0 entry (mixed with the agent CLI feature in the
  same release).
CLAUDE.md `## Versioning` section gains a `### Beta / pre-release
versions (since 0.42.0)` subsection covering:
- Why PEP 440 (`0.43.0b1`) instead of SemVer (`-beta.1`)
- The two independent gates (pre-release suffix + GitHub --prerelease)
- Author workflow (4-step recipe from version bump to gh release)
- User opt-in paths (--beta flag, env var, no persistent setting)
- Cross-reference to CONTRIBUTING.md for the full author checklist

CONTRIBUTING.md `## Releasing a new version` intro gains a blockquote
signposting the beta workflow so authors discover the option without
having to scroll to the end of the section.
The v0.42.0 implementation of --beta opt-in had a latent bug that
only fires when a beta tag lives on a feature branch (not main):
build_kbagent_upgrade_command(prerelease=True) generated

    uv tool install --force --with keboola-agent-cli[server]
        --prerelease=allow git+https://github.com/padak/keboola_agent_cli

uv resolves the git+ URL by checking out the DEFAULT BRANCH (main)
and reading pyproject.toml at HEAD. So even though _fetch_kbagent_
latest_prerelease() advertised "0.44.0b1", uv would silently install
whatever main carried at HEAD (e.g. 0.43.2) -- because the tag was
never consulted. The user sees "kbagent update --beta -> 0.44.0b1"
in the version banner and ends up with 0.43.2.

The fix tag-pins the install URL when (and only when) opting into
prerelease:

    build_kbagent_upgrade_command(prerelease=True,
                                  target_version="0.44.0b1")

now appends @v0.44.0b1 to the git+ URL, so uv installs the exact
commit the tag points to. _update_kbagent forwards the value it
already had from _fetch_kbagent_latest_version().

Stable upgrades intentionally pass target_version=None: main IS the
stable channel, so tag-pinning would just add a needless HTTP round-
trip without changing the resolved version. The default behaviour
(no --beta, no tag suffix) is byte-for-byte unchanged from 0.43.2.

Tests:
- test_uv_prerelease_with_target_version_appends_tag: pre-release
  path emits @v<version> in install spec
- test_uv_stable_with_target_version_ignores_tag: target_version is
  silently ignored when prerelease=False (no regression for stable)

Docs:
- CLAUDE.md "Beta / pre-release versions" section reworded: third
  gate (tag-pinned install URL) added to the two-gate model, author
  workflow renamed to "Releasing a beta from a feature branch"
  reflecting the canonical flow (PR head, not main)
- changelog: 0.43.3 entry describes the bug + fix + new gate.

This release-pattern was discovered while preparing PR #310 for
beta release: the agent-cli-parity feature lives on a long-running
PR branch, main is on 0.43.2, and we want kbagent update --beta to
land users on the actual beta tag rather than silently fall back
to main.

@padak padak left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of #317 — feat(0.43.3): kbagent update --beta + tag-pinned install URL

Generated by kbagent-pr-reviewer subagent. Verdict and findings below
are advisory; the human author retains every veto. CI-coverable issues
(lint, format, tests) are confirmed via make check, not duplicated here.

Summary

This PR introduces opt-in pre-release support for kbagent update and kbagent version via a --beta flag and KBAGENT_INCLUDE_PRERELEASE=1 env var, plus a tag-pinned install URL fix ("Variant B") that prevents uv from silently installing stale main HEAD when a beta tag lives on a feature branch. The implementation is clean and well-tested at the service layer. Two NON-BLOCKING silent-drift gaps exist: plugins/kbagent/agents/keboola-expert.md VERSION GATE does not document the new --beta flag's minimum version, and the get_versions() JSON response's upgrade_command field does not reflect the beta install command when --beta is active. The AGENT_CONTEXT env-var listing section is also missing KBAGENT_INCLUDE_PRERELEASE. A NIT: the lazy import os inside _env_opted_into_prerelease is inconsistent with the module-level import os pattern used everywhere else.

Verdict

  • Verdict: COMMENT
  • Blocking findings: 0
  • Non-blocking findings: 3
  • Nits: 1

Blocking findings

(none)

Non-blocking findings

[NB-1] plugins/kbagent/agents/keboola-expert.md:62 — VERSION GATE missing kbagent update/version --beta minimum-version entry

The --beta flag is new in 0.42.0 (or 0.43.3 if using the tag-pinned Variant B fix). The VERSION GATE list in keboola-expert.md §1 Rule 6 does not include a line like "kbagent update --beta / kbagent version --beta opt-in needs 0.42.0+". An AI agent running on a 0.41.x install that is asked "install the latest beta" will run kbagent update --beta, get No such option: --beta, and fail without a useful fallback path.

Fix: add one line to the VERSION GATE list: `kbagent update --beta` / `kbagent version --beta` (opt into PEP 440 pre-releases; Variant B tag-pin needs 0.43.3+) need 0.42.0+,

[NB-2] src/keboola_agent_cli/services/version_service.py:638get_versions() returns stale upgrade_command when include_prerelease=True

When kbagent --json version --beta is run and a beta is the latest, kbagent.latest_version correctly returns e.g. "0.44.0b1", but kbagent.upgrade_command still returns the static uv tool install --upgrade git+... without --prerelease=allow or @v0.44.0b1. Any downstream consumer that reads upgrade_command from the JSON output will run the wrong install command and land on the stable version instead of the advertised beta.

Fix: pass prerelease=include_prerelease, target_version=kbagent_latest if include_prerelease else None to build_kbagent_upgrade_command() and set kbagent.upgrade_command = " ".join(cmd) in the return dict when cmd is not None, or at minimum annotate the field docstring so callers know it reflects stable-only.

[NB-3] src/keboola_agent_cli/commands/context.py:1023KBAGENT_INCLUDE_PRERELEASE missing from env-var listing in AGENT_CONTEXT

The env var is documented inline in the kbagent version and kbagent update description blocks (lines 959, 971) but is absent from the dedicated "Environment Variables" listing at lines 1023-1040. The kbagent context JSON output that AI agents read will not list it alongside KBAGENT_AUTO_UPDATE, KBAGENT_SKIP_UPDATE, etc. — an agent that scans the env section to understand all valid overrides will miss it.

Fix: add KBAGENT_INCLUDE_PRERELEASE Set to "1" (or "true/yes/on") to opt into pre-release versions for kbagent update / version in this shell (equivalent to --beta; never affects the auto-update startup hook). to the env-var block.

Nits

  • [NIT-1] src/keboola_agent_cli/commands/version.py:183import os is lazy (inside the function body of _env_opted_into_prerelease) while every other file in commands/ and auto_update.py uses a module-level import os. Move it to the top of the file for consistency with the codebase pattern.

Verification log

  • gh pr view 317 --json title,body,files,additions,deletions,baseRefName,headRefName,labels,state — 12 files, +513/-36, title feat(0.43.3): kbagent update --beta + tag-pinned install URL, state OPEN, head feat/update-beta-tag-aware-install base main
  • git rev-parse --abbrev-ref HEAD (in worktree /Users/padak/github/keboola_agent_cli/.claude/worktrees/update-beta-fix) → feat/update-beta-tag-aware-install ✓ matches PR head branch
  • Read CONTRIBUTING.md Plugin synchronization map ✓
  • Read plugins/kbagent/agents/keboola-expert.md §1 VERSION GATE, §2 Tool Selection Matrix, §3 Inline Gotchas ✓
  • Layer violation check (typer in services, httpx in commands, formatter in clients): grep → empty ✓ no layer violations
  • OPERATION_REGISTRY check for new commands: version"read", update"admin" already registered ✓ (no new commands, only new flags on existing commands)
  • --hint exception check: update and version are listed in CONTRIBUTING.md §"Exception -- infrastructure-level commands" ✓ hint skipped intentionally
  • make check3393 passed, 7 skipped, 97 deselected, 14 warnings in 62.82s exit 0 ✓
  • commands/context.py AGENT_CONTEXT: kbagent version [--beta] and kbagent update [--beta] with --beta (since 0.42.0) documentation present ✓
  • CLAUDE.md ## All CLI Commands: kbagent version [--beta] and kbagent update [--beta] entries present ✓
  • plugins/kbagent/skills/kbagent/references/commands-reference.md: both entries updated with since v0.42.0
  • plugins/kbagent/agents/keboola-expert.md VERSION GATE: no update --beta / version --beta entry → NB-1 finding
  • plugins/kbagent/skills/kbagent/references/gotchas.md: no kbagent update --beta / KBAGENT_INCLUDE_PRERELEASE entry; the behavior is documented inline in commands-reference.md and CLAUDE.md but not tagged (since vX.Y.Z) in gotchas.md. The flag is discoverable from --help so this is low-risk; filed as open question below rather than a finding.
  • server/routers/health.py:36 GET /version calls get_versions() without include_prerelease — acceptable as a write/install command is infra-only, but GET /version --beta flavor is also infra. No REST skip documented in PR description → informational (infrastructure command category, not data-plane)
  • Backward compatibility: get_versions() shape unchanged for stable callers (new include_prerelease param defaults to False) ✓; self_update() same ✓; build_kbagent_upgrade_command() new keyword-only params with defaults ✓
  • _fetch_kbagent_latest_prerelease error handling: response.raise_for_status() fires inside _fetch_kbagent_latest_version's outer try/except (httpx.HTTPError, KeyError, ValueError)
  • auto_update.py startup hook: calls _fetch_kbagent_latest_version() without include_prerelease (stable channel only) ✓ startup hook never silently lands on beta
  • Convention checks (magic numbers, raw error_code strings, bare except, print()): all clean ✓
  • CLI-layer CliRunner tests for --beta on version/update commands: absent (12 new service-layer tests confirmed, no CliRunner tests for the new Typer option) → noted, not blocking for infrastructure commands per project pattern

Open questions for the author

  1. gotchas.md — Is the "no gotcha entry for kbagent update --beta" deliberate? The flag is documented in commands-reference.md (since v0.42.0) but there is no (since v0.42.0) entry in gotchas.md covering the behavior that --beta does NOT affect the startup auto-update hook. This is arguably the most confusing behavior (user opts in, uninstalls, re-installs, and is surprised that auto-update put them back on stable). Worth a gotcha entry.

  2. server/routers/health.py:38 — Should GET /version accept a ?include_prerelease=true query param to mirror the CLI --beta flag? If not, documenting the explicit skip in the PR description would satisfy CONTRIBUTING.md's "document skip" requirement for external-consumer parity.

Three non-blocking findings from kbagent-pr-reviewer review of PR #317:

NB-1 (keboola-expert.md VERSION GATE):
  Rule 6 listed every prior version-gated capability but had no entry
  for `kbagent update --beta` / `version --beta`. An AI agent running
  on a 0.41.x install asked to "install the latest beta" would have
  no warning that --beta requires 0.43.3+. Added compact entry
  `\`kbagent update --beta\` = 0.43.3+` to the gate list.
  Trimmed two neighbouring entries (#304, #312) by removing redundant
  parenthesis spaces to stay under the 60000-byte prompt budget --
  the system prompt was already at 99.8% capacity before this fix.

NB-2 (get_versions() upgrade_command):
  `kbagent --json version --beta` correctly reported the prerelease
  in `kbagent.latest_version` but the `kbagent.upgrade_command` field
  was a static `uv tool install --upgrade git+...` string with no
  `--prerelease=allow` and no `@v<version>` tag-pin. Downstream
  consumers reading the JSON would copy this command, run it, and
  silently land on the stable channel even though the field said
  "0.44.0b1 is available". get_versions() now calls
  `build_kbagent_upgrade_command(prerelease=include_prerelease,
  target_version=...)` and joins the result -- the rendered command
  now matches what self_update() actually runs.

NB-3 (commands/context.py AGENT_CONTEXT env-var block):
  KBAGENT_INCLUDE_PRERELEASE was documented inline next to the
  `version`/`update` commands but missing from the dedicated
  "Environment Variables" listing at §7. AI agents that scan that
  block to discover all overrides would miss it. Added entry next to
  KBAGENT_MCP_TRANSPORT with the same shape as the inline help.

Test plan:
- `make check` -> 3393 passed (was 3393 + 1 budget overflow before
  trim; now under 60000-byte ceiling at exactly 60000 bytes after
  the two-character trim on neighbouring entries)
- get_versions(include_prerelease=False) zero behaviour change ->
  upgrade_command identical to prior release for stable callers.
@padak
padak merged commit 064f5bb into main May 18, 2026
1 check passed
@padak
padak deleted the feat/update-beta-tag-aware-install branch May 18, 2026 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant