# Implementation Plan: Private AI Execution & Policy Foundation

**Branch**: `248-private-ai-policy-foundation` | **Date**: 2026-04-27 | **Spec**: [spec.md](spec.md)
**Input**: Feature specification from [spec.md](spec.md)

**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.

## Summary

Introduce a narrow AI governance foundation inside the existing Laravel monolith by reusing the workspace settings page for workspace-owned AI posture, reusing the system operational-controls page for a global `ai.execution` stop, and adding one in-process governed AI decision boundary plus a code-owned allowlist for exactly two internal-only use cases. Host-surface authorization remains a precondition; the AI boundary begins only after caller-side entitlement has already succeeded. The first slice is a preflight allow/block contract with audit-ready metadata, not a customer-facing AI workflow and not a model-provider runtime.

Filament v5 remains on Livewire v4, no panel-provider registration changes are needed (`bootstrap/providers.php` remains the authoritative registration location), no new globally searchable AI resource is introduced, and no new panel-only asset bundle is expected for v1.

## Technical Context

**Language/Version**: PHP 8.4, Laravel 12  
**Primary Dependencies**: Filament v5, Livewire v4, Pest v4, existing Settings/Audit/OperationalControls support services  
**Storage**: PostgreSQL via existing `workspace_settings`, `operational_control_activations`, and `audit_logs` persistence; no new AI tables  
**Testing**: Pest v4 (PHPUnit 12 runner), narrow unit + feature + architecture-guard coverage  
**Validation Lanes**: fast-feedback, confidence  
**Target Platform**: Laravel monolith in `apps/platform` running via Sail; admin `/admin` and platform `/system` panels  
**Project Type**: Web application (Laravel monolith with Filament panels)  
**Performance Goals**: decision evaluation remains synchronous and DB-only in v1; no outbound provider call or queue handoff is required to compute allow/block  
**Constraints**: no direct external provider calls with tenant data; no `OperationRun`; no result or prompt persistence; reuse existing workspace settings and ops controls; keep `/admin` and `/system` auth planes separate; no new asset bundle or second AI admin surface  
**Scale/Scope**: 2 approved use cases, 2 policy modes, 2 provider classes, 6 data classifications, 2 existing operator surfaces, 1 new governed in-process decision seam

## UI / Surface Guardrail Plan

> **Fill for operator-facing or guardrail-relevant workflow changes. Docs-only or template-only work may use concise `N/A`. Copy the spec classification forward; do not rename or expand it here.**

- **Guardrail scope**: changed surfaces on the existing workspace settings and system operational-controls pages
- **Native vs custom classification summary**: native Filament
- **Shared-family relevance**: workspace settings, operational safety controls, audit/status copy
- **State layers in scope**: page
- **Audience modes in scope**: operator-MSP, operator-platform, support-platform
- **Decision/diagnostic/raw hierarchy plan**: decision-first; diagnostics remain secondary on the control history path; no support-raw surface is introduced in v1
- **Raw/support gating plan**: collapsed; raw prompt, source, and provider payload detail are excluded from the slice entirely
- **One-primary-action / duplicate-truth control**: workspace settings keep `Save` as the single primary mutation action; the system controls card keeps `Pause AI execution` / `Resume AI execution`; workspace policy truth and runtime-stop truth stay on separate surfaces
- **Handling modes by drift class or surface**: review-mandatory; any extra AI page, direct `Run AI` action, or evidence viewer is exception-required
- **Repository-signal treatment**: review-mandatory now, future hard-stop candidate once the no-direct-provider guard exists
- **Special surface test profiles**: standard-native-filament
- **Required tests or manual smoke**: functional-core, state-contract
- **Exception path and spread control**: none; v1 remains inside the two existing pages
- **Active feature PR close-out entry**: Guardrail

## Shared Pattern & System Fit

> **Fill when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, navigation entry points, alerts, evidence/report viewers, or any other shared interaction family. Docs-only or template-only work may use concise `N/A`. Carry the same decision forward from the spec instead of renaming it here.**

