Skip to content

nerve config init-repo + config-repo setup runbook (13/13) - #216

Draft
alex-clickhouse wants to merge 1 commit into
config-refactor/12-reload-allfrom
config-refactor/13-repo-setup
Draft

nerve config init-repo + config-repo setup runbook (13/13)#216
alex-clickhouse wants to merge 1 commit into
config-refactor/12-reload-allfrom
config-refactor/13-repo-setup

Conversation

@alex-clickhouse

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

Copy link
Copy Markdown
Collaborator

What

nerve config init-repo scaffolds a workspace into a shareable config repo — the CI validation workflow, a secrets-aware .gitignore, a README, and a seeded config/settings.yaml — then prints the git/gh and instance-side steps it cannot do for you. Scaffolding is idempotent: an existing file is never overwritten, only reported as skipped, so re-running is safe (--dry-run shows the split without writing).

The generated workflow gates every PR: a gitleaks scan first, then nerve config validate --workspace . --portable-only --strict-keys. It needs no Actions secret to run.

Alongside it: --portable-only now covers cron as well as the machine-local config layers, docs/config.md gains the full setup runbook with the generated workflow embedded verbatim, and python -m nerve.config_validate becomes a supported entry point for environments that cannot install nerve.

Why

A config repo is only as good as the gate in front of it. Without CI, "reviewed configuration" means whatever a reviewer noticed by eye, and the failure modes that matter — a schedule the scheduler silently never runs, a secret committed in plaintext — are exactly the ones eyes miss.

Order matters. The secret scan runs before anything else is fetched: a validation failure that stopped the workflow early would leave a leaked credential unreported, and scanning before the install means it scans this repo and nothing else, so it needs no token of its own. Its image tag is pinned because an argument renamed in a later gitleaks would fail every config repo at once, with no pull request anywhere to explain it.

The validator tracks nerve's default branch rather than pinning to a revision. That is the safe direction for --strict-keys: a validator at or ahead of the instance knows every key the instance reads, so an unknown key is a typo rather than version skew. It is also the direction the rest of this flow already leans — propose_config_change and instance-side sync both leave an unrecognized key a warning, on the grounds that a key this nerve does not know is inert, and refusing an already-reviewed change over one would strand the instance on a stale config for a spelling the next upgrade may well know. Those asymmetries are now stated where they live rather than left to be rediscovered. A repo that would rather check against one release appends a ref to the install line; and where CI and an instance can still disagree, the --strict-keys failure says so, so a config repo does not get blamed for a version difference.

--portable-only had a hole. Cron resolution is file-aware: a workspace carrying no jobs falls back to the machine-local ~/.nerve/cron so an un-migrated install keeps working. That fallback is right at load time and wrong in a run asked to judge the shared bundle alone — it hands CI a file the repo does not contain, and a typo in that file fails a config repo with nothing wrong with it. It is the same substitution that skipping config.yaml / config.local.yaml prevents, one directory over. An explicit cron.jobs_file pointing outside the tracked subtree goes the same way.

config/settings.yaml is part of the scaffold for a narrow reason: a repo with no config/ at all fails its own workflow on the very first commit, because a portable-only run that opened no file under config/ is an error there, not a pass. An instance's workspace already has the file and skips it.

The standalone python -m nerve.config_validate path exists because everything the validator needs is the standard library plus PyYAML — a bundle can be checked by cloning nerve and setting PYTHONPATH, with no install and no resolution of nerve's full runtime. It shares the CLI's renderer, and the tests assert both directions of parity: identical output, and the same set of flags, since a flag the CLI has and this parser does not is a documented invocation this path cannot run.

🤖 Generated with Claude Code

