## Summary - add explicit BaselineSnapshot lifecycle truth with conservative backfill and a shared truth resolver - block baseline compare from building, incomplete, or superseded snapshots and align workspace/tenant UI truth surfaces with effective snapshot state - surface artifact truth separately from operation outcome across baseline profile, snapshot, compare, and operation run pages ## Testing - integrated browser smoke test on the active feature surfaces - `vendor/bin/sail artisan test --compact tests/Feature/Filament/BaselineSnapshotTruthSurfaceTest.php tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php` - targeted baseline lifecycle and compare guard coverage added in Pest - `vendor/bin/sail bin pint --dirty --format agent` ## Notes - Livewire v4 compliance preserved - no panel provider registration changes were needed; Laravel 12 providers remain in `bootstrap/providers.php` - global search remains disabled for the affected baseline resources by design - destructive actions remain confirmation-gated; capture and compare actions keep their existing authorization and confirmation behavior - no new panel assets were added; existing deploy flow for `filament:assets` is unchanged Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #189
59 lines
6.7 KiB
Markdown
59 lines
6.7 KiB
Markdown
# Research: BaselineSnapshot Artifact Truth & Downstream Consumption Guards
|
||
|
||
## Decision 1: Model BaselineSnapshot with a single persisted lifecycle enum and derived historical truth in V1
|
||
|
||
- Decision: Add a first-class BaselineSnapshot lifecycle state with `building`, `complete`, and `incomplete`, and derive historical `superseded` truth in presentation/resolution instead of persisting it as a lifecycle mutation.
|
||
- Rationale: The spec is intentionally narrow to BaselineSnapshot, but the constitution requires snapshots to remain immutable. A persisted three-state lifecycle is enough to separate artifact truth from run truth, while derived historical status lets the UI communicate that a snapshot is no longer current without mutating immutable artifacts.
|
||
- Alternatives considered:
|
||
- Separate lifecycle and historical-status enums. Rejected for V1 because it adds more schema and UI complexity than the spec requires.
|
||
- Persisted `superseded` lifecycle mutation. Rejected because it conflicts with the constitution rule that snapshots and backups remain immutable.
|
||
- Generic polymorphic artifact-state framework. Rejected because the spec explicitly limits scope to BaselineSnapshot.
|
||
|
||
## Decision 2: Keep staged item persistence and add explicit finalization instead of rewriting capture into one large transaction
|
||
|
||
- Decision: Preserve the existing deduplicated, chunked item-persistence strategy in `CaptureBaselineSnapshotJob`, but make snapshot creation begin in `building` and add an explicit finalization checkpoint before the snapshot can become `complete`.
|
||
- Rationale: The current job already deduplicates item payloads and inserts items in chunks. Replacing that with one large transaction would be a broader persistence rewrite with higher regression risk. The core integrity defect is not the existence of staged persistence; it is the lack of explicit artifact finalization and unusable-state marking when staged persistence fails.
|
||
- Alternatives considered:
|
||
- Wrap snapshot row plus all item writes in one transaction. Rejected because it is a larger behavioral change, can increase lock duration, and is not necessary to satisfy the artifact-truth invariant.
|
||
- Delete snapshot rows on failure. Rejected because the spec allows retaining incomplete artifacts for diagnostics and auditability.
|
||
|
||
## Decision 3: Treat `active_snapshot_id` as a cached pointer to the latest complete snapshot only
|
||
|
||
- Decision: Keep `baseline_profiles.active_snapshot_id`, but tighten its semantics so it may point only to a complete snapshot. Effective baseline truth resolves from complete snapshots, not from the latest attempt.
|
||
- Rationale: Existing services, stats objects, and Filament resources already use `active_snapshot_id` heavily. Replacing it wholesale would create unnecessary churn. Tightening its meaning and adding a shared effective-snapshot resolver keeps compatibility while enforcing the new truth rule.
|
||
- Alternatives considered:
|
||
- Remove `active_snapshot_id` and resolve current truth only by querying snapshots. Rejected because it would require broader UI/service refactoring and lose a useful current-pointer optimization.
|
||
- Leave `active_snapshot_id` semantics unchanged and only add compare-time checks. Rejected because profile truth and UI would still silently advance to incomplete snapshots.
|
||
|
||
## Decision 4: Use a centralized consumability/effective-truth service or helper for capture, compare, and UI
|
||
|
||
- Decision: Introduce one authoritative domain path for determining whether a snapshot is consumable and which snapshot is the effective current baseline for a profile.
|
||
- Rationale: The current code assumes snapshot validity in multiple places: capture promotion, compare start, compare execution, stats, and UI. Duplicated state checks would drift. A central resolver keeps the rule enforceable and testable.
|
||
- Alternatives considered:
|
||
- Inline `status === complete` checks in each service/page. Rejected because that duplicates rules and invites inconsistent handling of superseded or legacy snapshots.
|
||
- Put all logic only on the Eloquent model. Rejected because effective-truth resolution involves profile-level fallback rules, legacy handling, and operator-safe reason codes that fit better in a domain service/helper.
|
||
|
||
## Decision 5: Backfill legacy snapshots conservatively using proof, not assumption
|
||
|
||
- Decision: Backfill historical snapshots as `complete` only when completion can be proven from persisted item counts, `summary_jsonb`, and producing-run context where available. Mark ambiguous rows `incomplete`.
|
||
- Rationale: The defect being fixed is silent trust in partial artifacts. Blindly backfilling historical rows as complete would reintroduce the same governance-integrity problem under a new label. Conservative backfill is aligned with the spec and with the platform’s fail-safe posture.
|
||
- Alternatives considered:
|
||
- Mark every legacy snapshot `complete`. Rejected because it makes historical ambiguity look trustworthy.
|
||
- Mark every legacy snapshot `incomplete`. Rejected because it would unnecessarily invalidate proven-good history and create avoidable recapture churn.
|
||
|
||
## Decision 6: Completion proof must allow valid empty captures only when emptiness is explicitly proven
|
||
|
||
- Decision: Allow a snapshot to become `complete` with zero items only when the capture completed cleanly and the scope/result proves there were zero subjects to capture. Otherwise, zero items are not enough to infer completeness.
|
||
- Rationale: Existing tests allow empty captures for zero-subject scopes, and a zero-subject baseline can still be a valid outcome. The important distinction is between intentionally empty and ambiguously incomplete.
|
||
- Alternatives considered:
|
||
- Require at least one item for every complete snapshot. Rejected because it would make legitimate empty captures impossible.
|
||
- Treat any zero-item snapshot as complete. Rejected because it would misclassify failures that occurred before meaningful assembly.
|
||
|
||
## Decision 7: Reuse the existing artifact-truth and badge infrastructure for presentation
|
||
|
||
- Decision: Extend the existing `ArtifactTruthPresenter` and badge registration patterns for BaselineSnapshot lifecycle and usability rather than creating a new snapshot-specific presentation system.
|
||
- Rationale: The repo already centralizes artifact truth across EvidenceSnapshot, TenantReview, ReviewPack, and OperationRun. Reusing that system keeps state labels, colors, and operator explanations consistent and satisfies BADGE-001.
|
||
- Alternatives considered:
|
||
- Add ad-hoc state rendering in each Filament resource/page. Rejected because it would violate badge centralization and drift across surfaces.
|
||
- Introduce a separate baseline-only presenter. Rejected because the existing artifact-truth envelope already models the required dimensions.
|