Files
artifact-store/workplans/ARTIFACT-STORE-WP-0003-retention-lifecycle.md

3.9 KiB

id, type, title, repo, domain, status, owner, topic_slug, planning_priority, planning_order, created, updated, state_hub_workstream_id
id type title repo domain status owner topic_slug planning_priority planning_order created updated state_hub_workstream_id
ARTIFACT-STORE-WP-0003 workplan Retention Lifecycle: Defaults, Extensions, Holds, Deletion Eligibility artifact-store stack done codex stack high 3 2026-05-15 2026-05-16 84930f4c-3bcf-415e-a94c-bfa854a15871

ARTIFACT-STORE-WP-0003: Retention Lifecycle

Purpose

Implement the retention engine. By the end of this workplan, every package has a computed expires_at, operators can extend retention or apply / release holds, and the system can mark expired packages as eligible for deletion — without actually deleting bytes (GC is WP-0006).

Constraints

  • ADR-0002 (every retention change is an event).
  • docs/ARCHITECTURE-BLUEPRINT.md retention sections.

Prerequisites

  • WP-0001 done (retention_classes seeded, retention_state view exists).
  • WP-0002 done (HTTP surface exists to attach the new endpoints to).

D3.1 - Default Retention Application

id: ARTIFACT-STORE-WP-0003-T001
status: done
priority: high
state_hub_task_id: "25531837-d2ff-4252-b0d0-31283597737f"

Acceptance:

  • On POST /packages, the requested retention_class is validated and the v1.retention.default_applied event is written with the computed expires_at.
  • Default durations per class are operator-configurable via a config file (TOML); the file path is documented in OPERATOR.md.
  • permanent-record packages have expires_at = NULL and eligible_for_deletion = false.

D3.2 - Retention Extensions

id: ARTIFACT-STORE-WP-0003-T002
status: done
priority: high
state_hub_task_id: "66576e53-af4c-48dc-8dc3-cf8223a821c7"

Acceptance:

  • POST /packages/{id}/retention/extensions accepts {new_expires_at, reason}. The new value must be strictly later than the current; reason is mandatory.
  • Each extension writes a v1.retention.extended event; retention_state.current_expires_at updates on the same transaction.
  • A package's full extension history is recoverable from events.

D3.3 - Holds (Apply And Release)

id: ARTIFACT-STORE-WP-0003-T003
status: done
priority: high
state_hub_task_id: "8164e448-0e90-41aa-a973-77f8f607a0b3"

Acceptance:

  • POST /packages/{id}/retention/holds records a hold with a reason and actor; emits v1.retention.hold_applied.
  • A package with at least one active hold is never eligible_for_deletion regardless of expires_at.
  • POST /packages/{id}/retention/holds/{hold_id}/release requires a reason; emits v1.retention.hold_released.
  • Test: hold applied → expiry passes → eligibility stays false; hold released → eligibility flips to true.

D3.4 - Deletion Eligibility Sweeper

id: ARTIFACT-STORE-WP-0003-T004
status: done
priority: medium
state_hub_task_id: "fe13cd0d-aab7-4e0a-a7df-e6e535d4099b"

Acceptance:

  • A scheduled task (cron-style configurable interval; default 1 hour) scans packages whose expires_at has passed and no active hold exists, and emits v1.retention.deletion_eligible events.
  • The sweeper is idempotent: events are emitted at most once per package per eligibility transition.
  • The sweeper is invokable as a CLI subcommand for tests: artifactstore retention sweep.

D3.5 - Audit Surface For Retention

id: ARTIFACT-STORE-WP-0003-T005
status: done
priority: medium
state_hub_task_id: "7dce0c92-76d6-4bfc-bbc5-8e18b96139d2"

Acceptance:

  • GET /packages/{id}/retention/history returns the ordered list of retention events for a package.
  • The default response is the JCS projection; CBOR is available via Accept: application/cbor.

Success criteria

  • A guide-board run can be ingested, given release-evidence, later extended once, held for a quarter, released, swept, and marked eligible — all visible through both retention_state and the event log.
  • No bytes are deleted by this workplan; that is WP-0006.