agentsclimarketplace

Phase sdd rules

Skill zig999/siegard-code/dist/.claude/skills/phase-sdd-rules

Exit criteria checkers and worker routing table for the sdd (Specification-Driven Development) phase. Consumed by orchestrator-sdd.md to dispatch spec workers via select_worker.py and evaluate phase transition gates (check_handoff_manifest_approved, check_all_domains_validated, check_error_codes_synced). Includes check_structural_diff.py to determine if spec changes require domain worker dispatch during improve flows. Not user-invocable — orchestrators call scripts directly.From its SKILL.md

Install
npx -y skills add zig999/siegard-code --skill phase-sdd-rules

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 9 stars9 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
  • runs commandsInstructs the agent to run 8 commands, including `python3 .claude/skills/phase-sdd-rules/scripts/select_worker.py --task-type <type>` and 7 more.

SKILL.md

14.8 KB, ~3.8k tokens by cl100k_base, as published. Nobody here has run it

phase-sdd-rules

Phase rules skill for the sdd (Specification-Driven Development) phase. Provides exit criteria checkers and worker routing table consumed by orchestrator-sdd.md.

Contract

The orchestrator calls this skill's scripts directly. No inter-skill communication envelope needed. Every script returns a JSON object to stdout and exits 0 on success or 1 on error.


Phase identity

FieldValue
phase_namesdd
order1
requiredtrue
worker_defaultu-spec-writer

Concurrency ceiling (RESOURCE_LIMITS)

The orchestrator MUST enforce these per-batch ceilings before each dispatch in Step 5.1:

effective_modeMax concurrent workers per batch
standard2
targeted1

The ceiling is enforced as a hard cap — the orchestrator may dispatch FEWER than the cap (e.g., if only one task is ready) but MUST NOT exceed it. Violation is a protocol violation per RESOURCE_LIMITS.


Worker routing table

Maps task.type to worker sub-agent. Consumed by the orchestrator dispatcher.

task.typeworker subagent_type
spec-triageu-spec-triage
spec-writeru-spec-writer
spec-revieweru-spec-reviewer
spec-backu-spec-back
spec-frontu-spec-front
spec-validatoru-spec-validator
spec-complianceu-spec-compliance
* (default)u-spec-writer

scripts/select_worker.py

Returns the worker sub-agent name for a given task type.

Usage

python3 .claude/skills/phase-sdd-rules/scripts/select_worker.py \
  --task-type <type>

Output (exit 0)

{"worker": "u-spec-writer", "task_type": "spec-writer", "phase": "sdd"}

Error (exit 1, stderr)

{"status": "error", "reason": "internal_error", "detail": "<message>"}

Exit criteria

All three criteria must be met before the sdd phase can transition. Evaluated by orchestrator-sdd.md at the end of each cycle.