- **Cross-cutting feature marker**: yes
- **Systems touched**: `WorkspaceSettings`, `SettingsRegistry`, `SettingsResolver`, `SettingsWriter`, `Controls`, `OperationalControlCatalog`, `OperationalControlEvaluator`, `AuditActionId`, `AuditRecorder`, `WorkspaceAuditLogger`, `ContextualHelpResolver`, and `SupportDiagnosticBundleBuilder`
- **Shared abstractions reused**: existing workspace settings persistence + audit flow, existing operational-control evaluator/catalog, existing audit recorder/logger pipeline, existing product-knowledge resolver, and existing support-diagnostics bundle builder path
- **New abstraction introduced? why?**: one in-process governed AI decision boundary and one code-owned use-case catalog, because the current shared settings/ops/audit services do not own AI allow/block semantics
- **Why the existing abstraction was sufficient or insufficient**: settings, ops controls, and audit are already sufficient for persistence, emergency stop, and logging; they are insufficient for AI decision evaluation because the repo currently has no app-level AI seam at all
- **Bounded deviation / spread control**: none; future callers must depend on the new boundary rather than page-local AI helpers

## OperationRun UX Impact

> **Fill when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`. Docs-only or template-only work may use concise `N/A`.**

- **Touches OperationRun start/completion/link UX?**: no
- **Central contract reused**: `N/A`
- **Delegated UX behaviors**: `N/A`
- **Surface-owned behavior kept local**: initiation remains on the existing settings and controls pages only; no queued start UX is introduced
- **Queued DB-notification policy**: `N/A`
- **Terminal notification path**: `N/A`
- **Exception path**: none

## Provider Boundary & Portability Fit

> **Fill when the feature touches shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth. Docs-only or template-only work may use concise `N/A`.**

- **Shared provider/platform boundary touched?**: yes
- **Provider-owned seams**: none in v1; no vendor adapters, credentials, or model-selection UI are introduced
- **Platform-core seams**: AI use-case key, provider class, data classification, workspace AI policy, and governed decision contract
- **Neutral platform terms / contracts preserved**: `AI use case`, `provider class`, `data classification`, `source family`, `workspace AI policy`, and `execution decision`
- **Retained provider-specific semantics and why**: none; `local_private` and `external_public` are trust classes, not vendor names
- **Bounded extraction or follow-up path**: follow-up-spec for provider integration and usage governance; do not widen inside v1

## Constitution Check

*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*

- Inventory-first / snapshot truth: N/A. This slice adds no inventory or backup truth and does not change the Intune source-of-truth model.
- Read/write separation: PASS. Workspace policy writes stay on the existing settings flow, and pause/resume actions stay on the existing controls flow with confirmation + audit.
- Graph contract path: PASS. No Microsoft Graph contract or outbound provider call is introduced.
- Deterministic capabilities: PASS. Reuses `Capabilities::WORKSPACE_SETTINGS_VIEW`, `Capabilities::WORKSPACE_SETTINGS_MANAGE`, `PlatformCapabilities::ACCESS_SYSTEM_PANEL`, and `PlatformCapabilities::OPS_CONTROLS_MANAGE`; no raw capability strings are planned.
- Workspace isolation + tenant isolation: PASS. AI decision requests require a host surface that already resolved workspace context and optional tenant entitlement; the boundary does not become a cross-tenant shortcut.
- RBAC-UX plane separation: PASS. `/admin/settings/workspace` stays tenant-plane/workspace-scoped, `/system/ops/controls` stays platform-scoped, and wrong-plane access remains outside scope.
- Destructive confirmation standard: PASS. `Pause AI execution` and `Resume AI execution` remain confirmation-protected actions on the existing controls page.
- Global search safety: PASS / N/A. No new Resource, Global Search entry, or tenantless AI list is introduced.
- OperationRun and Ops-UX: PASS by non-use. This slice creates no `OperationRun`, queue, notification lifecycle, or Monitoring link.
- Data minimization: PASS. Audit stores decision metadata only; raw prompt, source payload, and output text remain excluded.
- Test governance (TEST-GOV-001): PASS. Proof stays in narrow unit + feature + architecture-guard coverage; no browser or heavy-governance family is required by default.
- Proportionality / no premature abstraction: PASS with bounded exception. One governed AI boundary and one bounded use-case catalog are justified by two concrete future consumers and safety needs; no provider marketplace, queue pipeline, or persistence layer is introduced.
- Persisted truth (PERSIST-001): PASS. Workspace AI policy reuses existing workspace settings; no AI table, cache, result store, or prompt ledger is added.
- Behavioral state (STATE-001): PASS. `disabled` and `private_only` directly change execution eligibility; provider classes and data classifications directly change allow/block behavior.
- Shared pattern first / UI semantics / Filament native UI: PASS. Existing settings, controls, and audit primitives are reused; no custom AI shell, second status framework, or duplicate truth surface is introduced.
- Provider boundary (PROV-001): PASS. Shared terms stay vendor-neutral (`provider class`, `data classification`, `AI use case`), and direct provider-specific seams are deferred.
- Filament/Laravel panel safety: PASS. Livewire v4 remains the Filament v5 runtime, `SystemPanelProvider` stays on the existing `/system` panel, and no provider-registration change beyond `bootstrap/providers.php` is needed.
- Asset strategy: PASS. No new panel-only or shared asset registration is planned; deployment keeps the normal `cd apps/platform && php artisan filament:assets` step if implementation later registers assets.

**Gate evaluation**: PASS (no constitution violation is required to deliver the narrow v1 slice).

- The governed boundary is an in-process decision seam only; it does not create provider execution, queueing, or result persistence.
- Workspace policy truth stays inside the existing settings stack and reuses existing audit behavior.
- The system kill switch reuses the existing operational-control evaluator and controls page rather than creating a second AI control surface.

**Post-design re-check**: PASS (design artifacts: [research.md](research.md), [data-model.md](data-model.md), [quickstart.md](quickstart.md), [contracts/private-ai-governance.openapi.yaml](contracts/private-ai-governance.openapi.yaml)).

## Test Governance Check

> **Fill for any runtime-changing or test-affecting feature. Docs-only or template-only work may state concise `N/A` or `none`.**

- **Test purpose / classification by changed surface**: Unit for the catalog, request/decision contract, operational-control precedence, and audit metadata shaping; Feature for the workspace settings and system controls surfaces; Feature/Guard for the no-direct-provider invariant
- **Affected validation lanes**: fast-feedback, confidence
- **Why this lane mix is the narrowest sufficient proof**: unit coverage proves the decision matrix without Filament boot cost, feature coverage proves the two existing operator surfaces plus authorization/audit integration, and one architecture guard protects against local provider bypasses; browser and heavy-governance coverage add cost without proving new business truth
- **Narrowest proving command(s)**:
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Ai/AiUseCaseCatalogTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Ai/AiDecisionAuditMetadataTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Ai/GovernedAiExecutionBoundaryTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceAiPolicySettingsTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceSettingsViewOnlyTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceSettingsNonMemberNotFoundTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceSettingsAuditTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/System/OpsControls/AiExecutionOperationalControlTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/System/OpsControls/OperationalControlManagementTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OperationalControls/OperationalControlAuthorizationSemanticsTest.php`
  - `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards/NoDirectAiProviderBypassTest.php`
