Skip to content

Adopting a project

State: open/active · Genre: plan

Status: working draft, 2026-10-02. Part of the strategy hub. Part of 1.0, in milestone 9, Release basics, of the roadmap.

What a solid agent-assisted project has set up, and the wizard that sets it up. The wizard is the Adopt function of the control plane made into a walkthrough, and each of its sections is one control-plane function. An interactive mock of the wizard is at wizard-prototype.html: choices on the left, the project’s state filling in on the right. The mock is a reference until the SDF shell lands.

  • The wizard writes a project profile and holds no state. Every answer lands in a file the loop already reads: sdlc.yaml, the model corpus, and the sdlc/ tree. Re-running the wizard is a drift report, which sdlc config scaffold and sdlc config explain already make cheap.
  • Dry-run, plan, apply. The same idiom as sdlc apply. The wizard shows what it would write, the person approves, it writes. It can also run on a branch in a worktree so the whole result is reviewed as one PR.
  • Presets are complete profiles. solo-dogfood, oss-library, team. Each is a fully defined sdlc.yaml, an inherited pack set, and a routing table. The person starts from a preset and edits.
  • The person’s own repo, never a sample. A sample doubles the surface to maintain and teaches the wrong files.

Milestone: none yet. M-KJFH is the closest: its derive step writes the step directories.

Proposed default for new projects. The committed, sdlc-owned tree is one directory, and the corpus moves under it under the name of the control-plane function it serves.

sdlc.yaml # stays at the root: tools discover a project by it
sdlc/
model/ # the corpus (today docs/planning/): products, capabilities, ...
workflows/<name>/ # a workflow defined as a directory of ordered steps
hooks/<git-hook>/ # git hooks, same step files
extend/<skill>/<point>/ # skill lifecycle extensions, same step files
journal/ # optional committed journal shards (see The operational record)
.sdlc/ # runtime state, gitignored, per machine

paths.model roots the corpus elsewhere for a project that wants it under docs/. The OKF bundle root follows the corpus root, so the bundle settled as docs/ moves with it. This repo keeps docs/planning/ through that key until it migrates. sdlc/ next to .sdlc/ reads as committed intent beside ignored runtime state.

Milestone: M-KJFH.

  1. Install and check. Plugin and binary present, sdlc project doctor clean.
  2. Adopt the repo. sdlc init, a valid sdlc.yaml, the control-plane ref, trust, register with SDF. One roster, with the other as its projection.
  3. Inherit packs. Pick the dev packs, the inherit mode, and preview what each pack turns on.
  4. Model a first slice. A product, two or three capabilities groomed with capability-groom, one decision.
  5. Checks. Detect runners, compose check and setup, arm hooks, run check once, capture the first baseline, then run a judge calibration.
  6. Routing. Probe hosts and harnesses, accept or edit the profile table and routing table.
  7. Autonomy and steering. Pick the posture, a channel, the events, the budget.
  8. Plan. A milestone with tasks that meet the readiness contract.
  9. Run one task by hand. task-work to a PR, then review and respond.
  10. Turn on the loop. Opt one PR in with the sdlc:orchestrate label, watch the plan in dry run, then live. Pause, resume, read the tick log, open SDF and the TUI.
  11. Extend and record. Drop a first file into sdlc/extend/, open the run summary of the task from step 9.

Each step checks the previous one before moving on, and can be resumed later. Surfaces: an sdlc skill, onboard, that walks the steps in a session, and the same steps as a page in SDF and the TUI from the shared console view models.

Milestone: M-KJFH.

Built: doctor, init, sdlc.yaml, the control-plane ref, lease authority, sdlc repo trust, SDF registration. Open: two rosters (SDF’s fleet and the planned projects: key). The wizard picks one as source.

Milestone: M-KJFH.

Nothing ships a starter corpus today. The published principles and the standards in this repo, including the review rubric and the response policy, are what a new project wants on day one.

A dev pack is more than entities. It carries everything a kind of project needs from this system: principles, standards with applies_to and an enforcement level, terms, workflow leaves, hooks, lifecycle extensions, profile defaults, and judge calibration fixtures.

PackEntitiesWorkflows and hooksExtensions and profiles
baseThe published principles, markdown formatting, the review rubric, the response policy, core terms.check composed from fmt-check, lint, typecheck, test leaves; pre-commit running check with on_fail: park.retro after implement and close-out; default profiles.
typescriptTyped errors, lockfile committed, tests colocated, no raw spawns outside a seam.Runner leaves from quality detect for node-scripts (npm, pnpm, yarn, bun), and moon with exec detection on; tsc --noEmit.A fast verify mode on changed hunks.
rustNo unwrap in library code, a capability per feature flag, clippy -D warnings as a standard.cargo fmt --check, clippy, cargo test.A longer check timeout.
oss-librarySemver, changelog, public API review, fork PRs untrusted.release-prepare; pre-push tag check.pr_review.untrusted: skip-init; a review profile on the cheaper model.
sdlcThis repo’s own conventions, for dogfooding.This repo’s check.Everything this repo has in sdlc/extend/.

Inherit modes, per pack or per entity:

ModeWhat happensWhen
copyThe entity is forked into the model corpus, editable, with a drift report against the pack version.Default, so a new project sees where things go and how they are written.
linkReferenced by ID and pack version, rendered in the docs index, not copied. Upgrades flow.A project that wants the pack’s rules unchanged.
extendLinked, plus a local file that tightens or scopes it.Narrowing applies_to, adding a rule.

An override is itself an entity: a local standard carries supersedes: S-0017“ or narrows applies_to.paths. No hidden override table. Pack upgrades are the first real compatibility surface and touch the 1.0 promise.

3. Deterministic checks: workflows, hooks and the chain

Section titled “3. Deterministic checks: workflows, hooks and the chain”

Milestone: M-0M28.

Built: detection, leaves composed into check by reference, baseline-gated mode, git hooks with when and on_fail, stage overrides on the chain. The wizard sequences it: detect, confirm leaves, compose check and setup, arm hooks through sdlc apply, run check once, capture the baseline.

Drop-in steps. A workflow, a hook, or an extension point can also be a directory of step files, ordered by name the way web frameworks route by file path:

sdlc/workflows/check/
10-fmt-check.sh
20-lint.sh
30-typecheck.run # one line: the name of a workflow to splice in
40-review-notes.md # a prompt step, run under a profile
ExtensionStep kindHeader
.shScript, run through the shell in the checkout root.# sdlc: on_fail=park when=deps-missing on the first lines.
.mdPrompt or skill step, launched as a session. Never valid under hooks/.Frontmatter: profile, on_fail, skill.
.runSplices another workflow’s resolved steps, cycle-checked.The file holds one workflow name.

The numeric prefix orders. The rest of the name is the step’s name in the journal and in notifications. A name defined both in sdlc.yaml and as a directory is an error, never a merge. sdlc config explain renders provenance across both forms.

Milestone: M-0M28.

Built: sdlc verify changes judges a diff against every standard whose applies_to.paths encloses a changed file, plus CLAUDE.md and AGENTS.md, and the judge stage runs it blocking.

Enforcement per standard. Today blocking and severity_floor are one global setting on the verify: block, so every matching standard is enforced the same way. The proposal is a field on the standard entity:

enforcement: blocking # blocking | advisory | off
severity_floor: tension # violation | tension | drift: the lowest severity that counts

blocking fails the judge stage and the hook that runs it. advisory posts the finding and passes. off keeps the standard as documentation only. The field sits next to the text it governs, and a copy-inherited standard is the project’s own file, so the project tunes it there. sdlc.yaml and the machine file may override by ID for one checkout.

Verify modes. The verify verb grows named modes so the same rules run at the right cost in the right place:

ModeScopeProfileWhere
fastChanged hunks, blocking standards only.mechanicalpre-commit
completeThe whole diff, every matching standard.judgepre-pr, the judge stage
reviewAdversarial read with the rubric.reviewThe review stage

Calibration run. Before blocking is on, judge the last ten merged PRs with the chosen standards and show the findings beside what reviewers actually said. The person sees the false-positive rate and tunes severity_floor or demotes a standard to advisory. The calibration reads the journal (dimension 10) once it exists.

The additive rule, stated once. A standard is a judgement rule. A workflow step is a deterministic rule. A hook, a stage binding or an extension point says when each runs. Adding a check is adding one file or one list entry, and no skill is edited.

Milestone: M-0M28.

Four mechanisms exist: git hooks:, chain stages.<name>.workflow, pr_review.prompt extend or replace, and skill extensions (the .lifecycle file in Skill lifecycle extensions, plus the older .sdlc/skill-ext/<slug>/<event>.sh convention). They should read as one system:

  • One vocabulary of points: before, after, rules, after-<step>. rules holds .md only. replace exists only where a skill declares a step replaceable. This settles the open question in the extensions page toward a fixed vocabulary.
  • One file shape: the drop-in steps of dimension 3, under sdlc/extend/<skill>/<point>/. Directories supersede the single .lifecycle file, which revisits the row settled on 2026-10-02. A skill with chain-stage hooks uses the stage’s bound workflow as the target.
  • One listing: sdlc extend list prints every point every installed skill declares, filled or unfilled for this project. The wizard walks it and scaffolds the directories with a commented example step in each.
  • One explanation: sdlc config explain covers extensions, so one command answers what runs at a point and who put it there.

