Skip to main content
Product Management/starter-stories

Starter Stories

You need grab-and-go user stories for common MVP patterns.

Use this when you're bootstrapping an MVP and need instant, high-quality user stories for the patterns every app needs — CRUD tables, forms with validation, Google/Outlook authentication, and role-based access control. Each story follows INVEST criteria with Gherkin acceptance criteria and an optional branded HTML prototype attached.

Related skills: Uses /story-write story format. Prototypes use /artium-prototype scaffold and brand tokens.


Process

Step 1: Choose your starter pack

Present the menu and ask the user to pick one or more categories:

Which MVP starter stories do you need? Pick one or more:

  1. CRUD Table — Create, read (list + detail), update, delete for a domain object
  2. Form — A specific form with required/optional fields, validation, and error handling
  3. Authentication — Google OAuth and/or Microsoft/Outlook OAuth sign-in
  4. RBAC Permissions — Role-based access control (Viewer, Editor, Admin, Owner)
  5. Agentic Coding System — Agent identity, tool execution, autonomy dials, confidence signals, memory, error recovery, observability, orchestration, streaming
  6. All of the above — Full MVP starter kit

Then ask:

  1. What is the domain object? (e.g., "Project", "Invoice", "Customer", "Task") — needed for CRUD and forms
  2. What fields does it have? (e.g., name, email, status, due date, amount) — list them with types if known
  3. Which fields are required vs. optional? — for form validation stories
  4. Any role customization? — The baseline is a 4-role model (Viewer, Editor, Admin, Owner). You can add custom roles on top of this baseline (e.g., "Billing Admin", "Approver"). What custom roles do you need, if any?
  5. Which auth providers? — Google, Microsoft/Outlook, or both
  6. Auth framework? — Framework-agnostic (generic OAuth), or framework-specific? Supported variants: NextAuth.js, Firebase Auth, Supabase Auth, Auth0. Pick one or stay generic.
  7. Generate prototypes? — Yes: get an integrated HTML prototype showing the full MVP shell. No: stories only.
  8. Where should the stories go? Options:
    • Conversation only (default)
    • Linear — push via MCP
    • Jira — push via Atlassian MCP (if connected)
    • Notion — create as Notion pages
    • Other — check available connectors

If Category 5 (Agentic Coding System) is selected, also ask:

  1. What is the agent's name? (e.g., "CodeBot", "DevAssist", "Pilot") — used in all story titles and scenarios
  2. What tool types will the agent use? (e.g., code execution, file system, terminal, API calls, database queries)
  3. What does the agent act on? (e.g., source code, test suites, CI pipelines, documentation)
  4. Which LLM provider? (e.g., Claude, GPT, Gemini, open-source, multi-provider)
  5. What context sources does the agent use? (e.g., codebase files, git history, project docs, conversation history)
  6. Multi-agent? — Will this system coordinate multiple agents? (yes/no) — yes unlocks Story 5.8

Building an agent product vs. adopting agentic workflows? These stories are for teams building an agentic coding product. If you're adopting agentic coding workflows on your team, see Agentic Coding Setups for practitioner-tested configurations (TDD multi-tab, Planner Pattern, Plan→Code→Verify, etc.).

Collect answers before proceeding. If the user gives a brief description, extract what you can and ask follow-ups for gaps.

Step 2: Generate stories

Generate stories for each selected category using the templates below. Customize field names, object names, and roles based on the user's answers from Step 1.

Every story uses the /story-write format:

## Title
(Brief description of what the user can do)

## Description
As a (user persona), I want to be able to (action), so that (purpose/value).

## Acceptance Criteria

Scenario 1: (Scenario name — happy path)
Given (initial context / system state)
When (user action or event)
Then (expected observable outcome)
And (additional outcome if needed)

Scenario 2: (Scenario name — error path)
...

Scenario 3: (Scenario name — edge case)
...

## Technical Details
- (Bullet points for engineers)

## Out of Scope
- (What is NOT included)

## Added Context
- (Dependencies, related stories)

Story templates

Category 1: CRUD Table

Generate the following stories, replacing (Object) with the user's domain object (e.g., "Project", "Invoice"):

Story 1.1: View (Object) list

## View (Object) list

## Description
As a user, I want to see a list of all (objects) in a table, so that I can browse and find what I need.

## Acceptance Criteria

Scenario 1: List displays with data
Given there are existing (objects) in the system
When I navigate to the (Objects) page
Then I see a table with columns for (key fields from Step 1)
And each row shows one (object)
And the list is sorted by most recently updated by default

Scenario 2: Empty state
Given there are no (objects) in the system
When I navigate to the (Objects) page
Then I see an empty state message: "No (objects) yet"
And I see a call-to-action button: "Create your first (object)"

Scenario 3: Pagination
Given there are more than 25 (objects)
When I navigate to the (Objects) page
Then I see the first 25 results
And I see pagination controls to navigate between pages

Scenario 4: Search and filter
Given I am on the (Objects) page
When I type a search term in the search bar
Then the table filters to show only (objects) matching the search term
And the match applies to (all text-type fields — e.g., name, description, owner)

## Technical Details
- Default page size: 25 rows
- Sortable columns: (all fields from Step 1)
- Searchable fields: include all text/string fields, not just table columns
- Search is case-insensitive, matches partial strings
- Loading state: skeleton rows while data loads

## Out of Scope
- Bulk actions (see Story 1.5)
- Export to CSV
- Saved filters / views

Story 1.2: View (Object) detail

## View (Object) detail

## Description
As a user, I want to click on an (object) to see its full details, so that I can review all information about it.

## Acceptance Criteria

Scenario 1: View detail page
Given I am on the (Objects) list page
When I click on an (object) row
Then I see the (Object) detail page
And all fields are displayed: (all fields from Step 1)
And I see "Edit" and "Delete" action buttons

Scenario 2: Direct URL access
Given I have a direct link to an (object) detail page
When I navigate to that URL
Then I see the (Object) detail page for that specific (object)

Scenario 3: Object not found
Given I navigate to a detail page URL for an (object) that doesn't exist
When the page loads
Then I see a "Not found" error page
And I see a link back to the (Objects) list

## Technical Details
- URL pattern: /(objects)/:id
- Breadcrumb navigation: (Objects) > (Object Name)

## Out of Scope
- Inline editing on the detail page
- Activity history / audit log

Story 1.3: Create new (Object)

## Create new (Object)

## Description
As a user, I want to create a new (object), so that I can add it to the system.

## Acceptance Criteria

Scenario 1: Successful creation
Given I am on the (Objects) page
When I click the "Create (Object)" button
Then I see a form with fields: (all fields from Step 1)
And required fields are marked with an asterisk (*)

Scenario 2: Submit valid form
Given I have filled in all required fields
When I click "Save"
Then the (object) is created
And I am redirected to the (object) detail page
And I see a success toast: "(Object) created successfully"

Scenario 3: Validation errors
Given I have left required fields empty
When I click "Save"
Then I see inline error messages below each empty required field: "(Field name) is required"
And the form is NOT submitted
And focus moves to the first field with an error

