TenantAtlas/specs/092-legacy-purge-final/plan.md
ahmido 5770c7b76b Spec 092: Legacy Purge (runs/routes/UI/test shims) (#110)
Implements Spec 092 legacy purge.

Key changes:
- Remove legacy Inventory landing page + view; link Inventory entry directly to Inventory Items.
- Update Drift landing copy to "operation runs"; remove URL heuristic from context bar.
- Remove legacy redirect shim route and assert 404 for old bookmarks.
- Staged job payload change: remove legacy ctor arg; keep legacy field for deserialization compatibility; new payload omits field.
- Remove legacy notification artifact.
- Remove legacy test shim + update tests; strengthen guard suite with scoped exception for job compat field.
- Add spec/plan/tasks/checklist artifacts under specs/092-legacy-purge-final.

Tests:
- Focused Pest suite for guards, legacy routes, redirect behavior, job compatibility, drift copy.
- Pint run: `vendor/bin/sail bin pint --dirty`.

Notes:
- Deploy B final removal of `backupScheduleRunId` should occur only after the compatibility window defined in the spec.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #110
2026-02-14 18:43:56 +00:00

8.4 KiB

Implementation Plan: Legacy Purge (Runs / Routes / UI / Test Shims)

Branch: 092-legacy-purge-final | Date: 2026-02-14 | Spec: /specs/092-legacy-purge-final/spec.md Input: Feature specification from /specs/092-legacy-purge-final/spec.md

Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.

Summary

Remove remaining legacy “run world” artifacts across routes, UI copy, redirect-only pages, and tests. Enforce permanence with guard tests and deliver the queued-job payload shape change via a staged rollout (compatibility release → final purge).

Technical Context

Language/Version: PHP 8.4.x (Laravel 12)
Primary Dependencies: Filament v5, Livewire v4, Pest v4, Laravel Sail
Storage: PostgreSQL
Testing: Pest (run via vendor/bin/sail artisan test)
Target Platform: Web application (Docker/Sail locally; Dokploy containers in staging/prod) Project Type: Laravel monolith
Performance Goals: No new performance goals (cleanup-only change).
Constraints: Must not change historical migrations; must preserve RBAC semantics; queued-job payload change must be staged to avoid unserialize failures.
Scale/Scope: Repo-wide purge + guard rails; small number of routes/views touched; staged deploy A/B for the job payload.

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

  • Inventory-first: clarify what is “last observed” vs snapshots/backups
  • Read/write separation: any writes require preview + confirmation + audit + tests
  • Graph contract path: Graph calls only via GraphClientInterface + config/graph_contracts.php
  • Deterministic capabilities: capability derivation is testable (snapshot/golden tests)
  • RBAC-UX: two planes (/admin vs /system) remain separated; cross-plane is 404; non-member tenant access is 404; member-but-missing-capability is 403; authorization checks use Gates/Policies + capability registries (no raw strings, no role-string checks)
  • Workspace isolation: non-member workspace access is 404; tenant-plane routes require an established workspace context; workspace context switching is separate from Filament Tenancy
  • RBAC-UX: destructive-like actions require ->requiresConfirmation() and clear warning text
  • RBAC-UX: global search is tenant-scoped; non-members get no hints; inaccessible results are treated as not found (404 semantics)
  • Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked
  • Run observability: long-running/remote/queued work creates/reuses OperationRun; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log; auth handshake exception OPS-EX-AUTH-001 allows synchronous outbound HTTP on /auth/* without OperationRun
  • Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter
  • Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens
  • Badge semantics (BADGE-001): status-like badges use BadgeCatalog / BadgeRenderer; no ad-hoc mappings; new values include tests
  • Filament UI Action Surface Contract: for any new/modified Filament Resource/RelationManager/Page, define Header/Row/Bulk/Empty-State actions, ensure every List/Table has a record inspection affordance (prefer recordUrl() clickable rows; do not render a lone View row action), keep max 2 visible row actions with the rest in “More”, group bulk actions, require confirmations for destructive actions (typed confirmation for large/bulk where applicable), write audit logs for mutations, enforce RBAC via central helpers (non-member 404, member missing capability 403), and ensure CI blocks merges if the contract is violated or not explicitly exempted

Gate status (pre-Phase 0): PASS

  • Inventory-first: no changes to inventory modeling; only removes a redirect-only landing page and ensures canonical entry points.
  • Read/write separation: no new writes introduced.
  • Graph contract path: no new Graph calls.
  • Deterministic capabilities: unchanged.
  • RBAC-UX + workspace/tenant isolation: routes removed are legacy shims; behavior for unauthorized access remains deny-as-not-found (404) where applicable.
  • Run observability: unchanged.
  • Filament Action Surface Contract: only copy/navigation/heuristic removal; no new resources/pages or actions.

Project Structure

Documentation (this feature)

specs/092-legacy-purge-final/
├── plan.md              # This file (/speckit.plan command output)
├── research.md          # Phase 0 output (/speckit.plan command)
├── data-model.md        # Phase 1 output (/speckit.plan command)
├── quickstart.md        # Phase 1 output (/speckit.plan command)
├── contracts/           # Phase 1 output (/speckit.plan command)
└── tasks.md             # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)

Source Code (repository root)

app/
├── Filament/
├── Http/
├── Jobs/
├── Models/
├── Providers/
├── Support/
└── ...

routes/
├── web.php
└── console.php

resources/
├── views/
└── ...

database/
├── migrations/          # immutable
└── factories/

tests/
├── Feature/
└── Unit/

specs/092-legacy-purge-final/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
└── contracts/

Structure Decision: Laravel monolith. Changes are localized to routing (routes/web.php), UI views (Blade), Filament pages/helpers, and Pest feature tests.

Complexity Tracking

No constitution violations are required for this feature.

Phase 0 — Outline & Research

Goals

  • Translate spec requirements into concrete code locations and guard patterns.
  • Decide how to deliver the queued-job payload change safely (Deploy A/B).

Research tasks (from spec + repo reality)

  • Identify all legacy redirect shims and legacy endpoints still present (routes + UI links + tests).
  • Identify all legacy identifiers that must be purged (class names, field names, copy).
  • Confirm which routes should return 404 Not Found after removal (per spec Clarifications).

Output

  • specs/092-legacy-purge-final/research.md

Phase 1 — Design & Contracts

Data model

No new entities. Document impacted payload/fields and removal timeline:

  • Background job payload: remove legacy backupScheduleRunId using staged rollout.
  • UI/route artifacts: remove redirect-only landing page and legacy route shims.

Contracts

This feature is not an external API feature, but it does define deterministic HTTP semantics for legacy endpoints.

  • Provide a minimal OpenAPI contract that documents legacy URLs returning 404 (deny-by-absence).

Output

  • specs/092-legacy-purge-final/data-model.md
  • specs/092-legacy-purge-final/contracts/*
  • specs/092-legacy-purge-final/quickstart.md

Agent context update

Run:

  • .specify/scripts/bash/update-agent-context.sh copilot

Constitution Check (post-Phase 1)

Gate status (post-Phase 1): PASS

  • No new Graph calls.
  • No new mutations/actions.
  • Route removals preserve deny-as-not-found semantics for legacy deep links.
  • Staged rollout plan prevents queued-payload failures.

Phase 2 — Implementation Planning (Deploy/PR sequence)

Deploy A (compatibility release)

  • Make the legacy job field optional / ignorable so older queued payloads still deserialize.
  • Stop passing/creating dummy legacy run IDs at dispatch sites.
  • Add/adjust tests ensuring both payload shapes are accepted.

Deploy B (final purge)

  • Remove the legacy job field entirely once the compatibility window has passed and queues are drained.
  • Remove any remaining references + guard against reintroduction.
  1. Routing + tests: remove legacy redirect routes (e.g., /admin/t/{tenant}/operations shim) and update tests to assert 404.
  2. UI cleanup: remove redirect-only Inventory landing + dead view; update Drift copy to “operation runs”; remove context-bar URL heuristic.
  3. Job staged rollout: implement Deploy A compatibility changes with tests.
  4. Final purge: implement Deploy B removal + tighten guards.

Test strategy (minimum)

  • Focused: run the feature test files that assert legacy URLs return 404 and any job serialization tests added for Deploy A/B.
  • Repo hygiene: run vendor/bin/sail bin pint --dirty.