Dashboard › cli › Session 0AjcBw58gIMn
0AjcBw58gIMnW3bosREAD-ONLY final correctness gate getsentry/cli PR #1569 exact base ec83887a16f780f32fba4b7d710bad262dba3a22 head a84012184c79c2566c7466aa6beda5f767199218. Immutable Git objects only; never worktree. Repo /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. MUST return substantive non-empty report. Inspect all changed source/tests. Verify all prior blockers including response-body parser cancellation/transport/malformed distinction; GHCR non-empty string token, OCI manifest and tag-list runtime shapes; body failure cache fallback; endpoint GitHub shape/exact tag; abort all stages; transport-only fallback; stable package SemVer; provenance/routing/channels; 404/affinity/pagination/redirect/SHA/delta/cache/offline/API. Findings-first exact file:line, classifications and regressions; residual gaps. Final exactly MERGE or DO-NOT-MERGE. Never modify; empty forbidden.
No files found
ec83887a16f780f32fba4b7d710bad262dba3a22 a84012184c79c2566c7466aa6beda5f767199218 ec83887a16f780f32fba4b7d710bad262dba3a22
M packages/cli/src/commands/cli/upgrade.ts M packages/cli/src/lib/binary.ts M packages/cli/src/lib/delta-upgrade.ts M packages/cli/src/lib/errors.ts M packages/cli/src/lib/ghcr.ts M packages/cli/src/lib/release-notes.ts M packages/cli/src/lib/upgrade.ts M packages/cli/src/lib/version-check.ts M packages/cli/test/commands/cli.test.ts M packages/cli/test/commands/cli/upgrade.test.ts M packages/cli/test/lib/binary.test.ts M packages/cli/test/lib/delta-upgrade.mocked.test.ts M packages/cli/test/lib/delta-upgrade.test.ts M packages/cli/test/lib/ghcr.test.ts M packages/cli/test/lib/release-notes.test.ts M packages/cli/test/lib/upgrade.test.ts M packages/cli/test/lib/version-check.test.ts packages/cli/src/commands/cli/upgrade.ts | 180 ++++-- packages/cli/src/lib/binary.ts | 191 +++++- packages/cli/src/lib/delta-upgrade.ts | 170 ++++-- packages/cli/src/lib/errors.ts | 8 + packages/cli/src/lib/ghcr.ts | ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089b7decf001EudSsQA3uKmJiq
<!-- lore:019e6874-7d6f-7fb9-8085-ac2f3472e22f -->
* **sensitive argv flags must never reach telemetry — redactArgv() in cli.ts**: \`SENSITIVE\_ARGV\_FLAGS = new Set(\['token', 'auth-token'])\` in \`src/cli.ts\`. \`redactArgv()\` replaces values of these flags with \`\[REDACTED]\` before any telemetry call. This is an absolute invariant — never pass raw \`process.argv\` to telemetry without running through \`redactArgv()\` first.
<!-- lore:019e610e-c11c-7b34-ae65-02bf3183ae62 -->
* **setup.ts bestEffort() wrapper: post-install steps must never crash setup**: \`src/commands/cli/setup.ts\` \`bestEffort(stepName, fn)\` wraps non-essential post-install steps (recording install info, shell completions, agent skills) in try/catch. On failure: calls \`warn(stepName, error)\` + \`captureException(error, { level: 'warning', tags: { 'setup.step': stepName } })\`. ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089b80d03001bSjGy0cefT0AoU
/** Changelog summary for the version range. Absent for offline or on fetch failure. */
changelog?: ChangelogSummary;
};
type UpgradeFlags = {
readonly check: boolean;
readonly force: boolean;
readonly offline: boolean;
readonly "no-agent-skills": boolean;
readonly method?: InstallationMethod;
/** Injected by buildCommand output wrapper — suppresses spinners */
readonly json?: boolean;
};
/**
* Resolve effective channel and version arg from the positional `version`
* parameter. "nightly" and "stable" are treated as channel selectors, not
* literal version strings.
*
* @returns `{ channel, versionArg }` where versionArg is undefined when the
* positional was a channel name (so we resolve to latest) or was omitted. ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089b832c10015N6sCUGi6b1ukF
161 "GitHub returned an invalid release pagination URL"
162 );
163 }
164 const url = new URL(match[1]);
165 const isSelectedSourcePath =
166 url.pathname === `/repos/${source.githubRepo}/releases`;
167 const isCanonicalRepositoryPath = CANONICAL_RELEASES_PATH_REGEX.test(
168 url.pathname
169 );
170 const page = url.searchParams.get("page");
171 if (
172 url.protocol !== "https:" ||
173 url.hostname !== "api.github.com" ||
174 !(isSelectedSourcePath || isCanonicalRepositoryPath) ||
175 page === null ||
176 !PAGE_NUMBER_REGEX.test(page)
177 ) {
178 throw new UpgradeError(
179 "network_error",
180 "GitHub returned an invalid release pagination URL"
181 );
182 }
183 const nextPage = new ...
^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 1) /** ^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 2) * Upgrade Module ^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 3) * ^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 4) * Detects how the CLI was installed and provides self-upgrade functionality. ^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 5) * Binary management helpers (download URLs, locking, replacement) live in ^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 6) * binary.ts and are shared with the setup --install flow. ^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 7) */ ^ff574c983 src/lib/upgrade.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 ...
1 /**
2 * Binary Management
3 *
4 * Shared utilities for installing, replacing, and managing the CLI binary.
5 * Used by both `setup --install` (fresh installs) and `upgrade` (self-updates).
6 */
7
8 import { spawnSync } from "node:child_process";
9 import {
10 existsSync,
11 mkdirSync,
12 readFileSync,
13 renameSync,
14 unlinkSync,
15 writeFileSync,
16 } from "node:fs";
17 import { chmod, copyFile, mkdir, realpath, unlink } from "node:fs/promises";
18 import { delimiter, dirname, isAbsolute, join, resolve, sep } from "node:path";
19 import { compare as semverCompare } from "semver";
20 import { getUserAgent } from "./constants.js";
21 import {
22 buildTlsErrorDetail,
23 customFetch,
24 isTlsCertError,
25 } from "./custom-ca.js";
26 import {
27 stringifyUnknown,
28 UpgradeError,
29 UpgradeTransportError,
30 } from ...
1 /**
2 * GHCR (GitHub Container Registry) Client
3 *
4 * Encapsulates the OCI download protocol for fetching nightly CLI binaries
5 * from ghcr.io/getsentry/cli. Nightly builds are pushed as OCI artifacts
6 * via ORAS with the version baked into the manifest annotation.
7 *
8 * Key design decisions:
9 * - Anonymous access: nightly package is public; no token needed beyond the
10 * standard ghcr.io anonymous token exchange.
11 * - Version discovery from manifest annotation: `annotations.version` in the
12 * OCI manifest holds the nightly version. Checking the latest version only
13 * requires a token exchange + manifest fetch (2 HTTP requests total).
14 * - Redirect quirk: ghcr.io blob downloads return 307 to Azure Blob Storage.
15 * Using `fetch` with `redirect: "follow"` would forward the Authorization
16 * header to Azure, which returns 404. ...
1 /** Delta upgrade discovery and application backed by binpatch. */
2
3 import { join } from "node:path";
4 // biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
5 import * as Sentry from "@sentry/node-core/light";
6 import {
7 applyPatchChainInMemory,
8 extractStableChain as binpatchExtractStableChain,
9 filterAndSortChainTags as binpatchFilterAndSortChainTags,
10 validateChainStep as binpatchValidateChainStep,
11 type DeltaTelemetry,
12 type DeltaUnavailableReason,
13 type ExtractStableChainOpts,
14 type GitHubRelease,
15 getPatchFromVersion,
16 getPatchTargetSha256,
17 ghcrSource,
18 githubReleaseSource,
19 type InstrumentHook,
20 MAX_NIGHTLY_CHAIN_DEPTH,
21 makeCache,
22 OciClient,
23 type OciManifest,
24 PATCH_TAG_PREFIX,
25 type PatchCache,
26 type PatchChain,
27 type ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089b92b33001sSqL4fxg0g5Lu3
629 * back to fetching with a higher per_page than the delta-upgrade path
630 * to cover larger version jumps.
631 *
632 * @param options - Version range, selected source, limit, and optional releases
633 * @returns Changelog summary, or null on failure
634 */
635 async function fetchStableChangelog(
636 options: FetchChangelogOptions & { source: UpgradeSource }
637 ): Promise<ChangelogSummary | null> {
638 const { fromVersion, toVersion, maxItems, prefetchedReleases, source } =
639 options;
640 const releases = prefetchedReleases
641 ? normalizeChangelogReleases(prefetchedReleases, source)
642 : await fetchReleasesForChangelog(source);
643 if (releases.length === 0) {
644 return null;
645 }
646 return buildChangelogSummaryForSource(releases, fromVersion, toVersion, {
647 ...
diff --git a/packages/cli/src/lib/release-notes.ts b/packages/cli/src/lib/release-notes.ts index ddf617535..903ca51f6 100644 --- a/packages/cli/src/lib/release-notes.ts +++ b/packages/cli/src/lib/release-notes.ts @@ -1,142 +1,149 @@ /** * Release Notes Parser & Aggregation * * Extracts user-facing changelog entries from GitHub Release bodies (stable) * or conventional commit messages (nightly). Uses `marked.lexer()` for * AST-based section extraction and produces structured data that can be * re-serialized as filtered markdown for rendering via `renderMarkdown()`. ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089b99d9c001F3b9nniCu1kSy8
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/ghcr.test.ts:487: controller.abort(reason);
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/ghcr.test.ts:492: downloadNightlyBlob("token", "sha256:abc", controller.signal)
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/ghcr.test.ts:497: test("preserves external cancellation during the redirect request", async () => {
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/ghcr.test.ts:498: const controller = new AbortController();
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/ghcr.test.ts:504: return Response.redirect("https://blob.storage.azure.com/file", 307);
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/ghcr.test.ts:506: ...
diff --git a/packages/cli/test/lib/binary.test.ts b/packages/cli/test/lib/binary.test.ts
index 6fcfb3b1a..29ead3828 100644
--- a/packages/cli/test/lib/binary.test.ts
+++ b/packages/cli/test/lib/binary.test.ts
@@ -1,93 +1,220 @@
/**
* Binary Management Tests
*
* Tests for shared binary helpers: install directory selection, paths,
* download URLs, locking, and binary installation.
*/
import {
chmodSync,
mkdirSync,
readFileSync,
realpathSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { access, readFile, writeFile } from "node:fs/promises";
import { join, sep } from "node:path";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import {
acquireLock,
compareVersions,
determineInstallDir,
fetchWithUpgradeError,
getBinaryDownloadUrl,
getBinaryFilename,
getBinaryPaths,
+ getGitHubReleaseByTagUrl,
getLegacyInstallDirs,
getPlatformBinaryName,
installBinary,
isDowngrade,
...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089b9f03b0017bCk3jHLZJdFnp
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 701) );
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 702)
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 703) const tags = await listTags("token", "patch-");
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 704) expect(tags).toHaveLength(40);
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 705) for (const tag of tags) {
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 706) expect(tag.startsWith("patch-")).toBe(true);
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 707) }
^ff574c983 test/lib/ghcr.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 708) ...
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/commands/cli/upgrade.test.ts:1338: test("rejects a pinned stable for Homebrew before network access", async () => {
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:87: mediaType: "application/vnd.oci.empty.v1+json",
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:158: test("returns null for empty digest", () => {
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:206: test("returns null for empty assets array", () => {
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:502: mediaType: "application/vnd.oci.empty.v1+json",
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:517: mediaType: "application/vnd.oci.empty.v1+json",
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:551: mediaType: ...
diff --git a/packages/cli/test/commands/cli.test.ts b/packages/cli/test/commands/cli.test.ts
index cd1514eb0..4eec11e9d 100644
--- a/packages/cli/test/commands/cli.test.ts
+++ b/packages/cli/test/commands/cli.test.ts
@@ -102,135 +102,129 @@ describe("feedbackCommand.func", () => {
});
});
// Test the upgrade command func
describe("upgradeCommand.func", () => {
let originalFetch: typeof globalThis.fetch;
let restoreStderr: (() => void) | undefined;
beforeEach(() => {
originalFetch = globalThis.fetch;
});
afterEach(() => {
restoreStderr?.();
restoreStderr = undefined;
globalThis.fetch = originalFetch;
});
// Note: We skip testing "unknown installation method" case because
// detectInstallationMethod() runs actual shell commands (npm list, etc.)
// which can be slow/flaky in CI. The unknown method handling is tested
// indirectly through the upgrade.ts unit tests in lib/upgrade.test.ts. ...
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:35: prefetchNightlyPatches,
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:36: prefetchStablePatches,
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:168: test("normalizes uppercase hex to lowercase", () => {
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:850: ["Toolkit", undefined, "cli@0.14.0-dev.1", "cli@0.14.0"],
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:851: ["legacy", LEGACY_UPGRADE_SOURCE, "0.14.0-dev.1", "0.14.0"],
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:868: test("uses the selected legacy GitHub repository", async () => {
a84012184c79c2566c7466aa6beda5f767199218:packages/cli/test/lib/delta-upgrade.test.ts:967: test("resolves prefixed Toolkit CLI releases and ignores other products", ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089baf1a20016LB4BHvLt7LC9r
cea7b2afba packages/cli/test/lib/upgrade.test.ts (Burak Yigit Kaya 2026-09-10 04:35:48 +0000 362) await expect(fetchLatestFromGitHub()).rejects.toThrow(
cea7b2afba packages/cli/test/lib/upgrade.test.ts (Burak Yigit Kaya 2026-09-10 04:35:48 +0000 363) "GitHub returned invalid release metadata"
cea7b2afba packages/cli/test/lib/upgrade.test.ts (Burak Yigit Kaya 2026-09-10 04:35:48 +0000 364) );
cea7b2afba packages/cli/test/lib/upgrade.test.ts (Burak Yigit Kaya 2026-09-10 04:35:48 +0000 365) });
cea7b2afba packages/cli/test/lib/upgrade.test.ts (Burak Yigit Kaya 2026-09-10 04:35:48 +0000 366)
cea7b2afba packages/cli/test/lib/upgrade.test.ts (Burak Yigit Kaya 2026-09-10 04:35:48 +0000 367) test("rejects an array from the legacy latest-release endpoint", async () => {
cea7b2afba packages/cli/test/lib/upgrade.test.ts (Burak Yigit Kaya ...
diff --git a/packages/cli/test/lib/delta-upgrade.mocked.test.ts b/packages/cli/test/lib/delta-upgrade.mocked.test.ts
index cd0a2fddc..f6186ab59 100644
--- a/packages/cli/test/lib/delta-upgrade.mocked.test.ts
+++ b/packages/cli/test/lib/delta-upgrade.mocked.test.ts
@@ -82,65 +82,65 @@ function tempFile(name: string): string {
return join(
tmpdir(),
`delta-iso-${Date.now()}-${Math.random().toString(36).slice(2)}-${name}`
);
}
// ============================================================================
// resolveStableDelta
// ============================================================================
describe("resolveStableDelta", () => {
test("resolves and applies a stable delta patch", async () => {
// Create a "current binary" to patch from
const oldBinaryPath = tempFile("old-binary.bin");
const destPath = tempFile("patched-binary.bin");
writeFileSync(oldBinaryPath, Buffer.from("old binary content for testing"));
// Set up ...
04867eaf9 feat(cli): add toolkit upgrade bridge 9c874298e fix(cli): address toolkit bridge review 723375b6c fix(cli): address upgrade review findings dbee7e9f2 test(cli): update toolkit upgrade fixtures 5ffa5cd0f fix(cli): harden upgrade source resolution c8c815bbe fix(cli): validate GitHub release pagination b6484e0fb fix(cli): validate resolved upgrade metadata c6030fbf4 fix(cli): bind nightly manifests to versions 2f9b5d0d2 fix(cli): isolate pinned upgrade resolution 4a1d5bad1 test(cli): update pinned version fixture e565b1afb fix(cli): validate stable upgrade metadata f1c10a6cb fix(cli): preserve pinned upgrade contracts dcf671999 test(cli): validate GitHub request origin e22f0eba6 fix(cli): preserve source and cancellation provenance 878459c49 fix(cli): fail closed on upgrade metadata 7d62ffa83 fix(cli): preserve paginated request cancellation cea7b2afb fix(cli): validate release response shapes a84012184 fix(cli): validate upgrade response ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_089bc1329001Kqor4ojM4KJh5R
existsSync,
openSync,
realpathSync,
statSync,
unlinkSync,
writeSync,
} from "node:fs";
import { writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, isAbsolute, join, sep } from "node:path";
import { setTimeout } from "node:timers/promises";
import { prerelease as semverPrerelease, valid as semverValid } from "semver";
import {
acquireLock,
cleanupOldBinary,
compareVersions,
determineInstallDir,
fetchWithUpgradeError,
getBinaryDownloadUrl,
getBinaryFilename,
getBinaryPaths,
getGitHubHeaders,
getGitHubLatestReleaseUrl,
getGitHubReleaseByTagUrl,
getGitHubRepositoryUrl,
getPlatformBinaryName,
type InstallationMethod,
isNightlyVersion,
KNOWN_CURL_DIRS,
PRIMARY_UPGRADE_SOURCE,
+ parseUpgradeJson,
releaseLock,
resolveUpgradeSource,
UPGRADE_SOURCES,
...
1 import { join } from "node:path";
2 import { makeCache, type PatchCache, type PatchChain } from "binpatch";
3 import { getConfigDir } from "./db/index.js";
4
5 export type { ChainMeta, PatchStepMeta } from "binpatch";
6 // biome-ignore lint/performance/noBarrelFile: preserve the existing cache API
7 export { chainFileName, patchFileName } from "binpatch";
8
9 function cache(): PatchCache {
10 return makeCache(join(getConfigDir(), "patch-cache"));
11 }
12
13 export function savePatchesToCache(
14 chain: Pick<PatchChain, "patches" | "expectedSha256">,
15 steps: { fromVersion: string; toVersion: string }[]
16 ): Promise<void> {
17 return cache().save(chain, steps);
18 }
19
20 export async function loadCachedChain(
21 currentVersion: string,
22 targetVersion: string
23 ): ReturnType<PatchCache["load"]> {
24 const result = await cache().load(currentVersion, ...
^ff574c983 test/commands/cli/upgrade.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 1070) const capturedUrls: string[] = [];
^ff574c983 test/commands/cli/upgrade.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 1071) const fakeContent = new Uint8Array([0x7f, 0x45, 0x4c, 0x46]);
5e19e7c6cd test/commands/cli/upgrade.test.ts (Burak Yigit Kaya 2026-05-21 18:51:57 +0100 1072) const gzipped = gzipSync(fakeContent);
^ff574c983 test/commands/cli/upgrade.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 1073)
^ff574c983 test/commands/cli/upgrade.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 1074) // GHCR flow: token exchange → manifest → blob redirect → blob download
^ff574c983 test/commands/cli/upgrade.test.ts (github-actions[bot] 2026-04-23 18:27:54 +0000 1075) mockFetch(async (url) => {
^ff574c983 test/commands/cli/upgrade.test.ts ...
926a66dead34fd22b367458e04d00e1aa692448b
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/delta-upgrade.ts:133-135,172-174,566-575
The selected UpgradeSource reaches the network strategy, but it never reaches getPatchCache(). Cache identity contains only fromVersion and toVersion, and every source shares the same patch-cache directory.
A legacy chain cached for 0.44.0 → 0.45.0 can therefore satisfy a later operation that selected Toolkit for those versions. resolveAndApply() can apply the legacy patches and expected SHA while reporting and routing the operation as Toolkit. This violates source affinity across delta, cache, prefetch, and offline paths.
Fix: namespace cache entries by a stable source key, or persist and verify source provenance in every cached chain. Preserve compatibility with existing unscoped entries only through an explicit legacy migration rule.
Regression: cache two distinct valid chains for the same version pair under Toolkit and legacy, then assert:
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:136-145
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts:238-243
After stripping cli@, discovery also strips a leading v. It therefore accepts cli@v1.2.3 and returns 1.2.3. Download construction then requests cli@1.2.3, not the release tag that discovery proved exists. ...
Date: September 10, 2026 * 🔴 (05:10) User stated package managers always need network access to fetch and install packages. * 🔴 (05:10) User provided the CLI example `sentry cli upgrade nightly`, described as “Switch to nightly channel and update”. * 🔴 (05:10) User-provided source shows `SPAWN_MAX_ATTEMPTS = 5`. * 🔴 (05:10) User-provided CLI help includes `sentry cli upgrade --no-agent-skills…
Date: September 10, 2026 * 🔴 (05:16) Git blame output for `packages/cli/test/commands/cli/upgrade.test.ts` lines 1070–1133 shows a GHCR download test originally introduced in commit `ff574c983` by `github-actions[bot]`; it creates `fakeContent = new Uint8Array([0x7f, 0x45, 0x4c, 0x46])`, compresses it with `gzipSync(fakeContent)`, captures requested URLs, and mocks the flow token exchange → mani…
Date: September 10, 2026 * 🔴 (05:13) User stated `detectInstallationMethod()` must always check Homebrew first because stored installation information may be stale—for example, a user may switch from a previously recorded curl install to Homebrew. The Homebrew `realpath` check is considered cheap and authoritative. * 🔴 (05:13) User-provided installation-detection priority is: 1. Homebrew via a …
* 🔴 (05:13) User-provided diff changed `packages/cli/test/lib/delta-upgrade.mocked.test.ts` stable delta fixtures from legacy `getsentry/cli` releases to Toolkit releases: patch URL changed from `https://github.com/getsentry/cli/releases/download/0.14.0/${BINARY_NAME}.patch` to `https://github.com/getsentry/toolkit/releases/download/cli@0.14.0/${BINARY_NAME}.patch`; release tags changed from `"0…
* 🔴 (05:12) User-provided tool output was truncated; the full output was saved to `/home/byk/.local/share/opencode/tool-output/tool_089baf1a20016LB4BHvLt7LC9r`. * 🔴 (05:12) User-provided blame output showed commit `cea7b2afba` by Burak Yigit Kaya adding `packages/cli/test/lib/upgrade.test.ts` coverage where `fetchLatestFromGitHub()` rejects with `"GitHub returned invalid release metadata"`. * �…
* 🔴 (05:11) User-provided diff for `packages/cli/test/commands/cli.test.ts` changed `upgradeCommand.func` coverage to mock GitHub releases as `[{ tag_name: "cli@1.0.0" }]`, invoke `func.call(context, { check: true, method: "curl", json: true })`, and expect `data.action` to be `"checked"` with `data.method` equal to `"curl"`; this replaced `{ tag_name: "v0.0.0-dev" }`, `check: false`, and expect…
* 🔴 (05:11) User-provided repository output at commit `a84012184c79c2566c7466aa6beda5f767199218` showed `packages/cli/test/commands/cli/upgrade.test.ts` coverage named `rejects a pinned stable for Homebrew before network access`. * 🔴 (05:11) User-provided repository output showed `packages/cli/test/lib/delta-upgrade.test.ts` fixtures using OCI media type `application/vnd.oci.empty.v1+json`. * �…
* 🔴 (05:11) User established the install-migration rule that `~/.local/bin` and `~/bin` are valid current installation targets but never migration sources; this convention is tested in `packages/cli/test/lib/binary.test.ts` via `getLegacyInstallDirs("/home/user")`. * 🔴 (05:11) User-provided repository output showed additional `packages/cli/test/lib/binary.test.ts` coverage for `samePath()`, inc…
Date: September 10, 2026 * 🟡 (05:10) `packages/cli/test/lib/binary.test.ts` expanded from 93 to 220 lines and added coverage for `getGitHubReleaseByTagUrl`, `parseUpgradeJson`, `resolveUpgradeSource`, `UPGRADE_SOURCES`, and `UpgradeSourceNotFoundError`. * 🟡 (05:10) `getBinaryDownloadUrl("1.0.0")` expectations changed from legacy `https://github.com/getsentry/cli/releases/download/.../1.0.0/` se…
* 🟡 (05:10) Tool output referenced baseline commit `a84012184c79c2566c7466aa6beda5f767199218` and showed source-aware upgrade test coverage across `packages/cli/test/lib/ghcr.test.ts`, `packages/cli/test/lib/release-notes.test.ts`, `packages/cli/test/lib/upgrade.test.ts`, and `packages/cli/test/lib/version-check.test.ts`. * 🟡 (05:10) `packages/cli/test/lib/ghcr.test.ts` includes tag-listing cov…
* 🟡 (05:10) Tool output showed `packages/cli/src/lib/release-notes.ts` was changed to support source-aware release-note retrieval and changelog construction. * 🟡 (05:10) Tool output showed `packages/cli/src/lib/release-notes.ts` replaced the `GITHUB_RELEASES_URL` import with `getGitHubReleasesUrl`, `PRIMARY_UPGRADE_SOURCE`, and `UpgradeSource` from `./binary.js`. * 🟡 (05:10) Tool output showed…
* 🟡 (05:10) Tool output showed a module described as `/** Delta upgrade discovery and application backed by binpatch. */`. * 🟡 (05:10) Tool output showed the delta-upgrade module imports `join` from `node:path` and namespace-imports `@sentry/node-core/light` as `Sentry`; the namespace import carries `biome-ignore lint/performance/noNamespaceImport` because the Sentry SDK recommends that import …
* 🔴 (05:09) User-provided `packages/cli/src/lib/binary.ts` states that trailing path separators are stripped, but never from a bare root like `/`. * 🔴 (05:09) User-provided `packages/cli/src/lib/binary.ts` states that a caller receives the successful probe response so it never repeats the successful probe/request. * 🟡 (05:09) Tool output showed `packages/cli/src/lib/binary.ts` is the shared bi…
Date: September 10, 2026 * 🟡 (05:09) Tool output showed `packages/cli/src/lib/upgrade.ts` detects how the CLI was installed and provides self-upgrade functionality; shared binary-management helpers for download URLs, locking, and replacement live in `binary.ts` and are also used by the `setup --install` flow. * 🟡 (05:09) Tool output showed `packages/cli/src/lib/upgrade.ts` imports binary helper…
🔴 (05:09) User stated installation-method detection must always check for Homebrew first because stored install information may be stale. 🔴 (05:09) User stated the installed-file verification routine throws `UpgradeError` when the file never becomes visible or stays empty. 🟡 (05:09) Tool output showed GitHub release pagination validation accepts only HTTPS URLs on `api.github.com` whose path i…
🔴 (05:09) User stated package managers always need network to fetch and install packages; `--offline` upgrades are supported only for curl-installed binaries, and `validateMethod()` throws `UpgradeError("unsupported_operation", "Offline upgrade is only supported for curl-installed binaries.")` when `offline` is true and `method !== "curl"`. 🔴 (05:09) User stated nightly builds are GitHub-only, …
🔴 (05:08) User required the final correctness gate for getsentry/cli PR #1569 to be strictly READ-ONLY: immutable Git objects only, never the worktree, and never modify anything. 🔴 (05:08) User required a substantive, non-empty report; “empty forbidden.” 🔴 (05:08) User stated: “Never modify; empty forbidden.” 🔴 (05:08) User stated repositories/artifacts must be “always fetchable from the base…