Phase 13: auto-release cron on VmAllocation.expires_at #14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes a Phase 12 correctness gap: under the quota purchase model,
VmAllocation.expires_atis set to the plan'svm_quota_period_endso all VMs from a plan expire together, but no cron enforces it. Without this tick, expired-plan VMs stay flaggedActivein the admin/allocationsview + user/dashboardindefinitely after the plan period ends.Landed as squash-merge
f8f7638ondevelopment.What landed
New module
crates/hero_onboarding_server/src/auto_release.rs(197 LOC, 10 pure-fn unit tests):find_expired_allocation_sids(rows, now_epoch)(pure) +run_tick(osis, provisioner)(async). Filter isstatus != Released && expires_at < now(strict<— exact boundary is grace-period, not expired).Provisioner::release()already setsstatus=Released+ stampsreleased_at, so this module owns no state mutation.AlreadyReleasedcounts asrows_already_released(idempotent).New HTTP route
POST /admin/auto-release-now(admin-secret-gated; 503 if no provisioner registered). ReturnsAutoReleaseSummaryJSON withrows_examined,rows_expired,rows_released,rows_already_released,errors.New cron action
hero_onboarding_auto_release_cron(1h default cadence;HERO_ONBOARDING_AUTO_RELEASE_CRON_INTERVAL_MSoverride). Mirrors the Phase 4/5/12 cron pattern:hero_onboarding_admin auto-release --oncePOSTs to the new route.New admin CLI subcommand
Cmd::AutoRelease(4 LOC, mirrorsCmd::DiscountLadder).Runbook §3.9 (~130 LOC) documents the cron + manual invocation curl recipe + short-cadence override + what the cron does NOT do (refund / provisioner teardown beyond release / persistent history). §8 further reading link added.
Smoke
scripts/smoke_auto_release.sh(~280 LOC, 19 checks): route shape, admin gating (missing/wrong/right secret), empty-DB tick, live un-expired allocations not touched, force-released row filtered out of scan, tick idempotency,AutoReleaseSummaryfield shape.Acceptance gates
cargo test --workspace: 120/120 (+10 vs s2-014 baseline of 110; all 10 new pure-fn unit tests for the filter logic)cargo fmt --check: cleancargo clippy --workspace --all-targets -- -D warnings: cleanlab build --release --install --workspace: VICTORY 3/3 (build #17)lab infocheck: 3/3 clean / 0 findingsscripts/smoke_auto_release.sh: 19/19 GREEN (new)smoke_discount_ladder.sh24/24 +smoke_vm_allocate.sh27/27What this does NOT do (intentional deferrals)
/admin/release(D-17 §Revisability — ~10 LOC + design conversation about pro-rata + grief surface; deferred until ops asks).VmAllocation.expires_at(operator-side test seam), or anow_epoch_overridequery param on the tick route (production-API smell). The 10 pure-fn unit tests cover the filter logic with crystal clarity; the live smoke proves the route wires up + the cron fires. Same gap-style assmoke_discount_ladderwhich similarly punts manipulate-clock-for-tier-promotion.D-NN / L-NN
None minted — mechanical addition mirroring three existing cron patterns. Slots stay at D-22 / L-09.
Tracking context
This closes the last in-scope correctness gap before Track B archives back to single-agent Track A operation. The next provisioner integration (v1.5
DeployerProvisionerper home#235 Track D) is the natural-pause-point recovery hook; Track A's deployer arc is currently at D2 (Forge user lifecycle).Meta: hero_onboarding#1.
Signed-by: mik-tf mik-tf@noreply.invalid