# Phase 0 — Research (096 Ops Polish Bundle) This feature is an operations / background-job hardening pass. The design intentionally reuses existing run observability and dedupe primitives already present in the codebase. ## Decision 1 — Use `OperationRunService` + DB unique indexes for dedupe **Decision:** Use `OperationRunService::ensureRunWithIdentity(...)` (tenant-scoped) and `OperationRunService::ensureWorkspaceRunWithIdentity(...)` (workspace-scoped) as the canonical dedupe mechanism, backed by the existing partial unique indexes for active runs. **Rationale:** - The repo already enforces “active-run dedupe MUST be enforced at DB level” via partial unique indexes and the `ensureRun*` helpers. - DB enforcement remains correct under concurrency and across multiple workers. - Keeps the single source of truth in `operation_runs` (Monitoring → Operations) rather than adding a second dedupe store. **Alternatives considered:** - Laravel job uniqueness (e.g., `ShouldBeUnique` / cache lock) — rejected because it introduces a second dedupe primitive outside the canonical `OperationRun` ledger and may behave differently across environments. - Scheduler-level overlap prevention only — rejected because the spec requires dedupe at execution time and must handle duplicate dispatch / redelivery. ## Decision 2 — Dedupe identity rule (per spec clarifications) **Decision:** Derive job identity as: - Prefer `operation_run_id` when available. - Otherwise `tenant_id + job_type + stable input fingerprint`. **Rationale:** - `operation_run_id` is already stable and non-secret. - Fallback fingerprint avoids secrets, stays deterministic, and is suitable for both logging and DB identity hashing. **Alternatives considered:** - Fingerprinting full payloads — rejected to avoid secrets/PII and to keep dedupe stable even if non-essential context changes. ## Decision 3 — Enforce dedupe at execute time (not just dispatch) **Decision:** Add an execution-time guard so a job skips early when it is not the canonical active run for its identity. **Rationale:** - Covers duplicate job dispatch/redelivery even if a caller fails to reuse the same `OperationRun` at dispatch time. - Aligns with spec FR-006 and the constitution’s “queued/scheduled ops use locks + idempotency”. **Alternatives considered:** - Rely on dispatch-only dedupe — rejected because duplicate jobs can still be enqueued and run concurrently. ## Decision 4 — Summary counters use “final attempt wins” semantics **Decision:** Persist `OperationRun.summary_counts` at terminal completion by overwriting with normalized counts (final attempt reflects truth; retries do not double count). **Rationale:** - Matches clarified requirement (“final attempt”) and avoids needing cross-attempt reconciliation. - Fits existing patterns in `OperationRunService::updateRun(...)` which sanitizes/normalizes summary keys. **Alternatives considered:** - Incremental counters (`incrementSummaryCounts`) across attempts — rejected because it risks double-counting under retries unless attempt IDs are tracked. ## Decision 5 — Housekeeping job tracking is workspace-scoped **Decision:** Track `ReconcileAdapterRunsJob` via a workspace-scoped `OperationRun` (`tenant_id = null`) using `type = ops.reconcile_adapter_runs`. **Rationale:** - The job is not tenant-specific and reconciles across runs; workspace-scoped runs are explicitly supported by the schema + service. **Alternatives considered:** - Create one run per tenant — rejected because it would misrepresent the job’s actual unit of work and inflate noise. ## Decision 6 — Seed tenant external ID must be UUID v4 **Decision:** Ensure the seed tenant’s `external_id` is generated as UUID v4 regardless of `INTUNE_TENANT_ID`. **Rationale:** - Matches clarified requirement and avoids coupling a human-readable env value to a UUID-constrained field. **Alternatives considered:** - Reuse `INTUNE_TENANT_ID` for `external_id` — rejected because it is not guaranteed UUID formatted.