Skip to content

nerve config validate — CI-ready config bundle validation (7/13) - #210

Open
alex-clickhouse wants to merge 1 commit into
config-refactor/06-file-watcherfrom
config-refactor/07-validate
Open

nerve config validate — CI-ready config bundle validation (7/13)#210
alex-clickhouse wants to merge 1 commit into
config-refactor/06-file-watcherfrom
config-refactor/07-validate

Conversation

@alex-clickhouse

@alex-clickhouse alex-clickhouse commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

What

nerve config validate checks a whole config bundle and exits non-zero on any error, so it drops straight into CI on a config repo. It covers unknown keys, cron job and gate parsing, source sanity, ${VAR} resolvability, and schedules the scheduler would not actually run as written.

--portable-only drops the machine-local layers and judges the tracked layer alone. --strict-keys and --strict-env promote warnings to errors. It runs even when the config cannot otherwise load, which is the point.

Why

Every class of error here was previously found by a daemon at start-up, after the change had already merged and synced — leaving the instance on its old config with a log line nobody was reading. Moving the check before the merge is the whole value.

Schedules are the clearest case. A 5-field crontab the scheduler rejects (99 * * * *) is refused at run time and logged. A schedule that is neither a crontab nor an interval (hourly, @daily) is never complained about at run time at all — it silently never fires.

--portable-only exists because a machine-local override can mask an invalid shared value, and, more commonly, a broken local file condemns a shared bundle with nothing wrong with it. Validating the tracked layer on its own is the only question whose answer does not depend on which host ran the check.

Validation reads and parses; it does not execute candidate gate-plugin code. "An invalid bundle is refused" is not a guarantee you can make about code you had to run in order to judge it, so an unrecognised gate type is reported as unverified rather than accepted or rejected. Testing a plugin is the author's job.

🤖 Generated with Claude Code

@alex-clickhouse alex-clickhouse changed the title nerve config validate — CI-ready config bundle validation nerve config validate — CI-ready config bundle validation (7/13) Jul 28, 2026
@alex-clickhouse
alex-clickhouse requested a review from Copilot July 28, 2026 10:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds CI-friendly configuration bundle validation (nerve config validate) and supporting validation primitives to hard-fail on structurally invalid config bundles (cron schedules, gate specs, wrong-shaped layers, unsafe cwd-resolving paths), while remaining intentionally lenient about unset ${ENV_VAR} secrets unless explicitly requested.

Changes:

  • Introduces validate_config_bundle() with detailed reporting and a new nerve config validate CLI command.
  • Tightens cron schedule and gate-spec validation (including new interval parsing semantics and gate spec field allowlists via spec_keys).
  • Expands test coverage and documentation for validation behavior, strictness flags, and plugin non-execution guarantees.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/test_cron_gates.py Adds regression tests for gate-shape normalization/logging behavior.
tests/test_config_validate.py Adds comprehensive tests for config-bundle validation semantics and CLI behavior.
tests/test_config_sources.py Ensures CLI emits clean errors for config mistakes (including bad backend) while preserving tracebacks for internal defects.
nerve/cron/service.py Splits interval parsing to expose _interval_seconds() for validation without changing daemon fallback behavior.
nerve/cron/jobs.py Prevents accidental normalization of wrong-shaped run_if/skip_when_idle, and logs+ignores invalid shapes instead of raising.
nerve/cron/gates.py Adds spec_keys allowlists and improves build_gate() robustness for non-string/unhashable type.
nerve/config_validate.py New validation module: layer reading, cwd-path footgun checks, cron/gate/schedule validation, and reporting.
nerve/cli.py Adds nerve config validate command and refines “clean error vs traceback” behavior in the CLI group callback.
docs/cron.md Documents schedule fallback vs validation behavior, and clarifies that validation does not execute plugin gates.
docs/config.md Adds a full “Validating Configuration” section including CI guidance and flags.
config.example.yaml Updates cron comments and documents cron.auto_reload example.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread nerve/config_validate.py Outdated
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from 726ca79 to 12c7340 Compare July 28, 2026 12:11
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from 12c7340 to 8c7a5fd Compare July 28, 2026 12:36
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from 8c7a5fd to e171c79 Compare July 28, 2026 13:14
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from e171c79 to a4016f2 Compare July 28, 2026 14:23
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from a4016f2 to 09a9260 Compare July 28, 2026 14:54
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch 2 times, most recently from b923d11 to aa8026e Compare July 28, 2026 16:03
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch 2 times, most recently from 2c7f820 to ee55d3e Compare July 28, 2026 18:40
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from ee55d3e to 51b1ebb Compare July 29, 2026 08:29
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch 3 times, most recently from 8947dbc to e165653 Compare July 29, 2026 10:31
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from e165653 to 37d5fa6 Compare July 29, 2026 13:28
@alex-clickhouse
alex-clickhouse marked this pull request as ready for review July 29, 2026 15:25
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from 37d5fa6 to 7a5ffb6 Compare July 30, 2026 08:01
Hard-failing validation over a whole bundle — unknown keys, cron job and gate
parsing, source sanity and ${VAR} resolvability — with a non-zero exit so it
drops straight into a config repo's CI.

Validation reads and parses; it never loads the bundle's own code. Importing a
gate plugin to check it would mean the bundle had already executed by the time we
decided it was unfit, so an unrecognized gate type is reported as unverified
rather than accepted or rejected. Built-in gate specs are built, which is what
type-checking a gate spec means.

Two silent-acceptance gaps are closed. A blank path setting is refused with a
message saying what it would actually do: cron.gate_plugins_dir set to "" is not
"no directory" but the daemon's working directory, every .py in which is imported
and executed at startup and on every cron reload. And a schedule the scheduler
will not run now fails CI — a bad crontab, a string that is neither crontab nor
interval, a zero interval, or a non-string that would raise past every handler
the daemon has. Source schedules were not checked at all and now are, through the
same helper the daemon uses, so the validator and the runtime cannot disagree
about what is valid.

Co-Authored-By: Claude <noreply@anthropic.com>
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/07-validate branch from 7a5ffb6 to 51fb706 Compare July 30, 2026 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants