SDLC current orchestration — technical map
State: open/active · Genre: plan
Companion to
README.md. A full map of how orchestration works in this repo today, read with one question in mind: where does orchestration logic live — in Claude (markdown skills, model judgment) or in deterministic TypeScript (thesdlcCLI / lib services)? Where is the boundary, and what crosses it?
Summary
Section titled “Summary”The SDLC plugin splits into two planes joined by a narrow text contract. The skill plane (34
markdown SKILL.md files Claude runs as instructions) holds the control flow, the sub-agent
fan-out, and the real judgment. The op plane (deterministic TypeScript under
solutions/ontological/lib/, exposed as the sdlc <noun> <verb> CLI — ~19 nouns, ~70 verbs) holds
every deterministic capability. Each op is a
defineOp({ path, input: ZodSchema, output: ZodSchema, handler }) in
solutions/ontological/lib/registry.ts; the CLI (solutions/ontological/cli/registry_adapter.ts)
is a generated generic adapter with no per-verb code. Skills cross the boundary by shelling out,
checking the exit code, and reading one SCREAMING-KEBAB marker line (e.g.
ACQUIRED task=… lease_id=…, TASK-WORK-DONE pr=#42). This is an RPC-style contract;
registry.ts:310 notes that the MCP JSON Schema is derived from the op input, and ops get an
injected OpCtx instead of reading the ambient cwd/env, so they can already run off-process.
The orchestration loop (orchestrate/SKILL.md) is ~80% deterministic
delegation. “Is there work?” is already decided in code (the orchestrate watch
op — no LLM). Each tick, Claude syncs main, fans out pr-check sub-agents
(classification is the pure pr classify op), counts in-flight (the task inflight op), checks the control-plane lease, then dispatches task-work against
task next candidates after winning a lease CAS. Verdict routing is a fixed
dispatch table. A large part of the skill (the ESCAPE_RECOVERY_MAP,
lenient-recovery, marker naming) is cost from using an LLM worker that
disappears once both sides are deterministic.
The lease system (services/lease/) is the most engine-ready piece: a
distributed advisory lease where leases are git refs (refs/sdlc/tasks/<id>),
CAS via git push --force-with-lease, host-UUID ownership with same-host inherit
and steal-on-expired — closer to a distributed lock service (etcd) than to flock.
It is authoritative; task status: frontmatter is a “workflow cache”
(schema.ts:74).
State lives in three places — the .sdlc/ runtime dir (gitignored:
orchestrator-log, pr-cursors, worktrees, baselines), git refs (the lease), and
committed frontmatter. No database; every tick rebuilds from disk + git + gh, so
restarting loses nothing.
For the MCP decision: the deterministic base is already an any-agent engine in
all but name. Only the per-tick control loop, the parallel dispatch scheduling, and
the flaky-sub-agent parsing really live in Claude — the last being pure cost the
engine removes. There is no MCP server today (plugin.json declares skills +
CLI only). The real model work shrinks to a few judgment calls (relevance,
thin/subjective-spec reading, writing code, answering reviews) that an engine would
invoke as bounded LLM tool calls.
0. The two planes
Section titled “0. The two planes”- Skill plane —
solutions/ontological/skills/<slug>/SKILL.md, run by Claude as instructions. Holds control flow, sequencing, sub-agent fan-out, and model-judgment steps. 34 skills ship. - Op plane — the deterministic TS “op substrate” under
solutions/ontological/lib/, exposed assdlc <noun> <verb>. Every capability is adefineOp({ path, input: ZodSchema, output: ZodSchema, handler })descriptor in a process-level index (solutions/ontological/lib/registry.ts). ~19 nouns, ~70 verbs. The CLI (solutions/ontological/cli/registry_adapter.ts) is a generated generic adapter with no per-verb code.
The boundary is a single-line text contract: skills shell out bun run ${CLAUDE_PLUGIN_ROOT}cli/sdlc.ts <noun> <verb> …, check the exit code, and
read one marker line (or --output json). No structured IPC, no shared memory
— argv in, exit code + marker out.
Key finding: the deterministic plane is already adapter-neutral. registry.ts:310
notes that the op’s Zod input is where “JSON Schema for MCP derives from it,” and
ops get an injected OpCtx ({ projectRoot, dryRun, io, git, gh, sdlcConfig },
registry.ts:175-182) instead of reading the ambient cwd/env — which is what makes
them callable off-process. What is Claude-only today: (a) the per-tick control
loop, (b) the parallel sub-agent fan-out, (c) a few judgment calls.
1. The orchestration loop — /sdlc:orchestrate
Section titled “1. The orchestration loop — /sdlc:orchestrate”solutions/ontological/skills/orchestrate/SKILL.md. One invocation = one reconcile-and-dispatch
tick. Long-running work stays inside dispatched sub-agents.
Two wake surfaces, both gated by deterministic code:
- Fixed-cadence
/loop /sdlc:orchestrate(2–6h) — the safety net; the only surface that catches CI-FAILED/CONFLICTS changes, lease expiry, and dead-stop notification. - Event-gated Monitor wrapping
sdlc orchestrate watch(orchestrator/ops/watch.ts) — a deterministic pending-work check, no LLM. Three predicates (evaluateTaskReady,evaluatePrAttention,evaluatePrGone) over live state; it prints at most onePENDING reasons=… prs=… tasks=…line, and only when there is real work; exit is always 0, so the presence of stdout is the only signal. Idle = no orchestrator transcripts.watch_loop.shpolls every 60s and never exits.
The “is there work?” decision is already in deterministic code; the model is woken only when the code says so and is handed the exact PR numbers / task basenames.
Per-tick steps — who decides:
| Step | What | Claude vs deterministic |
|---|---|---|
| 1 Sync main | git pull --rebase --autostash; abort on conflict | Claude runs git; abort is mechanical |
| 2 Reconcile PRs | per PR, dispatch Agent → /sdlc:pr-check <N>; route on verdict | Fan-out = Claude; classification is deterministic (pr classify); routing is a fixed table |
| 3 Count in-flight | task inflight --output json → {implementing, awaiting-review, stale, other} + caps | Fully deterministic (task/ops/inflight.ts) |
| 3a Control-plane gate | lease inspect refs/sdlc/control-plane --json; compare sdlc_version major | Fully deterministic (lease/control_plane.ts); branch on exit 0/5/other |
| 4 Dispatch ready tasks | task next → pickup order; per candidate lease task acquire (CAS); on win dispatch Agent → /sdlc:task-work; cap at max_implementations - implementing | Order, dependency filter, lease claim all deterministic (task/ops/next.ts, lease/runtime.ts); Claude owns the loop + cap math + parallel fan-out |
| 5 Digest | orchestrate log-tick … appends one line to .sdlc/orchestrator-log.md | Field grammar + tick numbering deterministic (orchestrator/ops/log-tick.ts) |
| 6 Notify | PushNotification only on a true dead-stop (no open PRs AND no pickable tasks AND ≥1 blocked/needs-definition) | Fixed boolean |
| 7 Return | re-print the digest; /loop schedules the next tick via ScheduleWakeup | Claude/harness |
Where judgment matters: almost nowhere in a meaningful way. Verdict routing
(MERGED|CLOSED→close-out, NEEDS-RESPONSE→pr-respond,
CONFLICTS|CI-FAILED→resolution sub-agent, CLEAN→nothing) is a fixed switch. The
large block at SKILL.md lines ~512-660 (TASK_WORK_VERDICT_RE, ESCAPE_MARKER_RE,
ESCAPE_RECOVERY_MAP, lenient last-line recovery, per-slug back-off) is only there
to handle an LLM sub-agent that adds prose or stops early — it goes away once both
sides are deterministic. The real model decisions are small (shorten /loop when
busy; write the human-readable digest text).
2. The task lifecycle — open → in-progress → PR → review → merged → closed
Section titled “2. The task lifecycle — open → in-progress → PR → review → merged → closed” [SKILL = Claude] {op/CLI = deterministic}
open/ready ──/sdlc:task-work───────────────────────────────────► in-progress + open PR 1 resolve {task resolve} 2 pre-flight {task probe-state} ← blocked_preflight / resume_candidate 2a acquire lease {lease task acquire} CAS-CREATE / same-host inherit 3a quality baseline {quality baseline capture} 3b permission probe {task preflight-permissions} 4 worktree git worktree add ; {quality run --key worktree_init} 5a readiness GATE [/sdlc:task-ensure-ready] → {task gap-report} + LLM judgment 5b start-commit {task start} {commit-to-main ephemeral wt} + lease→working 6 IMPLEMENT [Agent writes code] + {lease heartbeat-loop} sidecar 7 quality gate {quality run --diff-against-baseline} 8 post-mortem [Agent] {commit create --kind task-lifecycle} [/sdlc:spawn-task-pr] 9 sync/rebase [check_ancestry.ts] + git rebase 10 open PR gh pr create (lease footer) ; {lease task transition --phase awaiting-review} (+handoff.md) ▼awaiting-review ──/sdlc:pr-check (per tick)──► verdict {pr classify} ├─ NEEDS-RESPONSE ─/sdlc:pr-respond─► {lease parse-footer}{lease task fence}{lease task reacquire responding} │ [Agent addresses comments] {heartbeat} {transition awaiting-review} └─ MERGED ─/sdlc:task-close-out─► {lease task reacquire closing}{task close-commit}(status→closed/done) git worktree remove + branch -d {lease task archive} ▼closed/doneStep by step, where the boundary sits:
- Pickup: which task = deterministic (
task next,task/ops/next.ts). The relevance check (“does this still match reality?”) = model judgment, backed bytask probe-statefacts. - Claim: deterministic lease CAS (
lease task acquire,runtime.ts:acquireLease). Same-host inherit / cross-hostLeaseConflictis in code; the skill branches on exit 0/4/1. - Readiness gate (
/sdlc:task-ensure-ready): the facts come fromsdlc task gap-report(task/ops/gap-report.ts— “facts, not judgment, runs NO LLM”: missing sections, left-in placeholders, unresolved touchpoint paths, claim-resolver findings). The reading is model judgment:implementation-ready.mdleaves exactly three things to the LLM — “is a section thin?”, “is this AC subjective?”, “is this corpus assumption actually wrong?” (confirm before flagging; candidates fromsdlc task scan-corpus-assumptions). This is the clearest boundary in the system. - Start/flip: deterministic —
sdlc task startflipsstatus: in-progress, commits onorigin/mainvia an ephemeral worktree (git/commit-to-main.ts), and CAS-REPLACEs the lease toworking. - Implementation: model — the
Agentwrites code; the deterministiclease heartbeat-loop startsidecar keeps the lease alive at TTL/2. - Quality gate: deterministic —
quality run --diff-against-baselinegates only on new drift (quality/ops/run.ts,quality/baseline.ts). Walking each AC = judgment. - PR classification (
/sdlc:pr-check): fully deterministic —pr classify(pr/ops/classify.ts:294) is a priority-ordered pure function:MERGED→CLOSED→CONFLICTS→CI-FAILED→NEEDS-RESPONSE→CLEAN(+ERROR). The skill is a one-line wrapper. - Review response (
/sdlc:pr-respond): the lease steps are deterministic (parse-footer→fence→reacquire responding→transition awaiting-review); addressing comments = model judgment (theAgent). - Close-out (
/sdlc:task-close-out): almost entirely deterministic — the status gate, merged-PR detection, themerge-base --is-ancestorreachability check, thetask close-commitop (flip toclosed/done, add completion_note, regen, push via an ephemeral worktree), worktree/branch teardown,lease task archive. The skill “hand-edits no files.” The most engine-ready step.
Lifecycle fact: task/schema.ts:74-80 — under GitHub Ref Leases the status:
frontmatter is a workflow cache; the real claim is the lease ref
refs/sdlc/tasks/<id>. There is no declarative state machine; the legal-transition
policy lives in skill prose (§8).
3. The lease system
Section titled “3. The lease system”solutions/ontological/lib/services/lease/ +
solutions/ontological/conventions/lease-aware-skills.md. The GitHub Ref Leases protocol — the
most engine-like, any-agent piece.
Leases are git refs on a configured remote “authority” (SDLC_LEASE_AUTHORITY
env or lease_authority: in sdlc.yaml). The ref points at a commit whose tree
holds lease.json (canonical JSON) + an optional handoff.md. Namespaces:
refs/sdlc/tasks/<id> (active), refs/sdlc/archive/tasks/<id> (audit trail),
refs/sdlc/ops/<op>/<key>, refs/sdlc/control-plane.
CAS primitive (primitives.ts) = git push --force-with-lease:
casCreate : git push --force-with-lease=<ref>: <authority> <newSha>:<ref> # ref must NOT existcasReplace : git push --force-with-lease=<ref>:<expected> <authority> <newSha>:<ref> # ref must == expectedcasDelete : git push --force-with-lease=<ref>:<expected> <authority> :<ref>The payload is serialized with a hand-written canonicalJson (canonical_json.ts,
byte-for-byte match with Python json.dumps(sort_keys, separators)) and a pinned
commit identity (tree.ts) so the SHAs are deterministic. The authority ref is the
single source of truth; atomicity is git’s server-side ref-update CAS, not a clock.
Payload + phases (schemas.ts:70-93, zod .strict()): lease_id (stable
identity), owner (= the host_id UUID), lease_token (fences stale heartbeats),
phase, claimed_at, expires_at (nullable), pr_number. The phase enum
(:55-62): claimed | working | blocked | awaiting-review | responding | closing.
Rules (runtime.ts:425-426): entering claimed/working rotates lease_token;
awaiting-review/blocked set expires_at=null; otherwise floor(now)+ttl
(default 3600s).
Core ops (runtime.ts, each atomic per call, no cache): acquireLease
(CAS-CREATE, same-host inherit on conflict else LeaseConflict); transitionLease
(caller-driven CAS-REPLACE, no steal); heartbeatLease (bump expires_at);
reacquireLease (CAS-REPLACE with steal-on-expired — logs STOLEN … from=<owner>,
flips the owner, rotates the token; “two operators racing a PR resolve to one
owner”); releaseToArchive (idempotent archive-then-delete).
Ownership: currentHostId() (runtime.ts:207-216) = a stable per-host UUID at host.json in
sdlc’s fleet durable home (sdlcTier().durable().path). The owner is this UUID; the comparison
is exact string equality. This is a host-level, not process-level mutex — same-host runs inherit
each other’s claim (the orchestrate→task-work handoff); different hosts conflict.
Vs a process filelock: it is distributed (any host with push access), the
authority is a remote git ref, liveness is via TTL + advisory steal-on-expired (not
the kernel), identity is host UUID + lease_id + a rotating lease_token, it is
re-entrant on the same host by design, it keeps full history, and it fences via
token rotation + a PR-footer fence check. It is a distributed advisory lease
with fencing tokens (etcd/Chubby style), not flock. Expiry is advisory — only
reacquireLease checks the clock, and even then it just logs and proceeds; the real
safety is the CAS.
How Claude interacts: entirely through sdlc lease … CLI verbs, never
importing the library; it branches on exit codes (0 ok, 2 CAS-FAILED, 3
LEASE-EXPIRED, 4 LEASE-CONFLICT, 5 REF-NOT-FOUND, 6 SCHEMA-ERROR, 7
NAMESPACE-CONFLICT). It finds the lease by branch name (git branch --show-current
→ strip task/ → ref), so dispatched sub-agents and hand-run operators take the
same path. The heartbeat is a backgrounded deterministic sidecar (lease heartbeat-loop start). MCP-readiness: already done — nothing here is
Claude-coupled.
4. Gates — solutions/ontological/lib/services/gate/
Section titled “4. Gates — solutions/ontological/lib/services/gate/”Two different meanings of “gate”:
services/gate/= commit-time lefthook lint gates (fully deterministic, no LLM):gate worktree-scope,gate skill-prose(lints each SKILL.md againstinvariants.yaml— for example it requires orchestrate’s “in-flight limits” phrase),gate markdown-fixtures. This is CI/pre-commit machinery, not lifecycle gates.- The readiness gate is not here — it is
sdlc task gap-report+sdlc task probe-statein the task entity package, with the contract intask/implementation-ready.mdand the reading in/sdlc:task-ensure-ready(§2). Thegap-report.ts“facts, not judgment, runs NO LLM” comment is the clearest statement of the plugin’s design idea.
5. State
Section titled “5. State”No database. Three places:
- (a)
.sdlc/(gitignored):orchestrator-log.md(one line per tick; tick# = line count + 1),orchestrator-watch.json(ETag/emission bookkeeping, safe to delete),pr-cursors/<N>.json({last_seen_comment_at, last_invoked_at, self_posted_at[]}— for new-comment detection),worktrees/<basename>/,commit-worktrees/(ephemeral),quality-baselines/<sha>.json(kept to 5),runtime/lease-heartbeat-*.pid/.log,migration.log,skill-ext/<slug>/<event>.sh(optional project hooks). - (b) Git refs on the authority:
refs/sdlc/tasks/*,refs/sdlc/archive/tasks/*,refs/sdlc/control-plane,refs/sdlc/ops/*— the real coordination state (durable, distributed, auditable). - (c) Committed frontmatter (
docs/planning/<plural>/): taskstatus,autonomy,depends_on,priority,readiness_verified_at,definition_gap,completion_note,prs,last_reviewed— the workflow cache. - (d) Config —
sdlc.yaml:quality_checks:,worktree_init:,orchestrator:(max_implementations=5,max_awaiting_review=20),pr_check:(author_comments,ignored_authors),lease_authority:.
The persistence model: an append-only log + level-triggered projections
(cursors/watch-state recomputed each poll, so loss is fine) + git refs +
git-committed frontmatter. No process holds long-lived state; every tick rebuilds
from disk + git + gh. This is a good setup for moving the control out — an engine
restarting mid-flight loses nothing.
6. The skill→CLI contract
Section titled “6. The skill→CLI contract”Skills shell out and read (a) the exit code + (b) one marker line (or
--output json). Examples:
# Lease acquire (task-work 2a) — check exit code, read one tokenlease task acquire <basename># 0 → "ACQUIRED task=<basename> lease_id=<uuid> phase=claimed" (read lease_id=)# 4 → "LEASE-CONFLICT ref=<ref> reason=owner=<other-host-id>" (stop)
# In-flight (orchestrate 3) — JSONtask inflight --project-root <main-repo> --output json# → {counts:{implementing,awaiting-review,stale}, limits:{...}, tasks:[...]}
# Readiness facts (ensure-ready 3) — JSON of deterministic factstask gap-report <path> --output json# → {has_gaps, gaps[], sections[], placeholders[], touchpoints{...}, claims[]}
# PR classify (pr-check) — pass the verdict throughpr classify <pr-number> [--mock-state <path>]# → "<VERDICT> reason=\"...\"" VERDICT ∈ {MERGED,CLOSED,CONFLICTS,CI-FAILED,NEEDS-RESPONSE,CLEAN,ERROR}
# Digest (orchestrate 5) — pass computed fields, the op owns the grammarorchestrate log-tick --sync ok --prs MERGED=1,CLEAN=2 --implementing 3 --tasks-dispatched foo:claim=won …Marker discipline (solutions/ontological/skills/CLAUDE.md): every terminal marker is
SCREAMING-KEBAB, prefixed by the skill slug — TASK-WORK-DONE, ENSURE-READY-OK:,
TASK-CLOSE-OUT-DONE, PR-RESPONSE-COMPLETE:, TASK-AUTO-DEFINE-DEFINED:; the one
shared un-prefixed marker is ERROR reason="…". This naming exists only because
skills call skills as sub-agents and the parent LLM reads the child’s stdout for a
marker — bare DONE pr= markers collided across skills and cost two task-work
runs on 2026-05-28. The whole marker system + orchestrate’s escape/lenient/anomaly
recovery is cost from using an LLM worker — it protects against a worker that
does not parse contracts reliably; it would not exist if both sides were
deterministic.
Contract shape: argv in → (exit code, one marker line | JSON) out. This is an
RPC-style contract — one Zod-schema serialization away from an MCP tool call, and
registry.ts:310 says the MCP JSON Schema already derives from the same op
input.
7. Agent coupling — what survives moving the control to an engine
Section titled “7. Agent coupling — what survives moving the control to an engine”Already engine-ready (no Claude coupling) — pure ops with Zod I/O + an injected
OpCtx; the MCP/HTTP adapter is generated, not written:
- The whole lease subsystem (
services/lease/). - PR classification (
pr/ops/classify.ts) — a pure function; the skill is a wrapper. - The pending-work check (
orchestrator/ops/watch.ts) — already decides “is there work” in code. - In-flight categorization + pickup order (
task/ops/inflight.ts,next.ts). - The digest writer (
orchestrator/ops/log-tick.ts). - Readiness facts (
gap-report.ts),probe-state, quality run/baseline,commit-to-main,close-commit, allentities/taskCRUD, the cleanup scanner, detect-runners — the whole ~70-verb surface.
Tied to Claude/markdown today (would not survive as-is):
- The per-tick control loop itself (
orchestrate/SKILL.md) — sequencing, the parallelAgentfan-out, the cap math. This is the part in charge; an engine takes it over. - The sub-agent verdict-parsing code (escape-recovery, back-off, marker naming) — entirely cost from the LLM worker; it goes away in an engine world.
- The implementation step (task-work 6) — writing code is real model work; an engine still runs an LLM, but as a tool call.
- The review-comment response (pr-respond 6) — model judgment.
Real judgment vs convention an engine could do:
| Step | Real judgment? |
|---|---|
| task-work relevance check | Real — but backed by probe-state; an engine could pre-filter and escalate only unclear cases |
| ensure-ready thin/subjective-AC | Real — gap-report stops short on purpose |
| ensure-ready corpus-assumption confirm | Real — but candidate detection is deterministic |
| orchestrate verdict routing | Not judgment — a fixed table |
| orchestrate “shorten loop when busy” | Convention |
| close-out (all) | Not judgment — already mechanical |
| pr-check classification | Not judgment — already a pure op |
| AC-coverage walk | Real only for ACs that can’t be automated (UI feel, copy) |
Conclusion for the MCP decision: the deterministic base is already an any-agent engine in all
but name — a CLI adapter today, an MCP adapter as a generated sibling — and the op substrate was
built for it (the OpCtx injection seam, Zod I/O, “JSON Schema for MCP derives from it”). No MCP
server is declared today — solutions/ontological/.claude-plugin/plugin.json ships skills + CLI
only (name sdlc, version 0.2.0).
8. Entity / status model
Section titled “8. Entity / status model”11 entity types (entities/_registry.ts:237-249): backlog, capability, decision, driver, milestone, principle, product, reference, standard, task, term. There is no
epic type (epic-ness is a task’s parent_key).
Task status enum (entities/task/schema.ts:56-80) — 4 majors, 14 values:
planning/draft, planning/proposed, planning/needs-definition, planning/backlog # forming, not pickableopen/ready # pickablein-progress, in-progress/blocked # claimedclosed/{done,superseded,partially-superseded,obsoleted,relocated,no-repro,wontdo} # terminal (need completion_note).superRefine (schema.ts:207-220) enforces the one conditional rule: closed/* ⇒ completion_note. SCHEMA_VERSION = "5".
autonomy enum (schema.ts:103-113): human-only | supervised | autonomous/pr
(or absent). It gates autonomous pickup: task next --exclude-autonomy human-only;
autonomous/pr self-readies via ensure-ready→auto-define; work always stops at an
open PR — nothing ever auto-merges; the human stays at the merge gate.
Other frontmatter: depends_on (drives the task next dispatchability filter +
dependency lift), priority (boolean ordering override), readiness_verified_at
(stamped on pass), definition_gap (on fail), completion_note, last_reviewed,
prs, impact, complexity.
What drives transitions: no declarative state machine, no
transition-validation graph. The model checks only that status is a legal enum
value + the closed/* ⇒ completion_note rule. Transitions are done by skills
(which choose the next status) and written through one op — sdlc task update
(task/ops/update.ts → model/ops/_update.ts:137-206), which validates the schema
and rejects immutable-field changes — so it enforces shape, not transition legality.
The transition vocabulary is standardized only at the commit-message layer: the
task-lifecycle kind lists ["start","verify-ready","flag-needs-definition", "record-pr","close-done","close-obsoleted","post-mortem"]
(task/commits/lifecycle/schema.ts:33-41). The “which transition when” policy is
spread across skill prose. This is a prime thing to move into the engine — today
the status machine is implied in markdown; an engine would make it explicit and
checked, with the lease ref already serving as the real claim that status only
copies.
Key files
Section titled “Key files”solutions/ontological/skills/orchestrate/SKILL.md— the tick loopsolutions/ontological/skills/task-work/SKILL.md— the per-task flow (1430 lines)solutions/ontological/skills/{task-ensure-ready,task-auto-define,pr-check,pr-respond,task-close-out}/SKILL.mdsolutions/ontological/conventions/lease-aware-skills.mdsolutions/ontological/lib/registry.ts—defineOp/OpCtx/invokeOp, exit-code taxonomy (:81-101), MCP note (:310)solutions/ontological/cli/{sdlc.ts,registry_adapter.ts,_common.ts}— the generic CLI adaptersolutions/ontological/lib/services/lease/{primitives,runtime,schemas,control_plane,footer}.ts— the locksolutions/ontological/lib/services/orchestrator/ops/{watch,log-tick}.ts— the check + digestsolutions/ontological/lib/services/pr/ops/classify.ts:294— the PR verdict classifiersolutions/ontological/lib/services/quality/{ops/run.ts,baseline.ts}— the quality gate + baselinesolutions/ontological/lib/services/git/commit-to-main.ts— ephemeral-worktree main-commit isolationsolutions/ontological/lib/model/entities/task/{schema.ts,implementation-ready.md}— the status/autonomy model + readiness contractsolutions/ontological/lib/model/entities/task/ops/{next,inflight,probe-state,gap-report,update,close-commit}.tssolutions/ontological/.claude-plugin/plugin.json— the manifest: skills + CLI only, no MCP server today