adam bien's blog

SBCE: The Spec-Driven Boundary Control Entity 📎

SBCE (say "space"), short for Spec-Driven BCE, is a workflow: every capability is declared as a single spec, and the spec is the boundary contract (package-info.java or package-info.md for Web Components) of exactly one Boundary-Control-Entity business component (e.g. one Java package) with the same name. It ships as a AIrails skill: /sbce. No CLI, no dependencies.

BCE (skill: /bce) sets the architecture invariants: boundary/control/entity layering, package structure, naming rules. /sbce owns the workflow on top: declare a capability, then iterate on the code until it matches. SBCE provides the spec, BCE places the code, and a tech stack skill (java-cli-app, microprofile-server, web-components, web-static) adds code idioms and verification.

The spec lives in the BC's package doc: package-info.java as Markdown JavaDoc (JEP 467), co-located with the code it governs. No separate specs/ tree. The spec is just a better version of the JavaDoc a developer had to write anyway:

greetings
package-info.java   the spec: boundary ops + EARS requirements
boundary/           one method per declared operation
control/            implementation -- no spec section
entity/             maps to ## Entities

For a greetings BC, the entire spec is the package-info.java:

/// # Greetings
/// > Return a personalised greeting for a name.
///
/// ## Boundary
/// - `greet-visitor` — return a greeting for a name
///
/// ## Requirements
/// ### R1: Greet a visitor
/// - R1.1 — When a name is submitted, the BC shall return a
///   greeting containing that name.
/// - R1.2 — If the name is blank, then the BC shall reject
///   the request.
///
/// ## Out of scope
package airhacks.reception.greetings;

Everything in the spec is what, never how: the operation is verb-noun and transport-neutral (greet-visitor, not GET /greetings), and there is no frontmatter, because the BC name comes from the package. greet-visitor becomes the boundary's public method; each statement id (R1.1, R1.2) is stable and traces to at least one test that embeds it; the optional ## Entities section is simply omitted, because this BC owns no stateful domain nouns. Anything in the code without a spec counterpart is drift.

Every notation in SBCE is a stable, public standard: EARS for requirements, JavaDoc and package-info.java for placement, JEP 467 for the syntax, Mermaid for the generated BC map, and the decades-old Boundary-Control-Entity pattern itself. All of it is already in the LLM weights, so the skill never has to teach it: the complete workflow fits in roughly 4,000 tokens of SKILL.md. A self-invented spec DSL would burn thousands of extra tokens in every session.

Two modes drive the lifecycle:

/sbce new greetings         declare: author the spec, scaffold the layers
/sbce new "greet visitors"  declare from free text (github issue, Jira ticket): carves one or several BCs, new or existing
/sbce apply greetings       converge: close the spec-code gap, run tests until green

new loops clarifying questions until no boundary operation or EARS requirement has to be guessed. apply reads the gap in both directions: missing methods and tests are generated from the spec; undeclared methods or orphan trace ids are surfaced as drift, never silently absorbed. "Done" is a green run of the stack's own test loop, never the model's opinion.

/bce tells the code where to live; /sbce tells it what to promise.