U handoff validator
Skill zig999/siegard-code/dist/.claude/skills/u-handoff-validator
Validates a handoff-manifest.yaml against schema, semantic rules, and provenance (PROV — every pinned artifact hash must be log-notarized by a worker terminal or match the workflow's adoption baseline; the manifest itself must match its generation event) before it is consumed by Dev orchestrators. Single source of truth for manifest validation — replaces inline checks in BE and FE orchestrator cores.From its SKILL.md
npx -y skills add zig999/siegard-code --skill u-handoff-validatorAssembled 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 1 command, including `validate.py --manifest <path> --specs-dir <dir> [--caller <id>]`.
SKILL.md
7.9 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it
SKILL: Handoff Manifest Validator
Purpose
Validate the canonical handoff-manifest.yaml produced by u-spec-orchestrator before any Dev orchestrator consumes it. Returns a structured envelope (handoff-validation-envelope.yaml) that the caller uses to decide whether to proceed, halt, or escalate.
This skill consolidates rules that previously lived inline in u-be-orchestrator-core.md and u-fe-orchestrator-core.md. Both orchestrators now invoke this skill instead of duplicating checks.
When invoked
- By
u-be-orchestrator-coreat session start, if{SPECS_DIR}/handoff-manifest.yamlexists - By
u-fe-orchestrator-coreat session start, if{SPECS_DIR}/handoff-manifest.yamlexists - By
u-spec-to-dev-handoffprotocol before writing a new manifest (pre-write validation)
Inputs
| Field | Value |
|---|---|
manifest_path | Absolute path to {SPECS_DIR}/handoff-manifest.yaml |
caller | u-be-orchestrator-core | u-fe-orchestrator-core | u-spec-orchestrator |
specs_dir | Absolute path to {SPECS_DIR}/ — used to resolve package paths for integrity checks |
Outputs
validate.py prints a single JSON envelope to stdout (see Envelope shape) and signals validity via exit code (0 valid / 1 invalid). The caller acts only on status, errors[] (rule-id-prefixed strings), and halt_signal. The envelope conforms to handoff-validation-envelope.schema.yaml.
Validation rules
The deployed implementation is validate.py (stdlib-only, prod-hardening task 03b): it loads the manifest via minimal_yaml, evaluates the 13 structural rules below plus the PROV provenance rules (v2.35.0), and prints a flat JSON envelope. rules.yaml is the declarative catalog — documentation, NOT loaded at runtime. Each rule has:
PROV rules (v2.35.0) — provenance against the orchestration log, evaluated when a spec_baseline_recorded event exists for the workflow (otherwise degraded to warnings — A6' migration): PROV-010 (every pinned sha256 equals the latest worker-notarized hash after the baseline, or the baseline hash when untouched), PROV-020 (the manifest file's sha256 equals the hash recorded by handoff_manifest_generated), PROV-030 (a generation event exists — delivered_by backed by evidence, P8). Integrity rules prove the manifest matches the files; PROV proves the files came from the pipeline — a freelance edit laundered through manifest regeneration passes HDF-020/021 and fails PROV-010. On PROV failure the consuming orchestrator emits E25_unprovenanced_artifact.
id— stable identifier (FLOW-NNN or HDF-NNN)severity—blocking(populateserrors[]) orwarning(populateswarnings[])applies_to— DESCRIPTIVE ONLY: which caller the rule matters to.validate.pyevaluates all rules regardless of--caller; scoping is data-driven (see note under the catalog).check— declarative description of the predicatevalidate.pyhard-codes
Rule catalog
| ID | Description | Severity | Applies to |
|---|---|---|---|
| FLOW-030 | handoff.delivered_by must be u-spec-orchestrator | blocking | all |
| FLOW-031 | domains[] must have at least 1 entry | blocking | all |
| FLOW-032 | backend_package[] must have at least 1 entry | blocking | be |
| FLOW-033 | new_domain handoff must NOT include change_summary | blocking | all |
| FLOW-034 | major_evolution, fast_track, and reverse_eng handoffs MUST include change_summary | blocking | all |
| FLOW-035 | change_summary.dev_impact must be a valid enum value | blocking | all |
| FLOW-036 | change_summary.type must match handoff.type: fast_track → [patch, minor]; major_evolution → [major]; reverse_eng → [patch, minor, major] | blocking | all |
| FLOW-037 | for new_domain/major_evolution, backend_package[] must include both openapi and back-spec | blocking | be* |
| HDF-010 | handoff.type must be in {new_domain, major_evolution, fast_track, reverse_eng} | blocking | all |
| HDF-020 | Every backend_package[].sha256 must match the actual file at {specs_dir}/{path} | blocking | be |
| HDF-021 | Every frontend_package[].sha256 must match the actual file at {specs_dir}/{path} | blocking | fe |
| HDF-030 | change_summary.dev_impact = stop_domain_task_contracts — caller must halt affected domains | blocking | all |
| HDF-040 | frontend_artifacts omitted for backend-only handoffs — otherwise required fields present | blocking | fe |
* The Applies to column is descriptive (which caller the rule matters to).
validate.pyevaluates all rules regardless of--caller; scoping is data-driven: FLOW-037 fires only fornew_domain/major_evolution, HDF-021 only whenfrontend_packageis present, HDF-040 only whenfrontend_artifactsis present.
Execution protocol
validate.py --manifest <path> --specs-dir <dir> [--caller <id>]:
- Load
manifest_pathvia the stdlibminimal_yamlloader. If the file is missing or unparseable → emitstatus: invalidwith the reason inerrors[]and exit1(fail-closed). No external JSON-Schema validation runs —handoff-manifest.schema.yamlis the reference structure, not an executed step. - Evaluate all 13 rules (FLOW-030..037, HDF-010/020/021/030/040) against the loaded mapping, regardless of
--caller. Scoping is data-driven (FLOW-037 byhandoff.type; HDF-021 only whenfrontend_packagepresent; HDF-040 only whenfrontend_artifactspresent). HDF-020/021 read each pinned file under--specs-dirand compare sha256. - Each blocking failure appends a rule-id-prefixed string to
errors[](e.g."FLOW-030: …").change_summary.dev_impact: stop_domain_task_contractssetshalt_signal: true(HDF-030) — flow control for the caller, not an error. - Print the envelope as one JSON object; exit
0whenstatus: valid(emptyerrors), else1.
Envelope consumption rules
The caller MUST:
- Halt and escalate to human when
status: invalid(exit code1) - Halt affected domains when
halt_signal: true(HDF-030 —change_summary.dev_impact = stop_domain_task_contracts) - Proceed normally when
status: validandhalt_signal: false - Act on
statusandhalt_signalfor flow control;errors[]are rule-id-prefixed strings (e.g."FLOW-030: …") for diagnostics/surfacing, not for branching logic
Envelope shape
validate.py emits this flat JSON object (one line):
{
"status": "valid | invalid",
"errors": ["FLOW-030: …", "HDF-020: sha256 mismatch for …"],
"warnings": [],
"halt_signal": true,
"validated_by": "u-handoff-validator",
"caller": "u-spec-orchestrator | u-be-orchestrator-core | u-fe-orchestrator-core"
}
errors[] are rule-id-prefixed strings (not structured objects). This shape conforms to handoff-validation-envelope.schema.yaml. The SDD→Dev gate (check_handoff_manifest_approved.py) consumes status (must be valid) and surfaces errors[]; orchestrators additionally honor halt_signal.
Versioning
When adding or changing a rule:
- Change
validate.pyfirst — it is the executable source of truth (the SDD gate runs it). - Update
rules.yamland the rule catalog table in this SKILL.md to match. - Add a fixture pair (valid + invalid) under
tests/fixtures/. - Extend the validator test layer (
tests/test_layer5_flows.pyand/ortests/test_layer_hard_handoff_generation.py) to cover the new rule.
New rule IDs: use HDF-NNN (handoff-specific) for rules introduced after the extraction. FLOW-NNN IDs are preserved for backward compatibility with existing tests.
What ships with it: 2 files
22.1 KB alongside SKILL.md, 1 of them executable
- rules.yaml5.6 KB
- validate.pyruns16.5 KB