- Board renamed docs/plan/00-kanban.md -> docs/00-status.md and rebuilt: ▶ NEXT PLAN pointer (iteration 4 — emitter, corpus, `woc build`) then six buckets — stories, in progress, done, pending, discarded, learnings. It covered only the Rust runtime before, so the whole OOP track was invisible. All 16 inbound refs repointed; `Kanban:` banners renamed to `Status:`. - New discarded.md (settled rejections with reasons: inheritance, `abstract`, Money/SKU/Float, Dynamic/cast/macro/extern, AOT-to-C, Menhir, shared engine state) and learnings.md (plumbed≠enforced, vacuous goldens, exit-0-wrong- output, malloc-path ASan trick, deferred checks that never reach the VM). - RECOVERED docs/plan/exploration/blue-green-vm/00-vision.md — gone from disk, never committed (gitignored path), cited by five docs incl. principle 12. Root cause was broader: all seven forward-roadmap plans in docs/superpowers/plans/ were untracked and ignored, on one disk only. Dropped the docs ignore rules with a do-not-re-add note; added __pycache__/*.pyc. - Repaired broken links across docs/, 270 -> 36: fixes a regression from the earlier reference/ -> .dev/reference/ move (relative paths at ../../ and deeper were skipped), plus depth and reorg drift. The 36 residual point at content that does not exist and need decisions, not paths. - New spec docs/superpowers/specs/2026-08-10-logwatcher-gap-closure-design.md, applied: `and`/`or` verdict row; Part 3 gains `env` (six modules), swaps time.mono for iso/local, adds 22 bare core builtins; throw/time.mono/is cut (0 uses in the sample). Plan 8: Task 2 gains and/or, Task 5 drops throw, abstract+`is` task deleted, 8/9 renumber to 7/8. Plan 9 gains core builtins. Plan 10 gains the 307 -> 0 diagnostic gate. WO-E205 re-filed unreachable-by- design. types.ml header drops its false satisfaction-set claim. 00-code- review.md reduced to a stub — its rival Phase 1-4 roadmap retired.
7.4 KiB
ecommerce — a sample writeonce monorepo
Two apps (customer storefront + ops admin) sharing one database, built from a common pool of types + business logic. Mirrors the Nx / Angular workspace pattern: apps/* for deployable binaries, shared/* for libraries imported across apps.
This project is a docs artifact — illustrative
.wosource showing what a production-shaped writeonce workspace looks like. The master plan for the compiler + client runtime + per-app build is at../../plan/ui/00-overview.md. Sub-phases UI/01–07 implement each piece.
Layout
ecommerce/
├── wo.toml # workspace manifest (apps[] + shared[] + database)
├── README.md # this file
│
├── shared/ # code imported by one or more apps
│ ├── types/
│ │ ├── customer.wo # role union, policy, service rest expose
│ │ ├── product.wo # inventory + similar_to graph
│ │ ├── order.wo # tagged-union status + line_items array
│ │ └── purchase.wo # link Customer -> Product
│ ├── logic/
│ │ ├── checkout.wo # fn checkout / mark_paid / mark_shipped
│ │ └── seed.wo # on-startup demo seed + admin-ops bypass policy
│ └── components/ # reusable .htmlx partials (forward-looking — UI track)
│ ├── layout.htmlx # page chrome shared across apps
│ ├── money.htmlx # {{> money amount=total}}
│ └── order-row.htmlx # used by both orders tables
│
├── apps/ # one binary per app
│ ├── storefront/ # customer-facing
│ │ ├── wo.toml # listen :8080, connect WO_DB
│ │ ├── app.wo # routes: / → home, /product/:sku, /cart, /orders
│ │ └── ui/
│ │ ├── home/
│ │ │ └── home.wo # product list, live inventory
│ │ ├── product-detail/ # (future)
│ │ └── orders/
│ │ └── orders.wo # customer's own orders
│ │
│ └── admin/ # ops dashboard
│ ├── wo.toml # listen :8081, connect WO_DB
│ ├── app.wo # role: Admin | Ops; routes: /orders
│ └── ui/
│ └── orders/
│ └── orders.wo # live ops table with fulfillment actions
│
└── tests/ # workspace-level integration
└── checkout_test.wo
Each app's UI screens live in their own directory (apps/<app>/ui/<screen>/) with the Angular-style one-directory-per-component pattern — .wo declarative spec today, .htmlx template + .css stylesheet once the UI track's sub-phase 01–02 land.
What the two apps share
- Types (
shared/types/). Both apps see the sameCustomer/Product/Order/Purchasedefinitions. Row-level policies inside eachtypeblock control who sees what — the storefront's authenticated customer sees their own orders; the admin app's Admin/Ops role sees everyone's. - Logic (
shared/logic/). Thecheckout,mark_paid,mark_shipped, andrelease_inventoryfunctions inshared/logic/checkout.woare callable from either app (subject to role policy).shared/logic/seed.woruns once when the shared DB daemon starts. - Components (
shared/components/)..htmlxpartials — layout chrome, money formatting, an order-row renderer — reusable from either app's templates.
What's not shared (per-app)
app.wodeclares app-specific routes + role gate. Storefront has no/admin/*routes; admin has no/cartor/product/:sku.ui/is per-app. The storefront'sorders/orders.wo(customer's own orders, filtered by session) and the admin'sorders/orders.wo(all orders with fulfillment actions) are different screens — same underlyingOrdertype, different UI + policy scope.- Each app's
wo.tomlnames its own listen port and its own DB API key.
Running it
# 1. Start the shared DB daemon — headless, just the engine + wire protocol
wo db serve --data-dir ./data # listens on wo://127.0.0.1:5555
# 2. Start each app, pointing at the daemon
WO_DB=wo://127.0.0.1:5555 \
STOREFRONT_DB_KEY=$ADMIN_TOKEN \
wo run apps/storefront # HTTP on :8080
WO_DB=wo://127.0.0.1:5555 \
ADMIN_DB_KEY=$ADMIN_TOKEN \
wo run apps/admin # HTTP on :8081
Browser:
http://localhost:8080/→ storefront home (product list, live inventory)http://localhost:8081/orders→ admin live orders table
Building binaries
wo build apps/storefront # → target/wo/storefront
wo build apps/admin # → target/wo/admin
wo build --all # everything in apps/
Each binary is a static ELF with only the app's own app.wo + ui/ + the shared/ it imports baked in. Drop any binary on a server next to a running wo db serve and it works.
Stage 2 caveat
The current runtime at crates/rt/ is a single-process Stage 2 prototype — it doesn't yet know about:
[workspace]manifests (per-app build — UI sub-phase 05)- The
wo db servedaemon split (UI sub-phase 06) - Per-app
api_key_envauthorisation scope (UI sub-phase 07) .htmlxcompilation from##uiblocks (UI sub-phases 01–03)- Per-app policy composition (UI sub-phase 07)
So cargo run --bin wo -- run docs/examples/ecommerce today walks the whole tree, finds every .wo file under shared/ + apps/, parses the types, and serves the union REST API on :8080 — treating the monorepo as one giant app. Useful for exercising the types; not reflective of the production shape. See docs/plan/ui/00-overview.md for the sub-phase sequence that gets each piece online.
Comparison with the blog sample
The blog sample is still a single-app layout (types/, ui/, logic/ at the root) because the blog has exactly one front-end surface — there's no customer-vs-admin split. Both patterns are first-class; pick based on whether your schema serves one app or many. A flat single-app layout is a degenerate workspace with one apps/ entry.
Source pointers
- Master plan:
../../plan/ui/00-overview.md - Language spec the
##ui/##app/policyblocks obey:../../runtime/database/06-lowcode-fullstack.md - Wire protocol the app binaries speak to the DB daemon:
../../runtime/database/04-client-api.md - v1 template engine that
.htmlxcompilation will reuse:../../../.dev/reference/crates/wo-htmlx/ - Checkout transaction that's the canonical cross-paradigm test:
shared/logic/checkout.wo