Skip to content

finding(spec): the ttl.onlyWhen x archive refine tells authors something that stops being true once the Archiver honours ttl #10526

Description

@os-elon

Found while implementing #10347 (the Archiver now selects rows by the declared ttl cutoff). Out of that card's scope by its own fence, recorded rather than touched — the file is packages/spec (domain:spec lane) and reopening the refusal is explicitly a separate decision.

What goes stale

packages/spec/src/data/object.zod.ts refuses ttl.onlyWhen beside archive, and both its rationale comment and its author-facing message state the runtime fact that justified the refusal:

  • comment: "reapObject returns into archiveObject before the ttl reap ever runs, so with archive declared the filter guards a code path that is never executed ... while the Archiver itself copies and hot-deletes by created_at age alone"
  • message: "lifecycle.ttl.onlyWhen cannot be combined with archive — archive takes over the whole reap (the ttl sweep never runs) and the Archiver moves rows by age alone"

Once #10347 lands, the second half of each is false: with ttl declared the Archiver moves rows by the ttl cutoff on ttl.field, not by created_at age. An author who reads the message is told the sweep never runs, which is now wrong about their own object.

Two separable pieces

  1. Message and comment accuracy. Whatever is decided about the refusal itself, the wording should stop asserting a runtime behaviour that no longer exists. This is text, not accept-set.
  2. Whether the refusal should survive at all. lifecycle ttl accepts no row filter, so a transient object with terminal rows in-band cannot spare them — give ttl an onlyWhen mirroring retention.onlyWhen #10165 refused the pair on the stated grounds that the filter would guard a never-executed path. That premise is gone for plain ttl. Whether ttl.onlyWhen should now become meaningful under archive — the Archiver would have to spread the filter into its candidate read the way reap() does — is a real question, and it narrows/widens the acceptance face (Clause ②), so it needs its own ruling and the contract-review tier.

⚠️ Do not read this as an argument for either direction. #10347's ruling explicitly declined to authorize reopening the refusal; this is only the record that its stated reason has changed.

The retention.onlyWhen x archive refine beside it is unaffected: with retention and no ttl the Archiver still moves rows by created_at age.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions