TenantAtlas/specs/163-baseline-subject-resolution/research.md

66 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research: Baseline Subject Resolution and Evidence Gap Semantics Foundation
## Decision 1: Introduce a backend subject-resolution contract instead of UI-only relabeling
- Decision: Add an explicit backend resolution layer that classifies each compare or capture subject before lookup and returns a structured resolution outcome.
- Rationale: The current failure is rooted in the resolver path itself. `BaselineContentCapturePhase` still assumes every subject can be resolved via `Policy`, while compare scope already admits foundation types through inventory metadata. Renaming `policy_not_found` in the UI would preserve the wrong source-of-truth contract.
- Alternatives considered:
- UI-only copy fixes: rejected because they would leave incorrect persisted semantics and non-deterministic operator meaning.
- Per-surface one-off translation rules: rejected because compare, capture, and run detail would drift semantically.
## Decision 2: Persist both `subject_class` and `resolution_outcome`
- Decision: Store both the business class of the subject and the result of resolving it.
- Rationale: `subject_class` answers what kind of object the system is dealing with, while `resolution_outcome` answers what actually happened. A single field would either blur object identity or overload root-cause meaning.
- Alternatives considered:
- Only store `resolution_outcome`: rejected because operators and future renderer work still need to know whether the target was policy-backed, inventory-backed, foundation-backed, or derived.
- Only store `subject_class`: rejected because class alone cannot distinguish resolved, missing-local-record, throttled, or structurally unsupported states.
## Decision 3: Keep inventory-only foundation subjects in scope only when the runtime can truthfully classify them
- Decision: Inventory-only foundation subjects may remain compare or capture eligible only when the runtime explicitly supports an inventory-backed or limited-capability path for them.
- Rationale: The product already includes supported foundations in baseline scope via `InventoryPolicyTypeMeta::baselineSupportedFoundations()` and `BaselineScope::allTypes()`. Removing them wholesale would hide legitimate support cases. Allowing them in scope without a truthful path produces predictable false alarms.
- Alternatives considered:
- Remove all inventory-only foundations from compare and capture: rejected because it would throw away potentially valid baseline support.
- Keep all supported foundations in scope and tolerate broad `policy_not_found`: rejected because it preserves the current trust problem.
## Decision 4: Add a runtime consistency guard before compare and capture execution
- Decision: Add a deterministic support-capability guard in scope or service preparation that validates each supported type against an actual resolution path before compare or capture runs.
- Rationale: The specs core “config must not overpromise” requirement is best enforced before job execution. This prevents structurally invalid types from silently entering a run and only failing later as misleading gaps.
- Alternatives considered:
- Validate only after gap generation: rejected because it still emits misleading runtime states.
- Validate only in configuration review or documentation: rejected because runtime truth must not depend on manual discipline.
## Decision 5: Preserve transient and already-precise operational reasons as distinct outcomes
- Decision: Keep `throttled`, `capture_failed`, `budget_exhausted`, `ambiguous_match`, and related precise reasons intact, while adding new structural and missing-local-record outcomes beside them.
- Rationale: These reasons already carry actionable meaning and should not be re-modeled into a coarse structural taxonomy. The new foundation is about separating root-cause families, not flattening them.
- Alternatives considered:
- Replace the entire existing reason vocabulary: rejected because it would cause unnecessary churn and regress already-useful operator semantics.
- Collapse transient reasons into one retryable bucket: rejected because rate limiting, capture errors, and budget exhaustion still imply different remediation paths.
## Decision 6: Prefer development cleanup over legacy compatibility
- Decision: Newly created runs write the richer structured shape immediately, and obsolete development-only run payloads may be deleted or regenerated instead of preserved through a compatibility parser.
- Rationale: The repository is still in development, so preserving broad historical reason codes would keep ambiguous semantics alive in the runtime model for no real product benefit. Rebuilding local data and fixtures is cheaper and cleaner than carrying a long-term compatibility path.
- Alternatives considered:
- Keep a compatibility parser for old run shapes: rejected because it would preserve the old semantic contract in code paths that should move to the new model immediately.
- Backfill old runs with inferred outcomes: rejected because the original resolver context is incomplete and inference would still be unreliable.
## Decision 7: Reuse `OperationRun.context` as the canonical persistence boundary
- Decision: Store the richer gap semantics inside existing compare and capture run context rather than creating a new relational evidence-gap table.
- Rationale: Compare and capture results are already run-scoped, immutable operational artifacts. Monitoring and tenant review surfaces must stay DB-only at render time. Extending the existing run context keeps the persistence boundary aligned with execution truth.
- Alternatives considered:
- New evidence-gap relational tables: rejected because they add mutable join complexity for a run-bounded artifact.
- On-demand recomputation from current inventory and policy state: rejected because current state can drift away from the runs original truth.
## Decision 8: Upgrade existing surfaces instead of adding a new operator page
- Decision: Surface the richer semantics on existing canonical run-detail, tenant baseline compare landing, and related evidence-gap detail surfaces.
- Rationale: The feature is about truthfulness of semantics, not information architecture expansion. Existing surfaces already have the right operator entry points.
- Alternatives considered:
- Add a dedicated resolver diagnostics page: rejected because it would make core trust semantics secondary and harder to discover.
- Keep structured semantics backend-only: rejected because the operator value comes from clearer action guidance on current pages.