Scenario 4: Cancel creation
Given I am on the create (object) form
When I click "Cancel"
Then I am returned to the (Objects) list
And no (object) is created

## Technical Details
- Required fields: (required fields from Step 1)
- Optional fields: (optional fields from Step 1)
- All text inputs have placeholder text describing expected format
- Form preserves entered data if validation fails

## Out of Scope
- Duplicate detection
- Auto-save / draft mode
- Bulk creation

Story 1.4: Edit (Object)

## Edit (Object)

## Description
As a user, I want to edit an existing (object), so that I can update its information.

## Acceptance Criteria

Scenario 1: Open edit form
Given I am on the (Object) detail page
When I click the "Edit" button
Then I see the edit form pre-populated with the current (object) data
And all fields are editable

Scenario 2: Save changes
Given I have modified one or more fields
When I click "Save"
Then the (object) is updated with the new values
And I am redirected to the (object) detail page
And I see a success toast: "(Object) updated successfully"

Scenario 3: Validation errors on edit
Given I have cleared a required field
When I click "Save"
Then I see inline error messages for invalid fields
And the changes are NOT saved

Scenario 4: Cancel edit
Given I have made changes to the form
When I click "Cancel"
Then I see a confirmation dialog: "Discard unsaved changes?"
And if I confirm, I am returned to the detail page without saving

## Technical Details
- Same validation rules as create form
- Unsaved changes trigger browser beforeunload warning
- Last-modified timestamp updates on save

## Out of Scope
- Field-level edit history
- Concurrent edit detection / conflict resolution

Story 1.5: Delete (Object)

## Delete (Object)

## Description
As a user, I want to delete an (object) I no longer need, so that my list stays clean and relevant.

## Acceptance Criteria

Scenario 1: Delete from detail page
Given I am on the (Object) detail page
When I click "Delete"
Then I see a confirmation dialog: "Are you sure you want to delete (object name)? This action cannot be undone."

Scenario 2: Confirm deletion
Given I see the delete confirmation dialog
When I click "Delete" to confirm
Then the (object) is removed
And I am redirected to the (Objects) list
And I see a success toast: "(Object) deleted"

Scenario 3: Cancel deletion
Given I see the delete confirmation dialog
When I click "Cancel"
Then the dialog closes
And the (object) is NOT deleted

Scenario 4: Delete from list view
Given I am on the (Objects) list page
When I click the delete icon on a row
Then I see the same confirmation dialog
And the flow follows Scenarios 2-3

## Technical Details
- Soft delete preferred (mark as deleted, hide from UI, retain in database)
- No bulk delete in this story

## Out of Scope
- Bulk delete
- Undo / restore deleted (objects)
- Cascade delete of related records

Category 2: Form

Generate the following stories, replacing (Form Name) and fields with the user's specifics.

Note: When generating both CRUD and Form categories for the same domain object, the Form story (2.1) provides the detailed field-level validation spec while CRUD Story 1.3 (Create) covers the creation workflow. Cross-reference them in "Added Context" so they complement rather than duplicate.

Story 2.1: Fill out (Form Name)

## Fill out (Form Name) form

## Description
As a user, I want to fill out the (Form Name) form, so that I can (purpose — e.g., submit my information, create a request).

## Acceptance Criteria

Scenario 1: Form loads with correct fields
Given I navigate to the (Form Name) page
When the page loads
Then I see the following fields:
  | Field | Type | Required | Placeholder |
  | (field 1) | (text/email/dropdown/etc.) | Yes/No | "(placeholder text)" |
  | (field 2) | ... | ... | "..." |
  | ... | ... | ... | ... |
And required fields are marked with an asterisk (*)
And optional fields are labeled "(optional)"

Scenario 2: Successful submission
Given I have filled in all required fields with valid data
When I click "Submit"
Then the form is submitted
And I see a success message: "(Confirmation message)"
And I receive a confirmation (email / redirect / toast)

Scenario 3: Required field validation
Given I have left one or more required fields empty
When I click "Submit"
Then I see inline error messages below each empty required field
And the error message reads "(Field name) is required"
And the form is NOT submitted
And focus moves to the first field with an error

Scenario 4: Format validation
Given I have entered an invalid format (e.g., invalid email, phone with letters)
When I move focus away from the field (on blur)
Then I see an inline error message with the expected format
And the field border turns red

Scenario 5: Character limits
Given a text field has a maximum character limit
When I type and reach the limit
Then I see a character counter: "(current)/(max) characters"
And I cannot type beyond the limit

Scenario 6: Dropdown / select fields
Given I click on a dropdown field
When the options list opens
Then I see all available options
And I can search/filter the options if there are more than 10
And selecting an option closes the dropdown

## Technical Details
- Validation runs on blur (field-level) and on submit (form-level)
- Error messages clear when the user corrects the field
- Form does NOT reset on validation failure — user data is preserved
- Tab order follows visual field order top-to-bottom
- Placeholder text disappears on focus (not on type)

## Out of Scope
- Auto-save / draft mode
- Multi-step / wizard form flow (see Story 2.2 if needed)
- File upload fields (see Story 2.3 if needed)

Story 2.2: Multi-step form flow (optional)

## Multi-step (Form Name) form

## Description
As a user, I want to complete the (Form Name) form in multiple steps, so that I'm not overwhelmed by too many fields at once.

## Acceptance Criteria

Scenario 1: Step navigation
Given I am on step 1 of the form
When I fill in the required fields and click "Next"
Then I advance to step 2
And I see a progress indicator showing my current step

Scenario 2: Back navigation
Given I am on step 2 or later
When I click "Back"
Then I return to the previous step
And my previously entered data is preserved

Scenario 3: Step validation
Given I am on a step with required fields
When I click "Next" without filling required fields
Then I see validation errors for the current step only
And I am NOT advanced to the next step

Scenario 4: Final submission
Given I am on the last step
When I click "Submit"
Then all data from all steps is submitted together
And I see a success confirmation

Scenario 5: Progress indicator
Given I am on any step of the form
Then I see a step indicator showing: step names, current step highlighted, completed steps marked with a checkmark

## Technical Details
- Steps: (list steps and their fields)
- Data is held in client state until final submit
- Browser back button maps to form back navigation

## Out of Scope
- Save and resume later
- Step skipping (must complete in order)

Story 2.3: File upload field (optional)

## File upload in (Form Name) form

## Description
As a user, I want to upload files as part of the form, so that I can attach supporting documents.

## Acceptance Criteria

Scenario 1: Upload a file
Given I am on the form
When I click the file upload area or drag a file onto it
Then the file is uploaded
And I see the filename, size, and a remove button

Scenario 2: File type validation
Given I attempt to upload an unsupported file type
When the upload is rejected
Then I see an error: "Supported formats: (list). Please upload a different file."

Scenario 3: File size validation
Given I attempt to upload a file larger than (max size)
When the upload is rejected
Then I see an error: "File must be under (max size)"

Scenario 4: Remove uploaded file
Given I have uploaded a file
When I click the remove button
Then the file is removed from the form
And the upload area is available again