- **Fixture / helper / factory / seed / context cost risks**: low-to-moderate; reuse existing workspace settings, membership, platform-user, and operational-control fixtures, but avoid browser harnesses, provider emulators, or seeded AI history
- **Expensive defaults or shared helper growth introduced?**: no; the AI boundary should accept simple value objects/arrays, and feature tests should avoid broad `WorkspaceSettingsManageTest.php` workflow setup unless an implementation change genuinely needs that depth
- **Heavy-family additions, promotions, or visibility changes**: none expected; do not promote this slice into browser or heavy-governance families by default
- **Surface-class relief / special coverage rule**: standard-native-filament relief for the two existing pages, plus one direct service-level rule that blocked requests produce no provider resolution
- **Closing validation and reviewer handoff**: rerun the twelve focused test commands above, verify that `ai.execution` uses the existing operational-control path, verify that workspace policy changes still reuse the existing settings authorization and audit behavior, and verify that no app-level AI provider client exists outside the governed boundary
- **Budget / baseline / trend follow-up**: none expected; if workspace settings coverage broadens into the existing heavy-governance family, document the lane cost in-feature rather than hiding it
- **Review-stop questions**: lane fit, breadth, hidden setup cost, architecture-guard coverage, accidental provider/runtime scope growth
- **Escalation path**: `document-in-feature` for contained lane drift; `reject-or-split` if implementation introduces browser/heavy-governance cost, queue semantics, or provider integration
- **Active feature PR close-out entry**: Guardrail
- **Why no dedicated follow-up spec is needed**: routine narrow test upkeep stays inside this feature; broader AI runtime and provider workflows are already deferred to follow-up candidates

## Project Structure

### Documentation (this feature)

```text
specs/248-private-ai-policy-foundation/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── private-ai-governance.openapi.yaml
└── tasks.md             # Created later by /speckit.tasks, not by this plan step
```

