PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

Description

@aarontrowbridge

PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

Important

Problem

Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

Approach

Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

Approaches Considered

  • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
  • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
  • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

Scope

In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

Assumptions / Open Qs

Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


User Stories

  • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
  • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
  • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
  • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

Acceptance Criteria

  1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
  2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
  3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
  4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
  5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
  6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
  7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

Key Decisions

  • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
  • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
  • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
  • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
  • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

Modules & Interfaces

  • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
  • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
  • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
  • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
  • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

Testing Decisions

Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

Risks

  • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
  • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
  • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
  • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

Constraints & Invariants

  • Last-known-good is never deleted; adoption only via the full adopt gate.
  • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
  • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
  • The fork's default branch is never force-pushed; the repo is archived, not deleted.
  • All work on Harmoniqs repos goes through issue + PR (development gate).
Prior Art / Patterns
  • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
  • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
  • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
  • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
  • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

Source

Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

Notes

Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
    Skip to content

    PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

    Description

    @aarontrowbridge

    PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

    Important

    Problem

    Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

    Approach

    Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

    Approaches Considered

    • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
    • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
    • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

    Scope

    In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
    Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

    Assumptions / Open Qs

    Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


    User Stories

    • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
    • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
    • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
    • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

    Acceptance Criteria

    1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
    2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
    3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
    4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
    5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
    6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
    7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

    Key Decisions

    • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
    • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
    • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
    • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
    • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

    Modules & Interfaces

    • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
    • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
    • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
    • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
    • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

    Testing Decisions

    Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

    Risks

    • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
    • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
    • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
    • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

    Constraints & Invariants

    • Last-known-good is never deleted; adoption only via the full adopt gate.
    • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
    • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
    • The fork's default branch is never force-pushed; the repo is archived, not deleted.
    • All work on Harmoniqs repos goes through issue + PR (development gate).
    Prior Art / Patterns
    • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
    • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
    • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
    • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
    • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

    Source

    Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

    Notes

    Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

    Activity

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
      Skip to content

      PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

      Description

      @aarontrowbridge

      PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

      Important

      Problem

      Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

      Approach

      Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

      Approaches Considered

      • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
      • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
      • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

      Scope

      In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
      Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

      Assumptions / Open Qs

      Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


      User Stories

      • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
      • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
      • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
      • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

      Acceptance Criteria

      1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
      2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
      3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
      4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
      5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
      6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
      7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

      Key Decisions

      • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
      • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
      • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
      • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
      • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

      Modules & Interfaces

      • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
      • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
      • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
      • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
      • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

      Testing Decisions

      Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

      Risks

      • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
      • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
      • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
      • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

      Constraints & Invariants

      • Last-known-good is never deleted; adoption only via the full adopt gate.
      • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
      • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
      • The fork's default branch is never force-pushed; the repo is archived, not deleted.
      • All work on Harmoniqs repos goes through issue + PR (development gate).
      Prior Art / Patterns
      • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
      • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
      • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
      • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
      • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

      Source

      Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

      Notes

      Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Labels

      No labels
      No labels

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
        Skip to content

        PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

        Description

        @aarontrowbridge

        PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

        Important

        Problem

        Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

        Approach

        Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

        Approaches Considered

        • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
        • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
        • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

        Scope

        In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
        Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

        Assumptions / Open Qs

        Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


        User Stories

        • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
        • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
        • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
        • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

        Acceptance Criteria

        1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
        2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
        3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
        4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
        5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
        6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
        7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

        Key Decisions

        • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
        • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
        • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
        • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
        • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

        Modules & Interfaces

        • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
        • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
        • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
        • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
        • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

        Testing Decisions

        Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

        Risks

        • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
        • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
        • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
        • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

        Constraints & Invariants

        • Last-known-good is never deleted; adoption only via the full adopt gate.
        • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
        • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
        • The fork's default branch is never force-pushed; the repo is archived, not deleted.
        • All work on Harmoniqs repos goes through issue + PR (development gate).
        Prior Art / Patterns
        • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
        • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
        • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
        • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
        • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

        Source

        Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

        Notes

        Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

        Activity

        Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

        Metadata

        Metadata

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
          Skip to content

          PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

          Description

          @aarontrowbridge

          PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

          Important

          Problem

          Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

          Approach

          Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

          Approaches Considered

          • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
          • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
          • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

          Scope

          In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
          Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

          Assumptions / Open Qs

          Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


          User Stories

          • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
          • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
          • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
          • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

          Acceptance Criteria

          1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
          2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
          3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
          4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
          5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
          6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
          7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

          Key Decisions

          • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
          • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
          • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
          • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
          • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

          Modules & Interfaces

          • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
          • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
          • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
          • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
          • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

          Testing Decisions

          Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

          Risks

          • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
          • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
          • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
          • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

          Constraints & Invariants

          • Last-known-good is never deleted; adoption only via the full adopt gate.
          • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
          • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
          • The fork's default branch is never force-pushed; the repo is archived, not deleted.
          • All work on Harmoniqs repos goes through issue + PR (development gate).
          Prior Art / Patterns
          • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
          • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
          • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
          • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
          • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

          Source

          Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

          Notes

          Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Labels

          No labels
          No labels

          Type

          No type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
            Skip to content

            PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

            Description

            @aarontrowbridge

            PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

            Important

            Problem

            Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

            Approach

            Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

            Approaches Considered

            • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
            • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
            • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

            Scope

            In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
            Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

            Assumptions / Open Qs

            Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


            User Stories

            • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
            • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
            • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
            • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

            Acceptance Criteria

            1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
            2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
            3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
            4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
            5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
            6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
            7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

            Key Decisions

            • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
            • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
            • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
            • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
            • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

            Modules & Interfaces

            • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
            • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
            • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
            • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
            • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

            Testing Decisions

            Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

            Risks

            • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
            • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
            • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
            • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

            Constraints & Invariants

            • Last-known-good is never deleted; adoption only via the full adopt gate.
            • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
            • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
            • The fork's default branch is never force-pushed; the repo is archived, not deleted.
            • All work on Harmoniqs repos goes through issue + PR (development gate).
            Prior Art / Patterns
            • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
            • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
            • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
            • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
            • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

            Source

            Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

            Notes

            Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

            Activity

            Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

            Metadata

            Metadata

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

              Description

              @aarontrowbridge

              PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

              Important

              Problem

              Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

              Approach

              Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

              Approaches Considered

              • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
              • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
              • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

              Scope

              In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
              Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

              Assumptions / Open Qs

              Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


              User Stories

              • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
              • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
              • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
              • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

              Acceptance Criteria

              1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
              2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
              3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
              4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
              5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
              6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
              7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

              Key Decisions

              • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
              • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
              • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
              • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
              • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

              Modules & Interfaces

              • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
              • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
              • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
              • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
              • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

              Testing Decisions

              Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

              Risks

              • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
              • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
              • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
              • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

              Constraints & Invariants

              • Last-known-good is never deleted; adoption only via the full adopt gate.
              • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
              • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
              • The fork's default branch is never force-pushed; the repo is archived, not deleted.
              • All work on Harmoniqs repos goes through issue + PR (development gate).
              Prior Art / Patterns
              • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
              • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
              • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
              • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
              • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

              Source

              Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

              Notes

              Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Labels

              No labels
              No labels

              Type

              No type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                Skip to content

                PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines #451

                Description

                @aarontrowbridge

                PRD: Ship canonical opencode with Amicode — retire the fork-built binary from user machines

                Important

                Problem

                Amicode vendors one fork-built binary (harmoniqs/opencode, pinned v1.18.10-amicode.11) that plays two roles: the opencode CLI users get in the Amicode terminal, and the chat server carrying the entire Amicode product surface (18 server route modules + the customized app UI the deck iframes). It updates only when a new VSIX ships, so users sit marketplace-cadence behind upstream anomalyco/opencode (3 releases / 5 days behind at harmoniqs/opencode#159) and upstream bugfixes reach them late.

                Approach

                Migrate the Amicode surface onto layers that run on a stock canonical binary — the standard plugin loader (the amicode tool pack), config injection (persona/skills/permissions), an extension-host HTTP service (the 18 amicode routes + widget runtime), and an extracted app bundle composing upstream app components — then cut over in ONE release with zero fork-built bytes shipped, plus a runtime auto-updater that adopts new canonical releases after a smoke gate. Key reason: this solves the binary-currency problem outright and converts the maintenance coupling from weekly monorepo merges into a CI-pinned overlay against canonical's public client API.

                Approaches Considered

                • Bridge then flip (updater tracks fork releases now, flip later) — rejected: ships fork bytes indefinitely; the researcher chose one push.
                • Flip immediately, migrate later — rejected: the 18 routes and the iframed app UI die on a stock binary; migration must complete first.
                • Fork forever, tightly synced (weekly automated upstream merges) — rejected: users always one merge-hop behind; a bad merge stalls the whole fleet; sentinel cost is permanent.

                Scope

                In: migration manifest + five M0 feasibility gates; extension-host amicode service (18 route modules, 7 widget sources, parallel-run harness); app-bundle extraction incl. the 72 i18n keys; internal server cutover + dogfood soak; runtime updater (managed install, adopt gate, fault-injection + adopt-lag drills); the cutover release, fork archival, and retiring the #159 sync sentinel.
                Out: no incremental user-visible rollout (single cutover release); no touching users' opencode installs outside VS Code; no upstream feature PRs beyond what cutover requires; no new platforms (matrix stays darwin-arm64 / linux-arm64 / linux-x64, Windows via WSL).

                Assumptions / Open Qs

                Five unknowns are M0 gates, each answered with evidence before code moves: (1) canonical release assets carry verifiable digests; (2) the per-boot route-auth password flow has a stock-binary equivalent; (3) the widget/deck iframe origin model tolerates an extension-host service origin; (4) plugin registration is assertable on a stock binary; (5) every adopt-gate check has a confirmed signal source on stock canonical. The plan refuses to proceed on an unverified guess.


                User Stories

                • Onboarding — a researcher installs Amicode on a clean machine (even offline): the Amicode terminal's opencode is canonical upstream, amicode-aware (persona, skills, tools active), seeing the same sessions as the app.
                • Currency — upstream ships a release; within 48h a connected user runs it, without a VSIX release or any action on their part.
                • Broken release — an upstream release breaks our surface: the adopt gate fails, the user keeps last-known-good silently, sessions intact.
                • Existing user — upgrades across the cutover: every session written by the fork-built server remains readable; no capability they use today regresses.

                Acceptance Criteria

                1. fresh_install_fork_binaries == 0 — clean VM per platform (incl. WSL), offline: terminal opencode resolves to the vendored canonical bootstrap; no file matches any fork release-asset sha256.
                2. vsix_fork_binary_refs == 0 — shipped lock names anomalyco/opencode, no -amicode. tag, no vendored binary sha from the fork (docs/skills textual mentions out of scope).
                3. route_parity_pct == 100 — 18/18 amicode route modules pass fork-derived contract tests against the extension-host service (golden fixtures from the live fork server for modules known-red on the fork).
                4. app_e2e_unexpected_failures == 0 — ported app e2e suite: every failure on the quarantine list frozen at M0; anything else blocks.
                5. drill_adopt_lag_h <= 48 — a gate-passing upstream release goes from publish to adoption marker within 48h on a connected test machine.
                6. failed_update_data_loss == 0 — corrupted-download, plugin-load-failure, and app-health-failure drills each end with sessions intact and last-known-good current.
                7. cutover_session_retention_pct == 100 — dogfood DB + synthetic corpus (≥500 sessions, all part types) structurally identical pre/post cutover.

                Key Decisions

                • One push — no incremental user-visible rollout; the cutover release is the first user-visible change and ships zero fork-built bytes.
                • Managed canonical wins — inside the Amicode terminal the extension-managed binary resolves first on PATH; user installs elsewhere untouched.
                • Auto-adopt after smoke gate — activation + daily checks; adoption is an atomic symlink swap only after: digest verification, boot smoke, plugin-registration assert for the amicode tool pack, and app + service health against a consistent (sqlite-backup) copy of the live DB.
                • Port, don't rewrite — the 18 route modules move wholesale into the extension host; parity is judged by fork-derived contract tests, with golden fixtures recorded from the running fork server for the modules whose tests are known-red on the fork itself.
                • Vocabulary — upstream = anomalyco/opencode ("canonical opencode"); the fork is never user-facing. Code comments that call the fork binary "canonical" are corrected in the app-bundle milestone.

                Modules & Interfaces

                • Plugin pack (unchanged) — amicode tools via opencode's standard plugin loader; single-export contract.
                • Config injection (unchanged) — spawn-time config content carrying persona, skills paths, permissions, plugin paths.
                • Extension-host amicode service (new) — the ported routes serving widgets, vault browser, connections, and amicode app calls; the deck shell's iframe origin check widens to it.
                • App bundle (new, extracted from the fork) — composes upstream app components; CI-pinned against canonical releases; owns the amicode i18n keys.
                • Runtime updater (new) — managed install under the amico state dir with a current symlink; the VSIX vendors one canonical copy per platform as offline bootstrap.

                Testing Decisions

                Parity via fork-derived contract tests + golden fixtures; app e2e ported with a frozen quarantine list; three fault-injection drills plus a live adopt-lag drill; retention corpus ≥500 sessions; clean-VM matrix per platform including WSL; parallel-run harness (extension service alongside the fork server) as the migration's verification backbone.

                Risks

                • App↔canonical client-API drift becomes the recurring post-cutover cost; CI-pinned overlay is the mitigation, and breaking releases hold users on last-known-good by design.
                • The app-bundle extraction is the long pole; the parallel-run harness de-risks taking it slowly.
                • Post-adoption breakage (passes gate, breaks later) heals on the next release; the existing binary-override setting remains the manual escape hatch.
                • Supply chain: TLS + GitHub-API digest only, no third-party mirrors; digest absence refuses adoption.

                Constraints & Invariants

                • Last-known-good is never deleted; adoption only via the full adopt gate.
                • The chat DB stays pinned by the extension across every spawn — the binary never picks its DB by build channel (the 2026-08-08 channel-flip incident).
                • No coupling to canonical internals; supported surfaces are the plugin API, config injection, and the documented client HTTP API. No feature is added by re-forking.
                • The fork's default branch is never force-pushed; the repo is archived, not deleted.
                • All work on Harmoniqs repos goes through issue + PR (development gate).
                Prior Art / Patterns
                • Vault spec + compiled plan (durable record): armonia-aaron-trowbridge/amicode/specs/spec-20260820-044920-ship-canonical-opencode.md and amicode/plans/plan-20260820-044920-ship-canonical-opencode.md
                • Automate upstream sync: merge anomalyco/opencode v1.18.12 → v1.18.15+ before next amicode release opencode#159 — fork-side upstream-sync automation (retired by this work)
                • AMICODE-PATCHES.md in the fork — authoritative patch log and sync history
                • The extension's binary-resolution override (config → vendored) and the vendoring lock/script that already parameterize the source repo
                • The staging-project config-injection pattern (persona + skills + plugins onto a server binary) — proof the amicode-info layer is already binary-agnostic

                Source

                Durable record: personal Armonia vault, amicode/specs/spec-20260820-044920-ship-canonical-opencode.md (deliberated 2026-08-20, manual adversarial review — 1 blocking contradiction fixed, 9 advisories) · compiled plan at amicode/plans/plan-20260820-044920-ship-canonical-opencode.md · related: harmoniqs/opencode#159

                Notes

                Decisions locked with the researcher 2026-08-20: one-push cutover; managed-canonical PATH precedence inside the Amicode terminal; auto-adopt after smoke gate. The spec's deliberation was hand-run (spec tooling unavailable on the host) — a weaker review claim than a tooled one, noted for the record.

                Activity

                Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                Metadata

                Metadata

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions