Apply S-0001 against the codebase — full-sweep capability migration (completes D-0007 §Migration)
State: closed/archived · Genre: plan
EXECUTED — shipped as v0.2.0 via M-0003 (closed 2026-06-06, 20/20 tasks, PRs #298–#327). Retained as the design/working record.
Rev 2 — API surface redesigned via adversarial multi-agent rescan (4 competing designs × 3 critics each, 3-judge panel, synthesis, hostile refutation, plan re-audit). Judges voted umbrella unanimously; the final surface below is the synthesis plus refutation fixes. The adversarial trail is summarized at the end.
Context
Section titled “Context”S-0001-co-locate-first-promote-when-shared (as rewritten post-D-0007) says
capabilities are born in plugin/lib; only skill glue co-locates.
D-0007-deterministic-op-substrate fixed the target architecture — entity-oriented lib,
defineOp registry, generated adapters — and ended with “the migration sequence becomes a
milestone.” T-0010 (entity-op cores → lib, forwarding shims kept) and T-0014 (generated CLI) landed;
the rest of the sweep never did. Today:
- Violations remain:
lib/services/index/ops/generate.tsimports fromplugin/skills/index/generate_index.ts(lib→skill inversion);new_standard.tsis a thin shim living inskills/standard-new/;_schema_patterns.ts(225 ln library) is misfiled inscripts/. - Composites unpromoted:
project_cleanup,count_inflight_tasks, quality trio,status_dashboard,scan_planning_candidates,find_todos, the three lefthook gates,setup_planning,info_report— all capabilities per the S-0001 nature test, all still inscripts//skills/. - Callers uncut: 23 SKILL.md files reference raw
${CLAUDE_PLUGIN_ROOT}scripts|validators/paths; T-0010’s “one release” shim window has passed. - Duplication:
todayUtc()×5,SLUG_RE×3, sdlc.yaml reading ×3+, inlineyaml.parse×4, worktree/gh helpers scattered;lib/util/holds onlyyaml.ts. - Verified drift found during planning:
lefthook.yml:37,89invoke deleted.pygate scripts — those two gates are broken today.entities {audit,migrate,check-identifiers}are absent from a cleansdlc --help:model/ops/is not a walk root andcomposeNounsnever imports its barrel; onlyvalidateleaks in via atask updateimport side effect.site/scripts/regen.mjsregenScripts()scans only.pyfiles — its scripts reference section is already empty/stale.- The generated CLI adapter emits JSON only (
runDispatch→emitJson); but live consumers parse line-oriented text (task sortbasenames per line,quality run --line/--logwith stderr routing, dashboard URL pass-through).
Decisions recorded: full sweep, phased with approval gates · codemod callers + delete shims ·
deliverables land as SDLC entities (decision + milestone + member tasks) · task-lifecycle scripts
(start_task, ensure_ready_mutate, append_pr_url) deferred to
T-902K-task-transition-op-owns-commit-side-effect · registry/walk/adapter machinery may be
enhanced · renames of existing ops are in scope (P-codemod rewrites every caller anyway — cheapest
moment ever).
Phase 1 — Design deliverables (approval gate)
Section titled “Phase 1 — Design deliverables (approval gate)”On approval, authored as repo artifacts:
D-H7FS-op-substrate-surface(decision entity) — the path[] surface (§1a), machinery contract (§1b), adapter projection (§1d), naming principles,defineService, SERVICE_ERROR tier. Resolves D-0007’s open questions on long-running ops and skill-local triage.- Milestone
M-XXXX-op-substrate-full-sweep— references D-H7FS; holds the member-task table and AC. - 18 member task files (§Execution).
PR order per repo convention: tasks PR first, then milestone + D-H7FS PR. D-H7FS/milestone must be authored from THIS surface (not the superseded Rev-1 flat table).
1a. Final sdlc surface — path[]-based, 11 visible nouns + 4 hidden
Section titled “1a. Final sdlc surface — path[]-based, 11 visible nouns + 4 hidden”A command is a 2–3 segment kebab path. Depth-3 only where a group has ≥2 members; singleton
groups banned. * = hidden (revealed by --advanced; still invocable/addressable by skills,
lefthook, MCP, HTTP).
Visible nouns (11):
task backlog milestone standard entities commit report index quality project dashboard ·
Hidden nouns (4): plugin pr gate lease
| path | from | notes |
|---|---|---|
task create / task update | existing | unchanged |
task sort | legacy task_cli/sort.ts | becomes a discovered op model/entities/task/ops/sort.ts; CLI path unchanged; atomic LEGACY_NOUNS drain (§risk) |
task inflight | count_inflight_tasks.ts | re-homed onto task — its data is tasks; orchestrate speaks “in-flight tasks”. → model/entities/task/ops/inflight.ts |
task check-claims* / parse-touchpoints* / scan-placeholders* | existing | unchanged |
task lint-state-origin* | lint_task_state_commit_origin.ts | lefthook gate on the task noun (mirrors check-* family) → model/entities/task/ops/lint-state-origin.ts |
backlog create / milestone create / standard create | existing | standard create op already exists in lib — the work is retiring the skills/standard-new/new_standard.ts shim (~10 prose refs) |
entities audit / validate / migrate / check-identifiers* | existing on disk | only validate actually registers today — T0 imports the model/ops barrel at composeNouns bootstrap (resolved q-d: explicit-barrel path kept; T-U72C owns the walk-root question). entities validate absorbs the full validate_frontmatter contract (resolved q-e): 3-tier schema resolution (--schema → type: → parent-dir basename + singularize), entitiesDir defaulting to the plugin’s entities dir, OK/FAIL … (schema:) text rendering via the T0 render hook, exit 1 on any validation failure (the op currently returns {failed:N} and exits 0 — disqualifying for the 14 gating callers), --quiet; --list-types dropped (zero SKILL.md callers) |
commit create / commit get-schema | existing | unchanged (sibling parity with report preserved) |
report render / report get-schema | existing | unchanged |
index generate | existing | stays top-level (front-door verb); only the lib→skill inversion is fixed |
quality run | run_quality_checks.ts | executes checks — not a scan; own noun |
quality detect | detect_quality_runners.ts | sibling |
quality baseline capture / diff / prune | quality_baseline.ts | visible, depth-3 group (resolved q-b): each mode is a real leaf with its own zod contract — three clean MCP tools / HTTP routes instead of one --action discriminated union |
project setup | skills/setup/setup_planning.ts | bootstrap docs/planning + sdlc.yaml |
project doctor | install_check.ts | ”is this project ready to run sdlc?” (split from old conflated doctor) |
project cleanup | project_cleanup.ts | flat verb — one op, no singleton cleanup scan group; read-only, skill executes after approval |
project scan todos | find_todos.ts | depth-3: project scan group earns nesting with 2 members |
project scan candidates | scan_planning_candidates.ts | scans the project FOR planning candidates (not the corpus) |
dashboard start/stop/list | status_dashboard.ts | top-level noun — list is machine-global across checkouts, so nesting under per-project project would lie. defineService |
plugin info* | skills/info/info_report.ts | loaded-plugin runtime state; /sdlc:info skill shells to it (hidden ≠ unreachable) |
plugin resolve* | resolve_plugin_repo.ts | foreign-plugin repo resolution |
plugin install-permissions* | install_permissions.ts | long explicit name is free — no human types it |
pr classify* | skills/pr-check/classify_pr.ts | verdict enum; per-PR cursor stays with the op under .sdlc/pr-cursors/ |
gate skill-prose* | lint_skill_prose.ts | lefthook reads sdlc gate skill-prose |
gate worktree-scope* | worktree_scope_guard.ts | sibling |
lease task claim/acquire/reacquire/transition/archive* | existing lease_cli | the real two-axis depth-3 shape preserved → services/lease/ops/task/*.ts |
lease op claim* | existing | → services/lease/ops/op/claim.ts |
lease heartbeat/release/inspect/list/parse-footer/reconcile/migrate* | existing | flat depth-2; single-shot heartbeat kept distinct from the loop |
lease heartbeat-loop* | lease_heartbeat_loop.ts | defineService (start-only) → services/lease/heartbeat-service.ts |
Not promoted (deleted, live in git history): migrate_identifier_shape.ts (one-time codemod, corpus
migrated), check_dependencies.sh, _example_script.ts, _pyyaml.ts (shim). Skill glue stays
co-located: check_ancestry, preflight_permissions, dedup_search, summarize_dedup_telemetry,
verify_prs_against_pr, scan_validate_ac. Task lifecycle → T-902K (its future task transition
sits flat beside create/update/sort/inflight — confirm with T-902K owner).
Naming principles (decided everything): (1) nest only where the domain nests AND the group has
≥2 members; hard 3-segment cap. (2) Names truthful about direction/action (quality run not
scan quality; dashboard top-level because list is machine-global). (3) An op routes to the
noun its data is about (task inflight). (4) Don’t break memorized sibling parity (commit/report
get-schema; entity CRUD untouched). (5) Human-vs-machine asymmetry: hidden machine ops get long
explicit names; human verbs get short paths. (6) Preserve real domain distinctions over flattening
(lease task/op two-axis kept at depth-3).
1b. Machinery contract (T0 — blocks every port)
Section titled “1b. Machinery contract (T0 — blocks every port)”All verified against current code; this is a real generator rewrite, priced honestly:
- Descriptor:
OpDescriptor.path: string[](2–3 kebab segments, each KEBAB-validated; hard cap 3) replaces{noun, verb}. Back-compat normalizer indefineOp:{noun, verb}withoutpathsynthesizespath:[noun,verb]— the ~30 existing descriptors and thenew_*shims’ positionalinvokeOp(ENTITY,'create',…)calls compile unedited. Derived accessors:noun=path[0],verb=path.at(-1),groups=path.slice(1,-1). - Registry key:
keyOf = path.join(' ')— byte-identical for all 2-level paths.getOp/invokeOpgain a path-array overload, keep the 2-string overload. - CLI generator: recursive trie in
registry_adapter.ts— group bypath[0], recurse; interior nodes are description-only subcommands; commander nests natively. - Hidden rendering (net-new): the adapter has zero verb-level hidden handling today
(
task check-claimsleaks intosdlc task --help). Per-leaf hidden + a group hidden iff every descendant hidden (recursive generalization of the noun-level rule). --advancedpast the umbrella:wantsAdvancedonly honors it before the first positional today; thread it into the subtree sosdlc lease --advancedrenders.defineService: sibling descriptor{path, summary, hidden?, lifecycle:{start, stop?, list?}},kind:'service'brand, same path-keyed index, parallel validator (no input/output/handler → must not route throughdescriptorProblem), lifecycle dispatcher (must not route throughinvokeOp’s await-then-output.safeParse— a non-returning loop would hang it). Lives atservices/<svc>/<name>-service.ts— requires a second walk traversal (current walk only opens<name>/ops/).- Walk descent: bounded
ops/<group>/*.tssub-level (forservices/lease/ops/task/*.ts). Path stays authoritative (descriptor, not file location); dir nesting is organizational. - Output contract (refutation fix; full spec in D-H7FS §4): the adapter is JSON-only today, but
live consumers parse text (
task sortline-per-basename consumed by orchestrate;quality run --line/--logwith stderr-on-FAIL; dashboard URL/PID lines). Standardize a 2×2 — op class {sync, streaming} × projection {text, json/jsonl} — shaped by one--output text|json|jsonlparameter (--jsonalias). Defaults op-declared, never TTY-sniffed: render hook present → text (parity); absent → json. Streaming is a descriptor property;jsonlspecified now, implemented with the first machine-streaming consumer. Thecli.render(output, io)hook is the text projection. Without this, the byte-parity risk-control is unachievable for those ops. - Noun-scoped
--authority(refutation fix): the adapter has no inherited-flag mechanism; lease ops declareauthorityvia a shared optional zod fragment (flag moves from noun-level to verb-level — lease parity tests updated accordingly; callers codemodded anyway). entitiesbootstrap: import the@lib/model/opsbarrel atcomposeNounsso all four generic ops register (today onlyvalidatedoes, via side effect).check_op_modules: assert 2–3-segment kebabpath+ joined-path uniqueness + projected-name uniqueness (no two paths whose underscore-join collides — guards the hyphen-fold hazard).- SERVICE_ERROR tier in the OpError taxonomy (already-running, stale pidfile, port-in-use, SIGTERM-to-dead-pid) — spelled in D-H7FS.
1c. Target plugin/lib structure
Section titled “1c. Target plugin/lib structure”plugin/lib/ registry.ts # defineOp(path[]) + defineService + OpError(+SERVICE tier) configuration.ts # (exists) config/ # sdlc.yaml first-class sdlc_yaml.ts schema.ts # single loader (kills ×3 dup incl. lease_cli resolveAuthority) util/ # entity-agnostic infra yaml.ts # (exists; gains parse export) date.ts slug.ts wikilinks.ts frontmatter.ts paths.ts git.ts gh.ts schema_patterns.ts # scripts/_schema_patterns.ts moved verbatim markdown_extract.ts # from skills/index/ (mdast helper) naming.ts prose.ts # entity_naming/ + prose_match/ relocated model/ entities/task/ops/ # + sort.ts, inflight.ts, lint-state-origin.ts ops/ # barrel imported at CLI bootstrap (T0) services/ commit/ report/ index/ # (exist; index inversion fixed — generate.ts imports sibling, not skills/) quality/ops/{run,detect,baseline}.ts project/ops/{setup,doctor,cleanup,scan-todos,scan-candidates}.ts # `project scan` group dashboard/service.ts # defineService plugin/ops/{info,resolve,install-permissions}.ts pr/ops/classify.ts gate/ops/{skill-prose,worktree-scope}.ts lease/ # lib/lease/ relocated + footer.ts (prs_field split) ops/{task/*,op/claim,heartbeat,release,inspect,list,parse-footer,reconcile,migrate}.ts heartbeat-service.ts # defineServicelib/prs_field/ splits: generic prs: frontmatter → util/frontmatter.ts; lease-footer →
services/lease/footer.ts.
End state: plugin/scripts/ and plugin/validators/ deleted, no retained adapters (q-e
resolved: the validate_frontmatter contract is absorbed into the op in T4b); lefthook calls
bun run ${CLAUDE_PLUGIN_ROOT}cli/sdlc.ts <path…> directly.
1d. Adapter projection (one path, three doors)
Section titled “1d. Adapter projection (one path, three doors)”- CLI: segments are the command path verbatim; interior segments are description-only groups.
- MCP tool name: underscore-join, intra-segment hyphens preserved (
project_scan_todos,gate_worktree-scope). Forward-only map, not reversible from the string — uniqueness guaranteed by the projected-name project-check, not by reversibility. If a downstream registrar forbids hyphens, kebab→snake fold applies and the same check catches the new collision class at build time. - HTTP route:
/<seg>/<seg>[/<seg>], method by verb class (create/update/render/generate/migrate/start/stop/acquire/… → POST; get-schema/audit/validate/list/inspect/info/doctor/classify/sort/inflight/check-/scan-/lint-*/… → GET; unenumerated → POST). hiddensuppresses help/manifest listing only — every hidden op still resolves when addressed (lefthook + external agents must reach them).
1e. Shared functionality & types survey (drive-out map)
Section titled “1e. Shared functionality & types survey (drive-out map)”| duplication | sites | single home |
|---|---|---|
todayUtc() | ×5 scaffolders + task-work | util/date.ts |
SLUG_RE | ×3 scaffolders | util/slug.ts |
| sdlc.yaml reading | lease_cli resolveAuthority, run_quality_checks, validate_sdlc_yaml | config/sdlc_yaml.ts |
inline yaml.parse | ×4 | util/yaml.ts |
FRONTMATTER_RE + parse/replace | ×6 | util/frontmatter.ts |
| worktree helpers | worktree_scope_guard, count_inflight | util/git.ts |
| gh pr helpers | backlog_cli, lease_cli | util/gh.ts + services/lease/footer.ts |
| wikilink wrap/unwrap | scaffolders + _schema_patterns | util/wikilinks.ts |
pyResolve() | ×2+ | util/paths.ts |
| birth-status subsets | ×3 scaffolder shims | derive via statusEnum() |
Already consolidated: ID minting, authoring pipeline, exit codes, prose match, commit rendering.
Open questions — RESOLVED (human arbitration, 2026-06-05)
Section titled “Open questions — RESOLVED (human arbitration, 2026-06-05)”a. Gate family split → entity affinity. task lint-state-origin stays on the task noun beside
the check-* family; only gate {skill-prose, worktree-scope} cluster. Reversible one-line path
edit if the gate set grows. b. quality baseline sub-modes → depth-3 leaves.
quality baseline {capture, diff, prune} — each mode a real op with its own zod contract; no
--action discriminated union. Group rule satisfied (3 members). c.
Top-level at 11 → dashboard stays top-level. dashboard list is machine-global across
checkouts; nesting under per-project project would lie about scope. d.
entities wiring → barrel import. One import "@lib/model/ops" line at composeNouns bootstrap;
explicit registration stays first-class per D-0007 §2a; T-U72C retains ownership of any future
walk-root promotion. e. validate_frontmatter → absorb into the op, delete the shim.
entities validate gains the 3-tier resolution, entitiesDir default, text render + exit-1-on-fail
(the op exits 0 on validation failure today — naive codemod would silently green 14 skills’ gates);
shim output becomes the behavior-lock golden; validators/ end-state deletion holds with no guard
carve-out. New task T4b carries the absorb.
Execution — phases & member tasks (18)
Section titled “Execution — phases & member tasks (18)”| # | Headline | Phase | Size | Depends |
|---|---|---|---|---|
| T0 | path[] machinery substrate — everything in §1b, incl. render-contract hook, defineService + second walk, hidden rendering, entities barrel import, check_op_modules path[] + projected-name checks; rollback golden gate (§risk) | P2 | L | — |
| T1 | util modules (date, slug, wikilinks, frontmatter, paths, git, gh) + _schema_patterns lift + dedup drive-out | P2 | L | — |
| T2 | config/sdlc_yaml loader + schema; absorb validate_sdlc_yaml + lease_cli resolveAuthority | P2 | M | — |
| T3 | lib-root tidy: lease/→services/lease/, entity_naming→util/naming, prose_match→util/prose, prs_field split | P2 | M | — |
| T4 | S-0001 stragglers: fix index inversion (generate_index, markdown_extract, template → lib); retire new_standard.ts shim (~10 prose refs → sdlc standard create) | P3 | M | T0 |
| T4b | entities validate absorbs the validate_frontmatter contract (resolved q-e): 3-tier resolution + entitiesDir default + OK/FAIL render hook + exit-1-on-fail + --quiet (drop --list-types, zero callers); shim’s exact output locked as golden BEFORE the absorb; un-hide the op | P3 | M | T0 |
| T5 | quality {run, detect, baseline {capture,diff,prune}} → services/quality/ops/ (resolved q-b: baseline is a depth-3 group, 5 leaves total; render hook for —line/—log + stderr-on-FAIL parity) | P3 | L | T0 |
| T6 | task inflight (→ model/entities/task/ops/inflight.ts) + project scan {todos,candidates} (→ services/project/ops/) | P3 | M | T0 |
| T7 | project setup + project doctor + plugin {info,resolve,install-permissions} | P3 | L | T0 |
| T8 | gates: gate {skill-prose,worktree-scope} + task lint-state-origin; repoint all lefthook mechanisms: stale .py lines 37/89 AND the live check_skill_prose.ts wrapper (line 58 — its DEFAULT_LINTER spawns scripts/lint_skill_prose.ts); grep every .claude/skills/project-check/*.ts for scripts//validators/ spawn targets and repoint; prove gates red-on-fixture | P3 | M | T0 |
| T9 | task sort move (task_cli/sort.ts → model/entities/task/ops/sort.ts): atomic — LEGACY_NOUNS removal of task + op registration in ONE commit; parity test asserts registry dispatch (getOp(['task','sort']) resolves AND task ∉ LEGACY_NOUNS) so a shadowed op fails loudly (mergeSplitNoun: legacy wins on collision); line-output preserved via render hook | P3 | M | T0 |
| T10 | project cleanup composite (flat verb; split only at the human-approval seam) | P4 | L | T0, T7† |
| T11 | lease noun migration: cli/lease_cli/* (11 verb files) → services/lease/ops/ preserving the two-axis depth-3 shape; authority as shared input fragment (flag moves noun→verb level); single-shot heartbeat kept distinct from loop; atomic LEGACY_NOUNS drain + post-flip sdlc lease task acquire behavior test (split-brain window guard); re-point task-work/tests/lease_integration.test.ts | P4 | L | T0, T3 |
| T12 | services: dashboard {start,stop,list} → services/dashboard/service.ts + lease heartbeat-loop → services/lease/heartbeat-service.ts; lifecycle tests (start→list→stop via pidfile; loop ≥2 CAS-REPLACEs then stop) — consumes T0’s defineService, no machinery here | P4 | M | T0 |
| T13 | pr classify promotion (hidden pr noun; cursor travels with op) | P4 | M | T0 |
| T14 | Codemod — five blocking classes across 23 SKILL.md files: (a) non-capability shims → CLI verbs (new_task→task create ×5 skills, new_milestone ×4, new_backlog, audit_entities→entities audit ×4, validate_frontmatter→entities validate ×14 (absorb landed in T4b — resolved q-e), validate_base, migrate_entities→entities migrate, new_standard ~10 refs); (b) existing-op renames (count_inflight→task inflight, run_quality_checks→quality run, status_dashboard→dashboard *); (c) per-skill invariants.yaml tool:/phrase: pins of renamed scripts — edited in the SAME PR as that skill’s prose (verified list: orchestrate, project-cleanup, import-planning, milestones-from-file, pr-respond, task-work, task-close-out); (d) both eval suites’ SCRIPT spawns + their literal “script exists” tests (entities-audit → sdlc entities audit; import-planning → sdlc project scan candidates); (e) lefthook + invariants subset (blocking; docs/site prose is T16) | P5 | L | all ports |
| T15 | Delete shims + dirs: scripts/ + validators/ fully deleted (q-e resolved: no retained adapters); gate on grep proving ZERO SKILL.md refs to `scripts/new_* | audit_entities | migrate_entities | validators/* across all 23 files; legacy-path guard (check_no_legacy_script_pathsproject-check + per-skillforbidden_phrases) proven red on fixture; delete migrate_identifier_shape, check_dependencies.sh, _example_script, _pyyaml` |
| T16 | Long tail: rewrite site/scripts/regen.mjs regenScripts to project /reference/ops/ from the registry (it scans only .py today — already stale); docs/skills refresh via dev-update-docs (lefthook check_skill_docs goes red otherwise); ~138-file docs/site naming pass; cli README; D-0007/S-0001 consequences notes | P7 | M | T15 |
† T10 shares services/project/ops/ + the project trie with T6/T7 — same-noun ports are
sequential (below).
Sequencing & parallelism
Section titled “Sequencing & parallelism”Spine: design accepted → T0 (own PR, merges green first) → P2 infra (T1∥T2∥T3, may overlap T0 on disjoint files) → ports (all depend on T0) → T14 codemod → T15 deletion → T16.
- T0 blocks every port. Its green-gate: tsc +
bun test+check_op_modules+--help/--help --advancedsnapshot showing 3-segment paths AND all fourentitiesverbs. - Same-noun ports are sequential, not parallel (they share a noun dir + trie node): the
projectchain (T7 → T6-project-half → T10) and thetaskpair (T6-inflight, T8-lint-state-origin, T9-sort). Genuinely parallel fan-out is across distinct nouns: quality ∥ project-chain ∥ entities-absorb (T4b) ∥ dashboard ∥ plugin ∥ pr ∥ gate ∥ lease. - Atomic flips: T9 (
task) and T11 (lease) must remove the noun from LEGACY_NOUNS in the same commit that registers the ops — mergeSplitNoun’s first-positional probe + legacy-wins rule otherwise creates a silent shadow (task sort) or a split-brain (lease task acquire→ old dispatcher whilelease heartbeat→ registry).
Risk controls
Section titled “Risk controls”- T0 rollback gate: snapshot
sdlc --help(+--advanced) + a representative dispatch set BEFORE T0, assert byte-identical AFTER (the normalizer must make this hold); fixture tests proving the two net-new traversals (depth-3 op underops/<group>/,service.tspeer) BEFORE any real module uses them; T0 reverts cleanly with zero dependent PRs. - Behavior-lock tests spawn the CLI, not the moving module (verified: existing parity tests
import by relative path —
classify_pr.test.ts,info_report.test.ts— which the move itself breaks): every golden spawnsbun run cli/sdlc.ts <path…>so it survives the move unchanged. Internal-import rewrites belong in the MOVE commit, not T14. - Render parity: ops with text consumers (
task sort,quality run/baseline,dashboard) lock their text output via the T0 render hook; everything else locks JSON. - Promotion isolation (S-0001): every
git mvis its own commit. - Per-task verification:
bunx tsc --noEmit,bun test, quality checks, skill-prose lint, affected eval suite,check_op_modules.
PR strategy
Section titled “PR strategy”~20 PRs: tasks PR → milestone+D-H7FS PR → T0 (own, revertible) → one per member task. T14 (mechanical) and T15 (irreversible) never bundled with ports; T15 merges only when main is green with everything prior in.
Verification (milestone exit criteria)
Section titled “Verification (milestone exit criteria)”- Parity suite green: JSON goldens + text-render goldens (sort/quality/dashboard) byte-match
pre-migration snapshots, all spawned via
cli/sdlc.ts. - Eval suites green, spawning the CLI (entities-audit, import-planning re-pointed).
- Guards:
sdlc gate skill-prosezero violations;check_no_legacy_script_pathszero hits;sdlc quality runbaseline-clean;--help --advancedlists every path incl. depth-3; all lefthook gates fire red-on-fixture (proves the .py-drift + wrapper repoints). - Fresh-project smoke (CLI only; valid only post-T0):
mktemp -d→sdlc project setup→sdlc task create→sdlc entities audit(no drift) →sdlc index generate(idempotent 2nd run) →sdlc milestone create→sdlc entities validate. grep -rE 'plugin/(scripts|validators)/' plugin/ site/nothing live;lib/has zero imports fromskills/;LEGACY_NOUNScontains onlybacklog.
Adversarial trail (how this surface was reached)
Section titled “Adversarial trail (how this surface was reached)”- 4 designs: umbrella (project/plugin grouping, depth-3), flat-merged (strict 2-level), ontology (domain-derived), ergonomics (frequency-weighted). 12 critic reports; 3 judges (registry-maintainer, skill-author, MCP-consumer personas) ranked umbrella first unanimously; flat-merged last unanimously.
- Synthesis grafts:
task inflightre-homing (fixes consensus “worst guess” — wasinflight count);dashboardpulled top-level (machine-globallist); human-vs-machine naming asymmetry (ergonomics’ sharpest idea);qualitykept its own noun (scan qualitywould lie); lease two-axis preserved at depth-3 (all four designs flattened it lossily — caught by critics). - Refutation caught (now folded in): JSON-only adapter vs line-oriented consumers → render hook
in T0; lefthook scope was 2 stale
.pylines but 6 live.tsgate shims (incl.check_skill_prosewrapping the very script being moved) → T8 rescoped; lease--authorityhad no adapter mechanism → shared input fragment; mergeSplitNoun split-brain/shadow windows → atomic flips in T9/T11;entities audit/migratearen’t actually registered today → T0 barrel import; regen.mjs only scans.py→ T16 rewrite; eval/parity tests import moving modules by path → CLI-spawn mandate.
Out of scope
Section titled “Out of scope”Task-lifecycle ops (→ T-902K-task-transition-op-owns-commit-side-effect; its task transition
extends the task noun flat) · MCP/HTTP adapter implementation (the projection contract in §1d
binds them) · workflow-definition engine · generic-ops walk root (→ T-U72C; the barrel import is
in-scope independently) · lib/_example/.