Deterministic, general extension points
Status: open/active
Summary
Section titled “Summary”- Upstream skills expose stable, general hook points but never know this repo’s SDLC shape; move stable prose into deterministic code and plug local behavior into the same extension system a downstream consumer would use.
This standard captures the architectural rule behind two related project principles:
- use prose and Claude as a bootstrap harness only until behavior can move into deterministic code;
- keep the distributed SDLC plugin general, even while this repo dogfoods it heavily.
The short version: upstream skills may expose stable hook points, but they must not know about this repo’s SDLC-specific development shape. This repo’s own special behavior must plug into the same extension system a downstream consumer would use.
Prose is a bootstrap layer
Section titled “Prose is a bootstrap layer”Skill prose is allowed to discover workflow shape quickly. Once a step has a stable input, output, error mode, and testable behavior, it should move down into a script, schema, validator, or pure function. The skill then becomes a harness adapter that calls deterministic tools and uses the LLM only where judgment is still needed.
Stable prose wants to migrate when:
- the same instruction appears in multiple skills;
- a command output is parsed by prose;
- a step can be expressed as “read files, decide, write files, exit”;
- failures can be described by exit codes;
- the behavior needs fixtures or regression tests.
Claude is a front end, not the system
Section titled “Claude is a front end, not the system”The Claude Code plugin is the current harness. It is not the architecture boundary. Canonical state belongs in markdown, frontmatter, schemas, and project-local SDLC config. Deterministic tools should be callable from Claude, a future CLI, CI, or another agent harness with the same arguments and exit codes.
Claude-owned files belong under .claude/: settings, Claude hooks,
project-local Claude skills, commands, agents, and Claude-specific
scripts. SDLC-owned project-local runtime state and extensions belong
under .sdlc/.
General plugin code stays general
Section titled “General plugin code stays general”Anything shipped under solutions/ontological/ must make sense for any project that
installs the SDLC plugin. Dogfooding can reveal general improvements,
but it cannot justify hard-coded checks for this repository’s shape.
Extensions are contracts, not hidden prose
Section titled “Extensions are contracts, not hidden prose”The default extension mechanism should be script-first. A hook point has a name, a path, inputs, output expectations, exit-code semantics, and a statement about whether it may mutate files. This makes the extension usable by humans, Claude, a future CLI, and CI.
Prose-shaped extensions should be treated as exceptional. If a future case truly needs “additional instructions” instead of a script, it should still have a formal contract: when it is loaded, what context it may read, what it may change, and how conflicts with the base workflow are resolved.
Self-development uses the same extension system
Section titled “Self-development uses the same extension system”This repo does not get private development carve-outs in upstream
skills. If SDLC-on-SDLC development needs an extra warning, check, or
policy, it should live under this repo’s .sdlc/ extension tree or
under this repo’s Claude-local .claude/ tooling if it is truly a
Claude harness concern.
The existing .sdlc/skill-ext/task-work/step1-post.sh hook is the
right pattern: the upstream task-work skill declares a general
post-read hook, and this repo contributes the SDLC-specific
solutions/ontological/skills/*/SKILL.md scan locally.
How to apply
Section titled “How to apply”1. Keep a four-layer boundary
Section titled “1. Keep a four-layer boundary”Use this layering as the design test for future changes:
- Data: markdown, frontmatter, schemas, templates,
sdlc.yaml, and the compiled DAG definitions the engine executes. - Deterministic tools: validators, scripts, migrations, classifiers, and query helpers with CLI contracts — reached from a registration as the engine’s built-in and shell steps, and from a shell or CI directly.
- Process registrations: the versioned declaration of how a
kind:executes — readiness contract, step definitions, output contract, scheduling requirements, human-gate placement, and runner context. The registration declares the steps; the engine sequences them (D-VSLI-distributed-work-runner-architecture). - Harness adapters: Claude Code today, a future CLI or other harnesses later. A SKILL.md is one — the head a harness loads, generated from or validated against a registration, never where procedure truth lives.
A change belongs in the lowest layer that can own it honestly. Sequencing is layer 3 and belongs to a registration; prose that sequences is a layer-4 artifact standing in for one that has not been written yet.
2. Add machine-readable extension point descriptors
Section titled “2. Add machine-readable extension point descriptors”Keep the current .sdlc/skill-ext/<skill>/<event>.sh shape, but add a
small descriptor beside each skill when a skill exposes hooks. For
example:
version: 1skill: task-workevents: - id: after-task-read hook: .sdlc/skill-ext/task-work/after-task-read.sh when: after the task file is resolved and read inputs: env: TASK_FILE: absolute path to the resolved task file PROJECT_ROOT: absolute path to the consuming project root argv: [] exit: informational may_mutate: falseSKILL.md can still describe the hook for human readers, but the
descriptor becomes the source for docs generation, CLI discovery, and
contract tests. Existing numbered events such as step1-post can
remain for compatibility, but new events should prefer semantic names
such as after-task-read so renumbering prose does not break a hook
contract.
Re-key from the skill to the process at cutover. Both the skill: key and
the “after step N” anchor assume a SKILL.md is what runs the steps. Once a
process registration owns the step definitions and the engine executes them
(D-VSLI-distributed-work-runner-architecture), a skill step position has
no referent. The descriptor then keys on process: — with kind: naming the
routing key it is registered under — and anchors each event to a registered
step boundary:
version: 1process: implementationkind: implementationevents: - id: after-work-order-read step: read-work-order position: after hook: .sdlc/skill-ext/implementation/after-work-order-read.sh exit: informational may_mutate: falseThe on-disk shape does not move: hooks stay at
.sdlc/skill-ext/<slug>/<event>.sh, so every hook a consuming project has
already installed keeps firing. Only the descriptor’s key and the anchor
change — and the anchor gets more stable, since a registered step id survives
edits that renumbered prose never did.
3. Treat script hooks as the default, prose hooks as a later design
Section titled “3. Treat script hooks as the default, prose hooks as a later design”For now, keep extensions script-first. A script hook can be tested, versioned, made executable, and called from any harness.
If a real downstream use case needs an additional prose instruction, add it deliberately rather than by implication. A possible future shape:
prose_extensions: - id: task-definition-advisory path: .sdlc/skill-ext/task-define/task-definition-advisory.md loaded_at: before gap questions are generated allowed_effect: advisory-onlyThat mechanism should not land until there is a concrete case that a script hook cannot handle.
4. Make hooks easy to adopt and inspect
Section titled “4. Make hooks easy to adopt and inspect”Add deterministic support commands before hook usage spreads:
- list extension points exposed by the installed plugin;
- list hooks installed in the current project;
- validate that installed hooks are executable;
- run an individual hook with fixture inputs;
- scaffold
.sdlc/skill-ext/<skill>/<event>.shand update.gitignoreidempotently.
These can be scripts first and later surfaced by setup or a CLI.
5. Define a migration rule for prose-to-code extraction
Section titled “5. Define a migration rule for prose-to-code extraction”When a skill step becomes stable, extract it in this order:
- Write the contract: inputs, outputs, exit codes, and mutation scope.
- Add fixtures for the current prose behavior.
- Classify what stabilized — a sequence or a leaf. If what settled is a step boundary or a sequence — the order steps run in, what makes one ready, where a human gate sits, what happens on failure — it becomes a process registration: declare the steps and let the engine sequence them (layer 3 above). If what settled is a leaf action — one call with an input, an output, and an exit code — it becomes a registry op. Most stable prose contains both; split it before extracting either.
- Move the deterministic body into
solutions/ontological/lib/as a registry op (with the entity it acts on, or underservices//util/) — or, for a genuinely skill-private helper, a co-located script. - Replace the SKILL.md section with “call this op / script, interpret these statuses” — or, for a sequence, delete it: the registration is the procedure, and the head is generated from or validated against it.
- For a skill-private helper, promote it to a shared home only when a
second real caller appears; a capability is placed in
solutions/ontological/lib/from the start.
Amended 2026-06-02 per D-0007-deterministic-op-substrate: the
original ordering deferred all promotion to a second caller, matching
S-0001-co-locate-first-promote-when-shared. That co-locate-first rule
now applies to skill-private helpers only; capabilities are
entity-placed in solutions/ontological/lib/ immediately (and exposed via a generated
adapter). This still prevents premature shared APIs for the helper case.
6. Route SDLC dogfood findings through a decision gate
Section titled “6. Route SDLC dogfood findings through a decision gate”Before a dogfood follow-up edits solutions/ontological/, ask:
- Would this behavior help a non-SDLC consuming project with the same workflow?
- Does it reference this repo’s
solutions/ontological/,sites/,.claude/, or.sdlc/layout? - Could the behavior be expressed as a hook under
.sdlc/skill-ext/? - Could the generic part be an upstream extension point and the specific behavior be a local hook?
Only the generic extension point belongs upstream. The SDLC-specific implementation belongs locally.
Anti-examples
Section titled “Anti-examples”Bad upstream patterns inside solutions/ontological/:
- detecting
solutions/ontological/skills/*/SKILL.mdin a general workflow and adding behavior only useful when developing the SDLC plugin itself; - special-casing
solutions/ontological/lib/model/entities/ordocs/planning/tasks/in a way that assumes the consumer repo is this repo; - adding prose that tells downstream projects how to handle this repo’s own runtime/install quirks.
Good upstream patterns:
- expose a named extension point with a documented contract;
- move mechanical behavior into a script with fixtures;
- route project-specific behavior to
.sdlc/skill-ext/...; - keep absent hooks as no-ops.
Implementation status against this standard — specific violations,
in-flight migrations, and near-term extraction targets — lives in
task entities under docs/planning/tasks/, not in this file. The
standard captures the durable rule; the tasks track the fixes.