From f8cea3a2d1f84382640f2b8a45da2eafcdb4aa9f Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 20:23:25 +0530 Subject: [PATCH 1/8] fix: auto-register Altimate Base before `agent create` and `review` resolve a provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neither `altimate agent create` nor `altimate review` called `FreeTier.autoRegisterWithin()` before resolving a provider, unlike the other five entrypoints (`run`, `tui`, `serve`, `acp`, `web`). On a fresh install this meant `agent create` leaked a raw upstream error mentioning "OpenCode" with no remediation, and `review`'s AI lane silently produced zero findings with no visible signal. Found by the Chaos Gremlin (Support Engineer) persona during the v0.12.3 release review — both confirmed P0s, verified against the actual failure path, not indirect reasoning. Wires both call sites with the same `autoRegisterWithin(undefined, () => void printDisclosureOnceForHeadless(true))` pattern already used by run/acp/web, and extends the entrypoint-late-notice-wiring pin test to cover both new call sites. --- packages/opencode/src/cli/cmd/agent.ts | 11 ++++++++++ packages/opencode/src/cli/cmd/review.ts | 13 ++++++++++++ .../entrypoint-late-notice-wiring.test.ts | 21 +++++++++++-------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/cli/cmd/agent.ts b/packages/opencode/src/cli/cmd/agent.ts index f31bf206ee..ad242ca3a9 100644 --- a/packages/opencode/src/cli/cmd/agent.ts +++ b/packages/opencode/src/cli/cmd/agent.ts @@ -162,6 +162,17 @@ const AgentCreateCommand = effectCmd({ description = query } + // altimate_change start — auto-register Altimate Base before provider state is first built, + // mirroring cli/cmd/run.ts. Without this, a fresh install's first `agent create` resolves + // the default model before any provider is registered and fails with a raw upstream error. + { + const { FreeTier } = await import("../../altimate/free/client") + const { FreeTierConsent } = await import("../../altimate/free/consent") + const result = await FreeTier.autoRegisterWithin(undefined, () => void FreeTierConsent.printDisclosureOnceForHeadless(true)) + await FreeTierConsent.printDisclosureOnceForHeadless(result.status === "registered") + } + // altimate_change end + // Generate agent const spinner = prompts.spinner() spinner.start("Generating agent configuration...") diff --git a/packages/opencode/src/cli/cmd/review.ts b/packages/opencode/src/cli/cmd/review.ts index 717588c3dd..88d6395b41 100644 --- a/packages/opencode/src/cli/cmd/review.ts +++ b/packages/opencode/src/cli/cmd/review.ts @@ -76,6 +76,19 @@ export const ReviewCommand = cmd({ ) } await bootstrap(cwd, async () => { + // altimate_change start — auto-register Altimate Base before the AI lane resolves a + // provider, mirroring cli/cmd/run.ts. Without this, a fresh install's `altimate review` + // (including the `--post --mode gate` CI path) fails the AI lane's Provider.defaultModel() + // silently — no provider was ever registered — and the deterministic verdict ships with + // zero AI findings and no visible signal. Kept outside the latency timer below. + { + const { FreeTier } = await import("../../altimate/free/client") + const { FreeTierConsent } = await import("../../altimate/free/consent") + const result = await FreeTier.autoRegisterWithin(undefined, () => void FreeTierConsent.printDisclosureOnceForHeadless(true)) + await FreeTierConsent.printDisclosureOnceForHeadless(result.status === "registered") + } + // altimate_change end + // altimate_change — time the engine only. Output writing and posting happen after this and // must not be counted as review latency, nor turn a computed review into a failed one. const startedAt = Date.now() diff --git a/packages/opencode/test/altimate/entrypoint-late-notice-wiring.test.ts b/packages/opencode/test/altimate/entrypoint-late-notice-wiring.test.ts index b678dfc203..dddc038310 100644 --- a/packages/opencode/test/altimate/entrypoint-late-notice-wiring.test.ts +++ b/packages/opencode/test/altimate/entrypoint-late-notice-wiring.test.ts @@ -44,15 +44,18 @@ function autoRegisterWithinArgs(source: string): string | null { const REAL_CALLBACK = /\(\)\s*=>[\s\S]{0,80}?printDisclosureOnceForHeadless\(true\)/ describe("autoRegisterWithin() late-notice callback wiring per entrypoint", () => { - test.each(["run.ts", "acp.ts", "web.ts"])("%s always passes a real onLateRegistration callback", (file) => { - const source = read(file) - const args = autoRegisterWithinArgs(source) - expect(args, `${file} must call autoRegisterWithin()`).not.toBeNull() - expect(args, `${file}'s autoRegisterWithin() call`).toMatch(REAL_CALLBACK) - // Guards against a regression that passes the callback conditionally (that's serve.ts's job, - // not these three) — none of them may reference ALTIMATE_CLI_CLIENT or ternary out. - expect(args, `${file} must not gate its callback like serve.ts does`).not.toMatch(/\?\s*\(\)\s*=>/) - }) + test.each(["run.ts", "acp.ts", "web.ts", "agent.ts", "review.ts"])( + "%s always passes a real onLateRegistration callback", + (file) => { + const source = read(file) + const args = autoRegisterWithinArgs(source) + expect(args, `${file} must call autoRegisterWithin()`).not.toBeNull() + expect(args, `${file}'s autoRegisterWithin() call`).toMatch(REAL_CALLBACK) + // Guards against a regression that passes the callback conditionally (that's serve.ts's + // job, not these five) — none of them may reference ALTIMATE_CLI_CLIENT or ternary out. + expect(args, `${file} must not gate its callback like serve.ts does`).not.toMatch(/\?\s*\(\)\s*=>/) + }, + ) test("serve.ts passes a real callback when NOT serving the datamates (VS Code) client", () => { const source = read("serve.ts") From f58c388caaa468ea55f18c3646d3133ffb5c5c5a Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 20:23:31 +0530 Subject: [PATCH 2/8] fix: surface startup feedback during the TUI's auto-register wait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `tui.ts`'s up-to-3s auto-register wait gave zero terminal output — on a fresh install this reads as a hang on the very first launch (End User persona, v0.12.3 release review, P1). Gates a "Connecting to Altimate Base…" status line behind a 300ms delay so the common already-registered/fast path never flashes it. --- packages/opencode/src/cli/cmd/tui.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui.ts b/packages/opencode/src/cli/cmd/tui.ts index b578882847..af00b9e6b1 100644 --- a/packages/opencode/src/cli/cmd/tui.ts +++ b/packages/opencode/src/cli/cmd/tui.ts @@ -170,7 +170,16 @@ export const TuiThreadCommand = cmd({ // altimate_change start — auto-register Altimate Base before the worker is spawned. The // worker starts loading instance/provider state as soon as it boots (worker.ts's // `traceReady` chain), so this has to land on the parent thread first. - await FreeTier.autoRegisterWithin() + // + // A fresh install's first launch can take up to the 3s wait with zero terminal output, + // which reads as a hang. Gate the status line behind a short delay so the common + // already-registered path (near-instant) never flashes it. + const registerFeedback = setTimeout(() => UI.println("Connecting to Altimate Base…"), 300) + try { + await FreeTier.autoRegisterWithin() + } finally { + clearTimeout(registerFeedback) + } // altimate_change end // altimate_change start — hand the launch correlation id to the worker explicitly. A Bun // Worker does not see runtime mutations to process.env, so without this the worker mints its From 3d273655b7f45b1889c3c2ea95eaa8d438a7a314 Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 20:23:37 +0530 Subject: [PATCH 3/8] docs: fix stale consent-gate wording flagged by the v0.12.3 release review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two independent findings (CTO and PM personas): - `altimate-base-disclosure.ts`'s header comment still described the disclosure as something "a user actually consents against before any Base credential is minted" — stale since this release removed the consent gate entirely. It's now a post-registration notice, not a pre-credential prompt. - README's "Step 1: Choose an LLM provider (required before anything works)" directly contradicted the next sentence, which explains a fresh install registers Altimate Base automatically if you skip this step. --- README.md | 2 +- packages/core/src/altimate-base-disclosure.ts | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fd0a2fb03e..313cac85b9 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ installing it in your own repository. Then — in order: -**Step 1: Choose an LLM provider** (required before anything works): +**Step 1: Choose an LLM provider** (optional — Altimate Base is used automatically if you skip this): ```bash altimate # Launch the TUI /connect # Interactive setup — choose Altimate Base, sign in, or bring an API key diff --git a/packages/core/src/altimate-base-disclosure.ts b/packages/core/src/altimate-base-disclosure.ts index 4afce361ba..652afc9164 100644 --- a/packages/core/src/altimate-base-disclosure.ts +++ b/packages/core/src/altimate-base-disclosure.ts @@ -1,17 +1,19 @@ -// altimate_change start — the single definition of the Altimate Base consent disclosure. +// altimate_change start — the single definition of the Altimate Base disclosure notice. // -// This text is what a user actually consents against before any Base credential is minted, so it -// must be identical everywhere it is shown. Two packages render it and neither can import from the -// other: the TUI's disclosure dialog (`packages/tui`) and the HTTP disclosure route that serves -// hosts rendering their own dialog (`packages/opencode`, for the VS Code extension's chat panel). -// `packages/core` is the only module both already depend on, so the constant lives here. +// Altimate Base auto-registers with no consent gate — this text is shown once, as a notice, either +// right after registration or (for a headless surface) alongside it, not as a blocking prompt +// before any credential is minted. It must still be identical everywhere it's shown. Two packages +// render it and neither can import from the other: the TUI's onboarding notice (`packages/tui`) +// and the HTTP disclosure route that serves hosts rendering their own notice (`packages/opencode`, +// for the VS Code extension's chat panel). `packages/core` is the only module both already depend +// on, so the constant lives here. // // A new leaf file rather than an addition to an existing core module, so it adds no upstream // rebase surface. // // It states the core data terms up front. The persistent per-install-id linkage detail is // disclosed in docs/docs/configure/providers.md ("Data handling") rather than repeated in the -// gate (see #1268); keep the core terms in sync with that note. +// notice (see #1268); keep the core terms in sync with that note. export const ALTIMATE_BASE_DISCLOSURE = "Altimate Base is free and requires no signup. Requests and responses may be logged and used to improve Altimate's products, including the model. Secrets are automatically masked before storage, but don't rely on it — avoid sending secrets or confidential code. Usage can be rate limited." From b582d08ca19bc83ee05de2d3546b69346e5cdbd8 Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 20:23:42 +0530 Subject: [PATCH 4/8] chore: remove a duplicate altimate_change marker in server.ts Found while auditing the "Kilo Code Review" FAILURE check on PR #1361 (CTO and Tech Lead personas flagged it as an open loose end): every one of the bot's 9 findings was already fixed by a later commit in the same PR, and this harmless leftover duplicate `// altimate_change end` was noticed in the same import block along the way. --- packages/opencode/src/server/server.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 42cf06339d..cc38a66216 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -45,7 +45,6 @@ import { FreeTierConsent } from "../altimate/free/consent" import { InstanceStore } from "@/project/instance-store" import { AppRuntime } from "@/effect/app-runtime" // altimate_change end -// altimate_change end import { FileRoutes } from "./routes/file" import { ConfigRoutes } from "./routes/config" import { ExperimentalRoutes } from "./routes/experimental" From 8073937b14c4e39d4d848eb3d6166f6b1212b0fd Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 20:23:48 +0530 Subject: [PATCH 5/8] test: adversarial coverage for v0.12.3 release fixes Covers the new code from this release's review fixes: ordering/scoping invariants for the autoRegisterWithin() wiring in agent.ts/review.ts (must run before the provider is resolved, must stay outside review.ts's latency timer, must be unconditional regardless of --no-ai), the tui.ts feedback timer's delay bounds and try/finally cleanup, and a regression guard against the duplicate marker comment. --- .../skill/release-v0.12.3-adversarial.test.ts | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts diff --git a/packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts b/packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts new file mode 100644 index 0000000000..3b172b968c --- /dev/null +++ b/packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts @@ -0,0 +1,131 @@ +/** + * Adversarial coverage for the v0.12.3 release payload (v0.12.2..HEAD): the Altimate Base + * no-consent-gate PR (#1361, 429/49 tests of its own — see the entrypoint-late-notice-wiring + * pin) plus the routing-pin fix, PLUS the two P0s and P1 found by this release's own + * multi-persona review and fixed here: + * + * - `altimate agent create` and `altimate review` never called `FreeTier.autoRegisterWithin()` + * before resolving a provider (Chaos Gremlin/Support Engineer persona, both P0): a fresh + * install's first `agent create` or `review` run failed with a raw upstream error / silently + * produced zero AI findings, because no provider had ever been registered. + * - `tui.ts`'s up-to-3s auto-register wait gave zero terminal feedback on a fresh install + * (End User persona, P1): reads as a hang on the very first launch. + * + * This file does NOT re-test PR #1361's own extensive coverage (stale-zen-cycle, + * altimate-base-auto-register, altimate-base-headless-disclosure, routing-pin, etc. — all + * re-run and confirmed green during this release's Step 5/5c). It targets only the NEW code + * from this release's fixes, following the source-assertion pattern already established in + * test/altimate/entrypoint-late-notice-wiring.test.ts for these same Effect-based / heavy CLI + * command files. + */ +import { describe, test, expect } from "bun:test" +import { readFileSync } from "fs" +import { join, resolve } from "path" + +const cmdDir = resolve(import.meta.dir, "..", "..", "src", "cli", "cmd") + +function read(file: string): string { + return readFileSync(join(cmdDir, file), "utf-8") +} + +describe("v0.12.3: agent.ts / review.ts auto-register ordering", () => { + test("agent.ts registers Altimate Base BEFORE resolving/calling Agent.generate", () => { + const source = read("agent.ts") + const registerIdx = source.indexOf("autoRegisterWithin(") + const generateIdx = source.indexOf("agentSvc.generate(") + expect(registerIdx, "agent.ts must call autoRegisterWithin()").toBeGreaterThan(-1) + expect(generateIdx, "agent.ts must call agentSvc.generate()").toBeGreaterThan(-1) + // Registering AFTER the model/provider is already being resolved defeats the fix: a fresh + // install would still hit the unregistered path on its first LLM call. + expect(registerIdx, "registration must precede the generate() call, not follow it").toBeLessThan(generateIdx) + }) + + test("agent.ts's registration call is not swallowed inside the generate() error handler", () => { + const source = read("agent.ts") + // The existing `.catch((error) => { spinner.stop(...) ... })` on agentSvc.generate() must + // stay scoped to the generate call only — folding registration into that same try/catch + // would mask a registration failure as a generic "LLM failed to generate agent" message. + const catchIdx = source.indexOf(".catch((error) => {") + const registerIdx = source.indexOf("autoRegisterWithin(") + expect(catchIdx, "the existing generate() catch handler must still be present").toBeGreaterThan(-1) + expect(registerIdx).toBeLessThan(catchIdx) + }) + + test("review.ts registers Altimate Base BEFORE reviewPullRequest AND outside the latency timer", () => { + const source = read("review.ts") + const registerIdx = source.indexOf("autoRegisterWithin(") + const reviewCallIdx = source.indexOf("await reviewPullRequest({") + const timerIdx = source.indexOf("const startedAt = Date.now()") + expect(registerIdx, "review.ts must call autoRegisterWithin()").toBeGreaterThan(-1) + expect(reviewCallIdx, "review.ts must call reviewPullRequest()").toBeGreaterThan(-1) + expect(timerIdx, "review.ts must still time the engine via startedAt").toBeGreaterThan(-1) + expect(registerIdx, "registration must precede the review call").toBeLessThan(reviewCallIdx) + // A registration wait folded into the timed region would inflate every review_run latency + // metric on a fresh install (or after a credential rotation) with startup cost that has + // nothing to do with the review engine itself. + expect(registerIdx, "registration must be excluded from the engine latency timer").toBeLessThan(timerIdx) + }) + + test("review.ts's registration call runs even when --no-ai is set (matches R1: registers regardless of the caller's own model)", () => { + const source = read("review.ts") + const registerIdx = source.indexOf("autoRegisterWithin(") + const noAiCheckIdx = source.indexOf("noAi:") + expect(registerIdx).toBeGreaterThan(-1) + // The registration call must be unconditional — not gated behind `!args.noAi` — so the + // CI `--post --mode gate` deterministic-only path still benefits the NEXT invocation that + // does use the AI lane. Asserted by requiring no `noAi`/`args.ai` reference appears between + // the register call and its own closing brace. + const blockEnd = source.indexOf("// altimate_change end", registerIdx) + const between = source.slice(registerIdx, blockEnd) + expect(between, "the registration block must not branch on noAi/args.ai").not.toMatch(/noAi|args\.ai\b/) + expect(noAiCheckIdx).toBeGreaterThan(blockEnd) + }) +}) + +describe("v0.12.3: tui.ts startup-feedback timer", () => { + function readTui(): string { + return readFileSync(join(cmdDir, "tui.ts"), "utf-8") + } + + test("the feedback timer is gated behind a short, non-zero delay (never fires instantly)", () => { + const source = readTui() + const match = source.match(/setTimeout\(\(\) => UI\.println\("Connecting to Altimate Base…"\), (\d+)\)/) + expect(match, "tui.ts must set a delayed status line before autoRegisterWithin()").not.toBeNull() + const delayMs = Number(match![1]) + // Zero (or missing) would flash on every launch, including the fast/already-registered + // path this delay exists to protect; too long would defeat the "no feedback during a + // fresh-install hang" fix End User flagged. Bounded to a sane window around the ~250-300ms + // suggestion rather than pinned to one exact value. + expect(delayMs).toBeGreaterThan(0) + expect(delayMs).toBeLessThanOrEqual(500) + }) + + test("the feedback timer is always cleared, even if autoRegisterWithin() throws", () => { + const source = readTui() + const start = source.indexOf("const registerFeedback = setTimeout(") + expect(start, "must find the registerFeedback timer declaration").toBeGreaterThan(-1) + const scope = source.slice(start, start + 400) + // Must be a try/finally around the await, not a bare await — a throw from + // autoRegisterWithin() (it currently never throws, but must not be relied upon) would + // otherwise leak the timer and could still print the status line after the process is + // already tearing down. + expect(scope).toMatch(/try\s*\{[\s\S]*?await FreeTier\.autoRegisterWithin\(\)[\s\S]*?\}\s*finally\s*\{[\s\S]*?clearTimeout\(registerFeedback\)/) + }) + + test("autoRegisterWithin() is still called with no arguments in tui.ts (unchanged contract: TUI renders its own onboarding, no headless callback)", () => { + const source = readTui() + // Regression guard: wrapping the call in a delayed-feedback block must not have also, + // accidentally, started passing a headless disclosure callback — the TUI's own + // useAltimateBaseDisclosureNotice() owns that surface; a second notice source here would + // double-print. + expect(source).toMatch(/await FreeTier\.autoRegisterWithin\(\)\s*$/m) + }) +}) + +describe("v0.12.3: server.ts marker hygiene (found while auditing the Kilo Code Review findings)", () => { + test("no duplicate consecutive 'altimate_change end' markers remain in the register-route import block", () => { + const serverPath = resolve(import.meta.dir, "..", "..", "src", "server", "server.ts") + const source = readFileSync(serverPath, "utf-8") + expect(source).not.toMatch(/\/\/ altimate_change end\s*\n\s*\/\/ altimate_change end/) + }) +}) From 4fc06cb1c5db5d2a61e7e8a700a393794caefc40 Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 20:46:35 +0530 Subject: [PATCH 6/8] revert: restore the second altimate_change end marker in server.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not a duplicate. The two consecutive `// altimate_change end` lines close two separate blocks that happen to end at the same point: the outer "Altimate-only server endpoints" block (opened at the `McpRoutes` import) and the inner "registration must invalidate BOTH instance registries" block (opened at the InstanceStore import). Removing one left the outer block's start unpaired, which the marker-integrity test (test/upstream/bridge-merge.test.ts, test/branding/upstream-merge-guard.test.ts) caught in CI on PR #1362 — correctly, since I'd broken a real invariant while "cleaning up" what looked like leftover cruft without running the actual test that verifies pairing. --- packages/opencode/src/server/server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index cc38a66216..42cf06339d 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -45,6 +45,7 @@ import { FreeTierConsent } from "../altimate/free/consent" import { InstanceStore } from "@/project/instance-store" import { AppRuntime } from "@/effect/app-runtime" // altimate_change end +// altimate_change end import { FileRoutes } from "./routes/file" import { ConfigRoutes } from "./routes/config" import { ExperimentalRoutes } from "./routes/experimental" From 954de7076d86687a4f79e282f9ff0f76fcf797ea Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 20:47:08 +0530 Subject: [PATCH 7/8] test: drop the mistaken duplicate-marker regression guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followed the previous commit's correction — the "duplicate marker" this test pinned against was never a duplicate, so the test encoded the same wrong assumption. Removed rather than fixed forward, since there's nothing left to assert once the premise is gone. --- .../test/skill/release-v0.12.3-adversarial.test.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts b/packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts index 3b172b968c..b409ff0dc8 100644 --- a/packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts +++ b/packages/opencode/test/skill/release-v0.12.3-adversarial.test.ts @@ -121,11 +121,3 @@ describe("v0.12.3: tui.ts startup-feedback timer", () => { expect(source).toMatch(/await FreeTier\.autoRegisterWithin\(\)\s*$/m) }) }) - -describe("v0.12.3: server.ts marker hygiene (found while auditing the Kilo Code Review findings)", () => { - test("no duplicate consecutive 'altimate_change end' markers remain in the register-route import block", () => { - const serverPath = resolve(import.meta.dir, "..", "..", "src", "server", "server.ts") - const source = readFileSync(serverPath, "utf-8") - expect(source).not.toMatch(/\/\/ altimate_change end\s*\n\s*\/\/ altimate_change end/) - }) -}) From dc4efc2879008680246efe3d015e544358aad7dc Mon Sep 17 00:00:00 2001 From: Haider Date: Wed, 23 Sep 2026 21:06:19 +0530 Subject: [PATCH 8/8] docs: changelog for v0.12.3 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbec1ea10..49fbcfa867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.12.3] - 2026-09-23 + +**Heads-up before upgrading (every user):** + +- **Altimate Base now registers automatically, with no dialog to accept.** Since 2026-09-17, OpenCode's own free tier (Zen) has rejected keyless requests from Altimate Code ("OpenCode's free tier can only be used from within OpenCode"), so every install that had silently fallen back to it lost its free model. A fresh install — or one with no other usable model configured — now registers the free, no-signup Altimate Base automatically at startup and shows a one-time notice instead of a confirmation dialog; only the confirmation step is gone, not the disclosure. Opt out with `ALTIMATE_BASE_AUTO_REGISTER=0`, `altimate providers logout altimate-base`, or `disabled_providers` in config. (#1361) + +### Changed + +- **Altimate Base replaces keyless Zen as the automatic fallback model** when nothing else is configured. See heads-up above. (#1361) + +### Fixed + +- **`altimate agent create` and `altimate review` no longer fail on a fresh install with no model configured.** Neither command registered Altimate Base before resolving a provider, unlike every other entrypoint (`run`, `tui`, `serve`, `acp`, `web`): `agent create` leaked a raw upstream error mentioning "OpenCode", a brand the user has never seen, with no remediation; `review`'s AI lane silently produced zero findings with no visible signal. Found in this release's review. +- **The TUI's startup auto-register wait no longer reads as a hang on a fresh install.** A "Connecting to Altimate Base…" status line appears if registration takes more than 300ms; the common already-registered path is unaffected. Found in this release's review. +- **The pinned-workspace routing section follows the pinned workspace, not the project's own link.** (#1357) + ## [0.12.2] - 2026-09-22 Promotes [0.12.2-beta.1] to `latest` — the six bug fixes below — plus four small fixes from this release's review and one workspace-pilot fix that landed alongside (#1353). No new features. The beta was published earlier the same day and did not soak before promotion; the review below is what stood in for that.