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:
| Artifact | What it is | What it resolves |
|---|---|---|
Way | one 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" |
WayFamily | a 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/flowfrom 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
One command installs the whole family. Each member stays a valid kind: Way you can ways add on its own. Prefer a subset? --members alpha.
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 akind: 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 likeprepare-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 aWay. 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 fromways.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/requiresare well-formed.
The full artifact taxonomy
WayFamily joins four existing kinds. Each is a small, single-purpose contract:
| Kind | Is | Resolves |
|---|---|---|
Way | one gated flow (phases, gates, docs, skills, slots) | a single validated, installable process |
WayFamily | a set of member ways + shared payload, no flow | shipping several flows under one identity, installable together |
RulePack | standalone per-technology rules, no flow/gates | sharing AI context/conventions on their own |
SlotContract | the verb-list interface a binding must satisfy | defining a capability abstractly (vcs-host) |
Binding | maps {contract, impl} → the deps doctor checks | saying 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.jsonin thewaysrepo, with a self-contained example underexamples/v1alpha1/family/.