Skip to content

chore: sync swxtch with upstream anomalyco/opencode dev - #38

Merged
brentyates-swx merged 20 commits into
swxtchfrom
sync/upstream-dev
Sep 24, 2026
Merged

brentyates-swx merged 20 commits into
swxtchfrom
sync/upstream-dev

Conversation

@brentyates-swx

Copy link
Copy Markdown

Syncs swxtch with 17 upstream commits from anomalyco/opencode dev, through 2406400f0a.

Merged, not rebased

Both were tried in throwaway worktrees before deciding:

rebase merge
conflict stops on the first of our 16 commits, then again on #1 and #28 once
files conflicting same 3, spread across replays bun.lock, search.ts, npm.ts
published history rewrites 16 commits that 16 merged PRs reference preserved
push required force-push the default branch ordinary

The conflict surface is small either way — 5 files overlapped out of 108 on our side and 84 on theirs. Rebasing would buy a linear history at the cost of force-pushing swxtch and orphaning the commits every merged PR points at, which is a poor trade for a fork that will keep syncing.

Conflict resolutions

packages/core/src/filesystem/search.ts — took upstream's.
Upstream's anomalyco#50439 fixes the same import cycle our #2 did, independently. Theirs is slightly more conservative: it keeps import type { FileSystem } for the input types where ours also de-namespaced them to FindInput/GlobInput/GrepInput. Both break the cycle the same way, so taking theirs makes this file byte-identical to upstream and removes a permanent rebase conflict.

The other half of our #2 — the layer-node walk guard — is not upstream and is preserved. Its tests still pass against upstream's file.

packages/core/src/npm.ts — kept both import sets.
One hunk, purely additive: semver from our #28 (compare requested versions, not just dependency names) and createRequire/pathToFileURL from upstream's anomalyco#50413 (resolve an npm package entrypoint to a file under Node). Unrelated fixes; both survive.

bun.lock — regenerated from the merged manifests.

packages/web/package.json auto-merged (upstream bumped 1.18.31 → 1.18.32).

Verification

test/npm.test.ts auto-merged — both sides edited it — so a clean textual merge is not evidence the combined logic works. Ran it, and the tests either side of the resolutions, explicitly:

Going through a PR rather than pushing swxtch directly so CI runs the full unit and e2e suites against the merge before the default branch moves.

Note

#37 is open against the pre-sync swxtch. Once this lands, #37 wants rebasing onto the synced base rather than merging on a stale one.

🤖 Generated with Claude Code

https://claude.ai/code/session_017iG7xA9wRvNyFYZSXm8inU

MrMushrooooom and others added 18 commits September 21, 2026 08:40
Co-authored-by: frank <48937772+joe-ieta@users.noreply.github.com>
Co-authored-by: kernel-oops <300539221+kernel-oops@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Brings in 17 upstream commits (through 2406400). Merged rather than
rebased: a rebase stopped on the first of our 16 commits and would have
stopped again on #1 and #28, and it would have required force-pushing the
default branch, rewriting 16 commits that 16 merged PRs reference. The
conflict surface was small either way -- 5 files overlapped out of 108 ours
and 84 theirs.

Conflicts and how they were resolved:

- packages/core/src/filesystem/search.ts -- took UPSTREAM's. Their anomalyco#50439
  fixes the same import cycle our #2 did, independently and slightly more
  conservatively (it keeps `import type { FileSystem }` for the input types
  rather than de-namespacing them). Taking theirs keeps this file identical
  to upstream and removes a permanent source of rebase conflict. The other
  half of our #2, the layer-node walk guard, is not in upstream and is
  preserved; its tests still pass.

- packages/core/src/npm.ts -- kept both import sets. One hunk, purely
  additive: `semver` from our #28 (compare requested versions, not just
  dependency names) and `createRequire`/`pathToFileURL` from upstream's
  anomalyco#50413 (resolve npm package entrypoint to a file under Node). The two
  fixes are unrelated and both survive; test/npm.test.ts auto-merged and its
  19 tests pass.

- bun.lock -- regenerated from the merged manifests.

