Install
$ agentstack add skill-zig999-siegard-code-u-handoff-validator ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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 rules below, and prints a flat JSON envelope. rules.yaml is the declarative catalog — documentation, NOT loaded at runtime. Each rule has:
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: fasttrack → [patch, minor]; majorevolution → [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.py evaluates all rules regardless of --caller; scoping is data-driven: FLOW-037 fires only for new_domain/major_evolution, HDF-021 only when frontend_package is present, HDF-040 only when frontend_artifacts is present.
Execution protocol
validate.py --manifest --specs-dir [--caller ]:
- 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.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: zig999
- Source: zig999/siegard-code
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.