ways
Concepts

Ways & families

A way is one gated flow; a WayFamily is a set of related ways installed as one — what each is, and the problem the family solves.

ways packages ways of working. Until now there was exactly one packaging unit — kind: Way — and it quietly did two jobs. Slice 11 splits them into two honest artifacts:

ArtifactWhat it isWhat it resolves
Wayone gated flow — a single phase model + gates + generated documents + the skill(s) that drive it + the slots it needs"here is one process, validated and installable"
WayFamilya distributable set of related ways installed together, plus the payload they share"ship several flows under one identity — without faking a single flow"

A Way is still a Way: incu/dev (a feature flow) and incu/bugs (a bug flow) are legitimately different ways — bugs should have different gates. A family is the thing that lets you publish and install them together.

The problem a family solves

Before WayFamily, an author who wanted to ship several flows under one name had to abuse kind: Way — and a Way must declare exactly one flow. So a multi-flow bundle was forced to:

  • fake a single flow at the root (it copied the dev flow verbatim and hid bugs/assessment inside skills — anything reading the manifest's flow saw a fiction);
  • duplicate needs/requires/defaults/flow from a member, with drift risk (two sources of truth);
  • and it couldn't offer its members individually — the per-flow manifests were documentation, not installable artifacts.

WayFamily removes the workaround: a family references member ways (each keeps its own honest flow) and has no flow of its own.

How it fits together

Anatomy

A family references its members and carries the payload shared across them — but never a flow:

apiVersion: ways/v1alpha1
kind: WayFamily
metadata:
  name: example/suite
  displayName: Example Suite
  version: 1.0.0
  discipline: development
spec:
  members:                       # REQUIRED — the member ways (each keeps its own flow)
    - { id: alpha, path: ways/alpha }        # bundled member way dir (no "..")
    - { id: beta,  path: ways/beta }
    - { id: rev,   source: github:acme/review-way#v1.2.0 }   # or external, acquire-resolved

  skills:                        # shared building blocks — NOT flows of their own
    - { id: shared-prepare, path: skills/shared-prepare }
  knowledge:                     # rules placed ONCE for the whole family
    - { id: conventions, applicability: { mode: always }, file: rules/shared-conventions.md }
  needs:                         # slots common to the family (union'd with members' slots)
    - id: vcs-host
      contract: ways.dev/vcs-host@1
      required: true
  requires:
    cli:
      - { name: gh, required: true, capability: vcs-host }
  defaults:
    bindings: { vcs-host: github }
  # NOTE: no `flow:` — a family has members, not a flow. A `flow` key here is a schema error.
  • members[] (required, ≥ 1) — each entry resolves to a kind: Way, bundled (path, no ..) or external (source, resolved by the acquire layer). Each member keeps its own flow, gates, uses/slots, and conventions.
  • Shared payload (all optional, same shapes as on a Way) — skills[] (building blocks like prepare-pr), knowledge[] (rules), needs[] (slots), requires, defaults.bindings, templates[]. The family's effective slot set is the union of what its members need and what it declares itself.
  • No flow — the load-bearing difference from a Way. Declaring one fails validation.

Install as one, reuse as parts

npx ways.sh add ./example-suite            # installs every member + shared payload, one approval
npx ways.sh add ./example-suite --members alpha   # just the alpha flow (shared payload still lands)
npx ways.sh add ./ways/alpha               # a member on its own — still a valid, installable Way
  • One plan, one approval, one write. The install plan lists all chosen members + the shared payload as a single reviewable unit; each member then flows through the normal per-Way pipeline (validate → conformance → plan → placement). The lock gets an umbrella entry (kind: family + members[]) alongside each member's own entry — so a member stays independently installable and the whole family reproduces from ways.lock.
  • Selective member install. The family offers all members; the consumer chooses. On a terminal that's a member picker (all pre-checked — deselect what you don't want); scripted/CI it's --members alpha,beta (or accept the default = all). The shared payload installs regardless of the subset, because members depend on it.
  • Cross-member safety. Coexistence runs across the chosen members + shared payload: duplicate skill names or colliding slot bindings fail the install with an explicit conflict — no silent shadowing.

Family conformance

ways conformance gains family-specific rules. A WayFamily is conformant when:

  • every member reference resolves to a schema-valid, conformant Way;
  • the family declares no flow;
  • there are no cross-member collisions (skill names, member ids);
  • shared knowledge/requires are well-formed.

The full artifact taxonomy

WayFamily joins four existing kinds. Each is a small, single-purpose contract:

KindIsResolves
Wayone gated flow (phases, gates, docs, skills, slots)a single validated, installable process
WayFamilya set of member ways + shared payload, no flowshipping several flows under one identity, installable together
RulePackstandalone per-technology rules, no flow/gatessharing AI context/conventions on their own
SlotContractthe verb-list interface a binding must satisfydefining a capability abstractly (vcs-host)
Bindingmaps {contract, impl} → the deps doctor checkssaying what "GitHub satisfies vcs-host" actually requires

Continue with Slots & bindings for how members and families stay tool-agnostic, or Gates & conformance for what a single way's flow declares.

Normative source: schemas/ways/v1alpha1/wayfamily.schema.json in the ways repo, with a self-contained example under examples/v1alpha1/family/.

On this page