@alex-clickhouse alex-clickhouse changed the title nerve config init-repo + config-repo setup runbook nerve config init-repo + config-repo setup runbook (13/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 “config repo” scaffolding + CI validation workflow, and introduces a zero-install validator entry point (python -m nerve.config_validate) designed to safely validate a checked-out config repo (portable-only, no machine overlays, no execution of repo code).

Changes:

  • Add nerve config init-repo and nerve.config_repo.scaffold_config_repo() to scaffold a config repo (workflow, gitignore, README, seeded settings).
  • Add a standalone validator entry point (python -m nerve.config_validate) and unify output formatting via render_result.
  • Update portable-only validation to suppress machine-local cron fallback, plus docs/tests/templates to match the new runbook + CI flow.

Reviewed changes

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

Show a summary per file
File Description
tests/test_config_validate.py Adds portable-only cron fallback tests and coverage for the standalone validator entry point parity with the CLI.
tests/test_config_repo.py New end-to-end tests for repo scaffolding, workflow correctness, pin “honesty”, and CI-safe execution.
nerve/templates/skills/nerve-workspace/SKILL.md Documents operator-guided first-time config repo setup steps for the workspace skill.
nerve/templates/config-repo/validate-config.yml Adds scaffolded GitHub Actions workflow: gitleaks scan + pinned-source validator run under PYTHONSAFEPATH.
nerve/templates/config-repo/README.md Adds scaffolded README explaining repo layout and PR-based change flow.
nerve/templates/config-repo/gitignore Adds scaffolded .gitignore for secrets/machine-local state (including backup secret members).
nerve/sync_service.py Clarifies strict-keys behavior asymmetry between config-repo CI and instance sync validation.
nerve/config_validate.py Implements portable-only cron suppression, adds shared rendering, and adds standalone argparse-based entry point.
nerve/config_repo.py New module implementing idempotent scaffolding and validator ref pinning/notes.
nerve/config_pr.py Documents intentional asymmetry between proposal validation and stricter CI validation.
nerve/cli.py Updates config validate to use shared renderer and adds config init-repo command.
docs/config.md Adds/updates full runbook for config repo setup and embeds the generated workflow verbatim.

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

Comment thread nerve/config_repo.py Outdated
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from d2ab73d to f66524f Compare July 28, 2026 12:11
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from f66524f to d5e1d13 Compare July 28, 2026 12:36
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from d5e1d13 to 1a105bb Compare July 28, 2026 13:15
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from 1a105bb to ddf95a2 Compare July 28, 2026 14:23
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from ddf95a2 to aa5620b Compare July 28, 2026 14:54
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch 2 times, most recently from b6ab70d to b5b9a84 Compare July 28, 2026 16:03
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from b5b9a84 to a16d310 Compare July 28, 2026 16:20
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch 2 times, most recently from 6d2fa23 to 723bd92 Compare July 29, 2026 08:29
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from 723bd92 to 9026d50 Compare July 29, 2026 09:18
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from 9026d50 to 07c6a88 Compare July 29, 2026 09:31
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from 07c6a88 to 540bb6e Compare July 29, 2026 10:31
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from 540bb6e to e2915ea Compare July 29, 2026 13:28
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch 2 times, most recently from 90d495b to 15a4d05 Compare July 30, 2026 08:25
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from 15a4d05 to 61b660a Compare July 30, 2026 08:55
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch 2 times, most recently from ff86bbd to af2f6ee Compare July 30, 2026 09:48
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from af2f6ee to cf17f09 Compare July 30, 2026 10:06
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from cf17f09 to 97474c4 Compare July 30, 2026 10:47
Scaffolds a config repo — the tracked layout, a .gitignore that does not leak the
machine-local overlay, and a CI workflow — plus a runbook for the
PR-to-merge-to-sync flow.

The generated CI validates the bundle without installing nerve from the repo under
review, so a pull request cannot execute its own unreviewed Python in order to be
judged. It enforces what the docs promise rather than a weaker subset, and the
documented job matches the one actually generated instead of an idealized version
of it.

--portable-only now means it. Cron resolution is file-aware and would otherwise
fall back to the machine-local directory, handing a run that was asked to judge
the shared bundle alone a file the bundle does not contain — so a typo in a local
file could fail a config repo that has nothing wrong with it. Paths outside the
tracked subtree are reported as declined rather than silently substituted.

Co-Authored-By: Claude <noreply@anthropic.com>
@alex-clickhouse
alex-clickhouse force-pushed the config-refactor/13-repo-setup branch from 97474c4 to 2010043 Compare July 30, 2026 12:44
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