Skip to main content
Stages are the top-level organizational unit of a product tree. Each stage is a directory that groups primitives, flags, and artifacts for a given product phase. The stage registry in ready/manifest.yaml is the authoritative source for what stages exist, what they mean, and which one is the default. Tools never infer stage order or purpose from directory names.

What stages are

A stage holds the product truth for one discrete phase of work — a milestone, a release, a pilot, or a product concept. Primitives declare which stage they belong to via the milestone field. Flags and artifacts live inside stage directories. A stage is not a sprint and not a ticket board; it is a durable product grouping that persists in Git.

Stage kinds

Every stage entry in the root manifest declares a kind. The kind tells tools and agents what the stage is for.
KindDescription
normalA buildable product stage. Default-selectable when default_candidate: true.
horizonA future product concept shelf. Not intended for near-term implementation.
experimentAn alternative branch or product plan. Not default-selectable.
archiveA completed or abandoned historical stage. Not default-selectable.
templateA reusable starter shape for new stages. Not default-selectable.
Most projects start with one normal stage (e.g., m1) and one horizon stage (end-state). Add more normal stages as the product grows.

The stage registry

The root ready/manifest.yaml owns all stage configuration. Each entry in the stages list requires nine fields:
FieldTypeDescription
idstringStable stage id. Used in milestone fields on primitives.
titlestringHuman-readable label.
pathstringRelative path to the stage directory.
manifeststringRelative path to the stage manifest file.
kindstringStage kind: normal, horizon, experiment, archive, or template.
statusstringCurrent lifecycle status: active, complete, horizon, etc.
orderintegerSort priority for default selection. Higher order = preferred.
default_candidatebooleanWhether this stage is eligible for automatic default selection.
coding_claims_enabledbooleanWhether coding agents may claim flags in this stage.

Default stage selection algorithm

When default_stage in the root manifest is absent or does not point to a valid stage, tools select the default deterministically in four steps:
1

Use the explicit default_stage

If default_stage names a valid stage id, use it. This is always the first choice.
2

Highest-order normal default candidate

If default_stage is absent or invalid, choose the stage with the highest order value where kind: normal and default_candidate: true.
3

Highest-order normal stage

If no normal default candidate exists, choose the highest order stage where kind: normal, regardless of default_candidate.
4

Fall back to non-normal stages

Only fall back to horizon, experiment, archive, or template stages when no normal stage exists at all.
end-state is excluded from the default path not because of its name but because it is kind: horizon and default_candidate: false. If you rename it to vision and keep the same kind and flag, it behaves identically.

The end-state (horizon) pattern

Every product tree should include a horizon stage to hold long-term product concepts that are not yet implementation work. Set it up like this:
- id: "end-state"
  title: "End State"
  path: "ready/end-state"
  manifest: "ready/end-state/manifest.yaml"
  kind: "horizon"
  status: "horizon"
  order: 9999
  default_candidate: false
  coding_claims_enabled: false
Using order: 9999 keeps it sorted last in any view that uses order as a hint. default_candidate: false ensures tools skip it in the default selection algorithm.

A complete stages example

Here is a root manifest stages block with three stages: two active milestones and one horizon.
schema: "readyroom/product-tree-root/v1"
product: "my-product"
source_root: "ready"
default_stage: "m2"
governance_directory: "ready/governance"
stages:
  - id: "m1"
    title: "M1 — Core Onboarding"
    path: "ready/m1"
    manifest: "ready/m1/manifest.yaml"
    kind: "normal"
    status: "complete"
    order: 10
    default_candidate: true
    coding_claims_enabled: false

  - id: "m2"
    title: "M2 — Team Collaboration"
    path: "ready/m2"
    manifest: "ready/m2/manifest.yaml"
    kind: "normal"
    status: "active"
    order: 20
    default_candidate: true
    coding_claims_enabled: false

  - id: "end-state"
    title: "End State"
    path: "ready/end-state"
    manifest: "ready/end-state/manifest.yaml"
    kind: "horizon"
    status: "horizon"
    order: 9999
    default_candidate: false
    coding_claims_enabled: false
Because default_stage: "m2" is set explicitly, tools go straight to M2. If you remove the default_stage line, the algorithm falls to step 2 and still picks M2 because it has order: 20 — higher than M1’s order: 10 — with kind: normal and default_candidate: true.

Adding a new stage

1

Create the stage directory

mkdir -p ready/m2/premises ready/m2/intents ready/m2/standards
mkdir -p ready/m2/services ready/m2/flags/seed ready/m2/flags/delta
mkdir -p ready/m2/flags/discovery
mkdir -p ready/m2/artifacts/samples ready/m2/artifacts/resources
mkdir -p ready/m2/artifacts/snippets ready/m2/artifacts/designs
mkdir -p ready/m2/artifacts/manifests
touch ready/m2/README.md
2

Create the stage manifest

Copy the milestone manifest template to ready/m2/manifest.yaml and update the milestone, stage.id, stage.title, and source_root fields.
3

Add the stage to the root manifest

Append a new entry to the stages list in ready/manifest.yaml. Assign an order value higher than the previous active stage. Set kind: normal, status: active, and default_candidate: true for a standard buildable milestone.
4

Update default_stage if needed

If the new stage should be the default, update default_stage to its id. If you want the algorithm to resolve automatically, omit default_stage and rely on the order + default_candidate rules.

Moving work between stages

When a primitive belongs in a different stage, update two things: the milestone field inside the primitive’s .ready.yml file, and the file’s location in the directory tree. Keep ids stable — the id is the durable identity; the path and milestone field are refactorable.
Use a Git commit per batch of moved primitives so the diff is reviewable. A move with no content change is easy to verify; a move mixed with edits is harder.

Archiving a completed stage

When a stage is done, update its entry in the root manifest:
- id: "m1"
  title: "M1 — Core Onboarding"
  path: "ready/m1"
  manifest: "ready/m1/manifest.yaml"
  kind: "normal"
  status: "complete"       # or "archive" if you want kind-based exclusion
  order: 10
  default_candidate: true  # keep true so history compiles correctly
  coding_claims_enabled: false
If you want to fully exclude the stage from default selection, change kind to archive. The directory and its primitives remain in Git history — nothing is deleted.

The coding_claims_enabled field

coding_claims_enabled controls whether coding agents may claim flags inside a stage. Set it to true only when:
  • The stage has at least one seed or delta flag with claimable: true
  • The required services, credentials, and environments are confirmed available
  • The workspace Coding work switch is enabled
Leave it false by default. Enabling coding claims on a stage that is not ready leads to blocked agents and incomplete work. You can enable it per-flag via claimable: true on the flag itself; the stage-level field is an additional gate.
Do not set coding_claims_enabled: true on kind: horizon stages. Horizon stages hold future concepts, not ready implementation work.