Implements Spec 096 ops polish bundle: - Persist durable OperationRun.summary_counts for assignment fetch/restore (final attempt wins) - Server-side dedupe for assignment jobs (15-minute cooldown + non-canonical skip) - Track ReconcileAdapterRunsJob via workspace-scoped OperationRun + stable failure codes + overlap prevention - Seed DX: ensure seeded tenants use UUID v4 external_id and seed satisfies workspace_id NOT NULL constraints Verification (local / evidence-based): - `vendor/bin/sail artisan test --compact tests/Feature/Operations/AssignmentRunSummaryCountsTest.php tests/Feature/Operations/AssignmentJobDedupeTest.php tests/Feature/Operations/ReconcileAdapterRunsJobTrackingTest.php tests/Feature/Seed/PoliciesSeederExternalIdTest.php` - `vendor/bin/sail bin pint --dirty` Spec artifacts included under `specs/096-ops-polish-assignment-dedupe-system-tracking/` (spec/plan/tasks/checklists). Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #115
50 lines
2.5 KiB
Markdown
50 lines
2.5 KiB
Markdown
# Phase 1 — Data Model (096 Ops Polish Bundle)
|
|
|
|
No new tables are required. The feature reuses existing operational ledger and seed data structures.
|
|
|
|
## Entity: `operation_runs` (tenant-scoped and workspace-scoped)
|
|
|
|
**Purpose:** Canonical ledger for background operations (status, timestamps, outcomes, counters, failures).
|
|
|
|
**Key fields (existing):**
|
|
- `id` (PK)
|
|
- `workspace_id` (FK, required)
|
|
- `tenant_id` (FK, nullable)
|
|
- Tenant-scoped operations: non-null
|
|
- Workspace-scoped operations (housekeeping): null
|
|
- `user_id` (FK, nullable) — initiator user when applicable
|
|
- `initiator_name` (string) — stored label for system/user
|
|
- `type` (string) — operation type (e.g., `assignments.fetch`, `assignments.restore`, `ops.reconcile_adapter_runs`)
|
|
- `status` (string) — `queued` | `running` | `completed` (see `OperationRunStatus`)
|
|
- `outcome` (string) — `pending` | `succeeded` | `failed` (see `OperationRunOutcome`)
|
|
- `run_identity_hash` (string) — deterministic identity hash
|
|
- `summary_counts` (json/jsonb array) — normalized counters (keys constrained by `OperationCatalog::allowedSummaryKeys()`)
|
|
- `failure_summary` (json/jsonb array) — entries like `{ code: string, message: string }` (sanitized, stable)
|
|
- `context` (json/jsonb object) — non-secret context, may include selection metadata
|
|
- `started_at`, `completed_at` (timestamps)
|
|
- `created_at`, `updated_at`
|
|
|
|
**Constraints / indexes (existing):**
|
|
- Active-run dedupe is enforced at the DB layer using partial unique indexes:
|
|
- Tenant runs: unique on `(tenant_id, run_identity_hash)` for active statuses.
|
|
- Workspace runs: unique on `(workspace_id, run_identity_hash)` when `tenant_id IS NULL` for active statuses.
|
|
|
|
**Feature usage:**
|
|
- Assignment jobs: ensure a stable `run_identity_hash` based on the clarified identity rule; persist `summary_counts` at terminal completion.
|
|
- Reconcile housekeeping job: create/reuse a workspace-scoped run (tenant_id null) and persist success/failure + summary counts.
|
|
|
|
## Entity: `tenants`
|
|
|
|
**Purpose:** Tenant scope boundary; owns tenant-scoped `operation_runs` and policies.
|
|
|
|
**Field (in scope):**
|
|
- `external_id` (string)
|
|
|
|
**Feature requirement:**
|
|
- Seeded tenants must have `external_id` set to a UUID v4 string.
|
|
|
|
## Notes on validation / sanitization
|
|
|
|
- Summary counters must be normalized/sanitized before persistence (existing `OperationRunService` behavior).
|
|
- Failure summaries must store stable reason codes + sanitized messages (no secrets/tokens/PII/raw payload dumps).
|