Skip to content

feat: add ctx self-update and release/update mechanism#11

Merged
saldestechnology merged 6 commits into
mainfrom
feat/self-update
Jul 9, 2026
Merged

feat: add ctx self-update and release/update mechanism#11
saldestechnology merged 6 commits into
mainfrom
feat/self-update

Conversation

@saldestechnology

Copy link
Copy Markdown
Collaborator

Summary

Feature H — the final feature of the Quality Intelligence Suite. Stacked on #9 (feat/harness) — review the top 3 commits until #9 merges.

Determinism rule throughout: no automatic updates, ever — ctx is a quality gate, so gate semantics never change without explicit user action.

  • ctx self-update [--version X.Y.Z]: queries GitHub Releases, downloads the platform artifact, verifies sha256 against the release's SHA256SUMS (mismatch → exit 2, binary untouched), and atomically replaces the executable with a writability preflight. --json supported.
  • Passive update notice: interactive invocations only, ≤ once/24h (cache under the user cache dir), 1s timeout, silent failure — one line on stderr. Suppressed (meaning zero network calls, asserted via mock hit counts) by CTX_NO_UPDATE_CHECK, non-TTY stderr, --json, or Claude Code hook env vars — notices can never leak into model context.
  • ctx --version --check: explicit, cache-exempt comparison; plain ctx --version output is byte-identical to before (snapshot-tested).
  • Release CI: aggregated SHA256SUMS release asset + a new gate job running scripts/release-plugin-check.sh (locally runnable) — it generates the plugin scaffold from the release binary, fails the release if plugin.json ≠ tag version, and attaches ctx-claude-plugin-<version>.zip. Plugin/binary lockstep is by construction.
  • README/docs: Updating section (ctx self-update, /plugin update, CTX_NO_UPDATE_CHECK, marketplace sha-pinning for frozen installs), self-update command page + JSON payloads documented.
  • New deps: tar (runtime extraction) and windows-only zip — both already in Cargo.lock transitively; reqwest gains the blocking feature. The mock release server for tests is hand-rolled std-only.

Test plan

  • 311 tests green: 13 new unit (artifact-name mapping per target, SHA256SUMS parsing, 24h cache with injected clock, full suppression predicate matrix) + 9 new integration against a local mock server on a copy of the binary (replacement with old→new output, corrupted-checksum abort leaving the binary byte-identical, notice-once-then-cached with zero second hit, three suppression modes, version snapshot, cache-exempt --check).
  • clippy -D warnings, fmt --check, --no-default-features + --features mcp builds, docs npm run build — all clean. scripts/release-plugin-check.sh: OK (0.2.1 == 0.2.1).

🤖 Generated with Claude Code

…ement

- ctx self-update [--version X.Y.Z]: queries the GitHub Releases API,
  picks the artifact for the compile-time target (mirroring the release
  build matrix), verifies its sha256 against the release's aggregated
  SHA256SUMS file (mismatch aborts with exit 2, binary untouched), and
  atomically renames the extracted binary over the running executable
  (Windows: rename-aside to ctx.exe.old, cleaned up on the next run).
  Unwritable install locations are refused with guidance before any
  network work. --json emits a self_update envelope.
- Passive update notice: interactive runs check at most once per 24h
  (timestamp cache under the user cache dir), 1s timeout, silent
  failure, one stderr line. Suppressed entirely (no network call) by
  CTX_NO_UPDATE_CHECK, non-TTY stderr, --json, Claude Code hook env
  vars (CLAUDECODE, CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT), and for
  self-update/--version invocations. Never updates automatically.
- ctx --version --check: explicit comparison on stdout, exempt from
  suppression and the 24h cache; clap's auto version flag is replaced
  by an equivalent custom flag (plain --version/-V output unchanged).
- Tests: unit coverage for the artifact mapping, SHA256SUMS parsing,
  cache clock, suppression matrix, extraction, and atomic replace;
  end-to-end CLI tests against a hand-rolled mock HTTP server in
  testutil (CTX_UPDATE_BASE_URL/CTX_CACHE_DIR/CTX_UPDATE_FORCE_TTY
  test hooks), replacing a copied binary, never the real one.
- Releases now attach one aggregated SHA256SUMS file over all
  artifacts (consumed by ctx self-update; per-artifact .sha256 files
  are kept).
- New plugin job runs scripts/release-plugin-check.sh: builds the
  binary, scaffolds the plugin with 'ctx harness init --mode plugin'
  into a scratch dir, fails the release when the generated
  plugin.json version differs from the tag (lockstep is by
  construction; this asserts it), and attaches the scaffold as
  ctx-claude-plugin-<version>.zip. The script is locally runnable.
- docs/commands/self-update.md (+ website mirror and sidebar entry):
  update flow, checksum verification, passive-notice suppression
  rules, /plugin update as the plugin update path, sha-pinning the
  marketplace entry for frozen installs, exit codes.
- docs/json-output.md (+ website copy): self_update and version.check
  envelopes.
- README: 'Updating' subsection under Installation (self-update,
  CTX_NO_UPDATE_CHECK, /plugin update, sha-pinning, and the
  no-silent-updates determinism rule).
- CHANGELOG: Unreleased entries for the release & update mechanism.
# Conflicts:
#	src/commands/mod.rs
@saldestechnology
saldestechnology changed the base branch from feat/harness to main July 9, 2026 19:06
@saldestechnology
saldestechnology merged commit f9657ab into main Jul 9, 2026
7 checks passed
@saldestechnology
saldestechnology deleted the feat/self-update branch July 9, 2026 20:37
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