## Technical Details
- Accepted formats: (e.g., PDF, PNG, JPG, DOCX)
- Max file size: (e.g., 10MB)
- Max files: (e.g., 5)
- Upload method: (direct upload / presigned URL)

## Out of Scope
- Image preview / thumbnail
- Inline document viewer

Category 3: Authentication

Story 3.1: Sign in with Google

## Sign in with Google

## Description
As a user, I want to sign in using my Google account, so that I can access the application without creating a separate username and password.

## Acceptance Criteria

Scenario 1: First-time Google sign-in
Given I am on the sign-in page
And I do not have an existing account
When I click "Sign in with Google"
Then I am redirected to Google's OAuth consent screen
And I see the permissions being requested (email, profile)

Scenario 2: Grant consent and sign in
Given I am on Google's OAuth consent screen
When I select my Google account and grant consent
Then I am redirected back to the application
And a new account is created using my Google profile (name, email, avatar)
And I am signed in and see the main dashboard

Scenario 3: Returning user sign-in
Given I have previously signed in with Google
When I click "Sign in with Google"
Then I am signed in without re-granting consent (if session still valid)
And I see the main dashboard

Scenario 4: Deny consent
Given I am on Google's OAuth consent screen
When I deny consent or close the window
Then I am returned to the sign-in page
And I see a message: "Sign-in was cancelled. Please try again."

Scenario 5: Google account email already exists
Given an account already exists with my Google email (e.g., from manual registration)
When I sign in with Google
Then my Google identity is linked to the existing account
And I am signed in to the existing account

## Technical Details
- OAuth 2.0 Authorization Code flow with PKCE
- Scopes: openid, email, profile
- Store: Google sub ID, email, display name, avatar URL
- Session: JWT or secure httpOnly cookie
- Redirect URI must be registered in Google Cloud Console