Verified: bun install --frozen-lockfile clean (satisfies the #33 gate),
typecheck 30/30, core npm tests 19/19, layer-node tests 19/19, filesystem
search tests 29/29.

Claude-Session: https://claude.ai/code/session_017iG7xA9wRvNyFYZSXm8inU
@brentyates-swx

Copy link
Copy Markdown
Author

Session stamp

  • Session ID: c87b9b84-25b3-4804-a2ae-7e52148ee41f
  • Working repo (session launch cwd): /home/byates/swx-opencode
  • Worktree for this branch: /home/byates/swx-sync
  • Upstream added locally as a fetch-only remote (git remote set-url --push upstream DISABLED_NO_PUSH_TO_UPSTREAM), so nothing here can write to anomalyco/opencode.

The gate added in #22 ran `oxlint --config .oxlintrc.gate.json .` over the
whole inherited tree. Its purpose is to catch bugs in what we write, but as
built it policed code we merely inherit -- so upstream's choices could block
every future sync. This sync is the first instance: 20 no-unreachable errors,
0 before the merge.

They are not bugs. Upstream retires legacy console endpoints by inserting a
throw at the top of the handler and leaving the old body in place:

    const user = Actor.assert("user")
    throw new Error("Go subscriptions have moved to the new Console")
    const { successUrl, cancelUrl, method } = input

The rule is right and the code is deliberate. Fixing it in this fork would
mean diverging on upstream-owned files and re-resolving it on every sync,
which is the cost this gate exists to avoid paying elsewhere.

Excludes packages/console/** -- which our 16 commits touch in 0 places -- and
artifacts/**, which is scratch output rather than shipped source. No coverage
of our own code is lost.

Verified the gate still guards: injecting `throw` followed by a return into
packages/opencode/src/cli/cmd/db-export-usage.ts is still reported as
no-unreachable, and the tree is 0 errors once reverted.

Claude-Session: https://claude.ai/code/session_017iG7xA9wRvNyFYZSXm8inU
@brentyates-swx

Copy link
Copy Markdown
Author

CI caught a design flaw in the #22 correctness gate

The unit job failed on Lint for correctness bugs with 20 no-unreachable errors — 0 before this merge, 20 after, so upstream introduced them. The fork-guard audit and the lockfile gate both passed.

They are not bugs. Upstream retires legacy console endpoints by inserting a throw at the top of the handler and leaving the old body in place:

const user = Actor.assert("user")
throw new Error("Go subscriptions have moved to the new Console")
const { successUrl, cancelUrl, method } = input   // unreachable, deliberately

All 20 are that pattern, in packages/console/core/src/billing.ts and packages/console/app/src/routes/workspace/[id]/go/index.tsx.

The flaw is in what #22 shipped

That gate runs oxlint --config .oxlintrc.gate.json . — the whole inherited tree. Its purpose is to catch bugs in code we author, but as written it polices code we merely inherit, so upstream's stylistic choices can block every future sync. This is the first time that bit, and it would have bitten every sync from here on.

Fixed by scoping rather than weakening: packages/console/** and artifacts/** are excluded. Our 16 commits touch packages/console in 0 places, so no coverage of our own work is lost, and no rule is demoted.

The alternative — patching upstream's files to satisfy our lint — would mean diverging on upstream-owned code and re-resolving it on every sync. That is exactly the cost this fork is trying not to pay.

Verified the gate still guards

Injected throw new Error(...) followed by a return into packages/opencode/src/cli/cmd/db-export-usage.ts, a file we own:

x eslint(no-unreachable): Unreachable code.

Still reported. Reverted, and the tree is 684 warnings and 0 errors. A narrowed guard that no longer guards would be worse than the failure it replaced, so that check mattered more than the green run.

packages/app/e2e is upstream's suite and this fork does not maintain it, so
its flake should not block our feature work. #26 documents the known failure:
the mock server has no v2 catalog routes, so every protocol:"v2" spec that
boots a project runs against a crashed provider bootstrap, and
review-line-comment fails all three attempts whenever timing lines up.

Fixing that properly meant five files of divergence inside packages/app/e2e
(see the closed #37). This fork already carries 62 CI/test-infrastructure
files of divergence against 46 product files, which is the friction that
makes every resync with upstream more expensive. One line in a workflow we
already own entirely is the cheaper trade.

The suite still runs and still uploads artifacts, so the signal is kept -- a
failure now annotates the run instead of blocking it.

Also updates the pinned test.yml::e2e digest in check-workflow-guards.ts,
since changing the job necessarily changes it. The audit is green at 3/3.

Claude-Session: https://claude.ai/code/session_017iG7xA9wRvNyFYZSXm8inU
@brentyates-swx
brentyates-swx merged commit 4b6e116 into swxtch Sep 24, 2026
3 checks passed
@brentyates-swx
brentyates-swx deleted the sync/upstream-dev branch September 24, 2026 10:13
brentyates-swx added a commit that referenced this pull request Sep 25, 2026
CHANGESET.md at the root records both halves of what swxtch is: every
upstream sync merged in, and every change the fork carries on top, each
with its status against upstream. Backfilled from history: the fork base,
the 2026-09-22 sync (#38) with its conflict resolutions, and the 20 fork
changes since.

It stays current mechanically rather than by memory:

- sync-upstream.ts now merges with --no-commit and adds the sync's entry
  (range, commit count, conflicted files) inside the sync merge itself.
  On conflicts the entry is staged with the in-progress merge, and the
  conventional message goes to MERGE_MSG so the resolver's commit uses it.
- script/changeset-check.ts fails a PR against swxtch with no
  `- **#<number>**` entry. Dependabot and generated sync-upstream-<timestamp>
  branches are exempt; a hand branch cannot opt out by prefix.

Both run in the unit job; its audit digest is updated.

Claude-Session: https://claude.ai/code/session_01BUBanXuSck62cxd3dpZ1oA
brentyates-swx added a commit that referenced this pull request Sep 26, 2026
CHANGESET.md at the root records both halves of what swxtch is: every
upstream sync merged in, and every change the fork carries on top, each
with its status against upstream. Backfilled from history: the fork base,
the 2026-09-22 sync (#38) with its conflict resolutions, and the 20 fork
changes since.

It stays current mechanically rather than by memory:

- sync-upstream.ts now merges with --no-commit and adds the sync's entry
  (range, commit count, conflicted files) inside the sync merge itself.
  On conflicts the entry is staged with the in-progress merge, and the
  conventional message goes to MERGE_MSG so the resolver's commit uses it.
- script/changeset-check.ts fails a PR against swxtch with no
  `- **#<number>**` entry. Dependabot and generated sync-upstream-<timestamp>
  branches are exempt; a hand branch cannot opt out by prefix.

Both run in the unit job; its audit digest is updated.

Claude-Session: https://claude.ai/code/session_01BUBanXuSck62cxd3dpZ1oA
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.

6 participants