CriterionScriptDescription
handoff_manifest_approvedscripts/check_handoff_manifest_approved.pyhandoff-manifest.yaml exists and Status: approved
all_domains_validatedscripts/check_all_domains_validated.pyNo INVALID status in _validation/ (scoped to the change's domains on /u-improve via --workflow-id)
error_codes_syncedscripts/check_error_codes_synced.pyAll error.code values in in-scope specs are in error-codes.md (scoped to touched domains on /u-improve via --workflow-id)
spec_drift_reviewedscripts/check_spec_drift_reviewed.pyOpt-in (R04d). With sdd_policy.drift_check = warn/required in .orch/config.json, a current /u-drift report must exist for the approved specs. OFF by default — vacuously met

See exit-criteria.json for the machine-readable declaration.

Environment variables

VariableDefaultDescription
ORCH_PROJECT_DIR.Project root
SPECS_DIRspecsSpecs directory, relative to ORCH_PROJECT_DIR

scripts/check_handoff_manifest_approved.py

Criterion: handoff-manifest.yaml exists in SPECS_DIR and contains Status: approved.

python3 .claude/skills/phase-sdd-rules/scripts/check_handoff_manifest_approved.py

Output schema:

{
  "criterion": "handoff_manifest_approved",
  "met": true,
  "evidence": {
    "file": "specs/handoff-manifest.yaml",
    "exists": true,
    "status_found": "approved"
  }
}

scripts/check_all_domains_validated.py

Criterion: no INVALID status in any in-scope .yaml or .md file under SPECS_DIR/_validation/.

python3 .claude/skills/phase-sdd-rules/scripts/check_all_domains_validated.py [--workflow-id <wid>]

Scope (fix F1): with --workflow-id, an /u-improve gates only the domains the change touched (scope.py). Untouched domains inherit their last verdict, so a stale INVALID in an unrelated domain does not block the change — it is reported under out_of_scope_invalid for audit. For u-spec / greenfield / un-derivable scope the check stays global (every domain must be VALID). Without --workflow-id it is global (prior behavior).

Output schema:

{
  "criterion": "all_domains_validated",
  "met": true,
  "evidence": {
    "validation_dir": "specs/_validation",
    "exists": true,
    "total": 5,
    "passing": 5,
    "failing": [],
    "out_of_scope_invalid": [],
    "scoped": false,
    "scope_domains": null
  }
}

met is false if the _validation/ directory does not exist or contains no in-scope files.


scripts/scope.py

Derives the set of domains a change actually touches, so the gate, the handoff scan, and the orchestrator dispatch can restrict work to them (fix F1). Reads triage.json.

python3 .claude/skills/phase-sdd-rules/scripts/scope.py --workflow-id <wid>
# → {"scoped": true,  "domains": ["<affected>", ...]}   /u-improve
# → {"scoped": false, "domains": null}                  u-spec / greenfield / un-derivable

scoped: false (domains null) always means "no scoping — evaluate every domain" (never "empty scope"), keeping greenfield and legacy triage on prior behavior. Also exposes domain_of_validation_file(filename) used by the gate and the handoff scan to map a _validation/ artifact back to its domain.


scripts/check_spec_entry.py

Entry guard for /u-spec (R10). Answers one question — does {SPECS_DIR}/domains/ already hold a spec? — and projects what a full fan-out would cost. Consumed by the /u-spec command in Initial Validation, before any event is appended.

python3 .claude/skills/phase-sdd-rules/scripts/check_spec_entry.py \
  --specs-dir <dir> [--project-dir <dir>]
# exit 0 → {"entry":"greenfield",     "domain_count":0, "domains":[], "projected":{...}}
# exit 3 → {"entry":"non_greenfield", "domain_count":N, "domains":[...], "projected":{...}}

Pairs with scope.py: that script returns scoped: false for the u-spec trigger by design, so orchestrator-sdd dispatches the pipeline for every scanned domain. Correct on an empty repository; on a populated one it turns an addition into a full re-spec that outruns the per-session subagent spawn budget. This guard is what distinguishes the two cases, and it is the only thing that did.

projected.wall_clock_minutes uses WALL_CLOCK_MINUTES_PER_WORKER = 6 — measured across three real workflows (5.6 / 5.9 / 6.9 min per dispatched worker, over differing modes, domain counts and change sizes). Its purpose is to make the price visible before it is paid rather than after.


scripts/check_error_codes_synced.py

Criterion: every error.code / code: Exxx value found in an in-scope spec YAML/MD file is registered in SPECS_DIR/error-codes.md. Trivially met if no error codes are defined in specs.

With --workflow-id, an /u-improve gates only the codes referenced by touched domains (scope.py, fix F1) — an unregistered code living exclusively in untouched domains is reported under out_of_scope_missing and does not block. Files outside domains/<slug>/ are always in scope. Without --workflow-id (or for u-spec / greenfield) the check is global.

python3 .claude/skills/phase-sdd-rules/scripts/check_error_codes_synced.py [--workflow-id <wid>]

Output schema:

{
  "criterion": "error_codes_synced",
  "met": true,
  "evidence": {
    "error_codes_file": "specs/error-codes.md",
    "error_codes_file_exists": true,
    "spec_codes_found": ["E001", "E002"],
    "registered_codes_count": 10,
    "missing_codes": [],
    "out_of_scope_missing": [],
    "files_scanned": ["domain-auth.yaml", "domain-billing.yaml"],
    "scoped": false,
    "scope_domains": null
  }
}

scripts/identify_invalid_domains.py

Utility (repair loop Step R2): lists domains whose validation report in {SPECS_DIR}/_validation/ is INVALID and derives each one's defect origin from the machine-readable {domain}-validation-result.yaml (blocking_issues[].responsible). Feeds the SM's stage-granular repair (S16): origin "back" (all blocking issues belong to u-spec-back) routes a reduced ["spec-back", "spec-validator"] repair pipeline; any other origin (mixed, front, writer, missing or unparseable companion) returns null and keeps the full pipeline — mis-attribution degrades to redundant work, never to under-repair.

Usage

ORCH_PROJECT_DIR=<path> SPECS_DIR=<specs> python3 .claude/skills/phase-sdd-rules/scripts/identify_invalid_domains.py [--workflow-id <wid>]

With --workflow-id, an /u-improve restricts the repair-target set to the touched domains (scope.py, fix F1): a stale INVALID report in an untouched domain goes to out_of_scope_invalid and never enters invalid_domains — the repair loop must not dispatch workers for domains this workflow did not touch. Without --workflow-id (or for u-spec / greenfield) the scan is global.

Output (exit 0)

{
  "invalid_domains": ["chat", "ingestion"],
  "defect_origins": {"chat": "back", "ingestion": null},
  "out_of_scope_invalid": [],
  "scoped": false
}

scripts/check_structural_diff.py

Utility (not an exit criterion): determines whether a spec change requires dispatching a domain worker. Used by orchestrator-sdd.md during improve flows to decide if structural sections were modified (endpoints, schemas, auth_rules, data_models, etc.) and a domain worker must run.

Safe fallback: if improve-scope.json is missing or the spec is not listed in scope, returns domain_worker_required: true to avoid skipping required work.

Usage

ORCH_PROJECT_DIR=<path> python3 .claude/skills/phase-sdd-rules/scripts/check_structural_diff.py \
  --workflow-id <wid> \
  --spec-path <relative-path-to-spec>

Output (exit 0)

{
  "domain_worker_required": true,
  "changed_sections": ["endpoints", "schemas"],
  "structural_sections_found": ["endpoints", "schemas"]
}

Structural sections that trigger domain_worker_required: true: endpoints, schemas, error_codes, component_props, state_contracts, data_models, auth_rules, event_types, api_contracts


scripts/generate_handoff_manifest.py

Utility (not an exit criterion): deterministically produces SPECS_DIR/handoff-manifest.yaml from the validated specs on disk plus triage.json. Closes the gap where no pipeline worker produced the manifest the SDD exit gate requires (the phase previously dead-ended at E08).

Invoked by orchestrator-sdd.md in Step 6 after check_all_domains_validated.py (or check_all_improve_reviewers_completed.py in targeted mode) and check_error_codes_synced.py pass, and before check_handoff_manifest_approved.py. Deterministic (no LLM): sha256 must be exact and the output must round-trip through lib/minimal_yaml.py, which validate.py uses.

Usage

ORCH_PROJECT_DIR=<path> SPECS_DIR=<rel> \
  python3 .claude/skills/phase-sdd-rules/scripts/generate_handoff_manifest.py \
  --workflow-id <wid>

Behavior

  • Enumerates domains via glob domains/*/openapi.yaml; builds domains[], backend_package[] (openapi + back-spec per domain are required by FLOW-037; error-codes / conventions added when present), and — only if front/front.md exists — frontend_artifacts + frontend_package[]. Omitting the frontend blocks lets the Dev orchestrator infer stack=be (back-only handoff).
  • handoff.delivered_by is the const u-spec-orchestrator (required by FLOW-030); handoff.type is derived from triage (new_domain / major_evolution / fast_track). change_summary is emitted only for evolution handoffs.
  • sha256 of every package file is computed at generation time; paths are stored relative to ORCH_PROJECT_DIR so validate.py (--specs-dir = ORCH_PROJECT_DIR) resolves them.

Output (exit 0 when status=ok, exit 1 when status=blocked)

{"status": "ok", "check": "handoff_manifest_generated",
 "manifest_path": "specs/handoff-manifest.yaml", "manifest_id": "HANDOFF-20260601-120000",
 "domains": ["auth"], "stack_implied": "be", "reason": "triage_loaded"}

Fail-closed: no domains, a missing required backend artifact, a handoff_allowed: false in _validation/*-validation-result.yaml, a block_handoff / non_compliant in _validation/*-compliance.yaml, or a triage stack/front mismatch (triage.stack ∈ {fullstack, fe} but no front artifacts on disk — stack_mismatch_front_expected_but_missing, fix P0-1) yields status: blocked without writing the manifest. The orchestrator treats a blocked generation as criterion-not-met (Validation Repair Loop / E08).


scripts/check_spec_drift_reviewed.py

Exit criterion spec_drift_reviewed (R04d) — connects /u-drift to the phase boundary.

/u-drift does what the spec pipeline structurally cannot: it matches approved specs against the code by exact keys and reports what diverged, with evidence per finding. It runs standalone and has to be remembered, which is why a spec once declared three method signatures that do not exist and another cited a grep nobody ran.

python3 .claude/skills/phase-sdd-rules/scripts/check_spec_drift_reviewed.py [--workflow-id <wid>]

Policy in .orch/config.json:

{"sdd_policy": {"drift_check": "off" | "warn" | "required"}}
PolicyMissing / stale reportCritical findings
off (default)criterion vacuously metnot evaluated
warnreported, does not blockreported, does not block
requiredblocks the phase exitblocks the phase exit

Opt-in by design, not as a shortcut. /u-drift costs an LLM code-inventory pass. Making it mandatory on every sdd phase would add an agent and several minutes to every workflow — including the ones whose measured problem already is that the pipeline costs more than the change it specifies. A gate that worsens that gets switched off wholesale; one the operator enables for the handoffs that matter gets used.

"Reviewed" means the report exists and its spec_content_hash still matches the specs on disk. A report generated before the specs changed describes a state that no longer exists — the same staleness class R08 handles for validation verdicts.

What ships with it: 14 files

78.2 KB alongside SKILL.md, 13 of them executable

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.