Ark plan
Ark: in Halo lore, the Forerunner installation that forges the Halo rings and holds the means to rebuild everything. A monorepo is both — the place where shared code is built and the place that contains every project. Pairs with mjolnir (the Spartan armor / our workspace + agent tool).
Repo: 7hoenix/ark (personal account for now; transfer to an org later is lossless).
1. Vision
Section titled “1. Vision”A single Tauri + Elm + Rust monorepo that consolidates 7hoenix projects onto one best-of-breed architecture, with shared packages as the convergence target. New apps are just new directories on the golden path; shared improvements land once and propagate.
2. Core philosophy — golden path + incremental convergence
Section titled “2. Core philosophy — golden path + incremental convergence”The projects are iterations of the same ideas (ChessTrained → Trained; Planter → Functor). Each rev got better. Rather than freeze whichever app invented a pattern, we:
- Define the target architecture (“the golden path”) by harvesting the most-evolved version of each cross-cutting concern across projects.
- Extract shared packages that embody those best versions (synthesis, not copy-paste).
- Converge each app incrementally — one app, one concern at a time, each move a PR + changeset — until every app rides the shared packages.
The end state is shared packages as the single source of truth, with each app holding only its own domain on top.
3. Stack decisions (locked)
Section titled “3. Stack decisions (locked)”| Concern | Choice | Why |
|---|---|---|
| Task runner | moon | Language-agnostic; affected-only task graph + caching; one moon run interface. Native toolchains: Bun/Node, Rust (cargo). (Go is supported by moon but unused — mjolnir is all Rust.) Elm is NOT native — it runs as plain moon tasks wrapping the Elm CLI (elm make/elm-watch/elm-test/elm-review), with inputs/outputs declared by hand for caching. Elm tooling comes from bun devDeps (as jg already does). |
| Workspace backend | bun | Already pinned in mise; fast; native workspaces for the JS/TS parts (worker, docsite, tooling). |
| Toolchain pinning | mise | Already in use; source of truth for language versions. |
| Versioning / changelog | Knope | Changeset-style markdown-per-change, but language-agnostic — the highest-value shared packages are Elm/Rust, which npm-keyed Changesets can’t version. Same DX, feeds the docsite changelog. |
| Docsite | Astro Starlight | Static, fast, zero-JS-by-default docs site with built-in search; renders package changelogs. |
| Docsite host + auth | Cloudflare Pages + Access (Zero Trust) | Policy: email ends with @7hoenix.com. No app-side auth code. |
| VCS / workflow | jj + mjolnir | Unchanged. One mjolnir project instead of four; workspaces are isolated checkouts of the one repo. |
4. Repo layout
Section titled “4. Repo layout”ark/├── .changeset|.knope/ # changeset-style records (Knope config)├── .github/workflows/ # CI (moon affected) + release (Knope)├── .claude/│ ├── agents/ # per-project-type agents│ └── CLAUDE.md # repo-wide conventions├── .moon/ # workspace.yml, toolchain.yml├── apps/│ ├── jg/ # SRS app (golden-path reference)│ ├── trained/ # chess training│ ├── planter/ # roguelike-for-learning-programming│ └── planter-marketing-*/ # the 3 marketing sites (TBD: keep or fold)├── packages/│ ├── app-core/ # Elm: Effect/Command/Reply/Js/Page/Auth│ ├── ui/ # Elm: design system + Ui.Tokens + Tailwind theme│ ├── tauri-core/ # Rust: crates/{api,core} + generate_elm│ ├── worker/ # TS: Cloudflare Worker auth/JWT│ └── chess-core/ # Elm/Rust: chess domain├── docs/ # Astro Starlight (gated docsite)├── mise.toml├── package.json # bun workspaces└── moon.yml5. Target architecture (the golden path)
Section titled “5. Target architecture (the golden path)”Comparison of jg / Trained / Planter found jg is the most-evolved on 7 of 8 shared concerns and the most AI-navigable. The golden path = jg’s patterns, extracted.
| Concern | Standard (source) | Notes |
|---|---|---|
| Elm app architecture | jg | Explicit Command/Reply dispatch (no wildcards — compile error forces you to handle new variants), Effect.elm, Js.elm ports, Page/ routing |
| Design system / Book | jg | 14-chapter elm-book + Ui.Tokens swap point |
| Rust↔Elm codegen | jg (+ Trained’s import cleanup) | elm_rs + explicit generate_elm binary (every type hand-listed), Response deliberately freeform JSON, build.rs iOS linker/env baking |
| Auth + backend | jg | Cloudflare Worker: Apple OAuth, HS256 JWT + 90-day sliding expiry, R2 event store |
| Build / iOS | jg | makefile + dev-web harness (same Rust core + Elm as Tauri, routed over HTTP), env-var baking dev/prod |
| Testing | jg | Playwright e2e (dev-web driven, workers=1, retries=0); add elm-review (neither project has strong linting yet) |
| Styling | jg | Tailwind v4 @theme tokens, class-based dark mode, safe-area insets, semantic animations |
| Domain | app-local | Stays in each app — see §7 |
Elm package mechanism: Elm has no private/local packages. Shared Elm lives in packages/<name>/src and each app lists it in elm.json source-directories. Versioned at the repo level by Knope, not published to the Elm registry.
6. Shared packages (end state)
Section titled “6. Shared packages (end state)”| Package | Lang | Best-of-breed source | Contents |
|---|---|---|---|
app-core | Elm | jg | Effect / Command / Reply dispatch, Js port interop, Page routing, Auth state machine |
ui | Elm | jg (supersedes Planter’s 7hoenix/u7) | elm-book design system, Ui.Tokens, Tailwind v4 @theme. Mine u7 (Button/Dropdown/Text/Tooltip/Animation/Effects) for ideas. |
tauri-core | Rust | jg (+ Trained import cleanup) | crates/{api,core} split, generate_elm codegen, build.rs iOS/env |
worker | TS | jg | Cloudflare Worker: OAuth, JWT + sliding expiry, R2 event store |
chess-core | Elm/Rust | Trained | Chess/ (Board, Move, Square, Arrow), FEN/PGN, Stockfish glue |
7. App-local domains (stay put)
Section titled “7. App-local domains (stay put)”- jg — Spaced-Repetition System: Entries, Firings, Interactions, Rotation, Reclassification. (Tightly coupled — SRS is the app.)
- trained — Chess training:
Chess/+Coach/+Train/, Stockfish (lambda), scenario pipeline, iOS. - planter — Roguelike for learning programming: game logic, levels, audio, physics, asset/Blender pipeline.
8. Members, convergence & move-in order
Section titled “8. Members, convergence & move-in order”Two separate orderings (don’t conflate them):
- Convergence order (apps adopting shared packages): jg → Trained → Planter. mjolnir is not on this track.
- Move-in order (source landing in the repo): jg → mjolnir → Trained → Planter. mjolnir moves second because it’s the cheapest migration and unlocks dogfooding.
| App | Stack today | Lift | Plan |
|---|---|---|---|
| jg | elm-watch, elm_rs, Tailwind v4, Tauri v2 | reference | Extract its patterns into the shared packages |
| trained | same toolchain as jg | light | Adopt shared ui/app-core; contribute chess-core; align codegen |
| planter | Vite + vite-plugin-elm, elm-ts-interop, Tailwind v2 + SCSS, Tauri v2 | heavy (toolchain modernization, not engine) | Migrate Vite→elm-watch, elm-ts-interop→elm_rs, Tailwind v2→v4. Its 7hoenix/u7 package feeds ui. Ships 3 marketing sites (keep as apps or fold — TBD). |
- mjolnir — joins as
apps/mjolnir. Already cleanly layered (engine libsmj-core/mj-events/mj-agents/mj-session/mj-vcs/mj-views, an HTTPmj-daemon, frontendsmj-cli/mj-tui/mj-app). It’s a golden-path consumer, not an island: its Tauri+Elm frontend (mj-app, already excluded from the cargo workspace like jg’ssrc-tauri) ridesapp-core/ui/tauri-core(+workerfor remote auth); the engine crates are the app-local backend. Event-sourced like jg → shares the architectural styleapp-coreencodes. Move in second (right after jg): engine + desktop app are a cheap colocation move; the mobile (iOS) + remote frontend —mj-apptalking tomj-daemonover HTTP for remote workspace/agent control — is built once the shared packages mature. Full architecture + self-hosting dev loop: §17. - The bare Godot stub in Planter’s
Engine/is unrelated — ignore/delete on migration.
9. Versioning & docsite changelog flow (Knope)
Section titled “9. Versioning & docsite changelog flow (Knope)”- Change a package/app → write a changeset record (markdown: which component, bump, summary).
- Merge PR with the record included.
- Release workflow runs Knope → bumps versions, rolls summaries into per-component
CHANGELOG.md, opens/updates a release PR. - Docsite reads those changelogs → renders “what changed” per app/package. This is the work-style flow, language-agnostic.
10. Docsite + gate
Section titled “10. Docsite + gate”docs/= Astro Starlight; aggregates each app’s elm-book + changelogs into one living portfolio.- Deploy to Cloudflare Pages.
- Zero Trust → Access application over the Pages domain; policy = Allow if email ends with
@7hoenix.com. Login wall, no code.
11. mjolnir / jj integration
Section titled “11. mjolnir / jj integration”~/.mjolnir/projects.tomlcollapses to onearkproject (merge_strategy = "push_to_main").- Existing flow unchanged: jj + workspaces,
push/post-mergeskills. - New app = new
apps/dir, not a new mjolnir project.
12. Agents
Section titled “12. Agents”.claude/agents/ checked into the repo, traveling with every workspace/PR:
elm-tauri-app— knows the Effect/Command/Js golden pathworker— Cloudflare/jose/JWTchess— chess-core + Stockfishdocs— Starlight + changelogs
Per-app CLAUDE.md layers specifics. (jg/Trained already have strong CLAUDE.mds to seed these.)
13. CI/CD + PR/release
Section titled “13. CI/CD + PR/release”- One
.github/workflows: PR CI =moon ci(affected-only graph + cache); merge-to-main = Knope release. - Every app/package flows the identical PR → changeset → release path.
14. Phased rollout
Section titled “14. Phased rollout”- Phase 0 — Repo:
gh repo create 7hoenix/ark,jj git init,.gitignore,mise.toml,LICENSE, README. (Outward-facing — confirm before running.) - Phase 1 — Skeleton: bun workspaces,
.moon/*, one exampleapp+packageto prove linking + task graph + cache end to end. - Phase 2 — Golden-path extraction + mjolnir: move jg in; extract
app-core,ui,tauri-core,workerfrom it. Then move mjolnir in (cheapest migration; proves the standalone-Rust path + starts dogfooding — see §17). - Phase 3 — Versioning + docsite: Knope init; Starlight scaffold reading changelogs; release workflow.
- Phase 4 — Cloudflare gate: Pages deploy + Access
@7hoenix.compolicy. - Phase 5 — Agents:
.claude/agents/roster. - Phase 6 — Converge the rest: Trained (light; contribute
chess-core) → Planter (toolchain modernization; foldu7intoui; decide marketing sites). Collapseprojects.tomlas each lands. (mjolnir already moved in Phase 2.)
15. Out of scope / parked
Section titled “15. Out of scope / parked”- Functor (Godot) — stays its own repo + mjolnir project. Merge in later (with Git LFS, as an opaque moon project) only if it starts sharing the Rust core / mcp-server.
game-corepackage — deferred until a second game justifies it.- Engine decision (Tauri vs Godot) for the game family — deferred; revisit when reviving Functor.
- ChessTrained — dropped; its useful parts already folded into Trained.
16. Open decisions
Section titled “16. Open decisions”-
DoesYes —mjolnirlive inside Ark? Colocation island or golden-path?apps/mjolnir, moves in 2nd, and it’s a golden-path consumer (mobile/remote frontend onapp-core/ui/tauri-core/worker; see §8 + §17). - Planter marketing sites (×3): separate
apps/or folded intoplanter? -
elm-reviewconfig: standardize one ruleset across apps (neither jg nor Trained has strong linting today). - Package namespace for published Elm (
7hoenix/ui, etc.) vs source-dirs-only.
17. Developing mjolnir inside Ark (architecture + self-hosting pattern)
Section titled “17. Developing mjolnir inside Ark (architecture + self-hosting pattern)”mjolnir intends to grow a mobile app on the golden path for remote workspace/agent control. It’s already structured for this.
Current layering (maps straight onto Ark):
- Engine libs —
mj-core,mj-events(event-sourced +replaybin),mj-agents,mj-session,mj-vcs(jj),mj-views. App-local backend crates (promote topackages/only if reused elsewhere). mj-daemon(lib+bin) — HTTP API (/api/snapshot,/api/events), address viaMJ_BIND. The client/server seam already exists.- Frontends —
mj-cli,mj-tui, andmj-app(Tauri; already excluded from the cargo workspace, like jg’ssrc-tauri).
Target shape:
apps/mjolnir/= engine crates + daemon + cli + tui (existing Rust).mj-app(Tauri+Elm frontend) rides the golden path:app-core(event-sourced architecture — shared with jg),ui,tauri-core(Rust→Elm codegen for the daemon’s API types).- Mobile = the same frontend extended to iOS (jg/Trained already have the Tauri iOS path), talking to
mj-daemonover HTTP for remote control. - Remote auth: secure the daemon API with the
worker/JWT package (reused from jg) + a tunnel. Never expose an unauthenticated daemon.
So mjolnir is the richest golden-path consumer: app-core + ui + tauri-core + worker, over a large app-local engine.
Self-hosting dev loop (ouroboros made ergonomic):
- Stable binary (
cargo install/ released) manages real workspaces against~/.mjolnir. - Dev build (
apps/mjolnirtarget) runs against isolated state + alt port so it never touches real state:MJ_BIND=127.0.0.1:<altport>— already supported ✅MJOLNIR_HOME=~/.mjolnir-dev— needs adding: route all state resolution (db,projects.toml, workspaces) through one env-overridable home-dir fn (today~/.mjolniris effectively hardcoded).- A fixture/sandbox project as the dev instance’s only managed project.
- Promotion = release:
cargo install --path apps/mjolnir(or tagged release) → new stable binary. mjolnir’s artifact in the Knope flow. - Rule: never point a dev build at
~/.mjolnirwhile it’s managing real work.
Concrete first enabling task: add MJOLNIR_HOME (state-dir override) — the missing piece that makes the dev loop safe.