spec: finalize 069 v2 docs
This commit is contained in:
parent
3490fb9e2c
commit
458a94c6e9
@ -0,0 +1,35 @@
|
|||||||
|
# Specification Quality Checklist: Managed Tenant Onboarding Wizard UI (v2)
|
||||||
|
|
||||||
|
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||||
|
**Created**: 2026-01-31
|
||||||
|
**Feature**: [../spec.md](../spec.md)
|
||||||
|
|
||||||
|
## Content Quality
|
||||||
|
|
||||||
|
- [x] No implementation details (languages, frameworks, APIs)
|
||||||
|
- [x] Focused on user value and business needs
|
||||||
|
- [x] Written for non-technical stakeholders
|
||||||
|
- [x] All mandatory sections completed
|
||||||
|
|
||||||
|
## Requirement Completeness
|
||||||
|
|
||||||
|
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||||
|
- [x] Requirements are testable and unambiguous
|
||||||
|
- [x] Success criteria are measurable
|
||||||
|
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||||
|
- [x] All acceptance scenarios are defined
|
||||||
|
- [x] Edge cases are identified
|
||||||
|
- [x] Scope is clearly bounded
|
||||||
|
- [x] Dependencies and assumptions identified
|
||||||
|
|
||||||
|
## Feature Readiness
|
||||||
|
|
||||||
|
- [x] All functional requirements have clear acceptance criteria
|
||||||
|
- [x] User scenarios cover primary flows
|
||||||
|
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||||
|
- [x] No implementation details leak into specification
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Validation pass: 2026-01-31
|
||||||
|
- This spec intentionally references the product’s run/audit concepts where required by the project constitution (e.g., `OperationRun`), without prescribing implementation.
|
||||||
254
specs/069-tenant-onboarding-wizard-v2/contracts/openapi.yaml
Normal file
254
specs/069-tenant-onboarding-wizard-v2/contracts/openapi.yaml
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Managed Tenant Onboarding Wizard UI (v2) (Internal)
|
||||||
|
version: 1.0.0
|
||||||
|
description: >
|
||||||
|
Conceptual/internal API contract for onboarding sessions, onboarding evidence,
|
||||||
|
and starting onboarding tasks (OperationRun-backed).
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: https://example.invalid
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/tenants/{tenantId}/onboarding/session:
|
||||||
|
get:
|
||||||
|
summary: Get current onboarding session (or null)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TenantId'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OnboardingSessionResponse'
|
||||||
|
post:
|
||||||
|
summary: Create (or resume) onboarding session
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TenantId'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Created
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OnboardingSession'
|
||||||
|
|
||||||
|
/tenants/{tenantId}/onboarding/session/{sessionId}:
|
||||||
|
patch:
|
||||||
|
summary: Update onboarding session (step, assignment, handoff)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TenantId'
|
||||||
|
- $ref: '#/components/parameters/SessionId'
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/UpdateOnboardingSessionRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OnboardingSession'
|
||||||
|
|
||||||
|
/tenants/{tenantId}/onboarding/tasks:
|
||||||
|
get:
|
||||||
|
summary: List onboarding tasks and latest status
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TenantId'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/OnboardingTaskSummary'
|
||||||
|
|
||||||
|
/tenants/{tenantId}/onboarding/tasks/{taskType}/runs:
|
||||||
|
post:
|
||||||
|
summary: Start onboarding task (OperationRun-backed)
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TenantId'
|
||||||
|
- $ref: '#/components/parameters/TaskType'
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Run created (or reused)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OperationRunLink'
|
||||||
|
'409':
|
||||||
|
description: Blocked by concurrency guard
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
|
||||||
|
/tenants/{tenantId}/onboarding/evidence:
|
||||||
|
get:
|
||||||
|
summary: List onboarding evidence
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/TenantId'
|
||||||
|
- name: taskType
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/OnboardingEvidence'
|
||||||
|
|
||||||
|
components:
|
||||||
|
parameters:
|
||||||
|
TenantId:
|
||||||
|
name: tenantId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
SessionId:
|
||||||
|
name: sessionId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
TaskType:
|
||||||
|
name: taskType
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
OnboardingSessionResponse:
|
||||||
|
type: object
|
||||||
|
required: [session]
|
||||||
|
properties:
|
||||||
|
session:
|
||||||
|
oneOf:
|
||||||
|
- $ref: '#/components/schemas/OnboardingSession'
|
||||||
|
- type: 'null'
|
||||||
|
|
||||||
|
OnboardingSession:
|
||||||
|
type: object
|
||||||
|
required: [id, tenant_id, status, current_step]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
tenant_id:
|
||||||
|
type: integer
|
||||||
|
provider_connection_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [draft, in_progress, completed, abandoned]
|
||||||
|
current_step:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 5
|
||||||
|
assigned_to_user_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
locked_by_user_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
locked_until:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
UpdateOnboardingSessionRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
current_step:
|
||||||
|
type: integer
|
||||||
|
minimum: 1
|
||||||
|
maximum: 5
|
||||||
|
assigned_to_user_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
takeover:
|
||||||
|
type: boolean
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
OnboardingTaskSummary:
|
||||||
|
type: object
|
||||||
|
required: [task_type, status]
|
||||||
|
properties:
|
||||||
|
task_type:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [ok, warn, fail, unknown]
|
||||||
|
reason_code:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
last_run_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
|
||||||
|
OnboardingEvidence:
|
||||||
|
type: object
|
||||||
|
required: [id, tenant_id, task_type, status, recorded_at]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
tenant_id:
|
||||||
|
type: integer
|
||||||
|
onboarding_session_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
provider_connection_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
task_type:
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum: [ok, warn, fail, unknown]
|
||||||
|
reason_code:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
payload:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
operation_run_id:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
recorded_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
|
||||||
|
OperationRunLink:
|
||||||
|
type: object
|
||||||
|
required: [run_id, url]
|
||||||
|
properties:
|
||||||
|
run_id:
|
||||||
|
type: integer
|
||||||
|
url:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
ErrorResponse:
|
||||||
|
type: object
|
||||||
|
required: [error]
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
92
specs/069-tenant-onboarding-wizard-v2/data-model.md
Normal file
92
specs/069-tenant-onboarding-wizard-v2/data-model.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# Data Model: Managed Tenant Onboarding Wizard UI (v2) (069)
|
||||||
|
|
||||||
|
This is a Phase 1 design artifact. It describes the entities needed to implement evidence-driven onboarding, collaboration locks, and task execution history.
|
||||||
|
|
||||||
|
## Context / Scope Mapping
|
||||||
|
|
||||||
|
For v2, this plan maps the spec’s **Workspace / Managed Tenant** concepts to the existing `App\Models\Tenant` model.
|
||||||
|
|
||||||
|
## Entities
|
||||||
|
|
||||||
|
### OnboardingSession (new)
|
||||||
|
|
||||||
|
Represents a resumable onboarding state machine and collaboration surface.
|
||||||
|
|
||||||
|
**Identity**
|
||||||
|
- `tenant_id` (FK → `tenants.id`) + `status != completed` should have at most one “active” session per tenant (policy decision; optional enforcement).
|
||||||
|
|
||||||
|
**Fields (suggested)**
|
||||||
|
- `id` (bigint)
|
||||||
|
- `tenant_id` (FK → `tenants.id`)
|
||||||
|
- `provider_connection_id` (FK → `provider_connections.id`, nullable)
|
||||||
|
- `status` (string: `draft|in_progress|completed|abandoned`)
|
||||||
|
- `current_step` (smallint: 1–5)
|
||||||
|
- `assigned_to_user_id` (FK → `users.id`, nullable)
|
||||||
|
- `locked_by_user_id` (FK → `users.id`, nullable)
|
||||||
|
- `locked_until` (timestamp, nullable)
|
||||||
|
- `completed_at` (timestamp, nullable)
|
||||||
|
- `metadata` (jsonb; whitelisted UI metadata only)
|
||||||
|
- timestamps
|
||||||
|
|
||||||
|
**Validation / invariants**
|
||||||
|
- If `provider_connection_id` is set, it must belong to the same `tenant_id`.
|
||||||
|
- `locked_until` should be short-lived (e.g., 5–10 minutes) and renewed by activity.
|
||||||
|
|
||||||
|
**State transitions**
|
||||||
|
- `draft → in_progress → completed`
|
||||||
|
- `draft|in_progress → abandoned` (explicit action)
|
||||||
|
|
||||||
|
### OnboardingEvidence (new)
|
||||||
|
|
||||||
|
Stores evidence that drives UI statuses and provides historical visibility.
|
||||||
|
|
||||||
|
**Identity**
|
||||||
|
- Many evidence rows per tenant and per task type.
|
||||||
|
|
||||||
|
**Fields (suggested)**
|
||||||
|
- `id` (bigint)
|
||||||
|
- `tenant_id` (FK → `tenants.id`)
|
||||||
|
- `onboarding_session_id` (FK → `onboarding_sessions.id`, nullable)
|
||||||
|
- `provider_connection_id` (FK → `provider_connections.id`, nullable)
|
||||||
|
- `task_type` (string; stable key, e.g. `onboarding.permissions.verify`)
|
||||||
|
- `status` (string: `ok|warn|fail|unknown`)
|
||||||
|
- `reason_code` (string, nullable; stable code aligned with RunFailureSanitizer)
|
||||||
|
- `message` (string, nullable; sanitized, never secrets)
|
||||||
|
- `payload` (jsonb; whitelisted + sanitized evidence payload)
|
||||||
|
- `operation_run_id` (FK → `operation_runs.id`, nullable)
|
||||||
|
- `recorded_at` (timestamp)
|
||||||
|
- `recorded_by_user_id` (FK → `users.id`, nullable)
|
||||||
|
- timestamps
|
||||||
|
|
||||||
|
**Indexes (suggested)**
|
||||||
|
- `(tenant_id, task_type, recorded_at desc)`
|
||||||
|
- `(tenant_id, task_type)` for “latest evidence per type” query patterns
|
||||||
|
|
||||||
|
**Validation / invariants**
|
||||||
|
- `message` must be sanitized/redacted (no tokens/secrets/PII dumps).
|
||||||
|
- `payload` is not a raw provider response; store only what the UI needs.
|
||||||
|
|
||||||
|
### ProviderConnection / ProviderCredential (existing)
|
||||||
|
|
||||||
|
v2 uses the existing provider connection model:
|
||||||
|
- `provider_connections` is tenant-scoped.
|
||||||
|
- `provider_credentials.payload` is encrypted and must never be rendered.
|
||||||
|
|
||||||
|
**v2 scope**
|
||||||
|
- `provider_credentials.type = client_secret` only.
|
||||||
|
|
||||||
|
### OperationRun (existing)
|
||||||
|
|
||||||
|
Provider-affecting onboarding tasks must be executed via `OperationRun` + queued jobs.
|
||||||
|
|
||||||
|
**Concurrency rule**
|
||||||
|
- For onboarding tasks, identity must include at least `{ tenant_id, task_type }` so there is at most one active run per `(tenant, task_type)`.
|
||||||
|
|
||||||
|
## Derived Views / Queries
|
||||||
|
|
||||||
|
- **Latest evidence per task**: `where tenant_id = ? and task_type = ? order by recorded_at desc limit 1`.
|
||||||
|
- **Task history**: `where tenant_id = ? and task_type = ? order by recorded_at desc`.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- If we later introduce a real Workspace/ManagedTenant hierarchy, `tenant_id` can evolve into `workspace_id`, and the evidence/session tables can add `managed_tenant_id` without changing the core model.
|
||||||
117
specs/069-tenant-onboarding-wizard-v2/plan.md
Normal file
117
specs/069-tenant-onboarding-wizard-v2/plan.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
|
||||||
|
# Implementation Plan: Managed Tenant Onboarding Wizard UI (v2) (069)
|
||||||
|
|
||||||
|
**Branch**: `069-tenant-onboarding-wizard-v2` | **Date**: 2026-01-31 | **Spec**: [spec.md](./spec.md)
|
||||||
|
**Input**: Feature specification from `specs/069-tenant-onboarding-wizard-v2/spec.md`
|
||||||
|
|
||||||
|
**Note**: This plan is produced by `/speckit.plan`. It intentionally stops before `tasks.md` (that is `/speckit.tasks`).
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Build a ProviderConnection-first onboarding experience for Managed Tenants:
|
||||||
|
|
||||||
|
- A guided 5-step wizard for first-time setup.
|
||||||
|
- A persistent task board (visible starting Step 4) for reruns, recovery, and operational status.
|
||||||
|
- Evidence-driven status rendering (step/task status comes from stored evidence, not ephemeral job output).
|
||||||
|
- Collaboration support (session locking + takeover/handoff) with strict RBAC semantics.
|
||||||
|
|
||||||
|
Key design decisions are captured in [research.md](./research.md).
|
||||||
|
|
||||||
|
## Terminology & Routing
|
||||||
|
|
||||||
|
- **Managed Tenant** maps to `App\Models\Tenant` (current Filament tenant scope).
|
||||||
|
- Feature UI lives in the **tenant plane** (`/admin/t/{tenant}`), not the platform plane (`/system`).
|
||||||
|
- Concurrency keys use `tenant_id` meaning the internal tenant primary key (`tenants.id`).
|
||||||
|
|
||||||
|
## Phasing
|
||||||
|
|
||||||
|
### Phase 1 (this spec’s implementation target)
|
||||||
|
|
||||||
|
- Introduce onboarding session + evidence storage.
|
||||||
|
- Define a task catalog that maps onboarding tasks to existing provider operations and/or new OperationRun-backed operations.
|
||||||
|
- Implement wizard + task board pages with evidence-driven badges and safe failure summaries.
|
||||||
|
- Enforce concurrency rule: one active run per `(tenant_id, task_type)`.
|
||||||
|
|
||||||
|
### Phase 2 (explicitly out-of-scope for Phase 1)
|
||||||
|
|
||||||
|
- Real-time collaborative editing (presence indicators, typing, etc.).
|
||||||
|
- Multi-provider onboarding (beyond Microsoft-first ProviderConnection patterns already in the repo).
|
||||||
|
|
||||||
|
## Technical Context
|
||||||
|
|
||||||
|
**Language/Version**: PHP 8.4.x (project targets Laravel 12)
|
||||||
|
**Primary Dependencies**: Laravel 12, Filament v5, Livewire v4, Tailwind CSS v4
|
||||||
|
**Storage**: PostgreSQL (use JSONB for evidence payloads where appropriate)
|
||||||
|
**Testing**: Pest v4 (Feature tests + Livewire/Filament component tests)
|
||||||
|
**Target Platform**: Containerized web app (Sail local dev; Dokploy staging/prod)
|
||||||
|
**Project Type**: Web application (Laravel monolith)
|
||||||
|
**Performance Goals**: 95% of “start task” actions confirm queued within 2 seconds (aligns with SC-001)
|
||||||
|
**Constraints**: No secrets in UI/logs/run records; no external calls at render time; OperationRun-backed jobs are enqueue-only from UI
|
||||||
|
**Scale/Scope**: Multi-tenant admin UI; repeatable provider-backed tasks; frequent reruns; strong isolation between tenants
|
||||||
|
|
||||||
|
## Constitution Check
|
||||||
|
|
||||||
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||||
|
|
||||||
|
- Inventory-first: onboarding statuses are derived from stored evidence (last observed), not from snapshots or transient job state.
|
||||||
|
- Read/write separation: provider-affecting actions are only triggered explicitly by the user (task actions), with audit logging and tests.
|
||||||
|
- Graph contract path: any new Graph calls remain behind `GraphClientInterface` and contract registry (`config/graph_contracts.php`).
|
||||||
|
- Deterministic capabilities: onboarding actions reference the canonical capability registry (no raw strings, no role checks in feature code).
|
||||||
|
- RBAC-UX: tenant isolation via deny-as-not-found for non-members; member-but-missing-capability is forbidden; enforce server-side for every mutation/operation start.
|
||||||
|
- Run observability: every onboarding task start creates/reuses an `OperationRun` and enqueues work; Monitoring pages remain DB-only.
|
||||||
|
- Automation: run idempotency + active-run dedupe enforced at DB level; handle 429/503 with retry/backoff in jobs.
|
||||||
|
- Data minimization: evidence payloads are whitelisted/sanitized; failures store reason codes + sanitized messages only.
|
||||||
|
- Badge semantics (BADGE-001): step/task status uses `BadgeCatalog`/`BadgeRenderer` (no ad-hoc mappings).
|
||||||
|
|
||||||
|
No constitution violations are required for this feature.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
### Documentation (this feature)
|
||||||
|
|
||||||
|
```text
|
||||||
|
specs/069-tenant-onboarding-wizard-v2/
|
||||||
|
├── plan.md # This file (/speckit.plan command output)
|
||||||
|
├── research.md # Phase 0 output (/speckit.plan command)
|
||||||
|
├── data-model.md # Phase 1 output (/speckit.plan command)
|
||||||
|
├── quickstart.md # Phase 1 output (/speckit.plan command)
|
||||||
|
├── contracts/ # Phase 1 output (/speckit.plan command)
|
||||||
|
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source Code (repository root)
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/
|
||||||
|
├── Filament/
|
||||||
|
│ ├── Pages/
|
||||||
|
│ └── Resources/
|
||||||
|
├── Http/
|
||||||
|
│ └── Controllers/
|
||||||
|
├── Jobs/
|
||||||
|
├── Models/
|
||||||
|
├── Policies/
|
||||||
|
├── Services/
|
||||||
|
└── Support/
|
||||||
|
|
||||||
|
database/
|
||||||
|
└── migrations/
|
||||||
|
|
||||||
|
resources/
|
||||||
|
├── css/
|
||||||
|
├── js/
|
||||||
|
└── views/
|
||||||
|
|
||||||
|
routes/
|
||||||
|
└── web.php
|
||||||
|
|
||||||
|
tests/
|
||||||
|
├── Feature/
|
||||||
|
└── Unit/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Structure Decision**: Laravel monolith. Wizard/task board implemented as Filament Pages + actions; provider calls run in queued jobs tracked by `OperationRun`.
|
||||||
|
|
||||||
|
## Complexity Tracking
|
||||||
|
|
||||||
|
No constitution violations are required for this feature.
|
||||||
56
specs/069-tenant-onboarding-wizard-v2/quickstart.md
Normal file
56
specs/069-tenant-onboarding-wizard-v2/quickstart.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Quickstart: Managed Tenant Onboarding Wizard UI (v2) (069)
|
||||||
|
|
||||||
|
This quickstart is for developers validating the onboarding wizard + task board locally.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker + Docker Compose (Laravel Sail)
|
||||||
|
- Node.js + npm
|
||||||
|
|
||||||
|
## Local setup
|
||||||
|
|
||||||
|
- Start containers: `./vendor/bin/sail up -d`
|
||||||
|
- Install deps (if needed): `./vendor/bin/sail composer install`
|
||||||
|
- Run migrations: `./vendor/bin/sail artisan migrate --force`
|
||||||
|
- Build assets: `./vendor/bin/sail npm run build`
|
||||||
|
|
||||||
|
## Manual QA scenarios
|
||||||
|
|
||||||
|
### Scenario A — Start onboarding and link a Provider Connection
|
||||||
|
|
||||||
|
1. Sign in as a tenant Owner.
|
||||||
|
2. Navigate to the onboarding entry point.
|
||||||
|
3. Create/select a Provider Connection and attach credentials (client secret only).
|
||||||
|
4. Confirm secrets are never shown after save.
|
||||||
|
|
||||||
|
### Scenario B — Task board (Step 4+) and evidence-driven statuses
|
||||||
|
|
||||||
|
1. Navigate to Step 4.
|
||||||
|
2. Run “Verify permissions”.
|
||||||
|
3. Confirm:
|
||||||
|
- an `OperationRun` is created (or reused if already active),
|
||||||
|
- task status updates from stored evidence,
|
||||||
|
- failures show sanitized reason + message.
|
||||||
|
|
||||||
|
### Scenario C — Concurrency guard
|
||||||
|
|
||||||
|
1. Start a task.
|
||||||
|
2. Immediately attempt to start it again.
|
||||||
|
3. Confirm the action is blocked and links to the existing active run.
|
||||||
|
|
||||||
|
### Scenario D — Collaboration lock
|
||||||
|
|
||||||
|
1. User A opens onboarding session and acquires lock.
|
||||||
|
2. User B opens the same session.
|
||||||
|
3. Confirm User B sees a lock banner and cannot mutate unless takeover permission is granted.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Run targeted tests for the feature as they are added:
|
||||||
|
|
||||||
|
- `./vendor/bin/sail artisan test --compact --filter=Onboarding`
|
||||||
|
|
||||||
|
## Common issues
|
||||||
|
|
||||||
|
- If the UI doesn’t reflect changes, run `./vendor/bin/sail npm run dev` or `./vendor/bin/sail npm run build`.
|
||||||
|
- If task runs stay “Queued”, confirm the queue worker is running (Sail queue service).
|
||||||
98
specs/069-tenant-onboarding-wizard-v2/research.md
Normal file
98
specs/069-tenant-onboarding-wizard-v2/research.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Research: Managed Tenant Onboarding Wizard UI (v2) (069)
|
||||||
|
|
||||||
|
This document resolves Phase 0 unknowns and captures the design decisions used by [plan.md](./plan.md).
|
||||||
|
|
||||||
|
## Decision 1 — Scope mapping ("Workspace" vs current app tenant)
|
||||||
|
|
||||||
|
**Decision:** For v2, interpret **Workspace scope** and **Managed Tenant** from the spec as the existing Filament tenant model: `App\Models\Tenant`.
|
||||||
|
|
||||||
|
**Rationale:**
|
||||||
|
- The current app’s authorization plane, routing, and multi-tenancy are already built around `Tenant`.
|
||||||
|
- Provider connections are already tenant-scoped (`provider_connections.tenant_id`).
|
||||||
|
- Introducing a new Workspace/ManagedTenant hierarchy would expand the change surface (routing, policies, membership model, UI) beyond what is needed to ship the wizard + task board.
|
||||||
|
|
||||||
|
**Alternatives considered:**
|
||||||
|
- Add a new `workspaces` model/table with `managed_tenants` underneath it.
|
||||||
|
- Rejected for Phase 1: would require reworking tenancy, membership, policies, and navigation.
|
||||||
|
|
||||||
|
## Decision 2 — Evidence as the status source-of-truth
|
||||||
|
|
||||||
|
**Decision:** Step/task badges are derived from stored evidence rows; any "status" fields elsewhere are treated as derived cache.
|
||||||
|
|
||||||
|
**Rationale:**
|
||||||
|
- Matches FR-006 and prevents UI from being dependent on ephemeral job output.
|
||||||
|
- Enables stable audit/troubleshooting and deterministic rendering (Monitoring pages remain DB-only).
|
||||||
|
|
||||||
|
**Alternatives considered:**
|
||||||
|
- Cache the current status directly on `Tenant`.
|
||||||
|
- Rejected: easy to drift from reality and makes history/audit harder.
|
||||||
|
|
||||||
|
## Decision 3 — Task catalog representation
|
||||||
|
|
||||||
|
**Decision:** Implement onboarding tasks as a small, explicit catalog (PHP enum or config-backed list) that defines:
|
||||||
|
- `task_type` (stable key)
|
||||||
|
- prerequisites
|
||||||
|
- which OperationRun type/job implements the task
|
||||||
|
- which evidence type(s) it writes
|
||||||
|
|
||||||
|
**Rationale:**
|
||||||
|
- Keeps task definitions centralized and testable.
|
||||||
|
- Supports the task board UX (show prerequisites, last status, rerun).
|
||||||
|
|
||||||
|
**Alternatives considered:**
|
||||||
|
- Store task definitions in the database.
|
||||||
|
- Rejected for Phase 1: adds complexity/migrations and makes review harder.
|
||||||
|
|
||||||
|
## Decision 4 — Concurrency + idempotency for task starts
|
||||||
|
|
||||||
|
**Decision:** Enforce "one active run per (managed_tenant_id, task_type)" by:
|
||||||
|
- Using `OperationRunService::ensureRunWithIdentity(...)` with an identity payload that includes at least `{ tenant_id, task_type }`.
|
||||||
|
- Maintaining/using an active-run unique constraint for the computed identity hash.
|
||||||
|
|
||||||
|
**Rationale:**
|
||||||
|
- Aligns with the constitution (DB-level active run dedupe) and existing provider start-gate patterns.
|
||||||
|
- Prevents double-click duplicates and conflicting task runs.
|
||||||
|
|
||||||
|
**Alternatives considered:**
|
||||||
|
- Application-only mutexes/locks.
|
||||||
|
- Rejected: insufficient without DB-level protection.
|
||||||
|
|
||||||
|
## Decision 5 — Collaboration locking model
|
||||||
|
|
||||||
|
**Decision:** Add session-level locking fields to onboarding sessions:
|
||||||
|
- `locked_by_user_id`
|
||||||
|
- `locked_until`
|
||||||
|
|
||||||
|
**Rationale:**
|
||||||
|
- Simple, explainable behavior.
|
||||||
|
- Supports takeover/handoff and prevents silent overwrites.
|
||||||
|
|
||||||
|
**Alternatives considered:**
|
||||||
|
- Per-step/per-task fine-grained locks.
|
||||||
|
- Rejected for Phase 1: higher complexity with limited user value.
|
||||||
|
|
||||||
|
## Decision 6 — Legacy (v1) onboarding migration path
|
||||||
|
|
||||||
|
**Decision:** Provide a migration bridge that can:
|
||||||
|
- Create a Provider Connection for a tenant if the legacy `Tenant` credential fields exist.
|
||||||
|
- Move credentials into `provider_credentials` (encrypted) and stop rendering the legacy credential fields in the onboarding UI.
|
||||||
|
|
||||||
|
**Rationale:**
|
||||||
|
- Satisfies FR-021 without requiring a full backfill of historical run output.
|
||||||
|
- Aligns with the repo’s existing secure credential patterns (`ProviderCredential` + `CredentialManager`).
|
||||||
|
|
||||||
|
**Alternatives considered:**
|
||||||
|
- Keep using `Tenant.app_client_secret` for v2.
|
||||||
|
- Rejected: conflicts with ProviderConnection-first direction and increases leak risk.
|
||||||
|
|
||||||
|
## Decision 7 — Filament implementation shape
|
||||||
|
|
||||||
|
**Decision:** Implement wizard + task board as Filament Pages/Actions.
|
||||||
|
|
||||||
|
**Rationale:**
|
||||||
|
- Matches current admin UI architecture.
|
||||||
|
- Allows capability-gated actions with consistent RBAC UX patterns.
|
||||||
|
|
||||||
|
**Alternatives considered:**
|
||||||
|
- Standalone Blade controllers for v2 onboarding.
|
||||||
|
- Rejected: splits patterns and complicates authorization/testing conventions.
|
||||||
242
specs/069-tenant-onboarding-wizard-v2/spec.md
Normal file
242
specs/069-tenant-onboarding-wizard-v2/spec.md
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
# Feature Specification: Managed Tenant Onboarding Wizard UI (v2)
|
||||||
|
|
||||||
|
**Feature Branch**: `069-tenant-onboarding-wizard-v2`
|
||||||
|
**Created**: 2026-01-31
|
||||||
|
**Status**: Draft
|
||||||
|
**Input**: User description: "Spec 069 v2 — Managed Tenant Onboarding Wizard UI"
|
||||||
|
|
||||||
|
## Clarifications
|
||||||
|
|
||||||
|
### Session 2026-01-31
|
||||||
|
|
||||||
|
- Q: What is the v2 concurrency rule for starting onboarding tasks? → A: One active run per (tenant_id, task_type).
|
||||||
|
- Q: For v2, is Evidence the source-of-truth for step/task statuses, or are cached fields authoritative? → A: Evidence is the source-of-truth; cached fields (if any) are derived.
|
||||||
|
- Q: Which Provider Connection auth_mode(s) are in scope for v2? → A: `client_secret` only.
|
||||||
|
- Q: Where should the Task Board become visible in v2? → A: Starting Step 4.
|
||||||
|
|
||||||
|
## Terminology & Routing
|
||||||
|
|
||||||
|
This repository uses Filament multi-tenancy. For this spec:
|
||||||
|
|
||||||
|
- **Managed Tenant** = `App\Models\Tenant` (current Filament tenant scope).
|
||||||
|
- **Tenant-scoped / tenant membership** = tenant plane routing under `/admin/t/{tenant}`.
|
||||||
|
- **`tenant_id`** (in concurrency rules) = the internal tenant primary key (`tenants.id`) used by `OperationRunService`.
|
||||||
|
|
||||||
|
There is no separate “Workspace” model in the current codebase; any prior “workspace” wording refers to the current tenant scope.
|
||||||
|
|
||||||
|
## User Scenarios & Testing *(mandatory)*
|
||||||
|
|
||||||
|
<!--
|
||||||
|
IMPORTANT: User stories should be PRIORITIZED as user journeys ordered by importance.
|
||||||
|
Each user story/journey must be INDEPENDENTLY TESTABLE - meaning if you implement just ONE of them,
|
||||||
|
you should still have a viable MVP (Minimum Viable Product) that delivers value.
|
||||||
|
|
||||||
|
Assign priorities (P1, P2, P3, etc.) to each story, where P1 is the most critical.
|
||||||
|
Think of each story as a standalone slice of functionality that can be:
|
||||||
|
- Developed independently
|
||||||
|
- Tested independently
|
||||||
|
- Deployed independently
|
||||||
|
- Demonstrated to users independently
|
||||||
|
-->
|
||||||
|
|
||||||
|
### User Story 1 - Onboard a managed tenant with a provider connection (Priority: P1)
|
||||||
|
|
||||||
|
As a tenant Owner, I want to onboard a Managed Tenant using a dedicated Provider Connection, so that credentials are managed separately from tenant metadata and onboarding can be resumed and operated safely.
|
||||||
|
|
||||||
|
**Why this priority**: This is the primary “front door” into tenant onboarding and must support enterprise workflows (separation of concerns, repeatable checks, and recovery) from day one.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by creating an onboarding session, linking/choosing a Provider Connection, and completing at least one verification task that produces stored evidence and updates visible status.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** a tenant member with Owner permissions and no existing onboarding session, **When** they start onboarding for a new Managed Tenant, **Then** the system creates a session and shows a guided stepper with a preview of the onboarding plan (tasks + prerequisites) and role expectations.
|
||||||
|
2. **Given** an onboarding session in progress, **When** the user resumes it, **Then** the system shows step statuses based on stored evidence and session state (not transient run output).
|
||||||
|
3. **Given** a Managed Tenant without a linked Provider Connection, **When** the Owner selects an existing Provider Connection or creates a new one and assigns it, **Then** the tenant becomes linked to that connection and the UI never reveals secrets.
|
||||||
|
4. **Given** the user opens the consent/permissions step, **When** they run “verify permissions”, **Then** a run record is created, evidence is captured, and the step/task status updates to reflect the latest evidence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 2 - Operate and recover using a task board (Priority: P2)
|
||||||
|
|
||||||
|
As an Owner or Operator, I want a persistent onboarding task board with task history, reruns, and safe fix guidance, so that onboarding can be completed iteratively without resetting or manual database work.
|
||||||
|
|
||||||
|
**Why this priority**: Enterprise onboarding rarely succeeds in a single linear pass; users need repeatable tasks, clear “why failing?”, and safe recovery actions.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by running at least two onboarding tasks (one failing, one succeeding), verifying task state changes, and observing fix hints derived from standardized reasons.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** onboarding has reached the verification phase (or later), **When** the user opens the onboarding task board, **Then** they can see each task’s latest status (OK/Warn/Fail/Unknown), prerequisites, and last run metadata.
|
||||||
|
2. **Given** a task is blocked by prerequisites, **When** the user tries to run it, **Then** the UI disables the action and explains why (without leaking sensitive details).
|
||||||
|
3. **Given** a task fails, **When** the user views the task’s details, **Then** they see a sanitized reason and “fix hints” with recommended next actions.
|
||||||
|
4. **Given** a previously failed task is rerun after the user fixes the issue, **When** the rerun completes, **Then** the new evidence supersedes the prior status and the history remains viewable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 3 - Collaborate safely across multiple users (Priority: P3)
|
||||||
|
|
||||||
|
As an Owner, I want to hand off onboarding work to another workspace member and prevent conflicting edits, so that multiple users can collaborate without corrupting session state.
|
||||||
|
|
||||||
|
**Why this priority**: Collaboration and support escalation are common in enterprise environments; session locking and handoff reduce risk and clarify responsibility.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by having two users access the same onboarding session and verifying lock visibility, read-only behavior, and the takeover/handoff behavior under capability constraints.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** an onboarding session is active, **When** User A opens it, **Then** the session is locked for a short time window and User A is shown as the active editor.
|
||||||
|
2. **Given** the session is locked by User A, **When** User B opens the same session, **Then** User B sees a “locked by User A” banner and cannot perform mutating actions unless they have takeover permission.
|
||||||
|
3. **Given** takeover is permitted, **When** User B takes over the session, **Then** the lock transfers, the event is auditable, and User A sees the session become read-only.
|
||||||
|
4. **Given** handoff is permitted, **When** the Owner hands off the session to another user, **Then** the assignee is clearly shown in the UI and the handoff is auditable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### User Story 4 - Review onboarding evidence and history (Priority: P4)
|
||||||
|
|
||||||
|
As a Readonly/Auditor user, I want to view onboarding evidence and run history without being able to mutate anything, so that compliance and troubleshooting are possible without elevated access.
|
||||||
|
|
||||||
|
**Why this priority**: Evidence-driven onboarding is a core v2 goal; audit visibility builds trust and reduces support load.
|
||||||
|
|
||||||
|
**Independent Test**: Can be fully tested by opening a managed tenant’s onboarding view as a read-only user and verifying evidence visibility with all actions disabled.
|
||||||
|
|
||||||
|
**Acceptance Scenarios**:
|
||||||
|
|
||||||
|
1. **Given** a Managed Tenant has onboarding evidence, **When** a read-only user views onboarding, **Then** they can see the latest evidence per evidence type and the run history metadata.
|
||||||
|
2. **Given** the read-only user does not have operation/run permissions, **When** they view tasks, **Then** they cannot start runs and all mutation actions are disabled.
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
|
||||||
|
- Multiple onboarding sessions exist for the same Managed Tenant: the user is guided to resume the active session or open the task board without creating conflicting sessions.
|
||||||
|
- Evidence is missing (first-time onboarding) or stale: statuses render as Unknown and the UI encourages running the relevant tasks.
|
||||||
|
- A session lock expires while a user is mid-flow: the UI refreshes state and prevents silent overwrites.
|
||||||
|
- A user loses membership in the tenant while viewing onboarding: access becomes deny-as-not-found (404 semantics).
|
||||||
|
- Permissions/consent is partially satisfied: the UI shows a degraded state (Warn/Fail) with fix guidance rather than a generic error.
|
||||||
|
- Provider connection becomes disabled/invalid after initial success: tasks become runnable again and status changes reflect the latest evidence.
|
||||||
|
|
||||||
|
## Requirements *(mandatory)*
|
||||||
|
|
||||||
|
**Constitution alignment (required):** If this feature introduces any Microsoft Graph calls, any write/change behavior,
|
||||||
|
or any long-running/queued/scheduled work, the spec MUST describe contract registry updates, safety gates
|
||||||
|
(preview/confirmation/audit), tenant isolation, run observability (`OperationRun` type/identity/visibility), and tests.
|
||||||
|
If security-relevant DB-only actions intentionally skip `OperationRun`, the spec MUST describe `AuditLog` entries.
|
||||||
|
|
||||||
|
**Constitution alignment (RBAC-UX):** If this feature introduces or changes authorization behavior, the spec MUST:
|
||||||
|
- state which authorization plane(s) are involved (tenant `/admin/t/{tenant}` vs platform `/system`),
|
||||||
|
- ensure any cross-plane access is deny-as-not-found (404),
|
||||||
|
- explicitly define 404 vs 403 semantics:
|
||||||
|
- non-member / not entitled to tenant scope → 404 (deny-as-not-found)
|
||||||
|
- member but missing capability → 403
|
||||||
|
- describe how authorization is enforced server-side (Gates/Policies) for every mutation/operation-start/credential change,
|
||||||
|
- reference the canonical capability registry (no raw capability strings; no role-string checks in feature code),
|
||||||
|
- ensure global search is tenant-scoped and non-member-safe (no hints; inaccessible results treated as 404 semantics),
|
||||||
|
- ensure destructive-like actions require confirmation (`->requiresConfirmation()`),
|
||||||
|
- include at least one positive and one negative authorization test, and note any RBAC regression tests added/updated.
|
||||||
|
|
||||||
|
**Constitution alignment (OPS-EX-AUTH-001):** OIDC/SAML login handshakes may perform synchronous outbound HTTP (e.g., token exchange)
|
||||||
|
on `/auth/*` endpoints without an `OperationRun`. This MUST NOT be used for Monitoring/Operations pages.
|
||||||
|
|
||||||
|
**Constitution alignment (BADGE-001):** If this feature changes status-like badges (status/outcome/severity/risk/availability/boolean),
|
||||||
|
the spec MUST describe how badge semantics stay centralized (no ad-hoc mappings) and which tests cover any new/changed values.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ACTION REQUIRED: The content in this section represents placeholders.
|
||||||
|
Fill them out with the right functional requirements.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Functional Requirements
|
||||||
|
|
||||||
|
- **FR-001 (Tenant-scoped onboarding entry)**: System MUST provide prominent entry points to start/resume onboarding for the current tenant scope (e.g., from the Tenant view, Provider Connection view where linked, and tenant creation flow).
|
||||||
|
|
||||||
|
- **FR-002 (Two UI modes)**: System MUST provide (a) a guided 5-step flow and (b) a persistent onboarding task board view for the same onboarding session.
|
||||||
|
|
||||||
|
Task board placement (clarified): The task board MUST be visible starting Step 4 (Consent & Permissions) and remain available through Step 5.
|
||||||
|
|
||||||
|
- **FR-003 (Provider connection as first-class object)**: System MUST allow creating and managing a Provider Connection as a first-class object that can be linked to a Managed Tenant.
|
||||||
|
|
||||||
|
- **FR-004 (Secrets never disclosed)**: System MUST never display stored secrets or secret material in the UI, logs, notifications, or share links.
|
||||||
|
|
||||||
|
- **FR-005 (Role-aware guidance)**: System MUST clearly communicate which roles/capabilities are required for each onboarding action and which actions are restricted.
|
||||||
|
|
||||||
|
- **FR-006 (Evidence-driven statuses)**: Step statuses and task statuses MUST be derived from stored evidence and session state, not transient run output.
|
||||||
|
|
||||||
|
Evidence authority (clarified): Evidence records are the source of truth for statuses. Any status-like fields on other entities are optional caches derived from evidence.
|
||||||
|
|
||||||
|
- **FR-007 (Evidence types)**: System MUST store and display evidence for, at minimum: consent state, permission snapshot, connection diagnostics, and inventory/sync coverage (where applicable).
|
||||||
|
|
||||||
|
- **FR-008 (Latest evidence by type)**: For each evidence type, the UI MUST primarily surface the latest evidence with access to historical evidence/runs.
|
||||||
|
|
||||||
|
- **FR-009 (Onboarding plan preview)**: System MUST show a preview of the onboarding plan (tasks, ordering, prerequisites) early in the flow, before actions are executed.
|
||||||
|
|
||||||
|
- **FR-010 (Duplicate handling)**: If a Managed Tenant already exists or is already onboarded, the system MUST prevent duplicate onboarding and offer safe navigation to the existing tenant and/or its task board.
|
||||||
|
|
||||||
|
- **FR-011 (Provider connection selection/creation)**: Users MUST be able to choose an existing Provider Connection or create a new one within onboarding (subject to authorization). “Create within onboarding” may be satisfied either via an inline create flow or by navigating to the Provider Connection create page and returning to onboarding.
|
||||||
|
|
||||||
|
- **FR-011a (Provider connection auth modes v2 scope)**: Provider Connections MUST support `client_secret` auth mode in v2. Other auth modes (e.g., certificate, workload identity) are out of scope for v2.
|
||||||
|
|
||||||
|
- **FR-012 (Consent and permissions tasks)**: System MUST provide actions to (a) guide users to complete consent steps and (b) verify permissions via a repeatable task that produces stored evidence.
|
||||||
|
|
||||||
|
- **FR-013 (Task board tasks and reruns)**: System MUST provide a task board containing repeatable tasks with prerequisites, last status, and ability to run/rerun where authorized.
|
||||||
|
|
||||||
|
- **FR-014 (Default task set)**: The onboarding plan MUST include, at minimum, tasks equivalent to: permission verification, connection diagnostics, and an initial synchronization task (where supported).
|
||||||
|
|
||||||
|
- **FR-015 (Standardized reasons and fix hints)**: System MUST present failure reasons using standardized reason codes and display curated fix hints, without exposing raw provider error payloads.
|
||||||
|
|
||||||
|
- **FR-016 (Run safety and observability)**: Starting any provider-affecting onboarding task MUST be observable via a run record and MUST produce auditable events identifying who started what and when.
|
||||||
|
|
||||||
|
- **FR-017 (Rate and concurrency protections)**: System MUST guard against excessive or conflicting task execution for the same Managed Tenant (e.g., repeated starts or overlapping runs) and provide clear UX feedback when blocked.
|
||||||
|
|
||||||
|
Concurrency rule (clarified): The system MUST allow at most one active run per `(tenant_id, task_type)`. Attempts to start a second run while one is active MUST be blocked (and the UI SHOULD disable the action where possible).
|
||||||
|
|
||||||
|
- **FR-018 (Session locking)**: System MUST prevent conflicting edits via session locking with an expiry, showing clear UI banners to non-lock holders.
|
||||||
|
|
||||||
|
- **FR-019 (Takeover and handoff)**: System MUST support capability-gated session takeover and owner/manager handoff with clear UI signaling and auditability.
|
||||||
|
|
||||||
|
- **FR-020 (Resume everywhere)**: System MUST surface “Onboarding incomplete → Resume” prompts from the Managed Tenant view and the Provider Connection view (where linked).
|
||||||
|
|
||||||
|
- **FR-021 (Compatibility with v1 sessions)**: System MUST allow existing v1 onboarding sessions to be resumed in v2, including a safe migration path for legacy credential placement into a Provider Connection.
|
||||||
|
|
||||||
|
- **FR-022 (Strict legacy entry point behavior)**: Direct creation of a Managed Tenant outside the onboarding flow MUST be prevented; the onboarding flow is the canonical path.
|
||||||
|
|
||||||
|
- **FR-023 (Global search and navigation constraints)**: Onboarding sessions MUST not be discoverable via global search. Tenants MAY be discoverable within the current tenant scope without leaking non-member information.
|
||||||
|
|
||||||
|
- **FR-024 (RBAC UX semantics)**: Authorization MUST follow tenant membership semantics:
|
||||||
|
- non-member / not entitled to tenant scope → deny-as-not-found behavior
|
||||||
|
- member but missing capability → forbidden behavior
|
||||||
|
The UI MUST reflect capability absence via disabled controls and tooltips, while server-side enforcement remains authoritative.
|
||||||
|
|
||||||
|
- **FR-025 (Centralized badge semantics)**: Status labels (OK/Warn/Fail/Unknown) for steps and tasks MUST follow a centralized mapping and be covered by tests for any new/changed values.
|
||||||
|
|
||||||
|
### Assumptions
|
||||||
|
|
||||||
|
- The onboarding flow operates within the tenant plane admin area (`/admin/t/{tenant}`), not the platform plane (`/system`).
|
||||||
|
- Provider Connections support at least one credential mode initially, with future expansion possible.
|
||||||
|
- Evidence is considered the source of truth for onboarding status; derived “status fields” (if present) are treated as cache-only.
|
||||||
|
|
||||||
|
### Out of Scope (v2)
|
||||||
|
|
||||||
|
- Azure infrastructure deployment and app registration automation beyond guidance/validation.
|
||||||
|
- Provider Connection certificate/workload identity auth modes (future).
|
||||||
|
|
||||||
|
### Key Entities *(include if feature involves data)*
|
||||||
|
|
||||||
|
- **Tenant**: The current Filament tenant scope (`App\Models\Tenant`); the primary subject of onboarding.
|
||||||
|
- **Provider Connection**: A reusable, managed connection configuration for a provider (credentials + metadata), linkable to a Managed Tenant.
|
||||||
|
- **Onboarding Session**: A resumable, collaborative record representing onboarding progress, locks, and completion state.
|
||||||
|
- **Onboarding Task**: A repeatable unit of onboarding work with prerequisites and outcomes.
|
||||||
|
- **Evidence Record**: A stored snapshot/result of a task/check that drives UI status.
|
||||||
|
- **Reason Code**: A standardized explanation category used to generate safe user-facing guidance.
|
||||||
|
- **Run Record (`OperationRun`)**: An observable record for user-triggered operations that affect providers or tenant state.
|
||||||
|
|
||||||
|
## Success Criteria *(mandatory)*
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ACTION REQUIRED: Define measurable success criteria.
|
||||||
|
These must be technology-agnostic and measurable.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Measurable Outcomes
|
||||||
|
|
||||||
|
- **SC-001**: Tenant Owners can complete an initial onboarding run (up to “task board available”) in under 10 minutes in a healthy environment.
|
||||||
|
- **SC-002**: At least 90% of onboarding recoveries after a failed permission/consent check are completed without manual admin intervention beyond following provided fix hints.
|
||||||
|
- **SC-003**: For onboarded tenants, 95% of task status views render with clear, actionable status (OK/Warn/Fail/Unknown) and at least one evidence record per completed task type.
|
||||||
|
- **SC-004**: Session collaboration reduces conflicting-edit incidents to near-zero (no silent overwrites; lock/takeover/handoff events are visible and auditable).
|
||||||
|
- **SC-005**: No secret material is exposed in user-visible surfaces during onboarding (UI, downloadable artifacts, share links, notifications).
|
||||||
230
specs/069-tenant-onboarding-wizard-v2/tasks.md
Normal file
230
specs/069-tenant-onboarding-wizard-v2/tasks.md
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
description: "Tasks for feature implementation"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tasks: Managed Tenant Onboarding Wizard UI (v2) (069)
|
||||||
|
|
||||||
|
**Input**: Design documents from `specs/069-tenant-onboarding-wizard-v2/`
|
||||||
|
**Prerequisites**: `specs/069-tenant-onboarding-wizard-v2/plan.md` (required), `specs/069-tenant-onboarding-wizard-v2/spec.md` (required), `specs/069-tenant-onboarding-wizard-v2/research.md`, `specs/069-tenant-onboarding-wizard-v2/data-model.md`, `specs/069-tenant-onboarding-wizard-v2/contracts/`, `specs/069-tenant-onboarding-wizard-v2/quickstart.md`
|
||||||
|
|
||||||
|
**Tests**: REQUIRED (Pest). This feature changes runtime behavior and introduces new models, pages, and queued operations.
|
||||||
|
|
||||||
|
**Operations**:
|
||||||
|
- Onboarding tasks that hit providers MUST create/reuse an `OperationRun` and provide a “View run” link to the canonical monitoring hub via existing helpers (see `app/Support/OperationRunLinks.php`).
|
||||||
|
- Concurrency rule: one active run per `(tenant_id, task_type)` implemented via `OperationRunService::ensureRunWithIdentity()`.
|
||||||
|
- `tenant_id` here means the internal tenant primary key (`tenants.id`), not the Entra tenant GUID.
|
||||||
|
|
||||||
|
**RBAC**:
|
||||||
|
- Non-member access MUST be deny-as-not-found (404 semantics).
|
||||||
|
- Member but missing capability MUST be forbidden (403 semantics).
|
||||||
|
- Use canonical capability registry (`app/Support/Auth/Capabilities.php`) and existing `UiEnforcement` patterns.
|
||||||
|
|
||||||
|
**Badges**:
|
||||||
|
- All onboarding status badges MUST use `BadgeCatalog` / `BadgeRenderer` (no ad-hoc mappings) and include mapping tests.
|
||||||
|
|
||||||
|
## Phase 1: Setup (Shared Infrastructure)
|
||||||
|
|
||||||
|
- [ ] T001 Create onboarding feature folders `app/Filament/Pages/Onboarding/`, `resources/views/filament/pages/onboarding/`, `tests/Feature/Onboarding/`, `tests/Unit/Onboarding/`
|
||||||
|
- [ ] T002 [P] Add a focused Pest test file scaffold for onboarding in `tests/Feature/Onboarding/OnboardingSmokeTest.php`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Foundational (Blocking Prerequisites)
|
||||||
|
|
||||||
|
- [ ] T003 Create onboarding sessions migration in `database/migrations/` (new `onboarding_sessions` table per `specs/069-tenant-onboarding-wizard-v2/data-model.md`)
|
||||||
|
- [ ] T004 Create onboarding evidence migration in `database/migrations/` (new `onboarding_evidence` table per `specs/069-tenant-onboarding-wizard-v2/data-model.md`)
|
||||||
|
- [ ] T005 [P] Create `OnboardingSession` model in `app/Models/OnboardingSession.php`
|
||||||
|
- [ ] T006 [P] Create `OnboardingEvidence` model in `app/Models/OnboardingEvidence.php`
|
||||||
|
- [ ] T007 [P] Add factories for onboarding models in `database/factories/OnboardingSessionFactory.php` and `database/factories/OnboardingEvidenceFactory.php`
|
||||||
|
- [ ] T008 [P] Add onboarding session policy in `app/Policies/OnboardingSessionPolicy.php` (404 vs 403 semantics, capability-based)
|
||||||
|
- [ ] T009 [P] Add onboarding evidence policy in `app/Policies/OnboardingEvidencePolicy.php` (view-only access, capability-based)
|
||||||
|
- [ ] T010 Register new policies in `app/Providers/AuthServiceProvider.php`
|
||||||
|
|
||||||
|
- [ ] T011 [P] Create task-type enum/keys in `app/Support/Onboarding/OnboardingTaskType.php` (stable `task_type` strings)
|
||||||
|
- [ ] T012 [P] Create task catalog in `app/Support/Onboarding/OnboardingTaskCatalog.php` (prereqs, evidence types, operation run type/job mapping)
|
||||||
|
- [ ] T013 [P] Create evidence writer service in `app/Services/Onboarding/OnboardingEvidenceWriter.php` (sanitization via `App\\Support\\OpsUx\\RunFailureSanitizer`)
|
||||||
|
- [ ] T014 [P] Create onboarding lock service in `app/Services/Onboarding/OnboardingLockService.php` (lock acquire/renew/release + takeover)
|
||||||
|
|
||||||
|
- [ ] T015 [P] Add badge domain for onboarding task status in `app/Support/Badges/BadgeDomain.php`
|
||||||
|
- [ ] T016 [P] Add badge mapper for onboarding task status in `app/Support/Badges/Domains/OnboardingTaskStatusBadge.php`
|
||||||
|
- [ ] T017 Update badge catalog mapping in `app/Support/Badges/BadgeCatalog.php` for the new onboarding domain
|
||||||
|
- [ ] T018 [P] Add badge mapping unit tests in `tests/Unit/Badges/OnboardingBadgesTest.php`
|
||||||
|
|
||||||
|
- [ ] T019 [P] Add onboarding service tests for evidence sanitization in `tests/Unit/Onboarding/OnboardingEvidenceWriterTest.php`
|
||||||
|
- [ ] T020 [P] Add onboarding lock behavior unit tests in `tests/Unit/Onboarding/OnboardingLockServiceTest.php`
|
||||||
|
|
||||||
|
**Checkpoint**: DB schema, models, policies, badge semantics, and core services exist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: User Story 1 — Onboard a managed tenant with a provider connection (Priority: P1) 🎯 MVP
|
||||||
|
|
||||||
|
**Goal**: Create/resume an onboarding session, link/select a Provider Connection (client secret only), and run at least one evidence-producing verification task.
|
||||||
|
|
||||||
|
**Independent Test**: As an Owner, open wizard, select a provider connection, run “Verify permissions”, and see evidence-driven step/task status.
|
||||||
|
|
||||||
|
### Tests (write first)
|
||||||
|
|
||||||
|
- [ ] T021 [P] [US1] Feature test: Owner can create/resume onboarding session in `tests/Feature/Onboarding/OnboardingSessionLifecycleTest.php`
|
||||||
|
- [ ] T022 [P] [US1] Feature test: non-member is denied-as-not-found (404) in `tests/Feature/Onboarding/OnboardingAuthorizationTest.php`
|
||||||
|
- [ ] T023 [P] [US1] Feature test: readonly can view but cannot mutate in `tests/Feature/Onboarding/OnboardingReadonlyAccessTest.php`
|
||||||
|
- [ ] T059 [P] [US1] Feature test: onboarding plan preview is shown before any task execution in `tests/Feature/Onboarding/OnboardingPlanPreviewTest.php`
|
||||||
|
- [ ] T060 [P] [US1] Feature test: duplicate onboarding/session handling navigates to resume/task board safely in `tests/Feature/Onboarding/OnboardingDuplicateHandlingTest.php`
|
||||||
|
- [ ] T061 [P] [US1] Feature test: consent guidance is visible in Step 4 and is safe/sanitized in `tests/Feature/Onboarding/OnboardingConsentGuidanceTest.php`
|
||||||
|
- [ ] T062 [P] [US1] Feature test: role-aware guidance (capability required messaging) renders for tenant members in `tests/Feature/Onboarding/OnboardingRoleGuidanceTest.php`
|
||||||
|
- [ ] T063 [P] [US1] Feature test: user can create a provider connection from onboarding flow (navigate + return) in `tests/Feature/Onboarding/OnboardingCreateProviderConnectionTest.php`
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
- [ ] T024 [US1] Add onboarding wizard page in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php` (5 steps, evidence-driven status)
|
||||||
|
- [ ] T025 [US1] Add wizard Blade view in `resources/views/filament/pages/onboarding/tenant-onboarding-wizard.blade.php`
|
||||||
|
|
||||||
|
- [ ] T064 [US1] Implement onboarding plan preview in early steps (Step 1/2) using `OnboardingTaskCatalog` (tasks + prerequisites) in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php`
|
||||||
|
- [ ] T065 [US1] Implement duplicate onboarding/session handling: always resume active session; block conflicting session creation in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php`
|
||||||
|
|
||||||
|
- [ ] T026 [US1] Add “Resume onboarding” entry point on tenant view in `app/Filament/Resources/TenantResource/Pages/ViewTenant.php`
|
||||||
|
- [ ] T027 [US1] Add “Resume onboarding” entry point on provider connection pages in `app/Filament/Resources/ProviderConnectionResource/Pages/EditProviderConnection.php`
|
||||||
|
|
||||||
|
- [ ] T028 [US1] Implement provider connection selection/linking in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php` (uses tenant-scoped `ProviderConnection`, client_secret only)
|
||||||
|
- [ ] T029 [US1] Ensure secrets are never displayed by relying on existing Provider Credential patterns in `app/Services/Providers/CredentialManager.php` (wizard renders no secret fields)
|
||||||
|
|
||||||
|
- [ ] T066 [US1] Add “Create provider connection” path inside onboarding (navigate to ProviderConnection create and return to onboarding) in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php`
|
||||||
|
- [ ] T067 [US1] Add consent guidance + optional “Check consent state” action in Step 4 in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php` (sanitized UX only; no secrets)
|
||||||
|
|
||||||
|
- [ ] T030 [US1] Add “Verify permissions” onboarding task start action in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php` (enqueue-only, creates/reuses `OperationRun`)
|
||||||
|
- [ ] T031 [US1] Add onboarding verify-permissions job in `app/Jobs/Onboarding/OnboardingVerifyPermissionsJob.php` (writes `OnboardingEvidence` via `OnboardingEvidenceWriter`)
|
||||||
|
|
||||||
|
- [ ] T068 [US1] Add onboarding consent status job in `app/Jobs/Onboarding/OnboardingConsentStatusJob.php` (writes evidence)
|
||||||
|
|
||||||
|
- [ ] T032 [US1] Feature test: starting verify-permissions creates/reuses run + evidence in `tests/Feature/Onboarding/OnboardingVerifyPermissionsTaskTest.php`
|
||||||
|
|
||||||
|
**Checkpoint**: US1 usable as MVP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: User Story 2 — Operate and recover using a task board (Priority: P2)
|
||||||
|
|
||||||
|
**Goal**: Provide a persistent task board (visible starting Step 4) with task statuses, history, reruns, prerequisites, and safe fix hints.
|
||||||
|
|
||||||
|
**Independent Test**: Run two onboarding tasks (one fail, one success), see statuses/hints, rerun and observe evidence supersedes.
|
||||||
|
|
||||||
|
### Tests (write first)
|
||||||
|
|
||||||
|
- [ ] T033 [P] [US2] Feature test: task board visible starting step 4 in `tests/Feature/Onboarding/OnboardingTaskBoardVisibilityTest.php`
|
||||||
|
- [ ] T034 [P] [US2] Feature test: concurrency guard blocks second run in `tests/Feature/Onboarding/OnboardingTaskConcurrencyTest.php`
|
||||||
|
- [ ] T035 [P] [US2] Feature test: failing task shows sanitized reason + hints in `tests/Feature/Onboarding/OnboardingFixHintsTest.php`
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
- [ ] T036 [US2] Add task board page in `app/Filament/Pages/Onboarding/TenantOnboardingTaskBoard.php` (lists catalog tasks + latest evidence)
|
||||||
|
- [ ] T037 [US2] Add task board Blade view in `resources/views/filament/pages/onboarding/tenant-onboarding-task-board.blade.php`
|
||||||
|
|
||||||
|
- [ ] T038 [US2] Implement “Start task” actions (enqueue-only) in `app/Filament/Pages/Onboarding/TenantOnboardingTaskBoard.php` using `app/Services/OperationRunService.php` identity `{tenant_id, task_type}`
|
||||||
|
- [ ] T039 [US2] Implement prerequisite evaluation + disabled actions in `app/Support/Onboarding/OnboardingTaskCatalog.php`
|
||||||
|
- [ ] T040 [US2] Implement fix-hints mapping from reason codes in `app/Support/Onboarding/OnboardingFixHints.php`
|
||||||
|
|
||||||
|
- [ ] T041 [US2] Add onboarding connection diagnostics job in `app/Jobs/Onboarding/OnboardingConnectionDiagnosticsJob.php` (writes evidence)
|
||||||
|
- [ ] T042 [US2] Add onboarding initial sync job in `app/Jobs/Onboarding/OnboardingInitialSyncJob.php` (writes evidence)
|
||||||
|
|
||||||
|
- [ ] T043 [US2] Ensure “View run” links use existing operation hub routing via `app/Support/OperationRunLinks.php`
|
||||||
|
|
||||||
|
**Checkpoint**: Task board supports reruns, history, prereqs, and concurrency dedupe.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: User Story 3 — Collaborate safely across multiple users (Priority: P3)
|
||||||
|
|
||||||
|
**Goal**: Session locking + takeover/handoff with auditability; prevent conflicting edits.
|
||||||
|
|
||||||
|
**Independent Test**: User A locks session; User B sees read-only; Owner can takeover; actions audited.
|
||||||
|
|
||||||
|
### Tests (write first)
|
||||||
|
|
||||||
|
- [ ] T044 [P] [US3] Feature test: lock acquisition and read-only behavior in `tests/Feature/Onboarding/OnboardingSessionLockTest.php`
|
||||||
|
- [ ] T045 [P] [US3] Feature test: takeover allowed for Owner/Manager only in `tests/Feature/Onboarding/OnboardingSessionTakeoverAuthorizationTest.php`
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
- [ ] T046 [US3] Add lock UI banner + renew-on-interaction behavior in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php`
|
||||||
|
- [ ] T047 [US3] Implement takeover + handoff actions in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php` (capability-gated, uses `OnboardingLockService`)
|
||||||
|
- [ ] T048 [US3] Add audit log entries for takeover/handoff in `app/Services/Intune/AuditLogger.php` (new actions `onboarding.takeover`, `onboarding.handoff`)
|
||||||
|
|
||||||
|
**Checkpoint**: Collaboration is safe and auditable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: User Story 4 — Review onboarding evidence and history (Priority: P4)
|
||||||
|
|
||||||
|
**Goal**: Read-only users can view evidence + run metadata; no mutation.
|
||||||
|
|
||||||
|
**Independent Test**: As Readonly, view onboarding pages and evidence history; all actions disabled.
|
||||||
|
|
||||||
|
### Tests (write first)
|
||||||
|
|
||||||
|
- [ ] T049 [P] [US4] Feature test: readonly can view evidence list but cannot start runs in `tests/Feature/Onboarding/OnboardingEvidenceReadonlyTest.php`
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
- [ ] T050 [US4] Add evidence history section to task board UI in `resources/views/filament/pages/onboarding/tenant-onboarding-task-board.blade.php`
|
||||||
|
- [ ] T051 [US4] Ensure global search does not expose onboarding sessions by avoiding a Resource for sessions (no changes needed outside `app/Filament/Pages/Onboarding/`)
|
||||||
|
|
||||||
|
**Checkpoint**: Evidence/history supports audit use cases.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: Polish & Cross-Cutting Concerns
|
||||||
|
|
||||||
|
- [ ] T052 [P] Add v1-to-v2 credential migration action in `app/Services/Onboarding/LegacyTenantCredentialMigrator.php` (move `Tenant.app_client_secret` into `provider_credentials`)
|
||||||
|
- [ ] T053 Add v1 migration UI action (Owner only, requires confirmation) in `app/Filament/Pages/Onboarding/TenantOnboardingWizard.php`
|
||||||
|
- [ ] T054 Update tenant creation flow to steer into onboarding in `app/Filament/Resources/TenantResource/Pages/CreateTenant.php` (redirect to wizard; prevent credential setup outside onboarding)
|
||||||
|
|
||||||
|
- [ ] T055 [P] Add regression test: no secrets rendered in onboarding pages in `tests/Feature/Onboarding/OnboardingNoSecretsLeakTest.php`
|
||||||
|
- [ ] T056 [P] Add regression test: onboarding actions use `->requiresConfirmation()` when destructive-like in `tests/Feature/Onboarding/OnboardingDestructiveActionConfirmationTest.php`
|
||||||
|
|
||||||
|
- [ ] T069 [P] Confirm Graph contract registry coverage for new onboarding jobs; update `config/graph_contracts.php` if any new Graph calls are introduced (and add tests) in `tests/Feature/Onboarding/OnboardingGraphContractCoverageTest.php`
|
||||||
|
- [ ] T070 [P] Implement explicit v1-to-v2 “resume” semantics (define what v1 means; create v2 session when tenant has legacy credential; migrate credential) in `app/Services/Onboarding/LegacyTenantCredentialMigrator.php` + wizard entry points
|
||||||
|
|
||||||
|
- [ ] T057 Run formatter on changed files (Pint) via `composer.json` scripts (validate using `vendor/bin/sail bin pint`)
|
||||||
|
- [ ] T058 Run onboarding test subset via `tests/Feature/Onboarding/` using `vendor/bin/sail artisan test --compact`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies & Execution Order
|
||||||
|
|
||||||
|
### User Story Dependencies (graph)
|
||||||
|
|
||||||
|
- Setup → Foundational → US1 → US2 → US3 → US4 → Polish
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- US2 depends on the task catalog + evidence store (Foundational) and the wizard/session surface (US1).
|
||||||
|
- US3 depends on session existence + lock fields (Foundational + US1).
|
||||||
|
- US4 depends on evidence storage + task board UI (Foundational + US2).
|
||||||
|
|
||||||
|
### Parallel opportunities (examples)
|
||||||
|
|
||||||
|
**Foundational** (safe parallel work):
|
||||||
|
- T005/T006 models, T011/T012 catalog, T015/T016 badges, T019/T020 unit tests.
|
||||||
|
|
||||||
|
**US1**:
|
||||||
|
- T021–T023 tests can run in parallel.
|
||||||
|
- T024–T025 page + view can run in parallel.
|
||||||
|
|
||||||
|
**US2**:
|
||||||
|
- T033–T035 tests can run in parallel.
|
||||||
|
- T041 and T042 jobs can run in parallel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Strategy
|
||||||
|
|
||||||
|
### MVP scope (recommended)
|
||||||
|
|
||||||
|
- Complete Phase 1 + Phase 2 + Phase 3 (US1). Stop and validate using the independent test in the spec.
|
||||||
|
|
||||||
|
### Incremental delivery
|
||||||
|
|
||||||
|
- Add US2 for operational recovery (task board) next.
|
||||||
|
- Add US3 (collaboration lock) once core flow is stable.
|
||||||
|
- Add US4 (audit/read-only evidence) last.
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user