REQ-C-006: All Args Validated in Phase 1
Tier: Command Contract | Priority: P0
Source: §14 Argument Validation Before Side Effects
Addresses: Severity: High / Token Spend: Medium / Time: Medium / Context: Low
Description
Command authors MUST implement all argument and precondition validation within the command's validate() hook, not within execute(). The validate() hook MUST be free of side effects. Validation MUST check all arguments at once, collecting all errors before returning (not fail-fast on the first error). The framework enforces phase ordering (REQ-F-015) but depends on command authors correctly placing validation logic.
Acceptance Criteria
- A command with multiple invalid arguments reports all validation errors in a single invocation
- A validation hook that attempts to write a file is caught by the framework's side-effect detector (if implemented) or flagged in code review by the framework's linting rules
- The validation phase completes in under 100ms for all commands (no network calls in validate())
- Validation errors reference the specific parameter name and value that failed
Schema
Types: response-envelope.md
Validation failures exit with code 3 (ARG_ERROR) and carry a structured errors array. Each entry identifies the failing parameter and reason.
{
"errors": {
"type": "array",
"items": {
"type": "object",
"required": ["param", "code", "message"],
"properties": {
"param": { "type": "string", "description": "Flag or argument name that failed validation" },
"code": { "type": "string", "description": "Machine-readable validation error code" },
"message": { "type": "string", "description": "Human-readable description of the validation failure" },
"value": { "description": "The invalid value that was supplied" }
}
},
"description": "All validation errors collected in a single pass; present when phase is 'validation'"
}
}
Wire Format
$ tool deploy --env prod --version 1.2.3 --notify-slack "#invalid channel" --workers abc
{
"ok": false,
"data": null,
"error": {
"code": "ARG_ERROR",
"message": "Argument validation failed; no side effects have occurred",
"phase": "validation"
},
"warnings": [],
"meta": {
"errors": [
{ "param": "--notify-slack", "code": "INPUT_PARAM_INVALID", "message": "Channel name must start with #", "value": "#invalid channel" },
{ "param": "--workers", "code": "INPUT_PARAM_INVALID", "message": "Expected integer, got 'abc'", "value": "abc" }
],
"duration_ms": 4
}
}
Exit code: 3 (ARG_ERROR) — guaranteed no side effects.
Example
A command implements validation in its validate() hook, collecting all errors before returning. The execute() hook is never called if validation fails.
register command "deploy":
danger_level: mutating
exit_codes:
SUCCESS (0): description: "Deployment completed", retryable: false, side_effects: complete
ARG_ERROR(3): description: "Argument validation failed", retryable: true, side_effects: none
validate(args) → []error:
errors = []
if not valid_env(args.env):
errors.append(param="--env", code="INPUT_PARAM_INVALID",
message="Unknown environment '{}'".format(args.env))
if not valid_slack_channel(args.notify_slack):
errors.append(param="--notify-slack", code="INPUT_PARAM_INVALID",
message="Channel must start with #")
return errors # all errors collected before returning; execute() not called
execute(args):
# only reached when validate() returns no errors
deploy(args.env, args.version)
Related
| Requirement | Tier | Relationship |
|---|---|---|
| REQ-F-015 | F | Enforces: framework guarantees validate() completes before execute() runs |
| REQ-F-002 | F | Provides: ARG_ERROR (3) exit code carries the zero-side-effects guarantee |
| REQ-C-001 | C | Composes: ARG_ERROR (3) must be declared in the command's exit_codes map |
| REQ-O-009 | O | Extends: --validate-only runs the validate() hook in isolation without proceeding to execute() |