Skip to content

Prune deleted environments (opt-in)#20

Merged
miharp merged 1 commit into
mainfrom
feat/env-prune
Jul 24, 2026
Merged

Prune deleted environments (opt-in)#20
miharp merged 1 commit into
mainfrom
feat/env-prune

Conversation

@miharp

@miharp miharp commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Closes the last open "hard part": an environment deleted from the control repo was never removed from compilers — its symlink and version dirs lingered forever.

This is the delete-submodule-symlink half of what PE's file sync does. codavox already had the other half (r10k purges the env from staging, publish stops advertising it), so the gap was purely on the agent.

Behavior

With --prune-environments, after each poll the agent removes any environment the publisher no longer serves:

  • symlink removed immediately → new compiles for it fail loudly (correct — it's gone);
  • versions reaped by --min-age → an in-flight run's code-content still resolves the version until the age passes.

Guards (the point of the feature)

Deleting an operator's code is dangerous, so it's guarded the way PE guards its purge:

  1. Off by default — opt-in, mirroring r10k's purge_levels being a setting you enable.
  2. Only after a successful poll — a failed fetch returns before pruning, so a publisher outage is never read as "delete everything."
  3. Never on an empty advertisement — zero environments is far likelier a misconfig than a real mass deletion; prunes nothing, logs a warning. Deleting the last environment stays manual.

(r10k's own second guard — don't purge on a failed deploy — already applies on the deploy side.)

Tests — new, and they run in CI

internal/agent/prune_test.go, in-process (no binary build, no testing.Short() skip), so they run under -race in CI's unit-test job every run:

  • deletion path (symlink + versions gone, other env untouched)
  • opt-in default (no flag → nothing deleted)
  • empty advertisement → nothing deleted
  • publisher unreachable → nothing deleted
  • min-age protects a just-deployed version

Wiring & docs

agent.Config.Prune, the --prune-environments flag, and the agent.prune_environments config key. reap refactored into reapVersions (a deleted env reaps with no current and no keep-count, age guard only). Docs: agent.md, deploying.md (the r10k purge_levels requirement, since codavox only observes r10k's output), configuration.md, config.example.yaml, and design.md (env deletion moved from open → addressed).

Verified: go test -race ./..., golangci-lint 0 issues, gofmt/vet/markdownlint clean.

🤖 Generated with Claude Code

Closes the last open hard part: an environment deleted from the control repo
was never removed from compilers — its symlink and version dirs lingered
forever. This is the delete-submodule-symlink half of what PE's file sync does;
codavox already had the other half (r10k purges the env from staging and the
publisher stops advertising it), so the gap was purely on the agent.

With --prune-environments the agent, after each poll, removes any environment
the publisher no longer serves: the symlink goes immediately so new compiles
fail loudly, and the environment's versions are reaped by --min-age so an
in-flight run's code-content still resolves until the age passes.

Deletion is destructive, so it is guarded the way PE guards its purge:

- Off by default. Deleting an operator's code is opt-in, mirroring r10k's own
  purge_levels being a setting you enable.
- Only after a successful poll. A failed fetch returns before pruning, so a
  publisher outage is never read as 'every environment was deleted.'
- Never on an empty advertisement. A publisher serving zero environments is far
  more likely misconfigured than a real mass deletion, so it prunes nothing and
  logs a warning; deleting the last environment stays manual.

Wired through agent.Config.Prune, the --prune-environments flag, and the
agent.prune_environments config key. reap is refactored into reapVersions so a
deleted environment reaps with no current and no keep-count, only the age guard.

Tests (internal/agent/prune_test.go, run under -race in CI): the deletion path
and all three guards, plus min-age protection of a recent version. Docs:
agent.md, deploying.md (the r10k purge_levels requirement), configuration.md,
and design.md (environment deletion moved from open to addressed).

Signed-off-by: Michael Harp <mike@mikeharp.com>
@miharp
miharp merged commit 954d1ed into main Jul 24, 2026
8 checks passed
@miharp
miharp deleted the feat/env-prune branch July 24, 2026 16: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.

1 participant