6. Hosts, harnesses and models by kind of work

Section titled “6. Hosts, harnesses and models by kind of work”

Milestone: M-EG9L.

Built: engine and host per workflow name; verify engine, model and timeout set once and overridable per subtree by a nested sdlc.yaml; headless workflow dispatch limited to claude and codex (verify.engine also takes gemini and opencode); no fallback across hosts. Missing: the routing layer.

Profiles are named by kind of work, never by model:

profiles:
mechanical: { engine: claude, model: haiku, effort: low, headless: true }
design: { engine: claude, model: sonnet, effort: medium, headless: true }
review: { engine: codex, model: default, effort: high, headless: true }
judge: { engine: claude, model: haiku, effort: low, headless: true }
research: { engine: claude, model: opus, effort: high, headless: true }
interactive: { engine: claude, model: default, host: tmux, headless: false }

Workflows, stages and .md steps reference a profile instead of carrying engine and host.

The routing table maps task attributes to a profile: complexity × autonomy × stage.

routing:
default: design
rules:
- { stage: implement, complexity: small, autonomy: autonomous/pr, profile: mechanical }
- { stage: implement, complexity: large, profile: design }
- { stage: judge, profile: judge }
- { stage: review, profile: review }

The first matching rule wins. Both tables live in sdlc.yaml as project intent. The machine file and sdlc.local.yaml override bindings per key, because which models and hosts exist is a fact about a machine. The wizard probes harnessAvailable and the host binaries and proposes bindings. A profile whose engine is missing fails with a named reason, consistent with the host contract.

Milestone: M-0M28.

Policy is spread across the task autonomy field, sdlc:auto/<stage> labels, orchestrator limits, max_rounds, trust and the usage budget. The wizard presents it as one dial and shows which keys and labels each position sets:

PostureCeilingWhat it sets
observeauto/triageDry-run ticks, no dispatch, notifications on.
supervisedauto/implementDispatch to a PR, review by a person, max_rounds: 1.
autonomous-to-prauto/respondReview and respond rounds unattended, max_rounds: 3, budget set.

Autonomy-to-stage is the one nomenclature. The task field becomes autonomy: to/<stage>, mirrored by the sdlc:auto/<stage> label, with the posture as the project default and a task able to lower it. A person at the merge stays fixed in every posture.

Milestone: M-EG9L.

Built. Three channel shapes and ten events:

Channel typeFields
desktopNone. A notification on the machine running the loop.
ntfytopic_url.
webhookurl, format (slack, discord, raw).
EventFires when
pr.needs_responseA NEEDS-RESPONSE PR has exhausted orchestrator.review.max_rounds. Repeats every tick after that.
pr.ci_failedA CI-FAILED PR has exhausted max_rounds. Repeats every tick after that.
task.parkedA hook parked a task, or a work-tick step failed outright.
stage.ceiling_reachedAn item reached its autonomy ceiling.
phase.ready_for_reviewConfig only, no detector yet.
lease.expiredA lease ran out.
session.endedA dispatched session exited.
hook.blockedA block hook step failed.
orchestrator.dead_stopConfig only, no detector yet.
usage.budget_exceededThe rolling budget tripped.

The wizard picks a channel, routes the four that matter first (task.parked, hook.blocked, pr.ci_failed, usage.budget_exceeded), sets the budget, and runs one tick in dry run. pr.ci_failed repeats on every tick once it starts, so the wizard pairs it with a quiet channel. Journal events (dimension 10) are the source every detector reads, so the two config-only events get detectors for free once the journal exists.

Milestone: M-2QAE.