### Source Code (repository root)

```text
apps/platform/
├── app/
│   ├── Filament/Pages/Settings/WorkspaceSettings.php
│   ├── Filament/System/Pages/Ops/Controls.php
│   ├── Providers/Filament/SystemPanelProvider.php
│   ├── Services/Audit/
│   │   ├── AuditRecorder.php
│   │   └── WorkspaceAuditLogger.php
│   ├── Services/Settings/
│   │   ├── SettingsResolver.php
│   │   └── SettingsWriter.php
│   ├── Support/Audit/AuditActionId.php
│   ├── Support/Auth/
│   │   ├── Capabilities.php
│   │   └── PlatformCapabilities.php
│   ├── Support/OperationalControls/
│   │   ├── OperationalControlCatalog.php
│   │   └── OperationalControlEvaluator.php
│   ├── Support/ProductKnowledge/ContextualHelpResolver.php
│   ├── Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php
│   └── Support/Ai/        # likely new narrow namespace if implementation proceeds
└── tests/
    ├── Feature/SettingsFoundation/
    ├── Feature/OperationalControls/
  ├── Feature/System/OpsControls/
    ├── Feature/Guards/
    ├── Unit/Support/OperationalControls/
    ├── Unit/Support/ProductKnowledge/
    └── Unit/Support/Ai/
```

**Structure Decision**: Laravel monolith. Implementation stays entirely inside `apps/platform`, reusing existing settings, audit, and operational-control seams while adding only one narrow AI support namespace if code work later proceeds.

## Complexity Tracking

> **Fill when Constitution Check has violations that must be justified OR when BLOAT-001 is triggered by new persistence, abstractions, states, or semantic frameworks.**

| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| BLOAT-001 — governed AI decision boundary | One central allow/block seam is the smallest safe place to enforce workspace policy, operational controls, provider class gating, and audit metadata before any future AI caller can reach a model | Per-surface AI helpers would duplicate policy/control/audit logic and create bypass risk across product knowledge and diagnostics |
| BLOAT-001 — code-owned AI use-case catalog | Two concrete future adopters need a single allowlist and stable vocabulary now | Free-form string keys spread across callers would drift and be difficult to guard or audit consistently |
| STATE-001 — AI policy / provider / data-classification families | These values directly change whether execution is allowed and what may cross the trust boundary | Vendor names or presentation-only labels would not be enforceable, portable, or sufficiently reviewable |

## Proportionality Review

> **Fill when the feature introduces a new enum/status family, DTO/presenter/envelope, persisted entity/table/artifact, interface/contract/registry/resolver, taxonomy/classification system, or cross-domain UI framework.**

- **Current operator problem**: TenantPilot has no safe app-level AI seam today, so future AI work would otherwise begin as local provider calls and local prompt/policy logic that bypass workspace isolation, runtime controls, and auditability.
- **Existing structure is insufficient because**: the repo already has settings, operational controls, and audit infrastructure, but it has no place to classify AI use cases, provider trust classes, or data classifications, and no single decision service that every caller must use.
- **Narrowest correct implementation**: add one workspace setting (`ai.policy_mode`), one operational control key (`ai.execution`), one code-owned use-case catalog for exactly two internal-only consumers, one request/decision contract, and one audit metadata shape. Do not add provider adapters, queue semantics, result persistence, or customer-visible AI surfaces.
- **Ownership cost created**: maintain 2 use-case entries, 2 policy values, 2 provider classes, 6 data classifications, one bounded audit action/metadata shape, and one architecture guard.
- **Alternative intentionally rejected**: local AI helpers on each future surface and a broader multi-provider AI platform were both rejected because they either create safety drift or import speculative architecture before the first real runtime need exists.
- **Release truth**: current-release governance foundation and future-feature preflight seam; not a full AI execution product.

## Phase 0 — Research (output: research.md)

Research resolved the remaining implementation-shaping decisions:

- Reuse `WorkspaceSettings` plus `SettingsRegistry` / `SettingsWriter` for workspace-owned AI policy truth.
- Reuse `OperationalControlCatalog` / `OperationalControlEvaluator` and the existing `Controls` page for `ai.execution` rather than creating a second AI control surface.
- Model v1 as a governed decision boundary, not a provider runtime, queue, or result store.
- Lock the first slice to two code-owned internal use cases tied to `ContextualHelpResolver` and the support-diagnostics bundle path.
- Reuse existing audit infrastructure and keep the AI audit family minimal.

