Your spec lives in your code. Not in a markdown folder.
SBCE (say "space") is spec-driven development for people who
prefer code over markdown. The spec is the boundary package's own doc: one
package-info.java, co-located with the code it governs, rendered as the
published contract by javadoc. No parallel specs/ tree. No drift.
/sbce new checkout → /sbce apply checkout…or start from a feature. It asks, then suggests the BCs: /sbce new "let a customer check out a cart"
What SBCE adds to your project
No CLI
Nothing to download, version, or put on your PATH. The workflow is the skill.
No dependencies
Adds zero libraries to your build. Your project stays exactly as dependency-free as it was.
No installation
It's a skill: one Markdown file (about 180 lines) your agent reads, short enough to read end-to-end before you trust it. Nothing compiles, nothing runs, nothing ships in your artifact.
The problem
Every spec tool builds a second source of truth
Spec-kit, OpenSpec: each spawns a parallel markdown tree beside your code.
Two trees, two truths. The day after you write the spec, it starts to drift from
what the code actually does.
The usual way: two trees
specs/
└─ checkout.md # the "spec"
src/main/java/airhacks/
└─ checkout/
├─ boundary/
├─ control/
└─ entity/ # the real behaviour
# which one is true? the markdown drifts.
SBCE: one tree
src/main/java/airhacks/
└─ checkout/
├─ package-info.java # the spec IS the package doc
├─ boundary/
├─ control/
└─ entity/
# one artifact. versioned, refactored,
# and shipped with the code.
The idea
One business component. One spec. One package doc.
The boundary package's package-info.java, written as
JEP 467/// Markdown,
is the contract. The same file is source-of-truth and published Javadoc.
The BC name is the only identity; there is no frontmatter, no dotted path, no
separate file to keep in sync.
src/main/java/airhacks/checkout/package-info.java
/// # Checkout
/// > Accept a cart and turn it into a confirmed, cancellable order.
///
/// ## Boundary
/// - `place-order` — submit a cart for fulfilment
/// - `cancel-order` — withdraw an unfulfilled order
///
/// ## Requirements
/// ### R1: Place an order
/// - R1.1 — When a cart with at least one item is submitted, the BC shall create and confirm an order.
/// - R1.2 — If the cart is empty, then the BC shall reject the request.
///
/// ### R2: Cancel an order
/// - R2.1 — While an order is unfulfilled, the BC shall allow it to be cancelled.
/// - R2.2 — If the order is already fulfilled, then the BC shall reject the cancellation.
///
/// ## Entities
/// - Order, Cart
///
/// ## Out of scope
/// - payment capture
/// - shipping
package airhacks.checkout;
Boundary = operations
Verb-noun, transport-neutral. Each op maps to one boundary entry-point method.
Every statement carries a stable id (R1.2) and is mandatory and tested. No should/may; the oracle is binary.
Tests trace back
Each Rn.m binds to a test that embeds its id, bijective both ways. A statement with no test is a gap; a method with no statement is drift.
Concerns that span components live one altitude up, in the base package's own
package-info.java: cross-BC wiring, system invariants, shared vocabulary.
Optional, and still code. Still no specs/ tree.
The loop
Two modes: /sbce new → /sbce apply
One skill, two modes. Declare what the boundary promises, then converge the code to it
until the tests are green. "Done" is a green test run, never an opinion, never a
markdown checkbox.
/sbce new declare
Give it a BC name (checkout) or a feature in plain words
("let a customer check out a cart"). It loops clarifying questions until
no boundary op or requirement has to be guessed, decomposes a feature into one
or more BCs for you to confirm, then writes the spec into each
package-info.java.
/sbce apply converge
Reads the gap between spec and code in both directions, then closes it.
spec → code
Adds a boundary method per op, a test per requirement id, and the code to pass them.
code → spec
Surfaces any orphan method or test (code with no statement) as drift.
Loops the stack's own test suite until green. Idempotent: an in-sync, green BC is a no-op.
The stance
Code over markdown
The spec lives in the code
It's the boundary package's doc, not a file in a folder you forget to open. It moves, renames, and refactors with the code.
Tests are the oracle
Convergence ends on a green run of your stack's real test suite, not on a human approving a document.
No parallel tree, no drift
One artifact per business component. Nothing to diff, nothing to merge, nothing to fall out of sync.
Stack-neutral
SBCE owns the workflow and the spec↔BC mapping. Code shape and verification come from /bce and your stack skill.
Get started
Declare a component, converge to green
SBCE is an airails.dev skill: Markdown your agent
reads, not a tool you install. Add it to your agent, compose it with /bce
and your stack skill, then in your project:
your project
# declare by BC name…
/sbce new checkout
# …or by feature, decomposed into BCs you confirm
/sbce new "let a customer check out a cart"
# converge until the stack's tests are green
/sbce apply checkout
Composes with /bce for architecture invariants and a
stack skill (java-cli-app, microprofile-server,
web-components) for code idioms and the test loop that decides "done".