Expiry is part of provisioning
R7K1 gave every temporary environment an owner, deadline, resource graph, revocation order, and resumable removal path before creating it.
R7K1's first cleanup job ran each night and removed preview containers older than two days. It kept the host tidy until it did not.
A failed build had created a database but never a container, so age scanning missed it. Another preview's branch stayed open for weeks and kept resources indefinitely. A route survived after its container disappeared. A temporary credential remained valid after everything visible was gone.
Cleanup was a best-effort search for old things. Provisioning was a deliberate creation of new things. The asymmetry guaranteed leaks.
R7K1 made expiry part of provisioning. Before an environment received its first resource, it had an owner, deadline, resource identities, and a durable removal path.
Temporary needed a definition
Calling an environment temporary did not say how long it should live or which event ended it.
R7K1 defined default policy by purpose:
active branch review — short renewable window
closed or merged review — accelerated grace period
comparison pin — explicit owner and deadline
incident reproduction — separate protected policy
failed provisioning — short diagnostic retention
queued request — expires before consuming stale capacityThe deadline appeared in the environment record at request acceptance. It was not calculated later from container creation time, which excluded partial failures.
Policy carried a version so changing defaults did not silently rewrite every existing promise. A migration could update selected environments deliberately.
“Temporary” became a lifecycle contract with a timestamp and reason.
Every environment request named a review, repository, requester, responsible owner, and policy class. Automation could derive the owner from review metadata, but the result was stored explicitly.
If the owner disappeared or the review closed, R7K1 knew which policy transition to apply. Orphaned ownership shortened the deadline or requested reassignment; it did not make the environment immortal because nobody could approve removal.
Ownership did not grant exclusive technical access automatically. It identified who could justify extension, receive expiry notice, and answer whether unusual state required preservation.
The environment page showed owner and expiry. A resource without either failed admission.
This changed cleanup from an infrastructure guess into a product relationship. The system knew whom the temporary world existed for.
The resource graph was written during creation
The cleanup job had discovered resources by names and labels. Inconsistent naming made leaks invisible and risked deleting unrelated objects.
R7K1 recorded every intended and observed resource under environment identity:
application instance
route and certificate entry
database or schema
fixture volume
runtime credential
sandbox storage prefix
captured mail and webhook namespace
logs and diagnostic bundle
artifact referencesProvisioning created a resource intent before calling its provider and attached provider identity after observation. A crash between creation and receipt remained recoverable through the stable requested name and incomplete attempt.
Provider labels supported leak scanning; the lifecycle database remained the authoritative ownership graph. Each helped find drift in the other.
Removal could now traverse what the environment actually owned instead of searching the whole host for branch-like strings.
Expiry was visible while the preview was useful
The first nightly deletion surprised reviewers. The URL worked in the afternoon and returned not found the next morning.
R7K1 showed the deadline in the preview header and branch page. Warnings appeared before removal under a bounded schedule. They named what would disappear and how to extend if policy allowed.
This preview expires in 6 hours.
Owner: review 42
Extension available through Friday.Warnings were lifecycle state, not recurring notification spam. One durable warning event could be delivered through page, review status, and optional message without creating separate countdown jobs.
The page also distinguished route expiry from artifact retention. The running preview might disappear while build logs and manifest remained available longer.
Temporary became predictable to users, not only cheap for infrastructure.
An “extend forever” button would transfer cleanup responsibility back to human memory. R7K1 allowed bounded extensions with owner, reason, and new deadline.
The extension operation checked review state, resource class, policy maximum, and current removal phase. It could renew a ready environment before route detachment. It could not resurrect a database already removed under the same identity.
If removal had begun, extension either failed clearly or created a new environment from retained artifact and fixture. It did not race cleanup and hope the route survived.
Repeated extensions appeared in history. A frequently renewed environment suggested the workload was not temporary and needed another product model.
The system made exceptional longevity visible rather than hiding it behind a mutable timestamp.
Expiry requested removal; it did not delete instantly
At the deadline, R7K1 transitioned to expiry-requested. The state prevented new reviewer writes and began an ordered removal workflow.
ready
-> expiry-requested
-> route-detached
-> capabilities-revoked
-> runtime-stopped
-> data-removed
-> resources-verified-absent
-> removedThe exact order followed consequence. Detaching the route prevented new sessions. Revoking credentials limited a leaked runtime. Stopping application reduced writes before data removal. Verification closed the lifecycle only after external evidence.
Each state was durable and independently retryable. A provider outage during database deletion did not reattach the route or restart the container.
Expiry initiated a forward protocol whose progress survived controller restarts.
A storage bucket or log file left behind consumed space. A valid credential or route left behind retained capability.
R7K1 prioritized disabling access: branch route detached, runtime token revoked, sandbox integration credentials expired, and database user disabled. Physical deletion followed.
This did not make lingering data harmless. It reduced the window in which partial cleanup remained actively usable. Sensitive diagnostic environments could apply stricter immediate deletion and network isolation.
Credentials had independent expiry close to the environment deadline, providing another boundary if the controller failed. The removal process still verified revocation rather than trusting time alone.
Cleanup order became a security policy, not just an optimization for freeing disk.
Provider delete operations often return success when a resource is already absent. That is helpful for retries and dangerous when identity is ambiguous.
Before deletion, R7K1 resolved the resource through its recorded provider ID and ownership marker. A same-name database tagged to another environment caused review. An absent resource completed that step only if the lifecycle receipt proved the intended identity could no longer exist under another name.
Deletion calls reused one attempt identity where providers supported it. Repeated controller runs converged on absence without creating new resources or moving the environment backward.
The operation recorded observed-before and observed-after state. “Delete command exited zero” was not enough.
Idempotency did not mean blindly ignoring all missing and existing conditions. It meant repeated intent reached the same verified owned outcome.
The old cleaner keyed off ready-container creation time. Environments stuck in building or data-ready had no expiry route.
R7K1 assigned deadline at request creation and resource ownership at every transition. A provisioning timeout moved the environment toward failed retention and then removal, even if no application instance ever existed.
Build attempts, temporary workspaces, databases, registry uploads, and route reservations each had cleanup policy. A failed artifact could keep logs for diagnosis without keeping a running database.
This closed a common leak class: the less successfully an environment provisioned, the less likely the old script was to know what to remove.
Lifecycle began before resource creation precisely so incomplete creation could have an ending.
The application image might be shared by several environments. Removing one preview should release its reference, not delete the artifact immediately.
R7K1's resource graph distinguished owned resources from referenced shared artifacts. Environment removal decremented or removed the reference. Artifact garbage collection later considered every active environment, rollback pin, build receipt, and retention policy.
Logs and manifests also had different value. Runtime logs expired after a diagnostic window. The compact environment tombstone and build receipt could remain longer. Synthetic data usually disappeared with runtime; an explicitly exported fixture scenario entered version control separately.
This prevented one cleanup process from applying the same age rule to capabilities, mutable data, large immutable bytes, and small historical evidence.
Deletion followed ownership and recoverability, not physical location.
The tombstone proved an ending
Deleting the environment row would make the dashboard clean and leak detection impossible. R7K1 retained a compact tombstone:
environment ID and generation
artifact and fixture identity
owner and reason
created, activated, expired, removed times
resource removal receipts
known exception or unresolved leakThe tombstone contained no runtime secrets or full synthetic data. It answered why a URL disappeared and whether cleanup completed.
Provider scanners could flag a resource whose environment tombstone said removed. A bug report could connect an old screenshot to the artifact even after runtime vanished.
The tombstone had its own eventual retention. Ending operational state did not require erasing every trace immediately.
A temporary environment became accountable from request to proof of absence.
R7K1 periodically listed provider resources bearing preview ownership labels and compared them with lifecycle records.
It found:
- external resource with no environment record,
- removed environment whose resource still existed,
- active environment missing an expected resource,
- resource whose owner generation differed from recorded identity,
- credential or route past expiry,
- lifecycle record stalled beyond its transition bound.
The scanner did not delete unknown resources automatically. It quarantined or reported them with evidence. A missing label could make ownership uncertain, and aggressive cleanup could be worse than a leak.
Conversely, the lifecycle database alone could not prove absence. Provider observation supplied the second view.
Reconciliation made cleanup measurable rather than trusting that no error message meant no residue.
Deadlines used one trustworthy clock
Environment clients and branch code did not decide expiry. R7K1 used its control-plane clock and stored absolute deadlines.
Workers compared server-provided time and treated scheduler delays as reasons to begin late removal, not to extend automatically. A controller restart recalculated eligible transitions from the stored deadline.
The UI displayed local-friendly time from the authoritative instant. It did not increment a countdown and mistake suspended browser timers for elapsed time.
Provider credentials with expiry used their service clocks and a safety margin. Clock disagreement appeared in diagnostics rather than creating exact-seeming countdowns.
Time was part of lifecycle evidence, not a branch-controlled input.
A preview request could wait behind capacity while the branch advanced or review closed. Building it later would waste resources and possibly activate stale work.
Queued generations had deadlines and relevance checks. A newer generation superseded them before execution. Closing the review moved them directly toward cancelled tombstones. A manual pin could preserve one comparison candidate deliberately.
Builds already running could finish if the artifact was reusable by another request, but they lost authority to provision or activate the stale generation.
This applied expiry to intention, not only runtime. The system stopped doing old work merely because it had once entered a queue.
Temporary demand can expire before a container exists.
Expiry interacted with active sessions
Detaching a route while somebody was reviewing could discard unsaved synthetic work. The header warned ahead, and the final grace state stopped new sessions while allowing a short completion window according to policy.
R7K1 did not promise preservation of review-entered data after the deadline. It offered an export of safe synthetic scenario changes when useful, clearly separated from application production data.
For ordinary branch previews, data was recreatable and short-lived. Work that required durable collaboration belonged in another environment class with backup and ownership.
The policy appeared before use. Reviewers did not learn at deletion time that a preview was disposable.
Expiry design included the human transition out of the environment, not only infrastructure removal.
When a branch merged, the preview's purpose usually ended. Immediate removal made post-merge comparison and rollback investigation harder.
R7K1 applied a shorter grace period. The route displayed merged state and deadline. Production deployment remained a separate system; merge did not prove the code was active publicly.
If a release issue referred to the preview, an authorized pin could extend it under an incident policy. The owner and reason became visible.
Closing without merge used similar acceleration but retained failure logs long enough for diagnosis. Reopening the review could create a new environment or extend the old one only if removal had not crossed the safe boundary.
Repository state informed lifecycle policy without becoming a destructive command by itself.
Cost was an expiry input, not its only reason
Temporary environments consumed CPU, memory, storage, hostnames, credentials, logs, and operator attention. Expiry controlled all of them.
R7K1 estimated cost by phase. A stopped but retained database had different cost and risk from a running application. A shared artifact had marginal storage but no runtime allocation.
High cost could shorten default deadlines or require explicit approval for extension. It did not justify secretly deleting active work earlier than the displayed promise.
Security and clarity mattered alongside cost. Old credentials and forgotten routes were dangerous even on a host with unlimited disk.
Expiry was a product property because finite resources and finite attention both needed an end.
Provisioning failures were visible because a reviewer waited. Cleanup failures could remain silent while the system appeared successful.
R7K1 gave stalled removal its own queue, alerts, and dashboard. A route or credential failing to revoke had higher urgency than a log archive failing to delete. The failure record named capability and data consequence.
Retries used backoff and did not hold a global cleanup lock. One broken provider could not block every other environment's expiry.
After repeated failure, the system escalated to manual review with exact resource identity and safe commands. Completing the manual action updated the same receipt trail.
Removal became first-class operations work, not a background chore allowed to fail forever.
We rehearsed interruption at every step
Tests began with an environment owning route, container, data, credential, captured mail, logs, and shared artifact reference. The controller crashed after each removal effect in turn.
On restart, repeated reconciliation had to converge on:
no reachable route
no valid runtime capability
no owned mutable data or runtime resource
shared artifact retained only if another reference required it
tombstone with complete receiptsAnother fixture requested extension during each phase and asserted the allowed outcome. Leak scans compared simulated provider inventory with lifecycle state.
The final state mattered, but so did intermediate safety. Credential revocation should not wait behind optional log deletion.
Testing cleanup like provisioning changed which bugs the system considered release blockers.
Expiry made provisioned resources honest
Before the lifecycle redesign, creating a preview was celebrated and removing one was assumed. The system optimized the visible first half and outsourced the second half to time and naming conventions.
R7K1's admission contract became:
we know why this environment exists
who owns the decision to extend it
when it begins removal
which resources and capabilities belong to it
how every removal can resume
what evidence proves it endedIf provisioning could not supply those answers, it was not allowed to create the resource yet.
This did not eliminate leaks or manual recovery. It made them contradictions the system could find rather than ordinary debris.
Expiry is part of provisioning because “temporary” is not a property resources acquire with age. It is a promise made at creation about how the system will bring them to an accountable end.
The design also changed the emotional shape of cleanup. Removal stopped feeling like destruction of something vaguely valuable and became fulfillment of a visible agreement. Reviewers had time to respond, owners could justify exceptions, and operators had receipts instead of a quiet empty host. A clean resource list was useful; a lifecycle that could explain why it was clean was the real achievement.
That explanation made temporary infrastructure dependable enough to create freely without pretending its future disappearance would somehow manage itself.