TenantAtlas/specs/235-baseline-capture-truth/research.md
ahmido 2752515da5
Some checks failed
Main Confidence / confidence (push) Failing after 54s
Spec 235: harden baseline truth and onboarding flows (#271)
## Summary
- harden baseline capture truth, compare readiness, and monitoring explanations around latest inventory eligibility, blocked prerequisites, and zero-subject outcomes
- improve onboarding verification and bootstrap recovery handling, including admin-consent callback invalidation and queued execution legitimacy/report behavior
- align workspace findings/workspace overview signals and refresh the related spec, roadmap, and spec-candidate artifacts

## Validation
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/BaselineDriftEngine/BaselineCaptureAuditEventsTest.php tests/Feature/BaselineDriftEngine/BaselineSnapshotNoTenantIdentifiersTest.php tests/Feature/BaselineDriftEngine/CaptureBaselineContentTest.php tests/Feature/BaselineDriftEngine/CaptureBaselineFullContentOnDemandTest.php tests/Feature/BaselineDriftEngine/CaptureBaselineMetaFallbackTest.php tests/Feature/Baselines/BaselineCaptureTest.php tests/Feature/Baselines/BaselineCompareFindingsTest.php tests/Feature/Baselines/BaselineSnapshotBackfillTest.php tests/Feature/Filament/BaselineCaptureResultExplanationSurfaceTest.php tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php tests/Feature/Filament/OperationRunBaselineTruthSurfaceTest.php tests/Feature/Monitoring/AuditCoverageGovernanceTest.php tests/Feature/Monitoring/GovernanceOperationRunSummariesTest.php tests/Feature/Notifications/OperationRunNotificationTest.php tests/Feature/Authorization/OperatorExplanationSurfaceAuthorizationTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/AdminConsentCallbackTest.php tests/Feature/Filament/WorkspaceOverviewDbOnlyTest.php tests/Feature/Guards/Spec194GovernanceActionSemanticsGuardTest.php tests/Feature/ManagedTenantOnboardingWizardTest.php tests/Feature/Onboarding/OnboardingVerificationTest.php tests/Feature/Operations/QueuedExecutionAuditTrailTest.php tests/Unit/Operations/QueuedExecutionLegitimacyGateTest.php`

## Notes
- browser validation was not re-run in this pass

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #271
2026-04-24 05:44:54 +00:00

5.2 KiB

Research: Baseline Capture Truthful Outcomes and Upstream Guardrails

Decision 1: The latest relevant inventory sync is the only authoritative upstream basis for baseline capture

  • Decision: Evaluate baseline-capture eligibility against the latest relevant inventory sync outcome and coverage. Do not fall back to an older successful run when the latest relevant run is missing, blocked, failed, or otherwise not credible.
  • Rationale: The operator problem is false reassurance. Any stale-success fallback would preserve the exact failure mode this feature is meant to remove.
  • Alternatives considered:
    • Use the most recent successful inventory sync even if a newer one failed. Rejected because it hides current upstream truth.
    • Allow capture to proceed and only warn later. Rejected because a green run can still be interpreted as a trustworthy baseline refresh.

Decision 2: Reuse the existing baseline reason-code family and shared translator path

  • Decision: Add the new blocked/no-data reasons to BaselineReasonCodes and translate them through ReasonTranslator instead of introducing surface-local message trees.
  • Rationale: The same reasons must be understood on start surfaces, compare-readiness surfaces, Monitoring summaries, and audit-aligned explanations. A single translator path keeps those surfaces coherent.
  • Alternatives considered:
    • Keep separate copy in each Filament page. Rejected because wording and operator guidance would drift immediately.
    • Create a brand-new presenter family for baseline capture. Rejected because the existing translator and Ops UX summary builders already cover this responsibility.

Decision 3: Zero-subject capture is a truthful no-data outcome, not a successful baseline refresh

  • Decision: When capture finds zero in-scope subjects, complete the run as partially_succeeded, record a dedicated no-data reason code, and ensure any persisted snapshot artifact remains non-consumable by reusing existing incomplete lifecycle semantics.
  • Rationale: Zero captured subjects is real evidence, but it does not produce a trustworthy baseline for compare and must not be represented as a successful refresh.
  • Alternatives considered:
    • Keep zero-subject capture as succeeded. Rejected because it produces a false-green operator signal.
    • Add a new no_data snapshot lifecycle state. Rejected because existing incomplete/non-consumable semantics already express the required truth without widening the state model.

Decision 4: Current baseline truth remains anchored to the last consumable snapshot

  • Decision: Only advance BaselineProfile.active_snapshot_id when the new capture result is consumable. Blocked captures and zero-subject captures leave the previous consumable snapshot in place.
  • Rationale: The product already has BaselineProfile::resolveCurrentConsumableSnapshot(). The narrowest correct hardening is to stop promoting non-truthful results rather than inventing a second pointer or state family.
  • Alternatives considered:
    • Store a second pointer for “latest attempted snapshot.” Rejected because the existing latest-attempt and run-detail paths already provide diagnostics.
    • Clear active_snapshot_id on blocked or zero-subject capture. Rejected because it would discard previously trustworthy truth and make compare availability noisier than necessary.

Decision 5: Monitoring and compare-readiness surfaces extend the existing shared explanation builders

  • Decision: Drive dominant explanation text through BaselineCompareStats and GovernanceRunDiagnosticSummaryBuilder, with ReasonTranslator supplying the operator-safe wording.
  • Rationale: These shared helpers already own the explanation-first contract for baseline truth and Monitoring. Extending them keeps the feature inside existing UX boundaries.
  • Alternatives considered:
    • Add special-case summaries directly in ViewBaselineProfile and BaselineCompareLanding. Rejected because those pages are consumers, not the source of truth.
    • Push explanation logic into ad-hoc JSON context parsing per test/page. Rejected because it spreads behavior and makes regressions harder to prove.

Decision 6: Legacy empty-snapshot backfill changes stay conditional

  • Decision: Do not widen the feature into a migration or blanket backfill reclassification unless focused implementation evidence shows historical empty complete snapshots still participate in current runtime truth.
  • Rationale: The spec explicitly scopes compatibility work out unless it is required. The current-release problem is the live capture path, not historical rows in the abstract.
  • Alternatives considered:
    • Immediately rewrite all historical empty snapshots. Rejected because it widens the feature without proof that runtime truth currently depends on it.
    • Ignore the possibility completely. Rejected because the existing legacy backfill test demonstrates a concrete edge that may need adjustment if runtime truth reaches it.

Resolved Clarifications

  • No new table, external API contract, or operation type is required.
  • OperationRunOutcome::PartiallySucceeded already exists and is the correct no-data outcome family.
  • Filament global search remains out of scope because the relevant resources already disable it.