T-8JJE-the-sdlc-chain-decision-doc-chain-config-binding-stages-to
State: closed/done · Impact: medium · Complexity: large
A decision doc plus chain: config for the SDLC chain. Stages:
capture -> triage -> define -> ready -> implement -> check -> judge ->
review -> respond -> merge -> close-out, each bound to a workflow (or
explicitly bound to none, for a checkpoint stage). See
D-0019-sdlc-chain-stages-and-labels for the full design (stage
table, workflow bindings, rationale) — this task lands the config side
of that decision.
Approach
Section titled “Approach”- Add
solutions/ontological/lib/config/chain.ts, mirroringlib/config/workflows.ts’s shape:CHAIN_STAGE_NAMES— the fixed, ordered 11-name tuple (capture,triage,define,ready,implement,check,judge,review,respond,merge,close-out).ChainStageNameSchema = z.enum(CHAIN_STAGE_NAMES).DEFAULT_CHAIN_BINDINGS: Record<ChainStageName, string | null>— the built-in workflow name per stage from D-0019’s table (define,implement,check,judge,review->'pr-review',respond->'pr-respond',close-out;capture/triage/ready/merge->null).resolveChain(config)— returns the ordered 11-entry{ name, workflow }[], applying any per-stageworkflowoverride fromsdlc.yaml’schain.stagesover the default binding. A project can only override theworkflowvalue of an existing stage name — it cannot add, remove, or reorder stages (schema rejects an unknown key underchain.stages).
- Replace the strict-empty
chainstub insolutions/ontological/lib/config/load.ts(currently ~line 1098,z.object({}).strict().optional()with the “no field is defined yet” description) with a real shape:chain: z.object({ stages: z.partialRecord(ChainStageNameSchema, z.object({ workflow: z.string().trim().min(1).nullable() }).strict()) .optional() }).strict().optional(), updating the.describe()text (drop “no field is defined yet”) and keeping the existing.meta(keyMeta({...}))block’scategory/layers/applies_via/runs_in, updatingconsumersto point atlib/config/chain.ts#resolveChainand the (Phase-7, this milestone’s other tasks) issues-loop/labels-applier consumers. - Update
solutions/ontological/conventions/sdlc-yaml.md’s reserved- sections table/entry forchainto reflect the real shape (remove the “no field defined yet” language, add a short example mirroring theworkflows:override example already in that doc). - Write the decision doc (done — D-0019-sdlc-chain-stages-and-labels
— this task’s remaining work is the config landing to match it) and
link it from this task’s
relatedfrontmatter once merged. - Add
lib/config/chain.test.tscovering: default resolution (nochain:key -> the 11 built-in bindings, in order), a valid per-stage override (chain.stages.review.workflow: 'custom-review'resolves), an explicit-null override on a bound stage (turns a real workflow into a checkpoint), and a rejected unknown stage name underchain.stages(schema validation error, not a silent no-op). - Ship the
reviewstage’s default binding,pr-review, as a real skill atsolutions/ontological/plugin/plugins/sdlc/skills/pr-review/pr-review.md(this is the one built-in workflow name with no backing skill on disk today —lib/config/workflows.ts’spr-reviewentry and the atlas both flag it). Follow thepr-check/pr-respondvault-note convention (frontmatter:description,allowed-tools,ap-kind: skill,ap-plugin: sdlc; numbered steps; a Failure modes and a Notes section). Contract: given a PR number, read its diff, dispatch an adversarial review under the engine named byorchestrator.review.engine(lib/config/load.ts, default'claude'— already read by theprs/mergesticks, previously inert for lack of this skill) applying the S-0017 rubric (S-0017-code-review-rubric) as its lens, and post the findings as review comments on the PR (reusepr-respond’s existing comment- posting mechanism rather than inventing a second one). Emit a deterministic terminal marker, matchingpr-check/pr-respondconvention. - After authoring the skill note, rebuild the plugin:
cargo run -q -p agent-pants -- build --yes --config solutions/ontological/agent-pants.yaml, and commit the regenerated.claude-plugin/manifest output alongside the vault note.
| Area | Note |
|---|---|
docs/planning/decisions/D-0019-sdlc-chain-stages-and-labels.md | The chain decision doc (already written) |
solutions/ontological/lib/config/chain.ts | New: stage list, default bindings, resolveChain |
solutions/ontological/lib/config/load.ts | Replace the strict-empty chain stub (~line 1098) with the real schema |
solutions/ontological/conventions/sdlc-yaml.md | Document the real chain: shape (reserved-sections table + an example) |
solutions/ontological/plugin/plugins/sdlc/skills/pr-review/pr-review.md | New skill: closes the review stage’s workflow gap |
solutions/ontological/agent-pants.yaml / .claude-plugin | Plugin rebuild after the new skill note lands |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: The decision doc (D-0019-sdlc-chain-stages-and-labels) lists all 11 stages in order with their workflow binding (or explicit none).
- AC-2:
chain:config validates andresolveChain()returns all 11 stages, each naming a workflow (a string) or explicitlynullfor a checkpoint stage. - AC-3: An unknown stage name under
chain.stagesinsdlc.yamlfails config validation rather than being silently ignored. - AC-4:
bun test solutions/ontological/lib/config/chain.test.tspasses, covering default resolution, a per-stage override, an explicit-null override, and the unknown-stage-name rejection. - AC-5:
solutions/ontological/plugin/plugins/sdlc/skills/pr-review/pr-review.mdexists, follows thepr-check/pr-respondvault-note conventions, andcargo run -q -p agent-pants -- check --config solutions/ontological/agent-pants.yaml(thecheckverb’s drift gate) reports no drift after the rebuild.
Out of scope
Section titled “Out of scope”- The
labelsapplier and theissuesloop that will consumeresolveChain()(T-WPBJ, T-0AKS). - Any auto-merge workflow for the
mergecheckpoint (D-0019’s Open questions). sync --fix/ auto-resolving label-task conflicts (T-3YQO, deferred per D-0019 Option B).
Dependencies
Section titled “Dependencies”- none