---
type: Axis
title: repo-layout (repository structure axis)
description: The repo-layout design axis — how a project's source tree is organized, as a function of its project type (script / library / webapp / desktop / monorepo-member) and its containment context (standalone vs monorepo). A layout-altitude (altitude:layout), system-singleton axis whose commitment surface is the interface/wire contract (the import paths every consumer binds). The FOURTH design altitude (RFC #100 / RFC #75 5A), after function-sidecar / architecture / infra.
tags: [axis, architecture, layout, repo-layout, monorepo, rfc-100, deep]
timestamp: 2026-06-27T00:00:00Z
---

# Repo-layout (repository structure axis)

> **DEEP layout axis (4.9.0a1 / #915).** The single axis of the FOURTH design
> altitude (`altitude: layout`). It records how a project's source tree is laid
> out — and the load-bearing nesting it turns on: **a standalone project's layout
> is a valid SUBTREE of a monorepo.** The render phase (#916) reads this tier to
> emit (or manage) the actual directory tree; the analysis phase (#915) only
> classifies + records it.

## Concept

Repo-layout is the **repository-structure** axis (RFC #100): how a project's
source tree is organized, chosen as a function of the project's **type** (script
/ library / webapp / desktop / monorepo-member) and its **containment context**
(standalone vs monorepo). The ladder runs `standalone-by-idiom` (a standalone
project laid out by its language idiom — `src/<pkg>/`, `tests/`, a single
entrypoint) → `monorepo-member-shared-by-concept` (a member within a monorepo
whose shareable code hoists out of the member and into a common, by-concept tree
— `src/interceptors/users/create.py`, `src/utils/uuid.py` — while the member
keeps only its deployable shell under `projects/<sub>/`). Its **commitment
surface is the interface/wire contract** (§4): the layout fixes the import paths
every consumer binds, so moving a module is a breaking change to every importer —
the migration asymmetry is the cost of re-homing code + rewriting every call
site. Its **scope is system-singleton** (§5): a project picks one layout posture.

The two tiers are **nested, not disjoint**: the standalone layout is exactly the
subtree a member occupies inside the monorepo convention, so escalating is
hoisting shareable code up — not rewriting it.

## Why it is an axis (discriminator test — §2)

1. **Ordered tiers** — each rung adds structure (a standalone idiom → a
   by-concept shared tree shared across monorepo members).
2. **Cross-cutting bundle** — a rung dictates the directory convention, the
   import-path shape, the build-target granularity, and where shared code lives.
3. **Asymmetric migration cost** — hoisting a standalone tree into a monorepo's
   `src/<type>/<concept>` convention re-homes modules and rewrites every import;
   the reverse (extracting a member back to standalone) is a cheap copy. The cost
   is asymmetric and one-way-expensive.
4. **Requirement-keyed selection** — falsifiable probes (below) pick the rung.

## The monorepo convention (the by-concept tree)

A monorepo is marked by a top-level `projects/` directory. Its top-level layout
is fixed by convention: `projects/` (the deployables — one shell per member) ·
`docs/` (documentation) · `deployment/` (deployment config) · `src/` (the shared
code, organized **by-type-then-concept**: `interceptors/users/create.py`,
`utils/uuid.py`). A member's own subtree under `projects/<sub>/` is exactly the
`standalone-by-idiom` layout — which is why the analysis phase emits ONE
`<root>/layout.md` for a simple project but a top-level `<root>/layout.md` PLUS
one `<root>/projects/<sub>/layout.md` per member for a monorepo.

## Schema view (the §6 entry — validated by `axis_catalog`)

```yaml
id: repo-layout
kind: architecture-axis
altitude: layout
scope: system-singleton
selection:
  probes:
    - "Is this a single standalone project (no top-level projects/ dir)? → standalone-by-idiom."
    - "Does the repo hold multiple deployables under a top-level projects/ dir? → monorepo-member-shared-by-concept."
    - "Is there shareable code used by more than one deployable? → hoist it into src/<type>/<concept> (the monorepo tier)."
    - "Is the project a script / library / webapp / desktop standing alone? → standalone-by-idiom laid out by its language idiom."
    - "Would a second deployable need the same code? That requirement forces the monorepo tier (the by-concept shared tree)."
composes_with:
  - "(classify) project_type (script / library / webapp / desktop / monorepo-member) × containment (standalone / monorepo) → tier"
  - "(render #916) the tier selects the directory-tree skeleton the render phase emits/manages"
applies_when: []
tiers:
  - tier: standalone-by-idiom
    commitment_delta:
      surface: interface-wire-contract
      delta: "the project is laid out by its language idiom (src/<pkg>/, tests/, a single entrypoint); the import paths are the member-local contract."
    admin_tool: "the language's standard project tooling (the packaging manifest + the idiomatic src/tests layout)."
    enforcement:
      - "the layout follows the language idiom for the project type; there is no top-level projects/ dir."
      - "a standalone tree is a valid subtree of a monorepo member (the nesting invariant)."
    tests:
      - "the package imports resolve against the idiomatic layout; the entrypoint runs from the project root."
    forecloses: "sharing code across multiple deployables without first hoisting it (that requirement forces the monorepo tier)."
    migration_cost_to_next: "medium — introduce a top-level projects/ + src/ and hoist shareable modules into src/<type>/<concept>, rewriting their import paths."
  - tier: monorepo-member-shared-by-concept
    commitment_delta:
      surface: interface-wire-contract
      delta: "shareable code hoists out of the member into a common by-type-then-concept tree (src/interceptors/users/create.py, src/utils/uuid.py); the member keeps only its deployable shell under projects/<sub>/. The shared import paths are the cross-member contract."
    admin_tool: "the monorepo convention: projects/ (deployables) · docs/ · deployment/ · src/ (shared, by-type-then-concept)."
    enforcement:
      - "shareable code lives under src/<type>/<concept>, not duplicated inside a member; each member is a thin deployable shell under projects/<sub>/."
      - "every member's own subtree is itself a valid standalone-by-idiom layout (the nesting invariant holds downward)."
    tests:
      - "a member imports shared code via the src/<type>/<concept> path; a second member reuses the same module unchanged."
    forecloses: "member-local copies of code that two deployables share (the duplication the by-concept tree exists to prevent)."
    migration_cost_to_next: "n/a — top of the ladder (the by-concept shared tree)."
examples:
  - "The skill's own repo is a monorepo: the top-level projects/ holds the deployable members, docs/ holds documentation, deployment/ holds deploy config, and src/ holds the shared code organized by-type-then-concept (interceptors/<concept>, utils/<concept>). An MCP server member's own subtree (mcp-server/src/forge_manager_mcp/) is itself a valid standalone-by-idiom Python layout — the nesting invariant in action: the standalone layout IS the subtree a member occupies. A single throwaway script with just a main.py and a pyproject.toml sits at standalone-by-idiom (project_type: script); it would only climb to the monorepo tier the moment a second deployable needed to share its code."
```

## Selection procedure (§8)

Bidirectional check: de-escalate ("is there actually a second deployable that
shares code, or is this one standalone project?") and escalate ("does a second
deployable need this code → hoist it into the by-concept tree?"). Recorded as the
project's `layout_tier` in the `LayoutRecord` (`layout_tier = f(project_type,
containment_context)`); a monorepo records a top-level record plus one per member.

## Composition (§5)

Repo-layout drives the **render-phase directory skeleton** (#916, a later alpha):
`standalone-by-idiom` → the language-idiomatic tree for the project type;
`monorepo-member-shared-by-concept` → the projects/ + docs/ + deployment/ + src/
convention with shareable code under src/<type>/<concept>. The analysis phase
(#915) classifies + records the tier; the render phase reads it.
