Imported from AdamBien/airails (
bce/sbce/SKILL.md). Install upstream withnpx skills add AdamBien/airails --skill sbce. Copyright stays with the author.
Drive the spec-driven BCE workflow — one skill owns both the rules and the steps. Invoke as
/sbce <mode> <capability> (new or apply), or let it trigger from intent. Apply every rule
strictly.
Guiding principles
- The spec is the boundary contract — what the boundary promises, never how.
- The spec lives in the BC's package doc —
package-info.java(Java,///Markdown) orpackage-info.md(web) — co-located with the code it governs. There is no separatespecs/tree and no hand-typed package coordinate. In Java the///doc renders viajavadoc, so the same file is source-of-truth and published contract. - One capability spec ≡ one business component, named the same. No translation between "what" and "where".
- The task list is the gap, read off
specvsBCon demand — never a hand-maintained tasks file. - One spec per capability, the single source of truth — never diff or merge two specs.
- Split by determinism: tooling runs tests and places the BC; this skill writes the spec, the code, and closes the gap.
- "Done" is a green run of the stack's test loop — never your own opinion.
Spec ↔ BC mapping
The BC name is the only identity — a single lowercase token (checkout), never a dotted path
or a bc/capability field. The stack skill owns where it lands:
checkout # BC name == the only identity
spec = package doc, co-located with code:
Java: src/main/java/<base>/checkout/package-info.java # `///` Markdown (JEP 467)
web: app/src/checkout/package-info.md # Markdown
code = same folder, {boundary,control,entity}/ # stack skill places it
## Boundaryop ↔ oneboundaryentry-point method.## Requirements(EARS) ↔ behaviour and the tests covering it.## Entities↔ theentitylayer.
System doc (base package)
An optional doc one altitude up — the base package's package-info.java /
app/src/package-info.md — for concerns that span BCs and have no other home. Add a section
only when a real cross-BC concern appears; a one-BC system needs none. Author from
references/system-doc-template.md.
- Charter — one sentence for the whole assembly.
- Vision (optional) — one aspirational sentence: the outcome the assembly chases. Rationale, not contract — no
Sn, no test; the single non-verifiable line in the doc, and the deliberate exception to the traceability invariant. May be proposed by/sbce newdistilling a README seed (human accepts/edits). - Components — this system's concrete wiring: which BC may call which, which integration events cross boundaries (
/bceowns the generic layering; this owns the concrete dependencies). - System invariants — cross-cutting EARS
shallstatements (idSn) no single BC owns. - Ubiquitous language — shared domain nouns defined once, so each BC's
## Entitiesstays terse. - Decisions (optional) — append-only log of confirmed choices and their rejected alternatives (a stack pick, a carving, an integration style):
Dn — <choice>. _(why: …; rejected: …)_. Rationale, not contract — like Vision and a statement'swhy: no test, not a trace target. Ids stable; a reversed decision gets a new entry, the old one markedsuperseded by Dm— never edited or deleted. Litmus: testable behaviour → an EARS statement (tested); a standing project rule → README## Conventions; a point-in-time choice with rejected alternatives →Dn. A decision owned by a single BC may live in that BC's package doc under the same rules. - Stack — the composed stack skill + package base, so
applyreads it instead of re-inferring.
Never duplicate a BC's one-liner — a hand-typed BC index drifts; the gap is read, not stored. For a BC map, mark it generated and regenerate it from the per-BC docs.
Top-level README (optional projection)
An optional repo-root README.md — a human on-ramp that is a projection of the specs, not a
source of truth. Author from references/readme-template.md. Two slices, handled oppositely:
-
Generated (never hand-edited) — the system doc's Charter + Vision, a BC map (each BC name, its
>one-liner, a link to itspackage-info), and a Mermaid diagram of the declared## Componentswiring, fenced by<!-- sbce:generated:start -->/<!-- sbce:generated:end -->. -
Hand-maintained (outside the markers, since no spec covers it — so it can't drift):
## Conventions, build/run/test delegated to the stack skill, plus free-form meta (license, links, motivation). -
Doubles as the inception seed. The hand-written prose outside the markers is what
/sbce new(no argument) reads to bootstrap vision + specs (seenew); SBCE reads it, never rewrites it. -
Components diagram — projection, never inference. Render only the declared wiring in the system doc's
## Components(allowed calls + integration events) as a Mermaid graph: nodes are BCs, edges the declared directed relationships. Never infer edges by scanning code — that is discovery, not projection, and drift-prone. No## Components(a one-BC system) → nodes only, or omit. Basic Mermaidflowchart/graphsyntax (version-stable, corpus-dense); delegate diagram style to/mermaidor/bce-diagrams. -
## Conventionsis the home for project-specific, non-behavioral standards (coverage target, "money is always cents", review policy): declared, not verified — noSn, no test — and distinct from aSystem invariant, which must be behavioral and tested, and from aDndecision, which records a point-in-time choice with its rejected alternatives. -
Optional: a one-BC project needs none. No markers →
applyleaves the README untouched.
Determinism boundary
| Concern | Owner | Deterministic? |
|---|---|---|
| Run tests / "is it green" | the stack's verification loop (the composed stack skill) | yes — existing tooling, no LLM |
| Decompose a feature into BCs (new vs existing) | this skill's judgment, over a read-only scan of the source tree's package docs, user-confirmed | no — semantic |
Record a confirmed choice as a Dn decision |
this skill offers, user-confirmed — never recorded silently | no — semantic |
| Author the spec content (boundary ops, EARS requirements) | this skill's judgment | no — semantic |
| Place the BC — package doc + layer dirs at the source location | the composed stack skill (owns the package base / source root) | yes — stack-defined |
Structural sync, both directions (spec→code: op→method, Rn.m→test · code→spec: method→op, test-id→statement, entity→## Entities) |
this skill, made checkable by the stack's traceability convention | grep-level |
| "Does this code satisfy the requirement" | this skill's judgment, grounded by the requirement's passing test | no — semantic |
Regenerate the README generated block (Charter/Vision/BC map/## Components diagram) from the package docs |
this skill | yes — mechanical projection |
- Ask the stack skill "are you green?" — never name a runner or test kind, and never self-certify convergence.
Invocation modes: new · apply
Read mode + capability from the invocation (/sbce apply checkout). If mode is missing, infer: no
spec yet → new; spec exists but not converged → apply; else ask.
new — declare
Declare a new feature from a BC name (one precise BC), a natural-language feature
description (which may decompose into one or several BCs, new or existing), or the repo
README seed (/sbce new with no argument). The novelty is the intent, not the artifact —
coining a BC and extending one are both "new".
Clarify first (both paths). Resolve every ambiguity the contract needs before authoring. Vague
input ("store a session with title, description, conference, date") hides scope and edge cases —
a guessed spec makes the oracle verify assumptions, not intent.
- Loop, don't stop at one. Each answer exposes new gaps; re-derive and re-ask until resolved. Never proceed on partial answers.
- Never assume silently. Lean on a default only by naming it — "I'd assume create-only; confirm or correct".
- One ambiguity per question, specific over generic. Offer enumerable options with an "other" escape; skip what context already answers; no meta-questions.
- Interrogate per boundary op — its trigger/response (event-driven), invalid/edge triggers (
If…then), state constraints (While…) — and across the BC: scope (create-only vs full lifecycle, in/out), entities and fields (required/optional, identity, validation), and what "done" means. - Stop only when every boundary op and EARS statement, happy and unhappy, is answerable from the user's words such that another engineer would author the same spec. If in doubt, ask one more.
BC name (/sbce new checkout):
- Validate the name — a single lowercase token, no dots/spaces/uppercase. Reject otherwise.
- Ask the stack skill where the package doc lives. If it exists, do not overwrite — report and stop unless the user confirms a rewrite.
- Author the spec into the package doc from
references/spec-template.md: one-line responsibility, boundary ops, EARS requirements, optional entities, out-of-scope. - Ask the stack skill to place the doc and scaffold empty
boundary/control/entitydirs. Write no BC source. - Report the open gap (counts of ops / requirements) and point to
/sbce apply <bc-name>.
Feature description (/sbce new "let a customer check out a cart"):
- Scan existing BCs — read their package docs for responsibilities, and the system doc's
## Decisionsif present: never propose an alternative aDnrecords as rejected. - Propose a BC set — each tagged new (coin a verb-noun name) or extend-existing, each with the one-line responsibility it owns.
- Confirm the carving before any write — decomposition has no test oracle, so the human approves the BC set.
- Realise each entry via the BC-name steps: new → fresh doc + dirs; extend-existing → add ops / requirements to its single existing doc, never a second spec.
- If the carving introduces cross-BC wiring (a call, a shared noun, a system invariant), record it in the system doc — user-confirmed.
- A carving or stack choice the user confirmed against a proposed alternative is a decision — offer to record it as a
Dnin the system doc's## Decisions; never record one silently.
README seed (/sbce new, no argument):
The repo-root README.md doubles as an optional inception seed — a human (often a product
owner or analyst) writes free-form intent there and SBCE bootstraps from it. Read only the
hand-written prose outside the sbce:generated markers.
- Treat the seed prose as the feature description and run the feature-description steps above (scan → propose carving → confirm → author), with the clarify loop filling every gap the prose leaves.
- Additionally propose a
## Visionline distilled from the seed; the human accepts or edits it — never author it silently. - The seed is inception input, not a source of truth: once specs exist they are authoritative; the seed prose stays human-owned and is not kept in sync. Re-running
/sbce newsimply re-reads it and re-proposes through the same confirm-first path.
Guard: one capability ≡ one BC — output is 1..N package-doc specs, never a persisted feature artifact.
apply — converge
Make reality match the declared spec — the "make it so" step. Idempotent.
- Locate the package doc (the spec); if missing, tell the user to run
/sbce new <bc-name>first and stop. - Resolve the composed stack skill in order: the system doc's
Stackline if present, elseAGENTS.md/README, else ask once. Read the system doc's## Decisionsif present — never close a gap with an approach aDnrejected. - Run the stack's test loop. Green and no structural gap in either direction → stop and report "already converged".
- Else read the gap — both directions — and close it:
- spec → code (this skill closes it): each undeclared boundary op → a
boundarymethod; each untested statement idRn.m→ a traceable test (delegate the EARS→table transform to/ears-tests— one parameterized test per### Rn, one labeled row perRn.m); then write code to pass them. Invoke/bce(invariants) + the stack skill (idioms). - code → spec (surface, never auto-author): a
boundarymethod with no declared op, a test tracing an id no statement carries, anentitytype absent from## Entities— report each as drift and stop on it. The spec is the source of truth, so the user decides: declare it (/sbce new/ extend the doc) or delete the orphan. Never edit the spec to match code.
- spec → code (this skill closes it): each undeclared boundary op → a
- Re-run. Repeat 3–5, bounded to ≤3 passes, then surface remaining failures/drift to the user.
Green build + no structural gap or drift is the only definition of done.
Composition
- Own the workflow and the spec↔BC mapping; delegate everything else.
- BCE layering + naming bans (
*Impl/*Service) →/bce. Code idioms + verification → the stack skill. - Never duplicate or contradict
/bceor the stack skill; if either conflicts with a spec, surface it, don't guess.
Spec format rules
-
Sections in order:
# Title+ one-line responsibility,## Boundary,## Requirements, optional## Entities, optional## Decisions(BC-local confirmed choices — same rules as the system doc's section),## Out of scope. -
Boundary operations are verb-noun and transport-neutral (
place-order, notPOST /orders). -
Requirements are EARS statements — one of six patterns, the system always the BC — grouped under a titled
### Rn, each statement carrying a stable idRn.m(groupn, statementm). Reach forIf…thenandWhile…to capture error and edge cases.Pattern Template Ubiquitous The BC shall <response>.State-driven While <precondition>, the BC shall <response>.Event-driven When <trigger>, the BC shall <response>.Optional-feature Where <feature is included>, the BC shall <response>.Unwanted-behaviour If <trigger>, then the BC shall <response>.Complex While <precondition>, when <trigger>, the BC shall <response>. -
Every statement uses
shalland is mandatory and tested — noSHOULD/MAYgradation (the oracle is binary); express optionality with theWhere(optional-feature) pattern. -
Ids are stable: never renumber on reorder; a removed id is retired, not reused.
-
Every boundary op traces to a group
Rn; every statementRn.mtraces to ≥1 test that embeds its id — per-statement and bijective both ways: a newRn.mwith no test is a gap, and a method, trace id, orentitytype with no spec counterpart is inverse drift (surfaced, never absorbed into the spec). The trace form is the stack skill's call (anr1_2…method, a@requirement R1.2JavaDoc tag on the test, a system-test or Playwright name); SBCE only requires the id be grep-visible. -
Optional
why. AnyRn.mstatement (or boundary op) may carry a trailing_(why: …)_— terse origin/intent for the rule. Rationale, not contract: non-verified (the oracle ignores it), not a trace target (the statement still needs its test; awhyis never drift), and bound to its id (retires with the statement, never orphans; theRn.mprefix stays first so id-grep is unaffected). Capture why the rule exists, never how it currently works — immutable origin, not a description to re-sync. -
The
controllayer is implementation — pure how — so it has no spec section; only## Boundary,## Requirements, and## Entitiesmap to code. -
Stack-neutral throughout: no types, transports, framework verbs, or how.
Reference spec
The worked example — a checkout BC — lives in references/spec-template.md. The chain it
illustrates: the BC name comes from the package/folder, no frontmatter; the stack skill places
boundary/control/entity beside the doc; each boundary op becomes one boundary method
(place-order → placeOrder); each statement id (R1.1, R2.2, …) gets a test whose trace
embeds that id.