# Research — Private AI Execution & Policy Foundation

**Date**: 2026-04-27  
**Spec**: [spec.md](spec.md)

This document resolves planning unknowns and records the repo-backed decisions that keep Spec 248 narrow.

## Decision 1 — Reuse workspace settings for AI policy truth

**Decision**: Store workspace AI posture as a workspace setting at `ai.policy_mode` on the existing [WorkspaceSettings](../../apps/platform/app/Filament/Pages/Settings/WorkspaceSettings.php) page, with validation registered through [SettingsRegistry](../../apps/platform/app/Support/Settings/SettingsRegistry.php) and persistence/audit handled by [SettingsWriter](../../apps/platform/app/Services/Settings/SettingsWriter.php).

**Rationale**:
- The repo already has a singleton workspace settings surface, a central settings registry, and an audited writer path.
- Reusing that stack preserves workspace ownership and avoids inventing a second admin surface or a new AI persistence table.
- The existing workspace settings capabilities already separate view and manage permissions.

**Evidence**:
- [WorkspaceSettings](../../apps/platform/app/Filament/Pages/Settings/WorkspaceSettings.php) already owns the `/admin/settings/workspace` singleton route and uses `Capabilities::WORKSPACE_SETTINGS_VIEW` / `Capabilities::WORKSPACE_SETTINGS_MANAGE`.
- [SettingsRegistry](../../apps/platform/app/Support/Settings/SettingsRegistry.php) is the canonical place for setting definitions and validation.
- [SettingsWriter](../../apps/platform/app/Services/Settings/SettingsWriter.php) already persists workspace settings and records `workspace_setting.updated` / `workspace_setting.reset` audit events.

**Alternatives considered**:
- Add a dedicated `workspace_ai_policies` table.
  - Rejected: new persisted truth is unnecessary for a single workspace-owned mode and would violate the narrow v1 scope.
- Hide AI posture in environment config or feature flags.
  - Rejected: not workspace-owned, not operator-auditable, and not compatible with the product requirement for explicit workspace policy.

## Decision 2 — Reuse the existing operational-controls path for the runtime stop

**Decision**: Add `ai.execution` to [OperationalControlCatalog](../../apps/platform/app/Support/OperationalControls/OperationalControlCatalog.php), evaluate it through [OperationalControlEvaluator](../../apps/platform/app/Support/OperationalControls/OperationalControlEvaluator.php), and expose it only on the existing [Controls](../../apps/platform/app/Filament/System/Pages/Ops/Controls.php) page under the current `/system` panel.

**Rationale**:
- The repo already has a platform-only control-center pattern with confirmation, scope previews, and audit logging.
- Reusing it avoids a second AI-specific emergency-stop mechanism or a new system AI console.
- The platform plane auth guard and capability checks are already in place for this page.

**Evidence**:
- [Controls](../../apps/platform/app/Filament/System/Pages/Ops/Controls.php) already owns confirmation-protected pause/resume actions and history for operational controls.
- [OperationalControlCatalog](../../apps/platform/app/Support/OperationalControls/OperationalControlCatalog.php) is the existing source of control keys, labels, and supported scopes.
- [OperationalControlEvaluator](../../apps/platform/app/Support/OperationalControls/OperationalControlEvaluator.php) is the existing runtime lookup path.
- [SystemPanelProvider](../../apps/platform/app/Providers/Filament/SystemPanelProvider.php) and [PlatformCapabilities](../../apps/platform/app/Support/Auth/PlatformCapabilities.php) already enforce the `/system` plane and `platform.ops.controls.manage` capability.

**Alternatives considered**:
- Add an AI-specific console or admin page under `/system`.
  - Rejected: duplicates the existing ops-controls pattern and broadens v1 without adding new product truth.
- Use a deploy-time environment flag as the emergency stop.
  - Rejected: not operator-owned, not auditable, and not aligned with the current control-center workflow.

## Decision 3 — Treat v1 as a governed decision boundary, not an AI provider runtime

**Decision**: The new AI seam should be an in-process governed decision boundary that accepts a registered use-case request and returns an allow/block decision plus audit-ready metadata. It must not include provider adapters, outbound model execution, queue orchestration, or result persistence in this slice.

**Rationale**:
- The spec explicitly avoids direct external provider calls with tenant data, `OperationRun` semantics, result persistence, and a broad marketplace.
- The repo has no existing AI execution layer, so the smallest safe first step is the allow/block contract itself.
- A decision-first seam is enough to stop local provider calls from appearing feature by feature.

**Evidence**:
- There is no app-level AI support namespace in `apps/platform/app/**` today.
- Existing shared seams cover settings, ops controls, audit, product knowledge, and support diagnostics, but none of them own AI allow/block semantics.

**Alternatives considered**:
- Add feature-local AI helpers in product knowledge and diagnostics first.
  - Rejected: duplicates policy, provider-class, and data-classification rules across surfaces.
- Build a full provider abstraction layer now.
  - Rejected: speculative architecture before the first concrete provider runtime is even in scope.