**Output**: [research.md](research.md)

## Phase 1 — Design (outputs: data-model.md, contracts/, quickstart.md)

Design artifacts capture the narrow implementation shape:

- Existing persisted truth reused: `workspace_settings`, `operational_control_activations`, and `audit_logs`.
- New code-owned truth: AI policy mode, provider class, data classification, approved use-case definitions, and request/decision envelopes.
- Conceptual contracts cover the existing workspace settings page, the existing system controls page, and the in-process governed decision schema.
- Quickstart documents the intended slice order, validation commands, Filament/Livewire assumptions, and the no-new-assets posture.

**Artifacts**:

- [data-model.md](data-model.md)
- [contracts/private-ai-governance.openapi.yaml](contracts/private-ai-governance.openapi.yaml)
- [quickstart.md](quickstart.md)

## Phase 2 — Planning (for tasks.md)

Dependency-ordered implementation outline for the later `tasks.md` step:

1. Extend the existing settings registry and workspace settings page with `ai.policy_mode` and plain-language explanation content, without broadening the singleton settings workflow.
2. Add `ai.execution` to the operational-control catalog and controls page, keeping pause/resume confirmation-protected and audit-backed.
3. Introduce a narrow `Support/Ai` namespace containing the use-case catalog, request/decision value objects, and the governed decision boundary only.
4. Reuse the existing audit pipeline for workspace policy mutations and add one bounded AI decision action/metadata shape for allow/block evaluations.
5. Name `ContextualHelpResolver` and `SupportDiagnosticBundleBuilder` as the first adopters, but do not ship customer-facing AI UI, model-provider runtime code, or direct caller wiring beyond what the boundary contract itself requires.
6. Add focused unit, feature, and architecture-guard tests while keeping browser and heavy-governance families out of scope by default.
7. Run focused tests and Pint after implementation; no asset build is expected unless implementation later registers Filament assets.

## Post-Implementation Close-Out

- **Implementation status**: Implemented and validated on 2026-04-27.
- **TEST-GOV-001 outcome**: PASS. Proof stayed in focused Pest `Unit` and `Feature` lanes plus one architecture guard, with no browser or heavy-governance suite expansion.
- **Executed validation summary**:
  - AI boundary unit lane: 8 tests, 83 assertions passed.
  - AI execution controls feature lane: 1 test, 34 assertions passed.
  - Operational controls regression lane: 11 tests, 167 assertions passed.
  - Workspace settings lane: 20 tests, 267 assertions passed.
  - Platform authorization semantics lane: 6 tests, 26 assertions passed.
  - No-direct-provider guard lane: 1 test, 1 assertion passed.
  - Approved source-input lane: 2 tests, 30 assertions passed.
  - Adjacent product-knowledge/support-diagnostics regression lane: 14 tests, 107 assertions passed.
  - Final targeted feature validation rollup: 42 tests, 530 assertions passed.
  - Formatting: `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` passed.
- **Catalog lock and tenant-context declaration**:
  - `product_knowledge.answer_draft`: `tenant_context_permitted = false`
  - `support_diagnostics.summary_draft`: `tenant_context_permitted = true`
  - Boundary coverage plus the approved source adapters preserved that split.
- **Browser smoke result**: PASS.
  - `/admin/settings/workspace`: authenticated as a workspace manager, changed `Workspace AI policy` from the default effective disabled state to `Private only`, saved successfully, and confirmed the effective summary plus approved-use-case/provider-class copy updated on the real page.
  - `/system/ops/controls`: authenticated as a platform operator, opened the `AI execution` card, paused execution with confirmation and reason text, confirmed the `Paused globally` state and success notification, then resumed execution and confirmed the enabled state returned.
- **Environment note**: the integrated browser carried a stale or poisoned `localhost` system-panel session during smoke work. The product routes themselves were healthy; the system-panel smoke path completed successfully on `127.0.0.1` to get a clean host-scoped browser session. This was an environment/browser-session workaround, not a feature bug.
- **Guardrail close-out**: no confirmed in-scope findings remained after the code, validation, browser smoke, and artifact analysis loop. No new provider runtime, queue, result persistence, or customer-facing AI surface was introduced.
- **Follow-up-spec deferrals retained**:
  - public or external-provider execution
  - result persistence, cache, or prompt/output history
  - AI budgeting, credits, or cost controls
  - queued AI execution or `OperationRun` semantics
  - customer-facing AI workflows or approval flows
