Skip to content

Commit c05f3c7

Browse files
committed
new plan docs
1 parent 4857106 commit c05f3c7

5 files changed

Lines changed: 253 additions & 681 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Offworld Architecture Refactor Plan (2026-02-02)
2+
3+
Goal: Reduce package indirection, make heavy dependencies optional, clarify SDK API boundaries, and harden release safety without changing CLI behavior.
4+
5+
## Phase 1: Merge `@offworld/backend-api` Into the SDK
6+
7+
### 1. Add SDK copy step for Convex generated outputs
8+
9+
- Create `packages/sdk/scripts/copy-convex-generated.ts`.
10+
- Copy from `packages/backend/convex/_generated` to `packages/sdk/dist/convex/_generated`.
11+
- Copy these files: `api.js`, `api.d.ts`, `server.js`, `server.d.ts`, `dataModel.d.ts`.
12+
- Wire into SDK build: update [packages/sdk/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/package.json) to run `tsdown && bun run ./scripts/copy-convex-generated.ts`.
13+
14+
### 2. Add Convex subpath exports to SDK
15+
16+
- Update [packages/sdk/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/package.json) exports:
17+
- `"./convex/api": { "types": "./dist/convex/_generated/api.d.ts", "import": "./dist/convex/_generated/api.js" }`
18+
- `"./convex/server": { "types": "./dist/convex/_generated/server.d.ts", "import": "./dist/convex/_generated/server.js" }`
19+
20+
### 3. Update SDK sync imports
21+
22+
- Replace `@offworld/backend-api/api` with `@offworld/sdk/convex/api` in [packages/sdk/src/sync.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/src/sync.ts).
23+
- If sync is moved into `sync/` modules later, apply the change there instead.
24+
25+
### 4. Remove backend-api package
26+
27+
- Delete `packages/backend-api` directory.
28+
- Remove references in [scripts/bump-version.ts](file:///Users/oscargabriel/Developer/projects/offworld/scripts/bump-version.ts).
29+
- Remove from publish order in [release.yml](file:///Users/oscargabriel/Developer/projects/offworld/.github/workflows/release.yml).
30+
- Remove from SDK dependencies in [packages/sdk/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/package.json).
31+
32+
### Acceptance
33+
34+
- `@offworld/sdk/convex/api` and `@offworld/sdk/convex/server` resolve in built output.
35+
- No code imports `@offworld/backend-api`.
36+
37+
## Phase 2: Optional Convex + AI via Subpath Exports
38+
39+
### 1. Split sync into subpath entrypoint
40+
41+
- Create `packages/sdk/src/sync/index.ts` that re-exports current sync API.
42+
- Update [packages/sdk/tsdown.config.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/tsdown.config.ts) to include `src/sync/index.ts` entry.
43+
- Add `"./sync"` export in [packages/sdk/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/package.json) pointing to `dist/sync/index.mjs` and `dist/sync/index.d.mts`.
44+
- Remove sync exports from [packages/sdk/src/index.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/src/index.ts).
45+
46+
### 2. Make Convex an optional peer dependency
47+
48+
- Move `convex` from `dependencies` to `peerDependencies` in [packages/sdk/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/package.json).
49+
- Add `peerDependenciesMeta: { "convex": { "optional": true } }`.
50+
51+
### 3. Dynamic import Convex client
52+
53+
- Add `packages/sdk/src/sync/client.ts` with `getConvexClient()` using dynamic `import("convex/browser")`.
54+
- Throw `SyncUnavailableError` with a clear message when the import fails.
55+
- Update sync functions to use `getConvexClient()` instead of directly importing Convex.
56+
57+
### 4. Move AI behind `@offworld/sdk/ai`
58+
59+
- Keep `packages/sdk/src/ai/index.ts` as-is but export it under `"./ai"` in [packages/sdk/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/package.json).
60+
- Add `src/ai/index.ts` entry to [packages/sdk/tsdown.config.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/tsdown.config.ts).
61+
- Remove AI exports from [packages/sdk/src/index.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/src/index.ts).
62+
63+
### Acceptance
64+
65+
- Installing `@offworld/sdk` does not require `convex` unless `@offworld/sdk/sync` is used.
66+
- Default SDK entrypoint does not include AI exports.
67+
68+
## Phase 3: Public vs Internal SDK API
69+
70+
### 1. Define public surface
71+
72+
- Create `packages/sdk/src/public.ts` with curated, stable exports.
73+
- Update [packages/sdk/src/index.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/src/index.ts) to re-export only `public.ts`.
74+
75+
### 2. Add internal entrypoint
76+
77+
- Create `packages/sdk/src/internal.ts` to expose CLI-only helpers.
78+
- Add `"./internal"` export mapping in [packages/sdk/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/package.json).
79+
80+
### Acceptance
81+
82+
- Public entrypoint is small and documented.
83+
- CLI relies only on `@offworld/sdk/internal`, `@offworld/sdk/sync`, and `@offworld/sdk/ai`.
84+
85+
## Phase 4: Update CLI Imports
86+
87+
### 1. Handlers
88+
89+
- Update handlers importing sync and AI to use subpaths:
90+
- `@offworld/sdk/sync` for sync API.
91+
- `@offworld/sdk/ai` for AI generation.
92+
- `@offworld/sdk/internal` for CLI-only helpers.
93+
- Start with [apps/cli/src/handlers/pull.ts](file:///Users/oscargabriel/Developer/projects/offworld/apps/cli/src/handlers/pull.ts) and [apps/cli/src/handlers/push.ts](file:///Users/oscargabriel/Developer/projects/offworld/apps/cli/src/handlers/push.ts).
94+
95+
### 2. Tests
96+
97+
- Update SDK tests importing sync/AI to use new subpaths.
98+
99+
### Acceptance
100+
101+
- CLI builds and runs without importing sync/AI from the SDK root.
102+
103+
## Phase 5: Release Safeguards
104+
105+
### 1. Version verification
106+
107+
- Add `scripts/verify-versions.ts` to ensure `apps/cli`, `packages/sdk`, and `packages/types` versions match.
108+
- Add a `verify:versions` script at workspace root and run in CI pre-publish.
109+
110+
### 2. Update publish flow
111+
112+
- Remove backend-api from publish order in [release.yml](file:///Users/oscargabriel/Developer/projects/offworld/.github/workflows/release.yml).
113+
- Ensure build order still runs Convex codegen before SDK build.
114+
115+
### Acceptance
116+
117+
- CI fails on version mismatch before publishing.
118+
- Publishing does not rely on backend-api.
119+
120+
## Documentation Updates
121+
122+
- Update [packages/sdk/README.md](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/README.md) with new subpath usage.
123+
- Update [apps/cli/README.md](file:///Users/oscargabriel/Developer/projects/offworld/apps/cli/README.md) with SDK migration notes.
124+
125+
## Verification
126+
127+
- `bun run check`
128+
- `bun run typecheck`
129+
- `bun run test`
130+
- `bun run build`
131+
- Manual CLI smoke checks: `ow --version`, `ow pull <repo>`, `ow generate <repo>`, `ow push <repo>`
132+
133+
## Definition of Done
134+
135+
- `@offworld/backend-api` removed with no functional regressions.
136+
- `@offworld/sdk` default entrypoint avoids Convex and AI.
137+
- CLI distribution (npm + binaries + installer) unchanged in behavior.
138+
- Version drift is prevented before publish.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Offworld Architecture Review (2026-02-02)
2+
3+
Scope: CLI build/distribution, SDK packaging, Convex type flow, workspace release pipeline.
4+
5+
## Executive Summary
6+
7+
The core architecture is strong, but the current build and distribution path still has avoidable indirection and reliability hazards. The most impactful simplifications are: (1) remove `@offworld/backend-api` by moving Convex generated outputs into the SDK build pipeline, (2) split optional-heavy features (Convex sync + AI) into subpath exports and dynamic imports, and (3) tighten release safeguards (version lockstep + publish order) so the CLI and SDK never drift. These changes are structural and preserve existing CLI UX.
8+
9+
## Current State (Verified)
10+
11+
### CLI Build & Distribution
12+
13+
- The CLI is bundled with `tsdown` into ESM output with two entrypoints: `src/cli.ts` (bin) and `src/index.ts` (library). See [apps/cli/tsdown.config.ts](file:///Users/oscargabriel/Developer/projects/offworld/apps/cli/tsdown.config.ts).
14+
- The published npm package exposes `dist/cli.mjs` as the executable and `dist/index.mjs` for library exports. See [apps/cli/package.json](file:///Users/oscargabriel/Developer/projects/offworld/apps/cli/package.json).
15+
- Release binaries are compiled from the ESM bundle using `bun build --compile` in GitHub Actions. See the build job in [release.yml](file:///Users/oscargabriel/Developer/projects/offworld/.github/workflows/release.yml).
16+
- The installer downloads prebuilt binaries, verifies checksums, and installs a single `ow` binary. See [install](file:///Users/oscargabriel/Developer/projects/offworld/install).
17+
18+
### SDK Build & API Surface
19+
20+
- The SDK currently builds a single entrypoint (`src/index.ts`) with `tsdown` to `dist/index.mjs`. See [packages/sdk/tsdown.config.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/tsdown.config.ts).
21+
- The SDK default export surface includes sync and AI exports directly, meaning `convex` and `@opencode-ai/sdk` are pulled via the main entrypoint. See [packages/sdk/src/index.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/src/index.ts).
22+
- AI access already uses dynamic import internally (`@opencode-ai/sdk`). See [packages/sdk/src/ai/opencode.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/src/ai/opencode.ts).
23+
24+
### Convex Generated Types
25+
26+
- Convex generates API stubs under `packages/backend/convex/_generated`. See [packages/backend/convex/_generated/api.d.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/backend/convex/_generated/api.d.ts).
27+
- `@offworld/backend-api` is a packaging shim that copies those generated files into `dist/_generated` and exposes `./api` and `./server`. See [packages/backend-api/package.json](file:///Users/oscargabriel/Developer/projects/offworld/packages/backend-api/package.json) and [packages/backend-api/scripts/copy-generated.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/backend-api/scripts/copy-generated.ts).
28+
- SDK sync code imports the Convex API from that shim, creating a build and publish dependency chain. See [packages/sdk/src/sync.ts](file:///Users/oscargabriel/Developer/projects/offworld/packages/sdk/src/sync.ts).
29+
30+
### Release Pipeline and Publishing
31+
32+
- Release builds generate Convex code, run `turbo build`, compile binaries, then publish packages in a fixed order: `@offworld/types`, `@offworld/backend-api`, `@offworld/sdk`, `offworld`. See [release.yml](file:///Users/oscargabriel/Developer/projects/offworld/.github/workflows/release.yml).
33+
- The version bump script enforces a lockstep version across CLI, SDK, types, and backend-api, and regenerates build output. See [scripts/bump-version.ts](file:///Users/oscargabriel/Developer/projects/offworld/scripts/bump-version.ts).
34+
- There is no `scripts/verify-versions.ts` in the current tree, despite being referenced in prior change logs.
35+
36+
## Pain Points
37+
38+
1. **Backend API indirection**: The `@offworld/backend-api` package exists only to copy Convex generated files. This adds a build step and a release dependency without adding behavior.
39+
2. **Heavy dependencies in the default SDK entrypoint**: `convex` and AI-related code are available on the main entrypoint, which means consumers pay for them even if they only use local repo management.
40+
3. **Release fragility**: The release pipeline depends on build/publish order and the presence of the backend-api shim. Any mismatch between versions or missing codegen output breaks publishing.
41+
4. **Public vs internal API**: The SDK entrypoint currently acts as a catch-all export surface, which makes it hard to draw a stable boundary between “public API” and “CLI internals.”
42+
43+
## Recommended Approach (Simplify + Improve Reliability)
44+
45+
### 1) Merge Convex Type Surface Into the SDK
46+
47+
**Goal:** Remove `@offworld/backend-api` and expose generated Convex types directly from `@offworld/sdk`.
48+
49+
**Approach:**
50+
51+
- Add an SDK build step to copy `packages/backend/convex/_generated` into `packages/sdk/dist/convex/_generated` after `tsdown` completes.
52+
- Add explicit subpath exports for Convex types under the SDK package:
53+
54+
```json
55+
"./convex/api": { "types": "./dist/convex/_generated/api.d.ts", "import": "./dist/convex/_generated/api.js" },
56+
"./convex/server": { "types": "./dist/convex/_generated/server.d.ts", "import": "./dist/convex/_generated/server.js" }
57+
```
58+
59+
- Update SDK sync modules to import from `@offworld/sdk/convex/api` instead of `@offworld/backend-api`.
60+
- Remove `packages/backend-api` and eliminate it from publish workflows and version scripts.
61+
62+
This directly removes a full build stage and package, while keeping Convex types accessible in a standard, explicit location.
63+
64+
### 2) Split Optional Dependencies Behind Subpath Exports
65+
66+
**Goal:** Avoid loading Convex + AI in the default SDK entrypoint.
67+
68+
**Approach:**
69+
70+
- Move sync exports under `@offworld/sdk/sync` and AI exports under `@offworld/sdk/ai`.
71+
- Keep the main SDK entrypoint (`@offworld/sdk`) limited to core local functionality: config, clone, map, repo management, reference install, etc.
72+
- Make `convex` an optional peer dependency and dynamically import it in sync client creation, throwing a clear `SyncUnavailableError` when missing.
73+
74+
This reduces default bundle size and makes SDK behavior more predictable for offline/local-only users.
75+
76+
### 3) Define Public vs Internal SDK API
77+
78+
**Goal:** Separate stable public API from CLI-internal utilities.
79+
80+
**Approach:**
81+
82+
- Introduce `public.ts` (curated export list) and make `src/index.ts` re-export only that.
83+
- Add `internal.ts` and a `@offworld/sdk/internal` export for CLI-only helpers.
84+
85+
This reduces accidental dependency on internal APIs and gives room to evolve CLI internals without breaking external consumers.
86+
87+
### 4) Harden Release Safeguards
88+
89+
**Goal:** Prevent version drift and reduce publishing breakage.
90+
91+
**Approach:**
92+
93+
- Add a `scripts/verify-versions.ts` that checks versions across `apps/cli`, `packages/sdk`, and `packages/types` and fail CI if mismatched.
94+
- Run it before publish in CI and/or as `prepublishOnly` for packages.
95+
- Remove backend-api from the publish order once it is deleted.
96+
97+
## Impact on CLI Bundling & Distribution
98+
99+
- The CLI will continue to build as a single ESM bundle via `tsdown` and get compiled to a static binary via Bun in CI. This remains unchanged and reliable.
100+
- After the SDK split, the CLI should import sync and AI functionality from `@offworld/sdk/sync` and `@offworld/sdk/ai` (or `@offworld/sdk/internal`), preventing unwanted dependency loading on `@offworld/sdk` consumers.
101+
- The installer and release binary pipeline are already robust; the primary risk is ensuring the SDK build produces Convex stubs before CLI builds in CI. With an SDK copy step in its `build` script, the `turbo` build graph will handle ordering.
102+
103+
## Suggested Implementation Order
104+
105+
1. Merge backend-api into SDK with Convex subpath exports.
106+
2. Move sync + AI behind subpath exports; optional Convex peer dependency.
107+
3. Create `public.ts` + `internal.ts` and narrow the default SDK entrypoint.
108+
4. Add `verify-versions` to CI and prepublish tasks.
109+
110+
## Definition of Done
111+
112+
- `@offworld/backend-api` is removed without breaking sync functionality.
113+
- SDK default entrypoint no longer requires Convex or AI dependencies.
114+
- CLI builds and release binaries remain unchanged and succeed in CI.
115+
- Package version drift is caught before publish.

0 commit comments

Comments
 (0)