Definition and execution split into packages on one release train
State: open/proposed
Summary
Section titled “Summary”- Two halves, one product. The eight definition kinds (principle,
standard, decision, term, reference, product, driver, capability) and the
ops, gates, and skills that serve them become a package that publishes on
its own. The three execution kinds (task, milestone, backlog) and every
runtime service (leases, worktrees, PR flow, orchestration) stay in the
sdlcproduct, which depends on the definition package. - The cut is a kind-registration seam, not a directory move. Every place
that enumerates all eleven kinds becomes a composition of
EntityKindrecords. Registries compose first; files move last. - Four tier-0 packages, one version, one tag.
sdlc-core,sdlc-model,sdlc-definition, and thesdlcproduct live undersolutions/ontological/, share one semver, and release from the existingv*tag. Only@sksizer/sdlcand@sksizer/sdlc-definitionare published; core and model ship bundled inside them. - Three publish forms for the definition half: an npm library (schemas,
validate, audit, docs generation), an agent context pack (definition skills
and corpus templates, exported through the harness model), and a
standalone
sdlc-defCLI. All three come out of the one tarball. - The definition half depends on nothing in the execution half. It never reads project verbs, never consults the repo trust store, and never imports a runtime service. A standalone build with the product tree absent is the proof.
- The roster in D-ORMG-data-model is the cut line. Planning-meta, Product, and Architecture layers are definition; the Work layer is execution.
Context
Section titled “Context”SDLC is one workspace package, @sksizer/sdlc, holding the entity model,
the sdlc CLI, the Claude Code plugin, and the orchestration runtime. A
consumer who wants only the definition corpus, the thing that gives an agent
context and structure for code quality, must take the whole product with its
leases, worktrees, and PR flow.
D-0003-datamodel-categorization already sorts the entity kinds into five layers, and D-ORMG-data-model carries the roster. PR-0001-sdlc names “the entity-definition machinery” as a sibling product to spin out when it earns its way. D-VSLI-distributed-work-runner-architecture moves execution onto a domain-blind engine bound through ports, which leaves the corpus and its definitions as the part SDLC owns outright. Nothing yet says where the package boundary goes or how the definition half reaches a consumer.
Five files enumerate all eleven kinds: entities/_registry.ts,
entities/_contracts.ts, identifier.ts, corpus/satisfied.ts, and
project/ops/setup.ts. Five services leak across the line: docs, report,
commit, dashboard, and lease import execution kinds directly, and the
reference and backlog schemas name task and milestone in their text or
enums.
D-0014-cli-primary-npm-distribution rejected separate plugin and CLI
packages because their versions would skew. D-2APS-sdlc-standalone-binary-app
made the shipped artifact a compiled binary per platform selected through
optionalDependencies. S-0008-apps-consume-substrate-through-published-surfaces
requires apps and shared packages to consume the substrate only through a
published surface. D-0001-project-structure places a new package at tier
0 under its solution until an outside consumer proves it general.
Decision
Section titled “Decision”Split solutions/ontological/ into four packages along a kind-registration
seam and publish two of them from the existing release train.
Topology. All four are tier-0 workspace packages under
solutions/ontological/, connected by workspace:*:
| Package | Location | Holds |
|---|---|---|
sdlc-core | packages/ts/core/ | op registry, config loading and schema, verb resolver, utilities, preflight, report rendering engine, kind collectors |
sdlc-model | packages/ts/model/ | kind-agnostic spine: entity read and authoring, identifiers, contracts, corpus loading, the generic entities ops, the EntityKind composition layer |
sdlc-definition | packages/ts/definition/ | the eight definition kinds with their ops, reports, projections, and templates; docs generate; the comment-only, corpus-invocation, markdown-fixtures, and skill-prose gates; the definition skills; the sdlc-def bin |
sdlc (product) | the solution root | task, milestone, backlog; lease, orchestrator, PR, commit, git, repo, dashboard, worktree, quality, project, harness, plugin services; the worktree-scope gate; execution skills; the sdlc bin; the plugin manifest |
Dependencies point one way: sdlc → definition → model → core, and
sdlc → model. No back edge. sdlc-definition never imports the verb
resolver, the trust store, or any execution service; a boundary guard and a
standalone build enforce it.
Composable kinds. Each <type>/schema.ts exports an EntityKind record
built by defineEntityKind: type, plural, prefix, numbering, layer, schema,
schema version, contract, satisfied bands, assets directory, and optional
ops directory, site projection, index section, report kinds, commit kinds,
and migrations. composeKinds(packs) derives every roster that today is
hand-enumerated: ENTITY_SCHEMAS, CONTRACTS, PREFIXES,
SATISFIED_BY_TYPE, the setup directory list, site projection overrides,
index sections, report and commit collectors, and op discovery roots.
Backlog stays in execution; its promoted/<kind> enum becomes a factory over
the composed prefix table so no corpus migration is needed.
Publish forms. One tarball, @sksizer/sdlc-definition, serves all
three:
| Form | Surface |
|---|---|
| npm library | export map: . (kinds, schemas, corpus loading), ./docs, ./gates, ./pack |
| agent context pack | harness export --pack definition over D-HRNS-harness-model-and-exporters; SKILL.md frontmatter pack: definition selects the skills; kinds scaffold emits corpus templates |
| standalone CLI | bin sdlc-def, mounting the model and definition op roots; runs kinds scaffold, validate, audit, check-identifiers, get-schema, docs generate, and the four definition gates |
kinds scaffold is the corpus bootstrap: it creates docs/planning/<plural>/
and the body templates for every composed kind. It reads the composed kinds
and nothing else, so it needs no project verb and no trust store, and it
lives in sdlc-model. The execution product’s project setup calls the
same scaffolder for the full roster, then does its own work on top: gitignore
entries, harness install, worktree directories. A definition-only consumer
never needs an execution op to create the tree it validates.
Release. One semver across the four packages, enforced by a lockstep
guard at commit and release time. One v* tag. The release workflow that
today builds the platform binaries and the @sksizer/sdlc wrapper also
assembles and publishes @sksizer/sdlc-definition in the same run. Core and
model are bundled into the two published artifacts rather than published
themselves. The definition package does not join the per-library mirror
channel.
Plugin. One plugin manifest. The skills/ tree is assembled from the
definition and execution roots at pack time; a name collision fails the
build. Definition skills invoke the CLI through
${SDLC_CLI:-${CLAUDE_PLUGIN_ROOT}/cli/sdlc} so the same skill runs under
either bin.
A kind-registration seam moves nothing until it has to. The five all-kinds files and six leaking services are the real coupling. Once each kind describes itself and the rosters are derived, the packages fall out of the composition. Moving files first would carry the coupling with them.
One version because the data model is one thing. A consumer running
sdlc 1.4 against a definition corpus scaffolded by sdlc-definition 1.5
has two data models. D-0014-cli-primary-npm-distribution rejected that
skew for the plugin and CLI; the same reasoning holds here. Lockstep versions
and one tag make the skew impossible by construction.
The release train over the mirror channel. The per-library mirror
channel gives each library its own public source repo, its own tag prefix,
its own semver, and a standalone snapshot build that resolves dependencies
from the registry. That independence is the point of that channel, and it is
exactly what this package must not have while its seams are unsettled.
Putting sdlc-definition there would need three mirror repos (core, model,
definition), three registry releases in dependency order, and a version the
sdlc binary cannot pin to its own. The release train already publishes
several packages from one tag in one run. The trade is provenance: the
mirror publishes with trusted publishing and a public commit; the release
train publishes from this private repo with a token. When the package
becomes a library with a life of its own and is promoted out of tier 0, the
mirror channel is the next step, and that move is a version decoupling
decision of its own.
Bundle core and model. Publishing them separately creates two more
public names for seams the user has said are not settled. Bundling keeps the
public surface to two names and sidesteps the workspace:* copy problem in
the npm build until it is fixed properly.
Definition never depends on execution. The definition half runs validate, audit, and generation. None of that runs a project verb or needs trust to run a repo’s scripts. Keeping it that way is the cleanest test that the cut is real, and it is what lets the context pack run in a repo that has never adopted SDLC execution.
Options considered
Section titled “Options considered”Option A: Directory move first, then untangle
Section titled “Option A: Directory move first, then untangle”Move the eight definition kinds and their services into a package and fix imports until it builds. Rejected: the enumerating files and leaking services come along, so the package still imports the execution kinds. Every later step fights the move.
Option B: Separate versions and the mirror channel
Section titled “Option B: Separate versions and the mirror channel”Publish sdlc-definition as an ordinary library with its own semver.
Rejected for now, per the reasoning above. Revisit at promotion.
Option C: Split the sdlc bin into two products
Section titled “Option C: Split the sdlc bin into two products”Two plugins, two CLIs, two manifests. Rejected: D-0014-cli-primary-npm-distribution
already found that two ship vehicles skew. sdlc-def is an extra bin over a
subset of the same op roots, not a second product.
Option D: Aspects only, no package
Section titled “Option D: Aspects only, no package”Gate the execution features behind sdlc.yaml aspects and ship one package.
Rejected as the whole answer: a consumer who wants the library form or the
context pack still installs the runtime. Aspects remain the way a project
enables only the definition half of an installed sdlc.
Consequences
Section titled “Consequences”Easier. The definition corpus becomes consumable on its own in three
forms. A new kind is one EntityKind record and one directory; nothing else
is edited. The ontological control plane can read the model through a
published surface instead of the solution tree, satisfying
S-0008-apps-consume-substrate-through-published-surfaces. The packages
stay tier 0 until a consumer outside solutions/ontological/ imports one,
which is D-0001-project-structure’s promotion trigger; SDF is in-solution
and does not trip it.
Binds future work. Every new service declares which half it belongs to. Execution code may import definition code; the reverse is a build failure. The lockstep guard means a release bumps all four packages.
Accepts the roster as the cut line. D-ORMG-data-model’s layers decide which half a kind lives in. This is the last blocker named on M-K7DP-initial-product-definition’s data-model criterion.
Migration
Section titled “Migration”Registries compose first, files move last. Each step leaves the corpus
valid and docs generate --check byte-identical.
| Step | Change |
|---|---|
| 1 | Invert the one util-to-model edge (slugify re-export) so core can stand alone |
| 2 | Fix the prose leaks: reference related description, backlog enum text |
| 3 | defineEntityKind and composeKinds in place; every schema exports its kind |
| 4 | Report, commit, and site collectors read the composed kinds |
| 5 | Index generator sections come from kind.indexSection |
| 6 | Setup and enabledTypes read the composed kinds; aspects select halves |
| 7 | Backlog promoted/<kind> becomes a factory over the prefix table |
| 8 | Op discovery composes per-pack roots and emits per-pack manifests |
| 9 | Create core with a transitional alias, then codemod imports |
| 10 | Create model |
| 11 | Create definition and the assembled plugin skills tree |
| 12 | sdlc-def bin, export map, lockstep guard, second tarball in the release workflow, harness export --pack |
| 13 | Dashboard and worktree services settle in execution |
| 14 | Boundary verification: per-package lint overrides, boundary guard, acyclic project graph, standalone build with the product tree absent, round-trip install of the definition tarball under plain Node |
Step 12 waits for the npm build to resolve workspace:* dependencies. Until
it does, no package carrying a workspace dependency can publish.
Open questions
Section titled “Open questions”- Whether
sdlc-corestays a separate package or folds intosdlc-modelonce the standalone builds exist. Two packages are cheaper to merge than one is to split. - Which agent-asset projection the context pack uses, the TypeScript harness model or the Rust suspenders projection. This decision assumes the TypeScript model per D-HRNS-harness-model-and-exporters.