rafa pages
- rafa
- Repository layout
Repository layout
The workspace rafa's repository is moving to, which package holds which subject, what publishes, and where 0.47.0 still differs.
rafa's repository is becoming a monorepo: one repository holding several packages, each with its own tests and its own boundary, managed as a Bun workspace with Turborepo running each package's lint, type check and tests. This page describes the layout the migration is heading to (epic #801). Callouts mark where version 0.47.0 still differs.
If you use rafa: one package
You install one package, @open-tomato/rafa. In the target layout it bundles rafa's core and every feature package, so the commands, the config and the files rafa writes in your project are the same whichever package the code sits in. A package publishes under its own name only when it has a real use on its own.
Add-ons are the exception: they publish on their own and load into a project from config (Add-ons).
The target layout
| Where | Package | Holds | Publishes |
|---|---|---|---|
| root | @open-tomato/rafa | the command line: argument parsing, the command roster, and whatever no feature package holds | yes, to npm |
packages/rafa | @open-tomato/rafa-core | core: the contracts, the effort store and the ports every other package imports | no, bundled |
packages/rafa-board | @open-tomato/rafa-board | the tracker: issues, epics, boards, the roadmap, the GitHub project | no, bundled |
packages/rafa-run | @open-tomato/rafa-run | running plans: the loop, preflight, claims, the plan list and checks, rafa next's steps, switching | no, bundled |
packages/rafa-pr | @open-tomato/rafa-pr | pull requests: show, merge, triage, and the stale-docs plan | no, bundled |
packages/rafa-release | @open-tomato/rafa-release | releases: fragments, settle, tags, publishing, the ladder | no, bundled |
packages/rafa-effort | @open-tomato/rafa-effort | the effort store's readings and moves between devices | no, bundled |
packages/rafa-inventory | @open-tomato/rafa-inventory | agents, skills, tiers and the lessons rafa learns | no, bundled |
packages/rafa-hub | @open-tomato/rafa-hub | the hub server a team's devices sync through (rafa-hub) | no, you build its container |
packages/rafa-sync-service | @open-tomato/rafa-sync-service | the client module that gives the service sync strategy | no, loaded as a module |
packages/typescript-config, packages/eslint-config | @open-tomato/rafa-typescript-config, @open-tomato/rafa-eslint-config | the shared TypeScript and lint settings | no |
addons/<name> | @open-tomato/rafa-<name> | modules rafa does not bundle | yes, each on its own |
The stretch code and the CI readings are not placed yet; a later pass of the migration decides their package. [TBD: the package for rafa stretch and rafa ci]
In 0.47.0, packages/ holds core, the hub, the sync service, the two shared configs and the six feature packages, but the feature packages are facades: each re-exports its code from the root package's src/, where the files still live, and becomes its own code when its files move. Core already took the 36 root files no feature package reaches, such as the logger and the module loader. addons/ does not exist yet (#1257, an open spec), and none of the three add-ons can be installed. [verify: the release each feature package's move lands in]
Which subject lives where
Each subject page in this documentation names its target package, by its short name (rafa-board for @open-tomato/rafa-board). By package:
| Package | Subjects and commands |
|---|---|
rafa-board | issue, epic, board, roadmap |
rafa-run | plan, loop, claim, switch, cleanup, next |
rafa-pr | pr, docs |
rafa-release | release |
rafa-effort | effort |
rafa-inventory | agent, skill, instinct |
@open-tomato/rafa | init, doctor, status, config, module, update, self-update, bug, describe |
| not placed yet | stretch, ci |
The placement follows the migration's cut table, which places each folder and named file of src/ in one package; a subject's code can span more than one package where a file is named to another. [verify: the final placement, which the cut table ranks but does not certify]
What decides that a package publishes
Its own manifest. A workspace package publishes when its package.json has no "private": true; no config key lists the packages that publish. A publishing package gets its own version, changelog, tag and publish line, all settled by rafa release settle beside the root's (The release flow). The build treats it as an external, so the command line never carries a copy of its code.
A package that is too coupled to version on its own keeps "private": true and holds a file named REFACTOR_NEEDED.md, which says what couples it. The file is a note for people: no check reads it yet.
If you contribute to rafa
Import core by its public entries, @open-tomato/rafa/<entry>, from the root and from any package. Core's manifest name is @open-tomato/rafa-core, but no import line spells it. A feature package depends on core, and the root reaches a feature package only through the command roster or a declared export.
Tests are placed by kind:
- A unit test imports only its own module's code.
- A contract test checks the promise a module makes to the others.
- A use-case test at the root runs the real code of every
@open-tomato/*package and mocks only what lies outside them: the git remote, the npm registry, the Claude session.
To check only what a change reaches:
bun run verify:affected # turbo run lint check-types test --affected
[verify: bun run verify:affected in a fresh clone; not run here]
The full rules for a new or moved module, the cut table and the order the features move in are in the repository's context/source.md and docs/survey/cut-table.md.
Decisions not taken
- Publishing every package in lockstep (core, the six features and the command line). Dropped on 2026-10-15: in the maintainer's words, "there's no real use for most of them as they are right now". A package publishes on its own when a real use appears.
- Renaming the command line to
@open-tomato/rafa-cli. The rename only existed to free@open-tomato/rafafor core, which no longer publishes. - A small package that kept the old
rafacommand working after a rename. Two packages owning onerafacommand collide on a global install. - Pausing releases while the code moves. Each module becomes a private package that the root bundles, so every step can release.
- Leaves first. No feature folder was a leaf: each had 20 to 107 incoming edges from code outside core, so core moved first and the command line stays last.
- One version for every publishable package. Every plugin fix would release the command line, and every command-line fix the plugins.
- Add-ons under
packages/, or one repository per add-on. The first mixes what rafa is with what plugs into it; the second copies the build, release and checks rafa already has.