Skip to content

Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release #159

Closed

Description

@aarontrowbridge

Important

Problem — harmoniqs/opencode is 5 days / 77 commits / 3 releases behind anomalyco/opencode (v1.18.12 @ 7fe99387 from 2026-08-04 → v1.18.15 @ d7b115f + HEAD 0bff28de on 2026-08-09, 493 files +71454/-4423). Drift includes RTL/i18n, markdown-worker, and chronological message-ordering changes that touch fork-owned hotspots (message-timeline.tsx, markdown.tsx, language.tsx, titlebar.tsx). Cutting the next v*-amicode.* release without syncing bakes stale upstream bugfixes into the shipped binary and raises the next sync's conflict cost.
Approach — Land the one-off sync (v1.18.12 → v1.18.15+, 27 conflict files hand-merged) and, in the same slice, make future syncs automated: a scheduled notturno-style sentinel that fetches anomalyco/opencode dev, opens notturno/merge-upstream-YYYY-MM-DD, runs a parity-manifest + OPENCODE_CHANNEL=dev gate check, and posts a PR against local/amicode. Keep the fork — don't rebase away amicode surfaces.
Scope — in: one-off merge to 0bff28de (or v1.18.15), resolution of 27 conflict files, AMICODE-PATCHES.md update + tag v1.18.15-amicode.1, sentinel automation (schedule + manual dispatch), docs for sst → anomalyco remote rename. out: dropping the fork, switching default branch, or changing release channel semantics.
Assumptions — local/amicode stays default branch; anomalyco/opencode dev is the canonical upstream; OPENCODE_CHANNEL=dev remains the gate for amicode surfaces; amicode-release.yml tag-triggered release stays.

Acceptance Criteria

  • local/amicode merges anomalyco/opencode dev @ 0bff28de (or newer v1.18.15+ if HEAD moves) with all 27 conflict files resolved per patch-stack policy (adopt upstream bugfixes, keep fork branding/KaTeX/entity-rail/bug-dock, re-delete debug-bar.tsx).
  • AMICODE-PATCHES.md logs the sync (upstream SHA, file counts, decisions) and header points to anomalyco/opencode; package.json workspaces bump to 1.18.15.
  • env -u OPENCODE_CONFIG_CONTENT -u OPENCODE_SERVER_PASSWORD bun test + bun run typecheck pass (known flakes httpapi-v2-pty, project-picker-recent-search quarantined as before).
  • OPENCODE_CHANNEL=dev build succeeds and binary greps newLayoutDesigns:…()=>!0 (no prod-channel slip).
  • Tag v1.18.15-amicode.1 pushed → amicode-release.yml builds opencode-linux-x64 + opencode-darwin-arm64 artifacts with SHA256SUMS.
  • Scheduled automation exists (weekly + workflow_dispatch) that: re-adds upstream as anomalyco/opencode if missing, fetches dev, creates notturno/merge-upstream-YYYY-MM-DD, attempts merge, and opens/updates a PR against local/amicode with conflict summary + parity manifest. Zero-conflict merges auto-open; conflicted merges post the 27-file checklist.
  • No silent downgrade: if a better tier/template exists but needs entitlement, automation surfaces blocked_higher instead of downgrading.

Testing Decisions

Extend existing suites; create none. Primary signals: bun test (with env leak quarantine), bun run typecheck, script/build.ts --single --skip-install channel-gate grep, and smoke launch of built binary (entity rail / ask cards / vaults still mount). New automation gets a dry-run dispatch test (manual workflow_dispatch on a no-op day).

