# 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.