Skip to content

Commit 13ba57f

Browse files
anandgupta42claude
andcommitted
chore(upstream): three-layer regression backstop for bridge merges
Root cause of the v1.4.0 audit findings (16 regressions across 30 files): many altimate customizations were never wrapped in `altimate_change` markers, so the bridge merge tool — working as designed — overwrote them with upstream's version. The marker discipline relies on humans remembering to wrap; the tooling didn't catch the gap. This commit closes that gap with three layers of defense: 1. Broaden the branding scan `script/upstream/analyze.ts` LEAK_PATTERNS now catches bare `opencode` strings in user-visible contexts: yargs `describe` text, console/UI output, MCP `clientInfo.name`, User-Agent headers, OIDC audiences, GitHub workflow YAML, infrastructure identifiers, spawn binary names, etc. Reproductive testing: scanning the pre-fix v1.4.0 state, the broadened patterns flagged 13 of the 16 regressions. Runs in CI on every PR (`Branding leak audit` step). Also: scanner skips lines inside `altimate_change` blocks (so intentional explanatory comments don't false-positive). 2. requireMarkers allowlist for behavior-patched files New `MergeConfig.requireMarkers` field in `script/upstream/utils/config.ts` lists 38 files known to hold altimate behavioral patches (UA strings, retry loops, OAuth skew buffers, hour-aligned cleanup tasks, custom theme tweaks, etc.). - `analyze.ts --require-markers --strict` verifies each file in the list has at least one altimate_change block. Runs in CI. - `bridge-merge.ts` treats them as keepOurs: never overlays upstream content on these files, even if they have no markers. Hard-aborts the merge if any file in the list is missing markers (would mean we lost the patches before the merge ran). Three new categories now in the merge plan: requireMarkers, requireMarkersMissing, plus a "must merge upstream changes manually" line in the report. 3. Retroactive marker sweep Added altimate_change markers to 14 previously-unmarked files that held altimate brand strings or behavioral patches: - `cli/cmd/upgrade.ts`, `tui/attach.ts`, `tui/thread.ts`, `tui/component/upgrade-indicator.tsx`, `cli/cmd/import.ts`, `cli/cmd/plug.ts`, `cli/network.ts`, `cli/cmd/pr.ts` (was a pre-existing regression — `spawn("opencode")` would fail since the binary is `altimate-code`) - `mcp/config.ts`, `mcp/oauth-provider.ts`, `config/migrate-tui-config.ts`, `config/tui-migrate.ts`, `plugin/copilot.ts`, `plugin/github-copilot/copilot.ts` Plus `provider/provider.ts` + `session/llm.ts` UA brand fixes that the broadened scan caught. Catppuccin theme JSON files (`textMuted` brightness tweak) added to `keepOurs` glob — JSON can't carry inline markers, so the bridge merge tool just preserves them wholesale. Memory tools (`packages/opencode/src/memory/**`) added to `keepOurs` — they're a net-new altimate feature family (altimate_memory_* tools) not in upstream. Internal scripts (`script/beta.ts`, `script/raw-changelog.ts`, `packages/script/src/index.ts`) — wrapped legitimate references to upstream's bot identity / model IDs in `altimate_change` markers and flipped the model spawn command from `opencode run` to `altimate-code run` (the scripts were broken before — would call a binary that isn't installed). CI integration `.github/workflows/ci.yml` now runs both `--branding` and `--require-markers --strict` on every PR. Existing `--markers` check remains. Verified - bun turbo typecheck — 5/5 packages clean - bun test test/upstream test/util/error.test.ts test/permission test/server — 553 pass / 0 fail - bun run script/upstream/analyze.ts --branding — 0 leaks - bun run script/upstream/analyze.ts --require-markers --strict — 38/38 files have markers - 0 unmarked altimate-touched files outside the safety nets (verified by tree sweep) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3a6b59b commit 13ba57f

24 files changed

Lines changed: 425 additions & 35 deletions

File tree

.github/workflows/ci.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,17 @@ jobs:
449449
else
450450
bun run script/upstream/analyze.ts --markers --base ${{ github.event.pull_request.base.ref }} --strict
451451
fi
452+
453+
- name: Branding leak audit (broadened scan, see analyze.ts LEAK_PATTERNS)
454+
# Catches user-visible bare "opencode" strings that markers alone miss —
455+
# yargs describe text, console output, MCP client identity, workflow YAML,
456+
# User-Agent strings, OIDC audience, infrastructure identifiers.
457+
# Caught 13 of 16 v1.4.0 bridge merge regressions in retrospective testing.
458+
run: bun run script/upstream/analyze.ts --branding
459+
460+
- name: Require-markers regression backstop
461+
# Verifies every file in config.requireMarkers (38 files known to hold
462+
# altimate behavioral patches) has at least one altimate_change block.
463+
# If any patches were silently lost (refactor, accidental delete, bridge
464+
# merge oversight), CI fails here BEFORE the bug reaches production.
465+
run: bun run script/upstream/analyze.ts --require-markers --strict

packages/opencode/src/cli/cmd/import.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ export type ShareData =
1919
| { type: "session_diff"; data: unknown }
2020
| { type: "model"; data: unknown }
2121

22+
// altimate_change start — share URLs use altimate.ai (was opencode.ai)
2223
/** Extract share ID from a share URL like https://altimate.ai/share/abc123 */
24+
// altimate_change end
2325
export function parseShareUrl(url: string): string | null {
2426
const match = url.match(/^https?:\/\/[^/]+\/share\/([a-zA-Z0-9_-]+)$/)
2527
return match ? match[1] : null

packages/opencode/src/cli/cmd/plug.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export type PlugDeps = {
2828
readText: (file: string) => Promise<string>
2929
write: (file: string, text: string) => Promise<void>
3030
exists: (file: string) => Promise<boolean>
31+
// altimate_change start — accept altimate-code in addition to opencode/tui
3132
files: (dir: string, name: "altimate-code" | "opencode" | "tui") => string[]
33+
// altimate_change end
3234
global: string
3335
}
3436

packages/opencode/src/cli/cmd/pr.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { Process } from "@/util/process"
66

77
export const PrCommand = cmd({
88
command: "pr <number>",
9-
describe: "fetch and checkout a GitHub PR branch, then run opencode",
9+
// altimate_change start — upstream_fix: branding regression in describe
10+
describe: "fetch and checkout a GitHub PR branch, then run altimate-code",
11+
// altimate_change end
1012
builder: (yargs) =>
1113
yargs.positional("number", {
1214
type: "number",
@@ -87,10 +89,12 @@ export const PrCommand = cmd({
8789
const sessionMatch = prInfo.body.match(/https:\/\/opncd\.ai\/s\/([a-zA-Z0-9_-]+)/)
8890
if (sessionMatch) {
8991
const sessionUrl = sessionMatch[0]
90-
UI.println(`Found opencode session: ${sessionUrl}`)
92+
// altimate_change start — upstream_fix: branding + spawn the right binary
93+
UI.println(`Found altimate-code session: ${sessionUrl}`)
9194
UI.println(`Importing session...`)
9295

93-
const importResult = await Process.text(["opencode", "import", sessionUrl], {
96+
const importResult = await Process.text(["altimate-code", "import", sessionUrl], {
97+
// altimate_change end
9498
nothrow: true,
9599
})
96100
if (importResult.code === 0) {
@@ -109,18 +113,22 @@ export const PrCommand = cmd({
109113

110114
UI.println(`Successfully checked out PR #${prNumber} as branch '${localBranchName}'`)
111115
UI.println()
112-
UI.println("Starting opencode...")
116+
// altimate_change start — upstream_fix: branding + spawn the right binary
117+
// (we ship `altimate-code`, not `opencode`). Original spawn would fail with
118+
// ENOENT for users who don't have upstream's CLI installed alongside.
119+
UI.println("Starting altimate-code...")
113120
UI.println()
114121

115122
const opencodeArgs = sessionId ? ["-s", sessionId] : []
116-
const opencodeProcess = Process.spawn(["opencode", ...opencodeArgs], {
123+
const opencodeProcess = Process.spawn(["altimate-code", ...opencodeArgs], {
117124
stdin: "inherit",
118125
stdout: "inherit",
119126
stderr: "inherit",
120127
cwd: process.cwd(),
121128
})
122129
const code = await opencodeProcess.exited
123-
if (code !== 0) throw new Error(`opencode exited with code ${code}`)
130+
if (code !== 0) throw new Error(`altimate-code exited with code ${code}`)
131+
// altimate_change end
124132
},
125133
})
126134
},

packages/opencode/src/cli/cmd/tui/attach.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { existsSync } from "fs"
88

99
export const AttachCommand = cmd({
1010
command: "attach <url>",
11+
// altimate_change start — branding (was "opencode" in upstream)
1112
describe: "attach to a running altimate-code server",
13+
// altimate_change end
1214
builder: (yargs) =>
1315
yargs
1416
.positional("url", {
@@ -63,7 +65,11 @@ export const AttachCommand = cmd({
6365
const headers = (() => {
6466
const password = args.password ?? process.env.OPENCODE_SERVER_PASSWORD
6567
if (!password) return undefined
68+
// altimate_change start — Basic-auth username must match the server's
69+
// expected username ("altimate"). Upstream's was "opencode"; restoring
70+
// it would break the attach handshake against altimate-code servers.
6671
const auth = `Basic ${Buffer.from(`altimate:${password}`).toString("base64")}`
72+
// altimate_change end
6773
return { Authorization: auth }
6874
})()
6975
const config = await Instance.provide({

packages/opencode/src/cli/cmd/tui/component/upgrade-indicator.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ export function UpgradeIndicator(props: { fallback?: JSX.Element }) {
2121
<Show when={!isCompact()}>
2222
<text fg={theme.textMuted}>update available ·</text>
2323
</Show>
24+
{/* altimate_change start — upgrade hint shows the altimate-code CLI command */}
2425
<text fg={theme.textMuted}>altimate upgrade</text>
26+
{/* altimate_change end */}
2527
</box>
2628
)}
2729
</Show>

packages/opencode/src/cli/cmd/tui/thread.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,17 @@ async function input(value?: string) {
6464

6565
export const TuiThreadCommand = cmd({
6666
command: "$0 [project]",
67+
// altimate_change start — branding (describe text was "opencode" upstream)
6768
describe: "start altimate-code tui",
69+
// altimate_change end
6870
builder: (yargs) =>
6971
withNetworkOptions(yargs)
72+
// altimate_change start — branding (positional describe was "opencode" upstream)
7073
.positional("project", {
7174
type: "string",
7275
describe: "path to start altimate-code in",
7376
})
77+
// altimate_change end
7478
.option("model", {
7579
type: "string",
7680
alias: ["m"],
@@ -189,7 +193,9 @@ export const TuiThreadCommand = cmd({
189193
events: undefined,
190194
}
191195
: {
196+
// altimate_change start — internal worker URL (was opencode.internal)
192197
url: "http://altimate-code.internal",
198+
// altimate_change end
193199
fetch: createWorkerFetch(client),
194200
events: createEventSource(client),
195201
}

packages/opencode/src/cli/cmd/upgrade.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { extractChangelog } from "../changelog"
66

77
export const UpgradeCommand = {
88
command: "upgrade [target]",
9+
// altimate_change start — branding (was "opencode" in upstream)
910
describe: "upgrade altimate to the latest or a specific version",
11+
// altimate_change end
1012
builder: (yargs: Argv) => {
1113
return yargs
1214
.positional("target", {
@@ -28,7 +30,9 @@ export const UpgradeCommand = {
2830
const detectedMethod = await Installation.method()
2931
const method = (args.method as Installation.Method) ?? detectedMethod
3032
if (method === "unknown") {
33+
// altimate_change start — branding
3134
prompts.log.error(`altimate is installed to ${process.execPath} and may be managed by a package manager`)
35+
// altimate_change end
3236
const install = await prompts.select({
3337
message: "Install anyways?",
3438
options: [
@@ -46,7 +50,9 @@ export const UpgradeCommand = {
4650
const target = args.target ? args.target.replace(/^v/, "") : await Installation.latest()
4751

4852
if (Installation.VERSION === target) {
53+
// altimate_change start — branding
4954
prompts.log.warn(`altimate upgrade skipped: ${target} is already installed`)
55+
// altimate_change end
5056
prompts.outro("Done")
5157
return
5258
}

packages/opencode/src/cli/network.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ const options = {
1717
describe: "enable mDNS service discovery (defaults hostname to 0.0.0.0)",
1818
default: false,
1919
},
20+
// altimate_change start — upstream_fix: mDNS service identifier shipped to the
21+
// local network. Brand to altimate-code so users see "altimate-code.local"
22+
// instead of "opencode.local" in Bonjour/Avahi browsers.
2023
"mdns-domain": {
2124
type: "string" as const,
22-
describe: "custom domain name for mDNS service (default: opencode.local)",
23-
default: "opencode.local",
25+
describe: "custom domain name for mDNS service (default: altimate-code.local)",
26+
default: "altimate-code.local",
2427
},
28+
// altimate_change end
2529
cors: {
2630
type: "string" as const,
2731
array: true,

packages/opencode/src/config/migrate-tui-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import { Global } from "@/global"
1212

1313
const log = Log.create({ service: "tui.migrate" })
1414

15+
// altimate_change start — schema URL points to altimate.ai (was opencode.ai)
1516
const TUI_SCHEMA_URL = "https://altimate.ai/tui.json"
17+
// altimate_change end
1618

1719
const LegacyTheme = TuiInfo.shape.theme.optional()
1820
const LegacyRecord = z.record(z.string(), z.unknown()).optional()

0 commit comments

Comments
 (0)