Add macOS OpenCode Support

Work directly in:
/home/kernel-kain/Documents/Github/unfinished-business
Execution rules:
- Work only on the main branch.
- Begin with:
- git status
- git switch main
- git pull --ff-only origin main
- Preserve all unrelated changes, untracked files, and existing stashes. Do not apply, delete, or commit .backboard/, .unfinished-business/, .env files, credentials, or the existing stash unless this phase explicitly requires it.
- Read the relevant source, tests, AGENTS.md files, and documentation before editing.
- Implement only the phase described below. Do not begin the next phase.
- Maintain backward compatibility with existing saved projects through explicit migrations.
- Do not expose provider secrets, prompts containing secrets, or private model output in logs, telemetry, screenshots, or error messages.
- Add or update tests for every behavior changed.
- After implementation, update the relevant project documentation, including:
- README.md when user-facing behavior changes
- docs/ARCHITECTURE.md
- docs/DECISIONS.md
- docs/IMPLEMENTATION_PLAN.md
- docs/EVIDENCE.md
- docs/RELEASE_NOTES.md
- any phase-specific setup or integration documentation
- Mark the phase complete in the implementation plan only after its acceptance criteria pass.
- Run all relevant verification. At minimum:
- npm run typecheck
- npm test
- npm run build Run visual, security, budget, contract, migration, or integration tests when affected.
- Review the final git diff for accidental files, secrets, debugging output, and unrelated changes.
- Commit with the commit message specified by this phase.
- Push the commit to origin/main. Never force-push.
- If the push is rejected because origin/main advanced, fetch and rebase safely, resolve only scoped conflicts, rerun verification, and push normally.
- Finish with:
- implementation summary
- changed files
- documentation updated
- tests and results
- known limitations
- commit hash
- push result
Do not stop for ordinary implementation decisions. Stop only if there is a destructive conflict, missing required credential, irreconcilable remote conflict, or a decision that would materially change the product’s intended behavior.; I will send the phase prompt just after this so do not look for it
Baseline is clean and up to date:
- Branch:
main, trackingorigin/main, up to date - Working tree: no modified or staged files; only untracked
.backboard/, which I'll leave untouched - Stashes preserved (I won't touch them)
- HEAD:
ddf632a feat(demo): unify the project revival story
Send the phase prompt and I'll read the relevant source, tests, AGENTS.md files, and docs before implementing.
Implement Phase 7: support macOS alongside Linux.
Objectives:
- Remove Linux-only OpenCode assumptions.
- Support Apple Silicon and Intel macOS.
- Keep Linux deployment working.
Implementation requirements:
- Inspect current OpenCode CLI/client compatibility before changing package versions.
- Replace the Linux-only runtime dependency with either:
- the official cross-platform OpenCode CLI package; or
- a well-tested platform-aware resolver.
- Prefer a valid globally installed opencode executable when available.
- Keep CLI and client versions compatible and pinned through the lockfile.
- Isolate any Render-specific Linux binary behavior from local development.
- Replace hardcoded Linux executable paths.
- Replace hardcoded /tmp and browser paths with platform-aware resolution.
- Support:
- linux x64;
- macOS Apple Silicon;
- macOS Intel.
- Update setup UI and docs with npm and Homebrew installation paths.
- Add clear diagnostics for:
- executable missing;
- unsupported architecture;
- incompatible CLI version;
- permission problem;
- server failed to start.
- Add CI jobs for Linux and macOS covering:
- dependency installation;
- CLI resolution;
- typecheck;
- tests;
- build;
- workspace path safety;
- OpenCode smoke test where practical.
- Do not claim a platform is supported unless automated or recorded manual evidence exists.
Acceptance criteria:
- Linux behavior remains operational.
- No user-facing setup command contains a Linux-only package path.
- macOS resolves an appropriate OpenCode executable.
- Workspace security handles /Users, /Volumes, and resolved temporary paths safely.
- Setup documentation clearly distinguishes tested and untested configurations.
Commit message:
feat(platform): add macOS OpenCode support
Implementation summary
Platform Phase 7 is implemented and verified:
- Replaced
@opencode/cli-linux-x64-baselinewith the official cross-platform@opencode/cli@2.0.32, still locked with@opencode/client@2.0.32. - Added a single platform-aware resolver:
- explicit
UB_OPENCODE_EXECUTABLE - compatible global
opencodeonPATH - lockfile-installed
@opencode/clibinary
- explicit
- Supported platforms are limited to Linux x64, macOS Apple Silicon, and macOS Intel. Unsupported hosts fail before probing with
OPENCODE_PLATFORM_UNSUPPORTED. - Added diagnostics for missing CLI, permission failures, incompatible/untested versions, unsupported architecture, and server start failures.
- Updated the Render supervisor to use the shared resolver and environment-derived XDG paths, removing the hardcoded Linux binary path.
- Replaced Playwright’s
/tmppath withos.tmpdir()and added standard macOS browser locations. - Hardened workspace security for
/Users,/Volumes,/System,/private,/private/tmp,/private/var/tmp,/private/var/folders, and resolved temporary roots. - Added
npm run diagnose:platformandnpm run smoke:opencode. - Added CI for
ubuntu-latest,macos-15, andmacos-15-intel. - Fixed CI-discovered cross-platform path/timing races in terminal run handling.
Changed files
Core runtime:
src/server/opencode/executable.ts— new resolver and diagnosticssrc/server/render-start.ts— shared resolver, Render isolationsrc/server/security/workspace.ts— macOS/resolved-temp root safetysrc/server/opencode/connection.ts— platform diagnostic repair textsrc/server/experience/service.ts— deterministic terminal cancellation/retry behavior
Scripts and configuration:
scripts/opencode-platform-diagnostic.tsscripts/opencode-smoke.tsscripts/preflight.mjspackage.json,package-lock.jsonplaywright.config.ts.github/workflows/sentry-release.yml.env.examplerender.demo.yaml
UI:
src/web/setup.tssrc/web/landing/LandingPage.tsx
Tests:
tests/platform.test.ts— new platform/resolver/diagnostic/CI contract coveragetests/security.test.tstests/web.test.tstests/connection.test.tstests/observable-runs.test.tstests/oracle-discovery.test.tstests/fixtures/fake-opencode-server.mjs
Documentation updated
README.mddocs/ARCHITECTURE.mddocs/DECISIONS.md— Platform Phase 7 decisionsP7-01throughP7-08docs/IMPLEMENTATION_PLAN.mddocs/EVIDENCE.mddocs/RELEASE_NOTES.mddocs/CHANGELOG.mddocs/LOCAL_SETUP.mddocs/OPENCODE_INTEGRATION.mddocs/HOSTED_DEPLOYMENT.mddocs/OPERATIONS_AND_RECOVERY.mddocs/PRODUCT_BRIEF.mddocs/TOOL_CATALOG.mddocs/UX_AND_VISUAL_SYSTEM.mddocs/DEMO_AND_SUBMISSION.md
Tests and results
Local Linux verification:
npm run typecheck— PASSnpm test— 200 passed, 0 failednpm run test:platform— PASS, 25 testsnpm run build— PASSnpm run budget— PASS (initial JS 201.5 KiB, all JS 280.1 KiB, CSS 17.0 KiB, hero 581.2 KiB)npm run test:visual— PASS, 20 Playwright testsnpm run diagnose:platform— PASS,linux-x64, version2.0.32npm run smoke:opencode— PASS, server/api/inforeports2.0.32node scripts/preflight.mjs— PASS for required checksnpm audit --omit=dev --audit-level=high— PASS; six pre-existing moderate advisories remain- Render supervisor smoke — PASS, health endpoint returned
ok
CI:
- Run
41252770576— PASS forlinux-x64,darwin-arm64,darwin-x64 - Run
41253037115— final docs commit, PASS for all three platforms
Known limitations
- macOS evidence is automated CI evidence; no separate manual macOS session is recorded.
- Homebrew installs the current tap version, not necessarily
2.0.32; the resolver warns when the patch is untested. - Windows, Linux ARM64, and 32-bit hosts are unsupported and explicitly rejected.
- The official
@opencode/clipackage may install multiple compatible Linux x64 variants selected by its postinstall. - No live provider inference was performed in this phase.
- No schema migration was required; schema v7 and saved projects are unchanged.
- Render deployment remains an external, separately authorized action.
- GitHub Actions reports a Node 20 deprecation warning for
actions/checkout/setup-node/upload-artifact; it does not fail the jobs.
Commit hashes
- Phase commit:
1835049—feat(platform): add macOS OpenCode support - CI stabilization:
3ae73f3—fix(platform): stabilize macOS and CI verification - Runtime race stabilization:
2dd9acb—fix(runtime): stabilize terminal run races across platforms - Evidence/docs:
6641d71—docs(platform): record the green macOS CI matrix
Final HEAD: 6641d71
Push result
Pushed normally to origin/main:
No force-push. Final worktree contains only the pre-existing untracked .backboard/, which was preserved untouched.
Implement Phase 8: perform final quality, accessibility, security, and release hardening.
Objectives:
- Verify the complete upgraded workflow.
- Resolve remaining visual and accessibility problems.
- Produce honest release evidence and documentation.
Implementation requirements:
- Test the complete user journey:
- project source selection;
- Oracle questions;
- proposal approval;
- section drafting;
- revision preview;
- approvals;
- Maker scope;
- live progress;
- timeout recovery;
- Witness evidence.
- Add responsive coverage for at least:
- 390px
- 768px
- 1440px
- Audit:
- keyboard navigation;
- focus order;
- visible focus;
- screen-reader announcements;
- reduced motion;
- WCAG AA contrast;
- zoom and text reflow;
- long model names;
- long generated sections;
- empty, loading, error, and offline states.
- Add security tests for:
- secret redaction;
- malicious Markdown;
- repository path traversal;
- unsafe remote URLs;
- duplicate operations;
- stale revisions;
- replayed mutation requests.
- Upgrade vulnerable dependencies carefully, including Sentry packages, without combining unrelated major migrations.
- Run the complete project verification suite.
- Refresh visual snapshots only after manual inspection.
- Update:
- architecture;
- setup;
- OpenCode integration;
- security model;
- recovery behavior;
- supported platforms;
- limitations;
- release notes;
- implementation-plan completion state.
- Produce an evidence matrix mapping major product claims to tests, screenshots, or native session evidence.
- Prepare hackathon/release copy and assets in the repository, but do not publish an external submission without explicit authorization.
Acceptance criteria:
- All automated verification passes.
- No high-severity accessibility or security issue remains open.
- Documentation matches actual behavior.
- Every important product claim has evidence or is explicitly labelled as a limitation.
- The repository is clean after commit and push.
Commit message:
chore(release): harden and document the upgraded workshop
Implementation summary
Phase 8 hardening is complete and pushed.
- Added a deterministic complete workshop journey test: source selection → Oracle questions → proposal → product approval → revision preview/supersede/accept → all eight section approvals → Maker scope → live progress → timeout → reconciliation → Node evidence → Witness drafts → export.
- Added 768px responsive snapshots for landing, workshop, demo, and onboarding while keeping 390px and 1440px coverage.
- Fixed the tablet header so the brand, engine state, and model controls remain readable.
- Made revision review a keyboard-operable ARIA tab panel with Arrow/Home/End support, roving tabindex,
aria-controls, and a labelled panel. - Added polite live progress announcements to Activity & results.
- Added deliberate loading and offline states; a lost API connection now shows an actionable connection-loss alert and reload action instead of raw fetch text.
- Added coverage for 200% zoom, long model identities, long unbroken Markdown, focus return, extended contrast tokens, reduced motion, loading, offline, empty, permission, and provider-error states.
- Extended security coverage for local-source traversal/broad roots/system symlinks, unsafe remote URL schemes/ports/credentials/query/fragment/traversal, failed imports, stale revisions, duplicate approvals, and replayed memory mutations.
- Kept Sentry at
11.10.0and overrode only the vulnerable transitiveglob/minimatchpair. Bothnpm auditcommands now report 0 vulnerabilities. - Added a security model, hackathon/release copy, and an asset manifest. Nothing was published or submitted externally.
Changed files
Code and tests:
tests/journey.test.tstests/visual/hardening.spec.tstests/visual/landing.spec.tstests/visual/snapshots/landing-tablet.pngtests/visual/snapshots/workshop-tablet.pngtests/visual/snapshots/demo-tablet.pngtests/visual/snapshots/onboarding-tablet.pngtests/visual/snapshots/workshop-offline.pngtests/http-workflow.test.tstests/oracle-discovery.test.tstests/security.test.tstests/web.test.tssrc/web/components/RevisionReview.tsxsrc/web/components/RunInspector.tsxsrc/web/workshop/WorkshopPage.tsxsrc/web/styles.csssrc/server/security/workspace.tspackage.json,package-lock.json
Documentation and release assets:
docs/SECURITY_MODEL.mddocs/release/HACKATHON_COPY.mdREADME.mddocs/ARCHITECTURE.mddocs/DECISIONS.mddocs/IMPLEMENTATION_PLAN.mddocs/EVIDENCE.mddocs/RELEASE_NOTES.mddocs/CHANGELOG.mddocs/LOCAL_SETUP.mddocs/OPENCODE_INTEGRATION.mddocs/OPERATIONS_AND_RECOVERY.mddocs/DEMO_AND_SUBMISSION.mddocs/TOOL_CATALOG.mddocs/UX_AND_VISUAL_SYSTEM.md
Documentation updated
- Architecture now documents the accessibility/responsive/resilience verification layer.
- Security model maps trust boundaries and threats to tests.
- Setup and integration docs include the current verification gate.
- Recovery docs describe deterministic terminal cancellation/retry behavior.
- Evidence includes the Phase 8 matrix, commands, screenshots, CI run, and explicit limitations.
- Implementation plan marks Phase 8 complete.
- Release notes, changelog, tool catalog, UX system, product demo/submission docs, and platforms/limitations were refreshed.
- Hackathon pitch, short/long description, demo script, claim checklist, limits, and asset manifest are prepared in
docs/release/HACKATHON_COPY.md; no external submission was made.
Tests and results
Local Linux verification:
npm run typecheck— PASSnpm test— 207 passed, 0 failednpm run build— PASSnpm run budget— PASS: initial JS 201.5 KiB, all JS 280.4 KiB, CSS 17.0 KiB, hero 581.2 KiBnpm run test:visual— 26 passednpm run test:platform— PASS, 25 testsnpm run diagnose:platform— PASS,linux-x64, OpenCode2.0.32npm run smoke:opencode— PASS, server/api/inforeports2.0.32node scripts/preflight.mjs— PASS for required checksnpm audit --omit=dev --audit-level=high— PASS, 0 vulnerabilitiesnpm audit— PASS, 0 vulnerabilitiesnpm ci— PASS against the updated lockfile
CI:
- Run
41255172209— PASS forlinux-x64,darwin-arm64,darwin-x64 - Run
41255384016— final docs commit, PASS for all three platforms
Known limitations
- The complete journey test uses a deterministic guide and makes no live provider call.
- macOS execution is verified by CI; no separate manual macOS session is recorded.
- Automated Chromium checks cover the stated widths and states but do not replace a full manual screen-reader or cross-browser audit.
- The Playwright suite is run locally; CI covers install, CLI resolution, typecheck, tests, build, workspace safety, and OpenCode smoke.
- Sentry live release/source-map/trace verification still requires authorized credentials and a deployment.
- No public hosted URL, recorded video, published article, or external submission is asserted.
- The Node check is a bounded child process, not an OS sandbox; the app remains single-project and not multi-tenant.
- The
glob/minimatchoverride should be re-checked when Sentry updates its bundler dependencies.
Commit hashes
- Phase commit:
0ee2ac2—chore(release): harden and document the upgraded workshop - Evidence follow-up:
8f4637d—docs(release): record the Phase 8 CI evidence
Final HEAD: 8f4637d
Push result
Pushed normally to origin/main:
No force-push. Final worktree contains only the pre-existing untracked .backboard/, preserved untouched.
The project is running.
- App: http://127.0.0.1:5173
- Workshop: http://127.0.0.1:5173/workshop
- Guided sample: http://127.0.0.1:5173/demo
- API: http://127.0.0.1:4210
- OpenCode: http://127.0.0.1:4096
I connected the app to OpenCode successfully:
- State:
connected - OpenCode server:
2.0.32 - Compatible: yes
- Models discovered: 465
- Diagnostics: none
Port 4200 is already occupied by litellm, so this project is using UB_PORT=4210 from .env; Vite proxies /api there.
Logs:
- OpenCode:
/tmp/opencode/ub-run/opencode.log - Vite:
/tmp/opencode/ub-run/vite.log
The OpenCode password was not printed. Saved state remains under .unfinished-business/.
If the API needs restarting later, run npm run dev; if OpenCode needs restarting, use the project’s npx --no-install opencode serve --hostname 127.0.0.1 --port 4096 with the password from .env.