Built: the task.execution.spawn_from_post_mortem key in sdlc.yaml enables the procedure in lib/model/entities/task/spawn-from-post-mortem.md, which turns friction bullets from a task’s handoff into follow-up task PRs classified Local, Upstream-plugin or Cross-project-request. The catalog names a retro prose skill. Process improvement is intrinsic to the system, so:

  • A post-mortem is an after step on implement, respond and close-out, filled by the base pack with the retro skill. Every run leaves a structured record.
  • Friction is an entity. A friction kind with stage, skill, classification, evidence link into the journal, and cost. P-0010 already says capture, aggregate, act on signals.
  • An aggregation routine, weekly or every N runs, looks for recurrence: the same skill parked three times, the same judge finding declined five times, the same setup failure. Its output is a proposal PR, never a change: a draft standard, an extension step, or a routing rule.
  • Skill grading from runs. skill-author defines grading checks. Running them against run records finds the skills that produce rework or review rounds, and closes the loop between the prose package and operations.
  • A self-improvement budget, so the system never spends more on itself than on the work.

Milestone: M-2QAE.

The Record function and its gap: no single run history per task or PR. Evidence is spread over .sdlc/orchestrator-log.md, .sdlc/usage.jsonl, delivery records, the session registry, PR cursors, park markers under <git-common-dir>/sdlc/park/, lease handoffs and handoff.md, all per machine.

One append-only journal. Every event has the same shape, generated by sdlc in its own schema, never a harness transcript taken wholesale:

{ "ts": "...", "writer": "mbp-sk", "kind": "op.end", "subject": "T-ABCD", "source": "op:quality run",
"run": "R-7Q2M", "outcome": "fail", "duration_ms": 8120, "cost": { "tokens": 0 }, "trace": null }

Kinds: op.start and op.end with exit code, step.* for every workflow and hook step, dispatch.* and router.*, lease.*, judge.finding, review.round, session.*, usage.sample, friction.recorded. Logging happens during operations, not only after.

One API. sdlc journal append is the common write path. Ops emit start and end automatically from the op runner, one instrumentation point. Skills and agents call the command. Disk is the fallback when the CLI is unavailable, never the primary path.

Shards per writer. Raw journal files are .sdlc/journal/<writer>/<yyyy-mm>.jsonl, one writer per file, so machines never conflict. A project that wants history in the repo publishes shards under sdlc/journal/ through ordinary PRs, or to refs/sdlc/journal/<writer> once multi-machine lands (milestone 11). Transcripts stay by reference: harness, session ID, path.

Run summaries. A run entity, R-XXXX, per large operation such as working a task or responding to a review: a deterministic skeleton from the journal (timeline, stages, durations, cost, PRs, verdicts, friction) with a short narrative section an LLM writes. It is one link from a PR body, and from a Jira ticket when that is supported.

Consent, secrets, retention. Raw journals stay gitignored. A deterministic redaction step runs before any transcript excerpt or summary leaves .sdlc/. Raw shards expire after N days, summaries are kept. The wizard sets all three.

Consumers: the post-mortem routine, the usage budget, the calibration run, every notify detector, sdlc trace <task|pr|session>, and SDF’s Record panel.

Milestone: M-GX7X.

Built: nothing in sdlc. Zoo (apps/zoo) runs agents inside managed VM sandboxes as its own desktop app, and C-0074 names the capability: a harness never touches the operator’s machine directly when policy says so.

The dimension is a policy plus a host. The policy says which kinds of work must run sandboxed. The host is Zoo, offered next to terminal, tmux and Orca, and it is the only kind that satisfies the policy today. Work that must be sandboxed is refused when no sandbox host is available; it never falls back to the operator’s machine.

The owner expects this section to need the most work. What Zoo exposes for starting a VM and running a harness in it is the first unknown.

DateDecision
2026-10-03Built in SDF fixture-first (RM-DO9Z, seven milestones from M-V9RR to M-2QAE), not as an HTML mock. wizard-prototype.html is retired once the shell lands.
2026-10-02The wizard writes files after a dry run, from a preset, against the person’s own repo.
2026-10-02Inherit mode defaults to copy.
2026-10-02Profiles and the routing table are project intent in sdlc.yaml; the machine file overrides bindings.
2026-10-02Autonomy is named to a stage, to/<stage>, with three posture presets.
2026-10-02The journal is append-only, sharded per writer, written through one sdlc command, in sdlc’s own schema.
2026-10-02friction and run become entity kinds.
  • Move the corpus to sdlc/model/ by default, with paths.model to root it elsewhere. This repo migrates separately.
  • Directories of step files supersede the .lifecycle file. If yes, update the settled row and the extensions page.
  • Whether the five packs are the right first cut, and whether a pack is a vault package next to craft and sdlc or a directory inside sdlc.
  • Whether enforcement on the standard entity needs a schema bump before 1.0, or ships as part of the modular ontology milestone.
  • Whether a committed sdlc/journal/ is wanted at all before refs/sdlc/journal exists.

← Back to Notes