Implements the remaining items from issue #20:
- version is now forward-permissive: any value >= 1 is accepted; a
newer-than-known version loads best-effort (unknown fields ignored,
warning printed) instead of hard-failing on "must be 1"
- from_job input reference: `inputs: [{ from_job: <job> }]` resolves to
that job's single-file output + format and implies a dependency edge;
combined depends_on + from_job graph gets topological ordering and
cycle detection
- logfile size-rotation, on by default (5MB, keep 3), overridable per
job (log_max_size / log_keep) or file-wide via a top-level defaults block
- new commands: split (schema/table subsetting via select:), inspect
(rule validation -> markdown/json report, fails job on enforced-rule
errors), diff (compare exactly two schemas, never fails), scripts-exec
(run SQL script dirs against a live PostgreSQL database)
- atomic single-file output/report writes (temp file + rename)
- symlink-escape hardening in SafeJoin via EvalSymlinks preflight
Updates docs/JOB_FILES.md and examples/jobs/relspec.yml accordingly.
Add `relspec job list` and `relspec job run <name>` driven by YAML job
manifests (relspec.yml / relspec.<name>.yml), so multi-file merge and
conversion workflows can be expressed declaratively instead of as long
shell command lines.
v1 contract (see docs/JOB_FILES.md):
- `command` is a closed allow-list (convert, merge, scripts-list); no
field accepts a shell string or executable path.
- Deterministic discovery: default file first, then named files sorted
lexically; all files merged into one namespace; duplicate job names
across files are a hard error.
- Every path resolves relative to the job file's directory; absolute,
home-relative and directory-escaping paths are rejected at validation.
- Database credentials referenced by env-var name via `conn_env:`;
connection strings are never stored and are redacted from logs/plan.
- Full validation (version, unknown fields, command/format, per-command
input/output shape, path traversal, depends_on targets, dependency
cycles) runs before anything is read, written or executed; per-job
pre-flight then checks input existence, script dirs, env vars and the
output overwrite policy for the whole plan.
- `depends_on` closure runs in deterministic topological order;
`--no-deps` runs only the named job.
- `--dry-run` (alias `--plan`) prints the resolved plan and exits 0
without touching inputs, outputs or databases.
- A failing job propagates the underlying non-zero exit status, logs
FAILED (never OK), and writes no success marker.
pkg/jobs is side-effect free (discovery/parse/validate/plan only);
execution adapters live in cmd/relspec/job.go. Includes unit tests for
discovery, validation, planning and path safety, plus CLI tests for
end-to-end convert/merge, scripts-list across multiple directories,
dry-run, dependency chains, exit-code propagation and log redaction.
Deferred: live `scripts execute` from jobs, split/inspect/diff/templ
commands, job-to-job output wiring, log rotation/retention.
Refs #20
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>