## Decision 4 — Lock v1 to two approved internal-only use cases and derive them from existing seams

**Decision**: Keep the v1 catalog locked to exactly two use cases:

- `product_knowledge.answer_draft`, anchored to [ContextualHelpResolver](../../apps/platform/app/Support/ProductKnowledge/ContextualHelpResolver.php) and its code-owned knowledge source
- `support_diagnostics.summary_draft`, anchored to [SupportDiagnosticBundleBuilder](../../apps/platform/app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php) as a derived summary path

**Rationale**:
- These are the two named likely adopters from the spec and both already exist as internal-only seams.
- Limiting the catalog to two concrete consumers satisfies ABSTR-001 while still proving the shared decision vocabulary is reusable.
- Open-ended catalog growth would silently widen scope into a general AI platform.

**Evidence**:
- [ContextualHelpResolver](../../apps/platform/app/Support/ProductKnowledge/ContextualHelpResolver.php) already exposes `knowledgeSource()` for code-owned product knowledge.
- [SupportDiagnosticBundleBuilder](../../apps/platform/app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php) already produces the diagnostics data family used from the tenant dashboard and the tenantless operation viewer.

**Alternatives considered**:
- Allow any caller to register arbitrary AI use cases at runtime.
  - Rejected: creates speculative platform scope and weakens governance.
- Ship only one adopter in v1.
  - Rejected: the safety justification for the central catalog is stronger with the two real future consumers already identified by the spec.

## Decision 5 — Support diagnostics input must be a derived redacted summary, not the raw bundle

**Decision**: `support_diagnostics.summary_draft` should consume a derived redacted summary of the support-diagnostics bundle, not the raw `sections` array or the raw provider/context payloads already present in the bundle structure.

**Rationale**:
- The current support-diagnostics bundle is broad, structured, and designed for operator inspection, not AI transport.
- Passing the raw bundle would violate the explicit v1 ban on raw provider payloads, customer-confidential data, and raw evidence excerpts.
- A derived summary keeps the AI boundary honest: if the summary cannot be produced safely, the use case should stay blocked.

**Evidence**:
- [SupportDiagnosticBundleBuilder](../../apps/platform/app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php) currently produces a rich `sections` structure plus contextual help and redaction notes, not a purpose-built AI summary.

**Alternatives considered**:
- Feed the full support-diagnostics bundle into AI with field-level filtering.
  - Rejected: still too broad for v1, easier to get wrong, and unnecessary for the first governed foundation slice.

## Decision 6 — Reuse the existing audit pipeline and keep the AI audit family minimal

**Decision**: Reuse [WorkspaceAuditLogger](../../apps/platform/app/Services/Audit/WorkspaceAuditLogger.php) and the underlying [AuditActionId](../../apps/platform/app/Support/Audit/AuditActionId.php) / `AuditRecorder` path. Keep workspace policy mutations on the existing `workspace_setting.updated` / `workspace_setting.reset` actions and add one bounded AI decision action ID for governed decision evaluations with structured metadata only.

**Rationale**:
- Policy changes already flow through the workspace settings audit path and should not create a second mutation pattern.
- AI decision evaluations need a stable audit record, but the narrowest shape is one action ID plus metadata, not a full AI run ledger.
- The spec explicitly bans raw prompt, raw source payload, and output persistence.

**Evidence**:
- [SettingsWriter](../../apps/platform/app/Services/Settings/SettingsWriter.php) already logs workspace-setting updates and resets.
- [WorkspaceAuditLogger](../../apps/platform/app/Services/Audit/WorkspaceAuditLogger.php) already records workspace-scoped and tenant-scoped audit entries.
- [AuditActionId](../../apps/platform/app/Support/Audit/AuditActionId.php) is the canonical action registry.

**Alternatives considered**:
- Add a dedicated AI audit table or prompt history store.
  - Rejected: violates the v1 no-new-persistence constraint and imports a second source of truth.
- Split AI decisions into many action IDs (`allowed`, `blocked`, `control_blocked`, etc.).
  - Rejected for v1: one bounded decision action plus metadata is the smaller audit family.

## Decision 7 — Keep proof narrow: unit + feature + architecture guard

**Decision**: Prove the slice with narrow unit tests for the decision matrix, focused feature tests for the two existing operator surfaces, and one architecture guard that fails if direct AI-provider access appears outside the governed boundary.

**Rationale**:
- Unit coverage is the cheapest place to prove the allow/block matrix.
- Feature coverage is still needed because the slice touches the existing workspace settings and system controls surfaces.
- Browser and heavy-governance workflows would add cost without proving additional v1 truth.

**Evidence**:
- Existing settings and operational-controls tests already show the repo prefers focused Pest feature tests plus targeted unit tests over browser coverage for this class of work.

**Alternatives considered**:
- Add browser smoke coverage in v1.
  - Rejected: unnecessary for the narrow foundation slice and not the cheapest proof.
- Reuse the broad `WorkspaceSettingsManageTest.php` family as the primary proof.
  - Rejected: it is workflow-heavy and should not become the default proving lane for a narrow AI policy field.