Beginner May 3, 2026 · 2 min read

A Loom project has a small, predictable layout. Once you know the four directories, you know where everything lives.

my-app/
├── loom.yaml          ← project config (read by every command)
├── threads/           ← your Thread YAML files (you edit these)
├── custom/            ← ejected / hand-written Go (you edit these)
├── .loom/             ← generated artefacts (don't edit; regenerated by loom weave)
└── README.md

loom.yaml

The single source of truth for project-level settings. Auto-generated by loom new:

project: my-app
module: github.com/your-org/my-app
threads_dir: threads
output_dir: .loom
loom_version: 0.1.0
Key Purpose
project Display name; used as the OrbWeaver Project record name
module Go module path; used in import statements in generated Go
threads_dir Where loom check / loom weave look for Thread YAML
output_dir Where generated artefacts land
loom_version Reference; loom report includes it in build reports

CLI commands walk parent directories until they find a loom.yaml, so you can run them from any subdirectory.

threads/

One YAML file per model. The filename is conventionally lowercase-hyphenated:

threads/
├── customer.yaml
├── invoice.yaml
└── invoice-item.yaml

Filenames don't influence anything semantically — Loom reads the model: field inside each file. Naming files after their model just makes them easy to find.

custom/

Empty until you eject something. After loom eject Customer, this is what you see:

custom/
├── models/customer.go
├── handlers/customer.go
├── handlers/customer_test.go
└── routes/customer.go

Files moved here have their "Generated by Loom" header stripped — they're yours now. You're responsible for keeping them in sync with the Thread definition (Loom won't regenerate over them; that's the point).

.loom/

Re-creatable artefacts. Each weaver writes into its own subdirectory:

.loom/
├── sql/                        loom stitch consumes this
├── go/{models,handlers,routes}/    loom serve does NOT consume; reference
├── svelte/src/routes/<table>/  for users running a SvelteKit frontend
├── htmx/                       for users serving HTMX partials
├── pending-reports/            staging for loom sync
└── posted-reports/             archive of synced reports

Treat .loom/sql/, .loom/go/, .loom/svelte/, .loom/htmx/ like build output — gitignored, regenerated. Treat .loom/pending-reports/ and .loom/posted-reports/ as the build log — keep them in version control.

What's not in the project

  • Loom itself. The CLI is a global binary, installed once per machine.
  • Database. Connection comes from DATABASE_URL; no local file like dev.db.
  • Frontend tooling. If you run the SvelteKit output, you set up package.json / pnpm separately under .loom/svelte/.

This separation keeps your repo small. The biggest thing in a typical project is threads/ — pure YAML, never more than a few KB per model.

Was this article helpful?