Skip to content

Definition and execution split into packages on one release train

State: open/proposed

  • 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 sdlc product, 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 EntityKind records. Registries compose first; files move last.
  • Four tier-0 packages, one version, one tag. sdlc-core, sdlc-model, sdlc-definition, and the sdlc product live under solutions/ontological/, share one semver, and release from the existing v* tag. Only @sksizer/sdlc and @sksizer/sdlc-definition are 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-def CLI. 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.

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.

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:*:

PackageLocationHolds
sdlc-corepackages/ts/core/op registry, config loading and schema, verb resolver, utilities, preflight, report rendering engine, kind collectors
sdlc-modelpackages/ts/model/kind-agnostic spine: entity read and authoring, identifiers, contracts, corpus loading, the generic entities ops, the EntityKind composition layer
sdlc-definitionpackages/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 roottask, 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:

FormSurface
npm libraryexport map: . (kinds, schemas, corpus loading), ./docs, ./gates, ./pack
agent context packharness export --pack definition over D-HRNS-harness-model-and-exporters; SKILL.md frontmatter pack: definition selects the skills; kinds scaffold emits corpus templates
standalone CLIbin 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.

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.

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.

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.

Registries compose first, files move last. Each step leaves the corpus valid and docs generate --check byte-identical.

StepChange
1Invert the one util-to-model edge (slugify re-export) so core can stand alone
2Fix the prose leaks: reference related description, backlog enum text
3defineEntityKind and composeKinds in place; every schema exports its kind
4Report, commit, and site collectors read the composed kinds
5Index generator sections come from kind.indexSection
6Setup and enabledTypes read the composed kinds; aspects select halves
7Backlog promoted/<kind> becomes a factory over the prefix table
8Op discovery composes per-pack roots and emits per-pack manifests
9Create core with a transitional alias, then codemod imports
10Create model
11Create definition and the assembled plugin skills tree
12sdlc-def bin, export map, lockstep guard, second tarball in the release workflow, harness export --pack
13Dashboard and worktree services settle in execution
14Boundary 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.

  • Whether sdlc-core stays a separate package or folds into sdlc-model once 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.

← Back to Decisions