TenantAtlas/specs/093-scope-001-workspace-id-isolation/contracts/cli.md
ahmido 92a36ab89e SCOPE-001: DB-level workspace isolation via workspace_id (#112)
Implements Spec 093 (SCOPE-001) workspace isolation at the data layer.

What changed
- Adds `workspace_id` to 12 tenant-owned tables and enforces correct binding.
- Model write-path enforcement derives workspace from tenant + rejects mismatches.
- Prevents `tenant_id` changes (immutability) on tenant-owned records.
- Adds queued backfill command + job (`tenantpilot:backfill-workspace-ids`) with OperationRun + AuditLog observability.
- Enforces DB constraints (NOT NULL + FK `workspace_id` → `workspaces.id` + composite FK `(tenant_id, workspace_id)` → `tenants(id, workspace_id)`), plus audit_logs invariant.

UI / operator visibility
- Monitor backfill runs in **Monitoring → Operations** (OperationRun).

Tests
- `vendor/bin/sail artisan test --compact tests/Feature/WorkspaceIsolation`

Notes
- Backfill is queued: ensure a queue worker is running (`vendor/bin/sail artisan queue:work`).

Spec package
- `specs/093-scope-001-workspace-id-isolation/` (plan, tasks, contracts, quickstart, research)

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #112
2026-02-14 22:34:02 +00:00

1.6 KiB

CLI Contract — 093 Workspace ID Backfill

This feature adds an operator-only Artisan command to backfill missing workspace_id on tenant-owned tables.

Command

  • Name (proposed): tenantpilot:backfill-workspace-ids

Flags / Options (proposed)

  • --dry-run (default: false)
    • Prints counts per table and exits without writing.
  • --table=<name> (optional)
    • Restrict execution to a single table.
  • --batch-size=<n> (default: 5_000)
    • Batch size for updates (where chunking is used).
  • --resume-from=<cursor> (optional)
    • Resume from a saved cursor/checkpoint (implementation-defined).
  • --max-rows=<n> (optional)
    • Safety valve for partial runs.

Safety + Observability

Execution strategy (queued):

  • The command is a start surface only: authorize → acquire lock → create/reuse OperationRun → dispatch queued jobs → print a “View run” pointer.
  • The backfill mutations MUST execute inside queued jobs (batch/table scoped) to support large datasets.

Safety + observability requirements:

  • Must acquire a lock (cache/DB-backed lock) to prevent concurrent runs.
  • Must create/reuse an OperationRun for visibility and progress tracking.
  • Must write an AuditLog entry for start and end (outcome, counts, duration).
  • Must abort and report when a tenant→workspace mapping cannot be resolved.

Output

  • Printed “Run started” summary:

    • OperationRun identifier (or URL/route reference when available)
    • jobs dispatched count
    • selected tables / scope
  • Per-table totals:

    • scanned rows
    • rows missing workspace_id
    • rows updated
  • Final summary + recommended validation SQL.