**Framework-specific details** (include the variant matching the user's choice from Step 1):
- **NextAuth.js**: Use GoogleProvider; configure `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in env; session strategy: JWT; callback URL: `/api/auth/callback/google`
- **Firebase Auth**: Use `signInWithPopup(auth, googleProvider)`; enable Google in Firebase Console → Authentication → Sign-in method; user stored in Firebase Authentication, not Firestore
- **Supabase Auth**: Use `supabase.auth.signInWithOAuth({ provider: 'google' })`; configure Google provider in Supabase Dashboard → Authentication → Providers
- **Auth0**: Use Universal Login with Google social connection; configure in Auth0 Dashboard → Authentication → Social; callback URL: `/api/auth/callback`
- **Framework-agnostic** (default): Implement OAuth 2.0 directly; no framework dependency

## Out of Scope
- Google Workspace admin-restricted sign-in
- Two-factor authentication via Google
- Google API access beyond profile (Drive, Calendar, etc.)

## Added Context
- Related: Story 3.2 — Sign in with Microsoft
- Related: Story 3.3 — Session management and sign-out

Story 3.2: Sign in with Microsoft / Outlook

## Sign in with Microsoft / Outlook

## Description
As a user, I want to sign in using my Microsoft or Outlook account, so that I can access the application using my work or personal Microsoft identity.

## Acceptance Criteria

Scenario 1: First-time Microsoft sign-in
Given I am on the sign-in page
When I click "Sign in with Microsoft"
Then I am redirected to Microsoft's OAuth consent screen
And I see the permissions being requested (email, profile)

Scenario 2: Grant consent and sign in
Given I am on Microsoft's OAuth consent screen
When I select my Microsoft account and grant consent
Then I am redirected back to the application
And a new account is created using my Microsoft profile
And I am signed in and see the main dashboard

Scenario 3: Returning user sign-in
Given I have previously signed in with Microsoft
When I click "Sign in with Microsoft"
Then I am signed in without re-granting consent
And I see the main dashboard

Scenario 4: Deny consent
Given I am on Microsoft's consent screen
When I deny consent or close the window
Then I am returned to the sign-in page
And I see a message: "Sign-in was cancelled. Please try again."

Scenario 5: Microsoft email already exists
Given an account already exists with my Microsoft email
When I sign in with Microsoft
Then my Microsoft identity is linked to the existing account
And I am signed in to the existing account

## Technical Details
- OAuth 2.0 Authorization Code flow with PKCE
- Microsoft Identity Platform v2.0 endpoint
- Scopes: openid, email, profile, User.Read
- Supports both personal (outlook.com) and organizational (Azure AD) accounts
- Store: Microsoft oid, email, display name, avatar URL
- Redirect URI must be registered in Azure App Registration

**Framework-specific details** (include the variant matching the user's choice from Step 1):
- **NextAuth.js**: Use AzureADProvider; configure `AZURE_AD_CLIENT_ID`, `AZURE_AD_CLIENT_SECRET`, `AZURE_AD_TENANT_ID` in env; callback URL: `/api/auth/callback/azure-ad`
- **Firebase Auth**: Use `signInWithPopup(auth, microsoftProvider)` with `OAuthProvider('microsoft.com')`; enable Microsoft in Firebase Console → Authentication → Sign-in method
- **Supabase Auth**: Use `supabase.auth.signInWithOAuth({ provider: 'azure' })`; configure Azure provider in Supabase Dashboard → Authentication → Providers
- **Auth0**: Use Universal Login with Microsoft social connection; configure in Auth0 Dashboard → Authentication → Enterprise → Microsoft Azure AD
- **Framework-agnostic** (default): Implement OAuth 2.0 directly against Microsoft Identity Platform; no framework dependency

## Out of Scope
- Azure AD tenant-restricted sign-in
- Microsoft Graph API access beyond profile
- Conditional access policies

## Added Context
- Related: Story 3.1 — Sign in with Google
- Related: Story 3.3 — Session management and sign-out

Story 3.3: Session management and sign-out

## Session management and sign-out

## Description
As a signed-in user, I want my session to persist across browser tabs and page refreshes, and I want to sign out when I'm done.

## Acceptance Criteria

Scenario 1: Session persists across refreshes
Given I am signed in
When I refresh the page
Then I remain signed in
And I see my dashboard (not the sign-in page)

Scenario 2: Session persists across tabs
Given I am signed in
When I open the application in a new tab
Then I am signed in on the new tab as well

Scenario 3: Sign out
Given I am signed in
When I click my avatar/name in the header
And I click "Sign out"
Then my session is ended
And I am redirected to the sign-in page

Scenario 4: Session expiration
Given my session has expired (e.g., after 7 days of inactivity)
When I attempt to access a protected page
Then I am redirected to the sign-in page
And I see a message: "Your session has expired. Please sign in again."

Scenario 5: Sign-in page when already authenticated
Given I am already signed in
When I navigate to the sign-in page
Then I am redirected to the dashboard

## Technical Details
- Session token: JWT stored in httpOnly secure cookie
- Session lifetime: 7 days (configurable)
- Refresh token rotation on each use
- Sign-out clears both client cookie and server session

## Out of Scope
- "Remember me" checkbox (session length is fixed)
- Multi-device session management
- Force sign-out from other devices

## Added Context
- Related: Story 3.1 — Sign in with Google
- Related: Story 3.2 — Sign in with Microsoft

Category 4: RBAC Permissions

Story 4.1: Role definitions and permission matrix

## Role-based access control — role definitions

## Description
As a system, I need a defined set of roles with specific permissions, so that users can only access features appropriate to their role.

## Acceptance Criteria

Scenario 1: Default roles exist
Given the system is set up
Then the following roles exist with these permissions:

| Permission | Viewer | Editor | Admin | Owner |
|-----------|--------|--------|-------|-------|
| View (objects) | Yes | Yes | Yes | Yes |
| Create (objects) | No | Yes | Yes | Yes |
| Edit (objects) | No | Yes | Yes | Yes |
| Delete (objects) | No | No | Yes | Yes |
| Manage users | No | No | Yes | Yes |
| Assign roles | No | No | Yes | Yes |
| Delete workspace | No | No | No | Yes |
| Manage billing | No | No | No | Yes |

Scenario 2: New user default role
Given a new user signs up or is invited
Then they are assigned the "Viewer" role by default

Scenario 3: Permission enforcement
Given a user with "Viewer" role
When they attempt to create, edit, or delete an (object)
Then the action buttons are hidden or disabled
And if they attempt the action via direct URL, they receive a 403 Forbidden response

Scenario 4: Custom roles (if specified in Step 1)
Given the team has defined custom roles (e.g., "Billing Admin", "Approver")
When the system is configured
Then the custom roles appear alongside the baseline roles in the permission matrix
And each custom role has explicitly defined permissions for every action
And custom roles slot into the hierarchy relative to the baseline roles

## Technical Details
- Roles stored in database, associated with user record
- Permission checks at both UI level (hide/disable elements) and API level (reject unauthorized requests)
- Role hierarchy: Owner > Admin > (custom roles slot here) > Editor > Viewer
- Custom roles extend the baseline — they do not replace it
- Each custom role must define permissions for every row in the permission matrix

## Out of Scope
- Per-object permissions (all permissions are workspace-wide)
- Temporary / time-limited roles
- Runtime role creation by end users (custom roles are defined at setup time)

## Added Context
- Related: Story 4.2 — Assign and change user roles
- Related: Story 4.3 — Access denied experience
- Related: Story 1.5 — Delete (Object) (requires Admin or Owner role)

Story 4.2: Assign and change user roles

## Assign and change user roles

## Description
As an Admin or Owner, I want to change a user's role, so that I can control what they can access and do.

## Acceptance Criteria

Scenario 1: View team members and roles
Given I am an Admin or Owner
When I navigate to the Team Settings page
Then I see a list of all users with their current roles
And I see an option to change each user's role

Scenario 2: Change a user's role
Given I am on the Team Settings page
When I click the role dropdown for a user
And I select a new role
Then I see a confirmation: "Change (user name)'s role from (old role) to (new role)?"
And if I confirm, the role is updated immediately
And the user sees updated permissions on their next page load

Scenario 3: Cannot escalate beyond own role
Given I am an Admin
When I view the role dropdown for another user
Then I can assign: Viewer, Editor, Admin
But I cannot assign: Owner (only Owners can create other Owners)

Scenario 4: Cannot demote yourself
Given I am an Admin viewing my own row
Then the role dropdown is disabled for my own account
And I see a tooltip: "You cannot change your own role"

Scenario 5: Last Owner protection
Given there is only one Owner
When someone attempts to change the Owner's role
Then the action is blocked
And they see: "Cannot change role. At least one Owner is required."

## Technical Details
- Role changes take effect immediately (no re-login required)
- Email notification sent to user when their role changes
- Audit log entry created for every role change

## Out of Scope
- Invitation flow (covered separately)
- Role request / approval workflow
- Bulk role assignment

## Added Context
- Related: Story 4.1 — Role definitions and permission matrix
- Related: Story 4.3 — Access denied experience

Story 4.3: Access denied experience

## Access denied experience

## Description
As a user without sufficient permissions, I want to see a clear message when I can't access something, so that I understand why and know what to do.

## Acceptance Criteria

Scenario 1: UI element hiding
Given I have the "Viewer" role
When I view the (Objects) list
Then I do NOT see "Create", "Edit", or "Delete" buttons
And the interface appears clean (no disabled buttons cluttering the view)

Scenario 2: Direct URL access denied
Given I have the "Viewer" role
When I navigate directly to a URL I don't have permission for (e.g., /settings/team)
Then I see an "Access Denied" page
And the message reads: "You don't have permission to view this page. Contact your Admin for access."
And I see a button: "Go to Dashboard"

Scenario 3: API-level enforcement
Given I have the "Viewer" role
When I attempt an unauthorized API call (e.g., POST to create an object)
Then the API returns 403 Forbidden
And the response includes: { "error": "insufficient_permissions", "required_role": "Editor" }

Scenario 4: Feature discovery
Given I have the "Viewer" role
When I see a feature that requires a higher role
Then I see a subtle indicator: lock icon or "Upgrade role to access"
And clicking it shows: "This feature requires Editor access. Contact your Admin."

## Technical Details
- UI-level: conditionally render elements based on user role
- API-level: middleware checks role before processing request
- No information leakage: access denied pages don't reveal what the page contains

## Out of Scope
- Request access flow (e.g., "Request Editor access" button)
- Granular per-object permissions

Category 5: Agentic Coding System

Generate the following stories when the user selects Category 5. Use placeholders from intake questions 9–14: (Agent Name), (tool type), (action domain), (LLM provider), (context source).

Stories 5.1–5.6 are required (always generate). Stories 5.7–5.9 are optional (generate only if relevant or requested). Story 5.8 requires "Multi-agent? yes" from intake question 14.

Story 5.1: Agent identity and system prompt configuration

## (Agent Name) identity and system prompt configuration

## Description
As a developer, I want to configure (Agent Name)'s identity, capabilities, and behavior boundaries through a system prompt, so that the agent operates within defined guardrails and presents a consistent persona.

## Acceptance Criteria

Scenario 1: System prompt creation
Given I am on the (Agent Name) configuration page
When I create a new system prompt
Then I can define:
  | Field | Description |
  | Name | Display name for the agent |
  | Role description | What the agent does in one sentence |
  | Capabilities | List of tools and actions the agent can use |
  | Boundaries | Actions the agent must NOT take |
  | Tone | Communication style (e.g., concise, explanatory, formal) |
And the prompt is saved and takes effect on the next conversation

Scenario 2: Capability declaration
Given (Agent Name) has a configured system prompt
When a user starts a new conversation
Then (Agent Name) can describe what it can and cannot do
And it refuses requests outside its declared boundaries with a clear explanation

Scenario 3: Behavior boundary enforcement
Given (Agent Name) has a boundary: "Do not modify files outside the /src directory"
When a user asks (Agent Name) to edit a file in /config
Then (Agent Name) declines the action
And responds: "I'm configured to only modify files in /src. You can update this in my system prompt settings."

Scenario 4: System prompt versioning
Given I edit an existing system prompt
When I save the changes
Then the previous version is preserved in history
And I can view a diff between versions
And I can roll back to any previous version

## Technical Details
- System prompt stored as a structured document, not a raw string
- Capabilities map to tool registrations (see Story 5.2)
- Boundaries enforced at the tool execution layer, not just prompt-level
- (LLM provider) processes the system prompt as the first message in every conversation

## Out of Scope
- Fine-tuning or model training
- Per-user prompt customization (all users share the same agent identity)

## Added Context
- Cross-reference: Authentication (Category 3) for agent API key management
- Reference: [Agent Experience guide](../../practices/agent-experience/guide.md) for identity patterns

Story 5.2: Tool execution with intent preview

## (Agent Name) tool execution with intent preview

## Description
As a user, I want (Agent Name) to show me what it intends to do before executing a tool, so that I can approve, reject, or modify the action before it takes effect.

## Acceptance Criteria

Scenario 1: Intent preview before execution
Given (Agent Name) determines it needs to use a (tool type) tool
When it is ready to execute
Then it shows an intent preview with:
  | Field | Example |
  | Tool | (tool type) |
  | Action | What the tool will do |
  | Target | File, endpoint, or resource being acted on |
  | Expected outcome | What changes after execution |
And execution is paused until I respond

Scenario 2: User approves action
Given (Agent Name) is showing an intent preview
When I click "Approve" or type "yes"
Then (Agent Name) executes the tool
And shows the execution result (success, output, or error)

Scenario 3: User rejects action
Given (Agent Name) is showing an intent preview
When I click "Reject" or type "no"
Then (Agent Name) does NOT execute the tool
And asks: "Would you like me to try a different approach?"

Scenario 4: User modifies action
Given (Agent Name) is showing an intent preview
When I edit the proposed action (e.g., change target file, adjust parameters)
Then (Agent Name) updates the intent preview with my changes
And waits for approval of the modified action

Scenario 5: Execution result display
Given (Agent Name) has executed a (tool type) tool
When the execution completes
Then I see the output (e.g., terminal output, diff, API response)
And if the action modified (action domain), I see a before/after comparison
And if the action failed, I see the error with a suggested fix

## Technical Details
- Intent preview is non-blocking: user can continue chatting while reviewing
- Tool execution happens server-side in a sandboxed environment
- All tool calls are logged (see Story 5.7 for audit log)
- Timeout: if user doesn't respond within 5 minutes, the action expires

## Out of Scope
- Automatic execution without preview (see Story 5.3 for autonomy controls)
- Undo/rollback of executed actions (backlog candidate)

## Added Context
- Cross-reference: Story 5.3 (Autonomy dial) controls when preview is shown vs. auto-executed
- Cross-reference: CRUD (Category 1) patterns for tool execution audit logs

Story 5.3: Autonomy dial — user-configurable agent independence

## (Agent Name) autonomy dial

## Description
As a user, I want to configure how independently (Agent Name) operates, so that I can balance speed (agent decides) with control (I decide) based on my comfort level and the task risk.

## Acceptance Criteria

Scenario 1: Autonomy level selection
Given I open (Agent Name) settings
When I navigate to the "Autonomy" section
Then I see a dial with three levels:
  | Level | Behavior |
  | Supervised | Agent asks before every action |
  | Balanced | Agent auto-executes low-risk actions, asks for high-risk |
  | Autonomous | Agent executes all actions, notifies after completion |
And the current level is highlighted
And I can change it with one click

Scenario 2: Per-action-type overrides
Given I have set the autonomy level to "Balanced"
When I open "Advanced autonomy settings"
Then I can set overrides per action type:
  | Action type | Override |
  | Read files | Always auto-execute |
  | Write files | Always ask |
  | Run tests | Always auto-execute |
  | Run terminal commands | Always ask |
  | API calls | Always ask |
And overrides take precedence over the global level

Scenario 3: Risk classification
Given (Agent Name) is about to execute a tool in "Balanced" mode
When the tool is classified as high-risk (e.g., deletes data, writes to production, runs destructive commands)
Then (Agent Name) shows an intent preview regardless of autonomy level
And the preview includes a risk indicator: "⚠ High-risk action"

Scenario 4: Autonomy persistence
Given I set the autonomy level to "Balanced"
When I start a new conversation
Then the autonomy level is still "Balanced"
And my per-action-type overrides are preserved

Scenario 5: Session override
Given I am in a conversation with "Supervised" mode
When I type "go ahead and run all the tests without asking"
Then (Agent Name) temporarily escalates to "Autonomous" for test execution in this session only
And resets to "Supervised" when the conversation ends

## Technical Details
- Risk classification is configurable: team can define which tools are low/medium/high risk
- Autonomy level stored in user preferences, not conversation state
- Session overrides are explicit and logged
- Default for new users: "Supervised"

## Out of Scope
- Team-wide autonomy policies (admin forces a max level)
- Autonomy recommendations based on task type

## Added Context
- Cross-reference: Story 5.2 (Intent preview) is always shown in "Supervised" mode
- Cross-reference: Story 5.4 (Confidence signals) may trigger escalation even in "Autonomous" mode
- Reference: [Agent Experience guide](../../practices/agent-experience/guide.md) for autonomy dial patterns

Story 5.4: Confidence signals and uncertainty communication

## (Agent Name) confidence signals

## Description
As a user, I want (Agent Name) to communicate how confident it is in its responses and actions, so that I know when to trust its output and when to verify manually.

## Acceptance Criteria

Scenario 1: Confidence indicator on responses
Given (Agent Name) responds to a question about (action domain)
When the response is displayed
Then I see a confidence indicator:
  | Level | Indicator | Meaning |
  | High | ✅ solid | Agent is confident in this answer |
  | Medium | 🔶 caution | Agent is mostly sure but recommends review |
  | Low | ⚠ uncertain | Agent is guessing — user should verify |

Scenario 2: Low-confidence escalation
Given (Agent Name) is in "Balanced" or "Autonomous" mode
When it assesses low confidence on an action
Then it automatically escalates to "Supervised" for that specific action
And shows: "I'm not confident about this. Here's what I'd do — should I proceed?"

Scenario 3: Uncertainty explanation
Given (Agent Name) marks a response as medium or low confidence
When I click the confidence indicator
Then I see an explanation:
  | Field | Content |
  | Why uncertain | Reason (e.g., "ambiguous requirement", "no matching pattern in codebase") |
  | What would help | Suggested clarification (e.g., "Can you point me to the relevant test file?") |
  | Alternative approaches | Other options the agent considered |

Scenario 4: Confidence on multi-step plans
Given (Agent Name) proposes a multi-step plan
When it displays the plan
Then each step has its own confidence indicator
And the overall plan confidence is the lowest step confidence

## Technical Details
- Confidence derived from (LLM provider) response metadata (logprobs, self-assessment prompting)
- Confidence thresholds configurable per team
- Low-confidence escalation overrides autonomy dial setting (Story 5.3)
- Confidence data logged for observability (Story 5.7)

## Out of Scope
- Calibrating confidence accuracy over time (learning from corrections)
- Hiding confidence signals for non-technical users

## Added Context
- Cross-reference: Story 5.3 (Autonomy dial) — low confidence triggers escalation
- Cross-reference: Story 5.6 (Error recovery) — confidence drops to low on errors

Story 5.5: Conversation context and memory management

## (Agent Name) context and memory management

## Description
As a user, I want (Agent Name) to maintain context across my conversation and remember key project details, so that I don't have to repeat myself and the agent's responses stay relevant.

## Acceptance Criteria

Scenario 1: Context loading
Given I start a new conversation with (Agent Name)
When the conversation initializes
Then (Agent Name) automatically loads:
  | Context source | Example |
  | (context source) | Files, docs, history relevant to current work |
And shows: "Loaded context: (list of sources)" in a collapsible header

Scenario 2: Conversation history
Given I am mid-conversation with (Agent Name)
When I reference something from earlier in the conversation
Then (Agent Name) recalls it accurately
And does not ask me to repeat previously provided information

Scenario 3: Context window management
Given the conversation approaches the (LLM provider) context limit
When (Agent Name) needs to make room for new context
Then it summarizes older conversation turns (not the system prompt or key project context)
And shows: "Summarized earlier messages to stay within context limits"
And the summary is available for me to review in a collapsible section

Scenario 4: Pin important context
Given (Agent Name) has loaded multiple context sources
When I say "Remember this: (specific fact or decision)"
Then (Agent Name) pins that context so it is never summarized away
And I see a "📌 Pinned" indicator on that item
And I can unpin it later

Scenario 5: Context source management
Given I am on the (Agent Name) settings page
When I open "Context sources"
Then I see all configured (context source) sources
And I can add, remove, or reorder them
And changes take effect on the next conversation

## Technical Details
- Context loading runs before the first user message
- Pinned context occupies reserved token space (configurable max)
- Summarization uses a separate (LLM provider) call with a summarization prompt
- Context sources are project-level configuration (shared across team)

## Out of Scope
- Cross-conversation memory (agent remembers across separate conversations)
- Automatic context prioritization (agent decides what's important)

## Added Context
- Cross-reference: Story 5.1 (System prompt) is always loaded as top-priority context
- Reference: [Agent Experience guide](../../practices/agent-experience/guide.md) for memory patterns

Story 5.6: Graceful degradation and error recovery

## (Agent Name) graceful degradation and error recovery

## Description
As a user, I want (Agent Name) to handle errors and service disruptions gracefully, so that I don't lose my work and I know what's happening when things go wrong.

## Acceptance Criteria

Scenario 1: LLM provider timeout
Given (Agent Name) is processing my request
When the (LLM provider) API does not respond within 30 seconds
Then I see: "Response is taking longer than expected. Still waiting..."
And after 60 seconds: "The AI service is slow right now. I'll retry once, or you can try again."
And (Agent Name) retries with exponential backoff (1 retry)

Scenario 2: LLM provider error
Given (Agent Name) sends a request to (LLM provider)
When the API returns an error (500, rate limit, etc.)
Then I see: "I encountered an error: (brief description). Retrying..."
And if the retry fails: "The AI service is temporarily unavailable. Your conversation is saved — try again in a few minutes."
And my conversation history is preserved

Scenario 3: Tool execution failure
Given (Agent Name) is executing a (tool type) tool
When the tool fails (e.g., command returns non-zero exit, API returns error)
Then I see the error output
And (Agent Name) suggests a fix: "This failed because (reason). I can try (alternative approach) — should I?"
And it does NOT retry the same failing command automatically

Scenario 4: Partial result delivery
Given (Agent Name) is partway through a multi-step plan
When one step fails
Then it delivers the results of completed steps
And marks the failed step clearly: "❌ Step 3 failed: (reason)"
And asks: "Steps 1-2 succeeded. Should I retry step 3, skip it, or stop here?"

Scenario 5: Connection loss recovery
Given I lose network connection during a conversation
When the connection is restored
Then the conversation resumes from where it left off
And no messages are lost
And (Agent Name) confirms: "Connection restored. Continuing from where we left off."

## Technical Details
- Retry policy: 1 retry with exponential backoff (2s, then 4s)
- Conversation state persisted to server on every turn (not just client-side)
- Tool errors include stdout, stderr, and exit code in the displayed output
- Degraded state indicator visible in the UI header when service is impaired

## Out of Scope
- Automatic fallback to a different LLM provider
- Offline mode (agent works without network)

## Added Context
- Cross-reference: Story 5.4 (Confidence signals) — confidence drops to low during degraded state
- Cross-reference: Story 5.7 (Observability) — all errors logged in the action log

Story 5.7: Agent action log and observability dashboard (optional)

## (Agent Name) action log and observability

## Description
As a developer, I want to view a log of all actions (Agent Name) has taken, so that I can audit what happened, debug issues, and understand the agent's behavior over time.

## Acceptance Criteria

Scenario 1: Action log entry
Given (Agent Name) executes any tool or action
When the action completes
Then a log entry is created with:
  | Field | Content |
  | Timestamp | When the action occurred |
  | Action type | Tool name or action category |
  | Input | What the agent sent to the tool |
  | Output | What the tool returned |
  | Duration | How long the action took |
  | Outcome | Success, failure, or cancelled |
  | Conversation ID | Which conversation triggered this |

Scenario 2: Log browsing
Given I navigate to the (Agent Name) action log
When the page loads
Then I see a chronological list of all actions
And I can filter by: action type, outcome (success/failure), date range, conversation
And I can search log entries by keyword

Scenario 3: Action detail view
Given I am browsing the action log
When I click on a specific log entry
Then I see the full input and output (including terminal output, diffs, or API responses)
And I see the conversation context that led to this action
And I can jump to the original conversation

Scenario 4: Export capability
Given I am viewing the action log
When I click "Export"
Then I can export the filtered log as CSV or JSON
And the export includes all fields from Scenario 1

## Technical Details
- Logs stored in a structured format (not plain text) for queryability
- Retention: 90 days default, configurable
- Sensitive data (API keys, passwords) redacted in logs automatically
- Log writes are async — they do not slow down agent execution

## Out of Scope
- Real-time dashboard with charts (backlog candidate)
- Alerting on specific action patterns
- Cost tracking per action (LLM token usage)

## Added Context
- Cross-reference: Story 5.2 (Tool execution) — every tool call generates a log entry
- Cross-reference: Story 5.6 (Error recovery) — errors include full error details in the log

Story 5.8: Multi-agent orchestration (optional — requires multi-agent = yes)

## (Agent Name) multi-agent orchestration

## Description
As a developer, I want (Agent Name) to delegate subtasks to specialist agents and coordinate their results, so that complex workflows can be handled by the right agent for each job.

## Acceptance Criteria

Scenario 1: Task delegation
Given (Agent Name) receives a task that spans multiple (action domain) areas
When it determines a subtask is better handled by a specialist agent
Then it shows: "I'll delegate (subtask description) to (Specialist Agent Name)"
And the delegation includes: task description, relevant context, expected output format
And I can approve or reject the delegation

Scenario 2: Specialist agent execution
Given a subtask has been delegated to a specialist agent
When the specialist agent works on it
Then I see a status indicator: "🔄 (Specialist Agent Name) is working on (subtask)..."
And the specialist agent operates within its own defined boundaries (Story 5.1)
And its actions follow the same intent preview rules (Story 5.2)

Scenario 3: Result coordination
Given multiple specialist agents have completed their subtasks
When (Agent Name) receives all results
Then it synthesizes them into a coherent response
And shows which agent contributed which part
And flags any conflicts between agent outputs: "⚠ Conflict: (Agent A) suggests X but (Agent B) suggests Y"

Scenario 4: Conflict resolution
Given specialist agents have produced conflicting outputs
When (Agent Name) presents the conflict
Then I can choose which output to use
Or ask (Agent Name) to resolve it: "Merge these — prioritize (Agent A)'s approach for (reason)"

Scenario 5: Orchestration visibility
Given (Agent Name) is coordinating multiple agents
When I want to see the full workflow
Then I see a task breakdown showing:
  | Agent | Subtask | Status | Duration |
  | (Agent Name) | Orchestration | In progress | — |
  | (Specialist 1) | (subtask) | Complete | 12s |
  | (Specialist 2) | (subtask) | In progress | — |

## Technical Details
- Supervisor pattern: (Agent Name) is always the orchestrator; specialist agents don't delegate further
- Each specialist agent has its own system prompt and tool permissions
- Context sharing: orchestrator passes only relevant context to specialists (minimal privilege)
- Max concurrent agents: configurable (default: 3)

## Out of Scope
- Agent-to-agent direct communication (all communication goes through the orchestrator)
- Dynamic agent creation (agents must be pre-configured)
- Specialist agents delegating to other specialist agents (single-level delegation only)

## Added Context
- Cross-reference: RBAC (Category 4) for agent permission boundaries
- Cross-reference: Story 5.3 (Autonomy dial) applies to each agent independently
- Reference: [Agentic Patterns](../../tools/by-tool/agentic-patterns.md) for orchestration patterns

Story 5.9: Streaming output and progressive disclosure (optional)

## (Agent Name) streaming output and progressive disclosure

## Description
As a user, I want to see (Agent Name)'s output as it's being generated, so that I don't wait for a complete response and I can interrupt or redirect early.

## Acceptance Criteria

Scenario 1: Token-by-token streaming
Given I send a message to (Agent Name)
When (Agent Name) begins generating a response
Then text appears incrementally as it's generated (not all at once)
And I see a typing indicator while generation is in progress
And the response is readable as it streams (not garbled)

Scenario 2: Cancel mid-stream
Given (Agent Name) is streaming a response
When I click "Stop" or press Escape
Then generation stops immediately
And the partial response is preserved in the conversation
And I can send a new message to redirect

Scenario 3: Thinking/planning phase display
Given (Agent Name) is working on a complex task
When it enters a planning phase before executing
Then I see a collapsible "Thinking..." section
And the section shows (Agent Name)'s reasoning steps
And this section is visually distinct from the final response

Scenario 4: Progressive tool output
Given (Agent Name) is executing a (tool type) tool that produces long output
When output streams from the tool
Then I see it in a scrollable, fixed-height container
And the container auto-scrolls to show the latest output
And I can scroll up to review earlier output without interrupting the stream

Scenario 5: Multi-step progress
Given (Agent Name) is executing a multi-step plan
When it moves from one step to the next
Then I see a progress indicator: "Step 2 of 5: Running tests..."
And completed steps are collapsed with a summary
And the current step is expanded and streaming

## Technical Details
- Streaming uses server-sent events (SSE) or WebSocket from (LLM provider)
- Partial responses are valid markdown at every point (no broken formatting mid-stream)
- Cancel sends an abort signal to the LLM provider to stop token generation
- Thinking section content is not included in the conversation context for future turns

## Out of Scope
- Audio/voice streaming
- Streaming output to multiple clients simultaneously (collaboration)

## Added Context
- Cross-reference: Story 5.2 (Intent preview) — intent previews appear before streaming starts
- Cross-reference: Story 5.6 (Error recovery) — if streaming fails, partial response is preserved

Step 3: Review batch

Present all generated stories as a batch. Ask the user:

Review checkpoint: Here are your starter stories. Before I generate prototypes or push to your tracker:

  • Any stories to skip or remove?
  • Any missing scenarios or edge cases to add?
  • Any field names, roles, or flows to customize?
  • Ready to generate prototypes? (if selected in Step 1)

Make edits as needed. Continue until the user is satisfied with the story text.

Step 4: Generate prototypes (optional)

If the user requested prototypes in Step 1 (or requests them now), generate a single integrated HTML prototype that combines all selected categories into one app-like experience using the /artium-prototype scaffold.

The integrated prototype is one HTML file with a top-level nav that connects all selected categories into a cohesive MVP shell:

Auth section (entry point):

  • Sign-in page with "Sign in with Google" and "Sign in with Microsoft" buttons
  • Branded OAuth button styles
  • Post-login transition to the main app shell

Main app shell (post-login):

  • Header with user avatar, name, and sign-out dropdown
  • Sidebar or top nav linking to all sections

CRUD section:

  • List view with sample data in a table (with search bar, pagination, empty state toggle)
  • Detail view (accessible by clicking a row)
  • Create/edit form (modal or separate view)
  • Delete confirmation dialog

Form section:

  • The form with all fields laid out
  • Required field markers (asterisks)
  • Placeholder text in every field
  • Example inline validation error states
  • Success state

RBAC section:

  • Team settings page with user list and role dropdowns (including custom roles if specified)
  • Permission matrix reference table
  • Access denied page
  • Viewer-restricted view (buttons hidden, lock icons on restricted features)

Agentic Coding System section:

  • Agent chat interface with message input, streaming response area, and conversation history sidebar
  • Intent preview panel — shows proposed action with Approve / Reject / Modify buttons before execution
  • Autonomy settings page with three-level dial (Supervised / Balanced / Autonomous) and per-action-type overrides
  • Confidence indicator badges (High / Medium / Low) on agent responses
  • Action log view — table with timestamp, action, tool used, duration, and outcome columns
  • Context panel — sidebar showing loaded context sources with pin/unpin toggles
  • If multi-agent: orchestration dashboard showing active agents, delegated tasks, and status
  • Graceful degradation state — banner showing "Agent temporarily unavailable" with partial results

Save the integrated prototype to ./prototypes/starter-mvp/index.html.

If only one category is selected, generate a standalone single-category file instead (e.g., ./prototypes/starter-crud/index.html).

Preview using Claude Preview so the user can see it in their browser.

Step 5: Human-in-the-loop approval

Present the complete package:

  1. Story batch — all generated stories
  2. Prototypes (if generated) — live preview links
  3. Ask for explicit approval:

Final review: Here's your complete starter stories package:

  • (N) stories across (categories selected)
  • (N) prototypes (if generated)

Ready to push to (Linear / Jira / Notion)? Or keep in conversation only?

Do not push to any tracker until the user explicitly approves.

Step 6: Push to project tracker (optional)

If approved, push stories using the same approach as /story-write Step 8:

  • Linear: Use save_issue for each story. Attach prototype screenshots via create_attachment.
  • Jira: Use Atlassian MCP if connected.
  • Notion: Use notion-create-pages to create a page per story.
  • Conversation only: Stories stay in the chat for manual copy.

After pushing, confirm success with links to created issues/pages.


Related skills

  • /story-write — general-purpose story writing (this skill uses its format)
  • /artium-prototype — standalone prototype creation (this skill uses its scaffold)
  • /story-split — if any generated story is too large
  • /story-review — review generated stories for quality
  • /persona-create — create personas to reference in story descriptions
  • /prd-draft — if the user needs a full PRD before writing stories

Output locations

FormatLocationNotes
Story textConversationAlways produced. Source of truth until pushed.
Integrated prototype./prototypes/starter-mvp/index.htmlOptional. Single app-like experience combining all selected categories.
Single-category prototype./prototypes/starter-(category)/index.htmlFallback when only one category is selected.
Linear issuesLinear workspaceVia Linear MCP. One issue per story.
Jira issuesJira projectVia Atlassian MCP (if connected).
Notion pagesNotion workspaceVia Notion MCP. One page per story.

Example Output

Input

  • Company / product: Meridian — a B2B SaaS platform for managing commercial real estate lease portfolios
  • Categories selected: CRUD Table + Authentication + RBAC Permissions
  • Domain object: Lease, with fields: property name (text, required), tenant name (text, required), lease start date (date, required), lease end date (date, required), monthly rent (currency, required), status (dropdown: Active / Expiring / Expired / Draft, required), assigned broker (text, optional), notes (text, optional)
  • Auth providers + framework: Google and Microsoft / Outlook via NextAuth.js
  • Prototypes: Yes — integrated HTML prototype
  • Push to tracker: Linear

Output (abbreviated)

Meridian MVP Starter Stories

7 stories across CRUD, Authentication, and RBAC · NextAuth.js · Linear-ready


Category 1: CRUD Table — Lease


View Lease list

As a user, I want to see a list of all leases in a table, so that I can browse and find what I need.

Acceptance Criteria

Scenario 1: List displays with data Given there are existing leases in the system When I navigate to the Leases page Then I see a table with columns: Property Name, Tenant Name, Lease Start, Lease End, Monthly Rent, Status, Assigned Broker And each row shows one lease And the list is sorted by most recently updated by default

Scenario 2: Empty state Given there are no leases in the system When I navigate to the Leases page Then I see an empty state message: "No leases yet" And I see a call-to-action button: "Create your first lease"

Scenario 3: Pagination Given there are more than 25 leases When I navigate to the Leases page Then I see the first 25 results and pagination controls

Scenario 4: Search and filter Given I am on the Leases page When I type a search term in the search bar Then the table filters to show leases matching the term across Property Name, Tenant Name, Assigned Broker, and Notes

Technical Details

  • Default page size: 25 rows; sortable on all columns
  • Searchable fields: property name, tenant name, assigned broker, notes (case-insensitive, partial match)
  • Status column renders a color-coded badge: Active (green), Expiring (amber), Expired (red), Draft (gray)
  • Loading state: skeleton rows while data loads

Out of Scope

  • Bulk actions, CSV export, saved filter views

View Lease detail

As a user, I want to click on a lease to see its full details, so that I can review all information about it.

Acceptance Criteria

Scenario 1: View detail page Given I am on the Leases list page When I click a lease row Then I see the Lease detail page showing all fields and "Edit" / "Delete" action buttons

Scenario 2: Direct URL access Given I navigate directly to /leases/:id Then I see the correct Lease detail page

Scenario 3: Lease not found Given the lease ID doesn't exist Then I see a "Not found" error page with a link back to the Leases list

Technical Details

  • URL pattern: /leases/:id
  • Breadcrumb: Leases › [Property Name – Tenant Name]

Create new Lease

(Story 1.3 — full Gherkin scenarios for creation, validation, cancel, and redirect — abbreviated here)

Key scenarios

  • Successful creation → redirect to Lease detail + success toast "Lease created successfully"
  • Required field validation on submit → inline errors + focus on first error
  • Cancel → return to Leases list, nothing created

Technical Details

  • Required: Property Name, Tenant Name, Lease Start, Lease End, Monthly Rent, Status
  • Optional: Assigned Broker, Notes
  • Monthly Rent: currency input, min value $0, no negative values

Edit Lease · Delete Lease

(Stories 1.4 and 1.5 follow the standard CRUD templates with "Discard unsaved changes?" guard on cancel and "Are you sure you want to delete [Property Name – Tenant Name]? This action cannot be undone." confirmation dialog. Soft delete preferred.)


Category 3: Authentication


Sign in with Google

As a user, I want to sign in using my Google account, so that I can access Meridian without a separate password.

Acceptance Criteria

Scenario 1: First-time Google sign-in Given I am on the Meridian sign-in page When I click "Sign in with Google" Then I am redirected to Google's OAuth consent screen requesting openid, email, profile

Scenario 2: Grant consent Given I complete the Google OAuth consent flow Then I am redirected to /dashboard And a Meridian account is created with my Google name, email, and avatar And I see a welcome toast: "Welcome to Meridian"

Scenario 3: Returning user Given I have previously signed in with Google and my session is valid When I click "Sign in with Google" Then I am signed in without re-granting consent and land on /dashboard

Scenario 4: Deny consent When I close or deny the Google consent screen Then I return to the sign-in page with the message: "Sign-in was cancelled. Please try again."

Scenario 5: Email already exists Given an account exists for my Google email Then my Google identity is linked to that account and I am signed in

Technical Details

  • NextAuth.js: GoogleProvider; env vars GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET; session strategy: JWT; callback URL: /api/auth/callback/google
  • Scopes: openid email profile
  • Store: Google sub, email, display name, avatar URL

Sign in with Microsoft / Outlook

(Story 3.2 — mirrors Google story structure)

Technical Details

  • NextAuth.js: AzureADProvider; env vars AZURE_AD_CLIENT_ID, AZURE_AD_CLIENT_SECRET, AZURE_AD_TENANT_ID; callback URL: /api/auth/callback/azure-ad
  • Scopes: openid email profile User.Read
  • Supports both outlook.com personal and Azure AD organizational accounts

Session management and sign-out

(Story 3.3 — session persists on refresh and across tabs; 7-day JWT in httpOnly cookie; sign-out clears cookie and server session; expired session → sign-in page with "Your session has expired. Please sign in again.")


Category 4: RBAC Permissions


Role-based access control — role definitions

As a system, I need defined roles with specific permissions so users can only access features appropriate to their role.

Permission Matrix

PermissionViewerEditorAdminOwner
View leases
Create leases
Edit leases
Delete leases
Manage users
Assign roles
Delete workspace
Manage billing

Acceptance Criteria (abbreviated)

Scenario: New user default role Given a user signs up or is invited to Meridian Then they are assigned the Viewer role by default

Scenario: Permission enforcement — Viewer Given I have the Viewer role When I view the Leases list Then Create, Edit, and Delete buttons are hidden And a direct API call to POST /leases returns 403 Forbidden

Technical Details

  • Role hierarchy: Owner > Admin > Editor > Viewer
  • Checks enforced at both UI layer (conditional rendering) and API middleware layer

Assign and change user roles