Skip to content
open tomato/ rafa
GitHub
rafa pages
  1. rafa
  2. 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

The target layout: what the published package bundles, and what stays outside it.
The target layout: what the published package bundles, and what stays outside it.100%
WherePackageHoldsPublishes
root@open-tomato/rafathe command line: argument parsing, the command roster, and whatever no feature package holdsyes, to npm
packages/rafa@open-tomato/rafa-corecore: the contracts, the effort store and the ports every other package importsno, bundled
packages/rafa-board@open-tomato/rafa-boardthe tracker: issues, epics, boards, the roadmap, the GitHub projectno, bundled
packages/rafa-run@open-tomato/rafa-runrunning plans: the loop, preflight, claims, the plan list and checks, rafa next's steps, switchingno, bundled
packages/rafa-pr@open-tomato/rafa-prpull requests: show, merge, triage, and the stale-docs planno, bundled
packages/rafa-release@open-tomato/rafa-releasereleases: fragments, settle, tags, publishing, the ladderno, bundled
packages/rafa-effort@open-tomato/rafa-effortthe effort store's readings and moves between devicesno, bundled
packages/rafa-inventory@open-tomato/rafa-inventoryagents, skills, tiers and the lessons rafa learnsno, bundled
packages/rafa-hub@open-tomato/rafa-hubthe hub server a team's devices sync through (rafa-hub)no, you build its container
packages/rafa-sync-service@open-tomato/rafa-sync-servicethe client module that gives the service sync strategyno, loaded as a module
packages/typescript-config, packages/eslint-config@open-tomato/rafa-typescript-config, @open-tomato/rafa-eslint-configthe shared TypeScript and lint settingsno
addons/<name>@open-tomato/rafa-<name>modules rafa does not bundleyes, 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]

NOTE

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:

PackageSubjects and commands
rafa-boardissue, epic, board, roadmap
rafa-runplan, loop, claim, switch, cleanup, next
rafa-prpr, docs
rafa-releaserelease
rafa-efforteffort
rafa-inventoryagent, skill, instinct
@open-tomato/rafainit, doctor, status, config, module, update, self-update, bug, describe
not placed yetstretch, 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/rafa for core, which no longer publishes.
  • A small package that kept the old rafa command working after a rename. Two packages owning one rafa command 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.