Key Decisions

  • Merge, not rebase — preserve local/amicode history and AMICODE-PATCHES.md merge-commit traceability (1f09d0dc5 / 50f8eb003 pattern).
  • Sentinel lives in harmoniqs/amico or as fork GH workflow — notturno hermetic clone previously failed on missing git identity (harmoniqs/amico#322, fixed #324); reuse that fix rather than inventing a new merger.
  • Upstream remote is anomalyco/opencode — sst/opencode is stale (rename 0cf029478); update re-add logic and AMICODE-PATCHES.md historical refs.
  • i18n parity via script/translate-app.ts — prefer upstream's generator over manual EN fallback fill; fallback to copy-EN for 40 new locales if generator unavailable.
  • keep fork, pull mothership improvements — automation is pull-only; fork-owned surfaces (AmicodeThemeBridge, draft-store, marked KaTeX macros) are never auto-resolved to theirs.

Constraints & Invariants

  • local/amicode remains origin/HEAD; never force-push it.
  • OPENCODE_CHANNEL=dev on every build that ships amicode surfaces (gotcha 2 in patch log).
  • debug-bar.tsx stays deleted on the fork; marked.tsx → marked-theme.tsx extraction must re-thread KaTeX macros.
  • bun.lock resolution is theirs + bun install (mechanical).
  • Automation never pushes to upstream; push URL for upstream stays disabled.

Prior Art

  • AMICODE-PATCHES.md (sync 2026-08-01: 78 conflicts, 2026-08-04: 0 conflicts, sentinel phantom-conflict note)
  • packages/opencode/script/build.ts + packages/script/src/index.ts (version/channel, OPENCODE_VERSION/OPENCODE_CHANNEL)
  • .github/workflows/publish.yml (upstream, guarded anomalyco/opencode only) + .github/workflows/amicode-release.yml (fork, tag-triggered, channel-gate grep)
  • harmoniqs/amico#322 / harmoniqs/amico#324 (notturno sentinel identity fix)
  • dry worktree: 7fe99387..0bff28de 77 commits / 493 files, cf4f06b41..d1ea581d4 101 fork commits, 27-file conflict set (110 overlap)

Notes

  • Upstream drift as of 2026-08-09: v1.18.13 RTL/plural, v1.18.14 retry/proxy, v1.18.15 chronological ordering + session-export. Dry merge shows high-risk files: app.tsx, entry.tsx, language.tsx, titlebar.tsx, session-context-tab.tsx, message-timeline.tsx, markdown.tsx, message-part.tsx, marked.tsx, desktop-menu.ts.
  • No v1.18.12-amicode.* tag was cut after last sync — this slice should tag v1.18.15-amicode.1 directly.
  • Project board: harmoniqs org board (Backlog → Ready → In Progress → In Review → Done); degrade silently if gh lacks read:project.

Activity

  1. jeonghun-jj-lee commented on Aug 10, 2026

    @jeonghun-jj-lee
    Contributor

    Resolution Plan — Upstream Sync v1.18.16

    @Rchari1 — here's the detailed resolution plan for the 27 conflict files. The design was worked out in a deep-dive session analyzing both sides of every conflict.

    TL;DR


    Current State

    Fork (local/amicode) Upstream (upstream/dev)
    Commits since merge-base 103 79
    Files touched 456 (+49877/−1655) 501 (+71500/−4432)
    Last sync v1.18.10-amicode.4 (2026-08-04) —
    Current version 1.18.10 1.18.16

    Merge-base: 7fe993879f (v1.18.12 tag)


    Phase 1 — Foundation (3 files, must land first)

    These change APIs/exports that Phase 2 files depend on.

    File Resolution
    packages/ui/src/context/marked.tsx Accept upstream's gutted 28-line re-export. Our KaTeX extensions → new marked-katex.ts beside marked-parser.ts (contains KATEX_MACROS, singleDollarInlineRegex, katexExtension, renderMathInText; registered as a marked extension in the worker's parser).
    packages/session-ui/src/components/markdown.tsx Accept upstream's worker-based parseMarkdown() architecture. Our file-ref linkification + external-link bridging stay as post-render DOM ops in decorate() (run after worker returns HTML). normalizeDisplayMath goes into the worker pipeline.
    packages/session-ui/package.json Accept upstream's 1.18.16 + dep removals (katex/marked-shiki/marked-katex-extension moved to worker package). Keep our markdown-file-refs export.

    ⚠️ Risk: KaTeX's renderToString must work inside a web worker (no DOM access). It does — KaTeX has a server-side mode. But test this early.


    Phase 2 — Feature Files (11 files, upstream-first)

    For each file: git checkout upstream/dev -- <file>, then re-apply fork additions via manual edit.

    File We adopt from upstream We re-apply from fork
    app.tsx MarkedProvider removal, UiI18nBridge with layoutLocale/plural, onNativeTranslations AmicodeThemeBridge, AmicodeFileRefBridge, AmicodeSplash, SplitProvider, WorkbenchProvider, lockThemeId="oc-2"
    entry.tsx loadInitialLocale() async wrapper around render Clipboard bridge, webview context menu, webZoom, adoptHiddenProject, adoptBugReportFlag, URL param cleanup
    message-timeline.tsx Boundary fix (findIndex not string <), session export, RTL (ms-auto, -end-1), language.plural() Entity rail, thinking line, last-prompt bubble, archive toggle, compact tooltip, scrollToEnd fix
    message-part.tsx partDefaultOpen extraction, RTL dir="ltr"/"auto", pending tool details, PacedMarkdown always-on, i18n'd labels Amicode tool cards, AmicoSkillChip, receipt-run collapsing, message-part-groups, shell row labels, copyTextToClipboard bridge
    titlebar.tsx RTL direction sync, i18n'd "Update" → language.t(...), webSearchProviderLabel i18n navigateTab, sidebar-left removal, "BETA" channel indicator, update button accent styling
    layout.tsx RTL (start-0/end-0/border-s/rounded-ss/inset-inline-start), logs.export desktop-only Vault panel, zoom shortcuts/commands, SplitFrame, WorkbenchBridge, debug-bar removal
    session.tsx Boundary findIndex fix, RTL -end-1, rolled uses slice(index) useAmicodeCommands, postRouteInfo, setSessionCopyProvider, work-column width logic
    session-context-tab.tsx Session export button, boundary fix, Button import Context-tree engine, vault panel, brain-ref integration (23 imports)
    language.tsx Full locale rewrite (40+ loaders, RTL Direction, pluralCategory, DesktopNativeLocale) Remove the EN-only hack entirely — it was always temporary. No fork additions to re-apply.
    desktop-menu.ts i18n key system (labelKey + DesktopNativeKey) Rebrand via EN translation values: "Amicode" in en.ts. Keep accelerators for macOS zoom.
    placeholder.ts i18n'd placeholder with t("ui.promptInput.placeholder.normal", {...}) EN translation value = "Ask Amico anything, / for commands, @ for context..."

    Key upstream bugfixes we're adopting:

    • Chronological message boundaries (messages no longer sort incorrectly)
    • findIndex instead of string < comparison for boundary/rollback
    • Pending tool details now always visible (not gated on !running())
    • PacedMarkdown always-on (smoother streaming)

    Phase 3 — Mechanical (13 files)

    File Resolution
    debug-bar.tsx Re-delete (resolve modify/delete as "ours")
    7 i18n files (ar, da, de, fr, ja, ko, uk) Union keys: take upstream's expanded translations, append fork's amicode-specific keys with EN fallback values
    settings-model.ts + .test.ts Take upstream's i18n keys; set EN values = "Install Amicode" / "Update Amicode"
    ui.ts (server/shared CSP) Auto-resolves — both sides made identical blob: addition
    httpapi-ui.test.ts Union: upstream test structure + fork's additional cases (fingerprinted asset auth, widget-frame auth)
    markdown-stream.test.ts Union: fork's normalizeDisplayMath tests + upstream's projection/streaming tests

    Non-conflicting fork patches (preserved as-is, no action)

    • request.ts — PROMPT_COMMUNICATING injection (rich chat output for amicode sessions)
    • communicating.txt — shared communication rules prompt
    • All packages/app/src/amicode/* — entity rail, connections, bug dock, thinking line
    • packages/opencode/src/server/amicode/* — Pasqal auth, connections routes
    • packages/app/src/pages/session/composer/bug-dock* — bug report dock
    • provider/transform.ts — already byte-identical between fork and upstream (patch Widget grid UX: drag to move, Add-widget picker, create-via-chat #24 worked)

    Automation Workflow

    .github/workflows/sync-upstream.yml — lands in this same PR.

    # Schedule: weekly Sunday 03:00 UTC + manual dispatch
    on:
      schedule:
        - cron: '0 3 * * 0'
      workflow_dispatch:

    Steps:

    1. Re-add upstream remote as anomalyco/opencode (push URL disabled)
    2. git fetch upstream dev
    3. Create branch notturno/merge-upstream-YYYY-MM-DD
    4. git merge upstream/dev --no-commit
    5. Zero conflicts → commit + open/update PR against local/amicode with parity manifest
    6. Conflicts → open/update draft PR with conflict manifest (per-file list + category)
    7. Never auto-resolve, never auto-merge — always needs human review

    Verification Gates

    All must pass before merge:

    1. env -u OPENCODE_CONFIG_CONTENT -u OPENCODE_SERVER_PASSWORD bun test (quarantine: httpapi-v2-pty, project-picker-recent-search)
    2. bun run typecheck
    3. OPENCODE_CHANNEL=dev build succeeds + grep 'newLayoutDesigns:…()=>!0' confirms channel gate
    4. Smoke: --version → 1.18.16, amicode surfaces render (entity rail, KaTeX math in chat)

    After merge

    1. Tag v1.18.16-amicode.1 → triggers amicode-release.yml
    2. Update AMICODE-PATCHES.md header (upstream SHA, file counts, per-file decisions)
    3. Bump opencode.lock.json ref in the amicode extension repo

    Approach considered and rejected

    • Phased PRs (one per phase) — intermediate broken-import states, 3× CI, slower
    • Rebase fork onto upstream — destroys history, breaks all amicode tags, contradicts "keep the fork" requirement
    • Partial auto-resolve in automation — too risky for architecture changes that need judgment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

hitlNeeds a human decision/review

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions