Compare commits

..

4 Commits

Author SHA1 Message Date
Ahmed Darrazi
065061ae75 merge: agent session work 2026-01-04 03:19:13 +01:00
Ahmed Darrazi
105241d6ae fix: highlight deviceComplianceScript diffs 2026-01-04 03:18:59 +01:00
Ahmed Darrazi
ad7e32c4fc merge: agent session work 2026-01-04 02:33:17 +01:00
Ahmed Darrazi
0c8106277c feat: add deviceComplianceScript policy type 2026-01-04 02:32:59 +01:00
11747 changed files with 29491 additions and 1623547 deletions

View File

@ -1,167 +0,0 @@
---
name: pest-testing
description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works."
license: MIT
metadata:
author: laravel
---
# Pest Testing 4
## When to Apply
Activate this skill when:
- Creating new tests (unit, feature, or browser)
- Modifying existing tests
- Debugging test failures
- Working with browser testing or smoke testing
- Writing architecture tests or visual regression tests
## Documentation
Use `search-docs` for detailed Pest 4 patterns and documentation.
## Basic Usage
### Creating Tests
All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
### Test Organization
- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
- Browser tests: `tests/Browser/` directory.
- Do NOT remove tests without approval - these are core application code.
### Basic Test Structure
<!-- Basic Pest Test Example -->
```php
it('is true', function () {
expect(true)->toBeTrue();
});
```
### Running Tests
- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
- Run all tests: `php artisan test --compact`.
- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.
## Assertions
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
<!-- Pest Response Assertion -->
```php
it('returns all', function () {
$this->postJson('/api/docs', [])->assertSuccessful();
});
```
| Use | Instead of |
|-----|------------|
| `assertSuccessful()` | `assertStatus(200)` |
| `assertNotFound()` | `assertStatus(404)` |
| `assertForbidden()` | `assertStatus(403)` |
## Mocking
Import mock function before use: `use function Pest\Laravel\mock;`
## Datasets
Use datasets for repetitive tests (validation rules, etc.):
<!-- Pest Dataset Example -->
```php
it('has emails', function (string $email) {
expect($email)->not->toBeEmpty();
})->with([
'james' => 'james@laravel.com',
'taylor' => 'taylor@laravel.com',
]);
```
## Pest 4 Features
| Feature | Purpose |
|---------|---------|
| Browser Testing | Full integration tests in real browsers |
| Smoke Testing | Validate multiple pages quickly |
| Visual Regression | Compare screenshots for visual changes |
| Test Sharding | Parallel CI runs |
| Architecture Testing | Enforce code conventions |
### Browser Test Example
Browser tests run in real browsers for full integration testing:
- Browser tests live in `tests/Browser/`.
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
- Use `RefreshDatabase` for clean state per test.
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging.
<!-- Pest Browser Test Example -->
```php
it('may reset the password', function () {
Notification::fake();
$this->actingAs(User::factory()->create());
$page = visit('/sign-in');
$page->assertSee('Sign In')
->assertNoJavaScriptErrors()
->click('Forgot Password?')
->fill('email', 'nuno@laravel.com')
->click('Send Reset Link')
->assertSee('We have emailed your password reset link!');
Notification::assertSent(ResetPassword::class);
});
```
### Smoke Testing
Quickly validate multiple pages have no JavaScript errors:
<!-- Pest Smoke Testing Example -->
```php
$pages = visit(['/', '/about', '/contact']);
$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
```
### Visual Regression Testing
Capture and compare screenshots to detect visual changes.
### Test Sharding
Split tests across parallel processes for faster CI runs.
### Architecture Testing
Pest 4 includes architecture testing (from Pest 3):
<!-- Architecture Test Example -->
```php
arch('controllers')
->expect('App\Http\Controllers')
->toExtendNothing()
->toHaveSuffix('Controller');
```
## Common Pitfalls
- Not importing `use function Pest\Laravel\mock;` before using mock
- Using `assertStatus(200)` instead of `assertSuccessful()`
- Forgetting datasets for repetitive validation tests
- Deleting tests without approval
- Forgetting `assertNoJavaScriptErrors()` in browser tests

View File

@ -1,53 +0,0 @@
---
name: speckit-git-commit
description: Auto-commit changes after a Spec Kit command completes
compatibility: Requires spec-kit project structure with .specify/ directory
metadata:
author: github-spec-kit
source: git:commands/speckit.git.commit.md
---
# Auto-Commit Changes
Automatically stage and commit all changes after a Spec Kit command completes.
## Behavior
This command is invoked as a hook after (or before) core commands. It:
1. Determines the event name from the hook context (e.g., if invoked as an `after_specify` hook, the event is `after_specify`; if `before_plan`, the event is `before_plan`)
2. Checks `.specify/extensions/git/git-config.yml` for the `auto_commit` section
3. Looks up the specific event key to see if auto-commit is enabled
4. Falls back to `auto_commit.default` if no event-specific key exists
5. Uses the per-command `message` if configured, otherwise a default message
6. If enabled and there are uncommitted changes, runs `git add .` + `git commit`
## Execution
Determine the event name from the hook that triggered this command, then run the script:
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name>`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name>`
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`).
## Configuration
In `.specify/extensions/git/git-config.yml`:
```yaml
auto_commit:
default: false # Global toggle — set true to enable for all commands
after_specify:
enabled: true # Override per-command
message: "[Spec Kit] Add specification"
after_plan:
enabled: false
message: "[Spec Kit] Add implementation plan"
```
## Graceful Degradation
- If Git is not available or the current directory is not a repository: skips with a warning
- If no config file exists: skips (disabled by default)
- If no changes to commit: skips with a message

View File

@ -1,72 +0,0 @@
---
name: speckit-git-feature
description: Create a feature branch with sequential or timestamp numbering
compatibility: Requires spec-kit project structure with .specify/ directory
metadata:
author: github-spec-kit
source: git:commands/speckit.git.feature.md
---
# Create Feature Branch
Create and switch to a new git feature branch for the given specification. This command handles **branch creation only** — the spec directory and files are created by the core `/speckit.specify` workflow.
## User Input
```text
$ARGUMENTS
```
You **MUST** consider the user input before proceeding (if not empty).
## Environment Variable Override
If the user explicitly provided `GIT_BRANCH_NAME` (e.g., via environment variable, argument, or in their request), pass it through to the script by setting the `GIT_BRANCH_NAME` environment variable before invoking the script. When `GIT_BRANCH_NAME` is set:
- The script uses the exact value as the branch name, bypassing all prefix/suffix generation
- `--short-name`, `--number`, and `--timestamp` flags are ignored
- `FEATURE_NUM` is extracted from the name if it starts with a numeric prefix, otherwise set to the full branch name
## Prerequisites
- Verify Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, warn the user and skip branch creation
## Branch Numbering Mode
Determine the branch numbering strategy by checking configuration in this order:
1. Check `.specify/extensions/git/git-config.yml` for `branch_numbering` value
2. Check `.specify/init-options.json` for `branch_numbering` value (backward compatibility)
3. Default to `sequential` if neither exists
## Execution
Generate a concise short name (2-4 words) for the branch:
- Analyze the feature description and extract the most meaningful keywords
- Use action-noun format when possible (e.g., "add-user-auth", "fix-payment-bug")
- Preserve technical terms and acronyms (OAuth2, API, JWT, etc.)
Run the appropriate script based on your platform:
- **Bash**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --short-name "<short-name>" "<feature description>"`
- **Bash (timestamp)**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --timestamp --short-name "<short-name>" "<feature description>"`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -ShortName "<short-name>" "<feature description>"`
- **PowerShell (timestamp)**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -Timestamp -ShortName "<short-name>" "<feature description>"`
**IMPORTANT**:
- Do NOT pass `--number` — the script determines the correct next number automatically
- Always include the JSON flag (`--json` for Bash, `-Json` for PowerShell) so the output can be parsed reliably
- You must only ever run this script once per feature
- The JSON output will contain `BRANCH_NAME` and `FEATURE_NUM`
## Graceful Degradation
If Git is not installed or the current directory is not a Git repository:
- Branch creation is skipped with a warning: `[specify] Warning: Git repository not detected; skipped branch creation`
- The script still outputs `BRANCH_NAME` and `FEATURE_NUM` so the caller can reference them
## Output
The script outputs JSON with:
- `BRANCH_NAME`: The branch name (e.g., `003-user-auth` or `20260319-143022-user-auth`)
- `FEATURE_NUM`: The numeric or timestamp prefix used

View File

@ -1,54 +0,0 @@
---
name: speckit-git-initialize
description: Initialize a Git repository with an initial commit
compatibility: Requires spec-kit project structure with .specify/ directory
metadata:
author: github-spec-kit
source: git:commands/speckit.git.initialize.md
---
# Initialize Git Repository
Initialize a Git repository in the current project directory if one does not already exist.
## Execution
Run the appropriate script from the project root:
- **Bash**: `.specify/extensions/git/scripts/bash/initialize-repo.sh`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/initialize-repo.ps1`
If the extension scripts are not found, fall back to:
- **Bash**: `git init && git add . && git commit -m "Initial commit from Specify template"`
- **PowerShell**: `git init; git add .; git commit -m "Initial commit from Specify template"`
The script handles all checks internally:
- Skips if Git is not available
- Skips if already inside a Git repository
- Runs `git init`, `git add .`, and `git commit` with an initial commit message
## Customization
Replace the script to add project-specific Git initialization steps:
- Custom `.gitignore` templates
- Default branch naming (`git config init.defaultBranch`)
- Git LFS setup
- Git hooks installation
- Commit signing configuration
- Git Flow initialization
## Output
On success:
- `✓ Git repository initialized`
## Graceful Degradation
If Git is not installed:
- Warn the user
- Skip repository initialization
- The project continues to function without Git (specs can still be created under `specs/`)
If Git is installed but `git init`, `git add .`, or `git commit` fails:
- Surface the error to the user
- Stop this command rather than continuing with a partially initialized repository

View File

@ -1,50 +0,0 @@
---
name: speckit-git-remote
description: Detect Git remote URL for GitHub integration
compatibility: Requires spec-kit project structure with .specify/ directory
metadata:
author: github-spec-kit
source: git:commands/speckit.git.remote.md
---
# Detect Git Remote URL
Detect the Git remote URL for integration with GitHub services (e.g., issue creation).
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and return empty:
```
[specify] Warning: Git repository not detected; cannot determine remote URL
```
## Execution
Run the following command to get the remote URL:
```bash
git config --get remote.origin.url
```
## Output
Parse the remote URL and determine:
1. **Repository owner**: Extract from the URL (e.g., `github` from `https://github.com/github/spec-kit.git`)
2. **Repository name**: Extract from the URL (e.g., `spec-kit` from `https://github.com/github/spec-kit.git`)
3. **Is GitHub**: Whether the remote points to a GitHub repository
Supported URL formats:
- HTTPS: `https://github.com/<owner>/<repo>.git`
- SSH: `git@github.com:<owner>/<repo>.git`
> [!CAUTION]
> ONLY report a GitHub repository if the remote URL actually points to github.com.
> Do NOT assume the remote is GitHub if the URL format doesn't match.
## Graceful Degradation
If Git is not installed, the directory is not a Git repository, or no remote is configured:
- Return an empty result
- Do NOT error — other workflows should continue without Git remote information

View File

@ -1,54 +0,0 @@
---
name: speckit-git-validate
description: Validate current branch follows feature branch naming conventions
compatibility: Requires spec-kit project structure with .specify/ directory
metadata:
author: github-spec-kit
source: git:commands/speckit.git.validate.md
---
# Validate Feature Branch
Validate that the current Git branch follows the expected feature branch naming conventions.
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and skip validation:
```
[specify] Warning: Git repository not detected; skipped branch validation
```
## Validation Rules
Get the current branch name:
```bash
git rev-parse --abbrev-ref HEAD
```
The branch name must match one of these patterns:
1. **Sequential**: `^[0-9]{3,}-` (e.g., `001-feature-name`, `042-fix-bug`, `1000-big-feature`)
2. **Timestamp**: `^[0-9]{8}-[0-9]{6}-` (e.g., `20260319-143022-feature-name`)
## Execution
If on a feature branch (matches either pattern):
- Output: `✓ On feature branch: <branch-name>`
- Check if the corresponding spec directory exists under `specs/`:
- For sequential branches, look for `specs/<prefix>-*` where prefix matches the numeric portion
- For timestamp branches, look for `specs/<prefix>-*` where prefix matches the `YYYYMMDD-HHMMSS` portion
- If spec directory exists: `✓ Spec directory found: <path>`
- If spec directory missing: `⚠ No spec directory found for prefix <prefix>`
If NOT on a feature branch:
- Output: `✗ Not on a feature branch. Current branch: <branch-name>`
- Output: `Feature branches should be named like: 001-feature-name or 20260319-143022-feature-name`
## Graceful Degradation
If Git is not installed or the directory is not a Git repository:
- Check the `SPECIFY_FEATURE` environment variable as a fallback
- If set, validate that value against the naming patterns
- If not set, skip validation with a warning

View File

@ -1,129 +0,0 @@
---
name: tailwindcss-development
description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes."
license: MIT
metadata:
author: laravel
---
# Tailwind CSS Development
## When to Apply
Activate this skill when:
- Adding styles to components or pages
- Working with responsive design
- Implementing dark mode
- Extracting repeated patterns into components
- Debugging spacing or layout issues
## Documentation
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
## Basic Usage
- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
## Tailwind CSS v4 Specifics
- Always use Tailwind CSS v4 and avoid deprecated utilities.
- `corePlugins` is not supported in Tailwind v4.
### CSS-First Configuration
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
<!-- CSS-First Config -->
```css
@theme {
--color-brand: oklch(0.72 0.11 178);
}
```
### Import Syntax
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
<!-- v4 Import Syntax -->
```diff
- @tailwind base;
- @tailwind components;
- @tailwind utilities;
+ @import "tailwindcss";
```
### Replaced Utilities
Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
| Deprecated | Replacement |
|------------|-------------|
| bg-opacity-* | bg-black/* |
| text-opacity-* | text-black/* |
| border-opacity-* | border-black/* |
| divide-opacity-* | divide-black/* |
| ring-opacity-* | ring-black/* |
| placeholder-opacity-* | placeholder-black/* |
| flex-shrink-* | shrink-* |
| flex-grow-* | grow-* |
| overflow-ellipsis | text-ellipsis |
| decoration-slice | box-decoration-slice |
| decoration-clone | box-decoration-clone |
## Spacing
Use `gap` utilities instead of margins for spacing between siblings:
<!-- Gap Utilities -->
```html
<div class="flex gap-8">
<div>Item 1</div>
<div>Item 2</div>
</div>
```
## Dark Mode
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
<!-- Dark Mode -->
```html
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
Content adapts to color scheme
</div>
```
## Common Patterns
### Flexbox Layout
<!-- Flexbox Layout -->
```html
<div class="flex items-center justify-between gap-4">
<div>Left content</div>
<div>Right content</div>
</div>
```
### Grid Layout
<!-- Grid Layout -->
```html
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
</div>
```
## Common Pitfalls
- Using deprecated v3 utilities (bg-opacity-*, flex-shrink-*, etc.)
- Using `@tailwind` directives instead of `@import "tailwindcss"`
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
- Using margins for spacing between siblings instead of gap utilities
- Forgetting to add dark mode variants when the project uses dark mode

View File

@ -1,173 +0,0 @@
## Source of Truth
If any Filament behavior is uncertain, lookup the exact section in:
- docs/research/filament-v5-notes.md
and prefer that over guesses.
# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES)
# Filament Blueprint (v5)
## 1) Non-negotiables
- Filament v5 requires Livewire v4.0+.
- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`).
- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results.
- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions).
- Prefer render hooks + CSS hook classes over publishing Filament internal views.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/render-hooks
- https://filamentphp.com/docs/5.x/styling/css-hooks
## 2) Directory & naming conventions
- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture.
- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`.
Sources:
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/advanced/modular-architecture
## 3) Panel setup defaults
- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels.
- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel.
- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review.
- Assets policy:
- Panel-only assets: register via panel config.
- Shared/plugin assets: register via `FilamentAsset::register()`.
- Deployment must include `php artisan filament:assets`.
Sources:
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/advanced/assets
## 4) Navigation & information architecture
- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately.
- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows.
- Treat cluster code structure as a recommendation (organizational benefit), not a required rule.
- User menu:
- Configure via `userMenuItems()` with Action objects.
- Never put destructive actions there without confirmation + authorization.
Sources:
- https://filamentphp.com/docs/5.x/navigation/overview
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/navigation/user-menu
## 5) Resource patterns
- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows.
- Global search:
- If a resource is intended for global search: ensure Edit/View page exists.
- Otherwise disable global search for that resource (dont “expect it to work”).
- If global search renders relationship-backed details: eager-load via global search query override.
- For very large datasets: consider disabling term splitting (only when needed).
Sources:
- https://filamentphp.com/docs/5.x/resources/overview
- https://filamentphp.com/docs/5.x/resources/global-search
## 6) Page lifecycle & query rules
- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading.
- Prefer render hooks for layout injection; avoid publishing internal views.
Sources:
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 7) Infolists vs RelationManagers (decision tree)
- Interactive CRUD / attach / detach under owner record → RelationManager.
- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields.
- Inline CRUD inside owner form → Repeater.
- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists.
Sources:
- https://filamentphp.com/docs/5.x/resources/managing-relationships
- https://filamentphp.com/docs/5.x/infolists/overview
## 8) Form patterns (validation, reactivity, state)
- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side.
- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns).
- Custom field views must obey state binding modifiers.
Sources:
- https://filamentphp.com/docs/5.x/forms/overview
- https://filamentphp.com/docs/5.x/forms/custom-fields
## 9) Table & action patterns
- Tables: always define a meaningful empty state (and empty-state actions where appropriate).
- Actions:
- Execution actions use `->action(...)`.
- Destructive actions add `->requiresConfirmation()`.
- Navigation-only actions should use `->url(...)`.
- UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified.
Sources:
- https://filamentphp.com/docs/5.x/tables/empty-state
- https://filamentphp.com/docs/5.x/actions/modals
## 10) Authorization & security
- Enforce panel access in non-local environments as documented.
- UI visibility is not security; enforce policies/access checks in addition to hiding UI.
- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization.
Sources:
- https://filamentphp.com/docs/5.x/users/overview
- https://filamentphp.com/docs/5.x/resources/deleting-records
## 11) Notifications & UX feedback
- Default to explicit success/error notifications for user-triggered mutations that arent instantly obvious.
- Treat polling as a cost; set intervals intentionally where polling is used.
Sources:
- https://filamentphp.com/docs/5.x/notifications/overview
- https://filamentphp.com/docs/5.x/widgets/stats-overview
## 12) Performance defaults
- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies.
- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing.
Sources:
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/styling/css-hooks
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 13) Testing requirements
- Test pages/relation managers/widgets as Livewire components.
- Test actions using Filaments action testing guidance.
- Do not mount non-Livewire classes in Livewire tests.
Sources:
- https://filamentphp.com/docs/5.x/testing/overview
- https://filamentphp.com/docs/5.x/testing/testing-actions
## 14) Forbidden patterns
- Mixing Filament v3/v4 APIs into v5 code.
- Any mention of Livewire v3 for Filament v5.
- Registering panel providers in `bootstrap/app.php` on Laravel 11+.
- Destructive actions without `->requiresConfirmation()`.
- Shipping heavy assets globally when on-demand loading fits.
- Publishing Filament internal views as a default customization technique.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/assets
## 15) Agent output contract
For any implementation request, the agent must explicitly state:
1) Livewire v4.0+ compliance.
2) Provider registration location (Laravel 11+: `bootstrap/providers.php`).
3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled).
4) Which actions are destructive and how confirmation + authorization is handled.
5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy.
6) Testing plan: which pages/widgets/relation managers/actions are covered.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/testing/testing-actions

View File

@ -1,79 +0,0 @@
# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES)
## Version Safety
- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere).
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire”
- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs).
- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps.
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements”
## Panel & Navigation
- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel”
- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path”
- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments).
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster”
- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction”
- [ ] Cluster directory structure is treated as recommended, not mandatory.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters”
- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed.
- Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction”
## Resource Structure
- [ ] `$recordTitleAttribute` is set for any resource intended for global search.
- Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles”
- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles”
- [ ] Relationship-backed global search details are eager-loaded via the global search query override.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results”
## Infolists & Relations
- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job”
- [ ] RelationManagers remain lazy-loaded by default unless theres an explicit UX justification.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading”
## Forms
- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily.
- Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur”
- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers).
- Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers”
## Tables & Actions
- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate).
- Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions”
- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`.
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact).
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
## Authorization & Security
- [ ] Panel access is enforced for non-local environments as documented.
- Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel”
- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries.
- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required.
- Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization”
## UX & Notifications
- [ ] User-triggered mutations provide explicit success/error notifications when outcomes arent instantly obvious.
- Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction”
- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load.
- Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)”
## Performance
- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript”
- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default).
- Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes”
## Testing
- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes.
- Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?”
- [ ] Actions that mutate data are covered using Filaments action testing guidance.
- Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions”
## Deployment / Ops
- [ ] `php artisan filament:assets` is included in the deployment process when using registered assets.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade”

View File

@ -1,4 +0,0 @@
[mcp_servers.laravel-boost]
command = "./scripts/platform-sail"
args = ["artisan", "boost:mcp"]
cwd = "/Users/ahmeddarrazi/Documents/projects/wt-plattform"

View File

@ -1,76 +0,0 @@
---
description: Scan the TenantPilot repository for architecture and safety violations against the workspace-first, RBAC-first, audit-first governance model.
---
You are a Senior Staff Engineer and Enterprise SaaS Architecture Auditor reviewing TenantPilot / TenantAtlas.
This is not a generic code review. Audit the repository against the TenantPilot audit constitution at `docs/audits/tenantpilot-architecture-audit-constitution.md`.
## Audit focus
Prioritize:
- workspace and tenant isolation
- route model binding safety
- Filament resources, pages, relation managers, widgets, and actions
- Livewire public properties and serialized state risks
- jobs, queue boundaries, and backend authorization rechecks
- provider access boundaries
- `OperationRun` consistency
- findings, exceptions, review, drift, and baseline workflow integrity
- audit trail completeness
- wrong-tenant regression coverage
- unauthorized action coverage
- workflow misuse and invalid transition coverage
## Output rules
Classify every finding as exactly one of:
- Constitutional Violation
- Architectural Drift
- Workflow Trust Gap
- Test Blind Spot
Assign one severity:
- Severity 1: Critical
- Severity 2: High
- Severity 3: Medium
- Severity 4: Low
Anything directly touching isolation, RBAC, secrets, or auditability must not be rated Low.
For each finding provide:
1. Title
2. Classification
3. Severity
4. Affected Area
5. Evidence with specific files, classes, methods, routes, or test gaps
6. Why this matters in TenantPilot
7. Recommended structural correction
8. Delivery recommendation: `hotfix`, `follow-up refactor`, or `dedicated spec required`
## Constraints
- Do not praise the codebase.
- Do not focus on style unless it affects architecture or safety.
- Do not suggest random patterns without proving fit.
- Group multiple symptoms under one deeper diagnosis when appropriate.
- Be explicit when a local fix is insufficient and a dedicated spec is required.
## Repository context
TenantPilot is an enterprise SaaS product for Intune and Microsoft 365 governance, backup, restore, inventory, drift detection, findings, exceptions, operations, and auditability.
The strategic priorities are:
- workspace-first context modeling
- capability-first RBAC
- strong auditability
- deterministic workflow semantics
- provider access through canonical boundaries
- minimal duplication of domain logic across UI surfaces
Return the audit as a concise but substantive findings report.

View File

@ -1,104 +0,0 @@
---
description: Turn TenantPilot architecture audit findings into bounded spec candidates without colliding with active spec numbering.
---
You are a Senior Staff Engineer and Enterprise SaaS Architect working on TenantPilot / TenantAtlas.
Your task is to produce spec candidates, not implementation code.
Before writing anything, read and use these repository files as binding context:
- `docs/audits/tenantpilot-architecture-audit-constitution.md`
- `docs/audits/2026-03-15-audit-spec-candidates.md`
- `specs/110-ops-ux-enforcement/spec.md`
- `specs/111-findings-workflow-sla/spec.md`
- `specs/134-audit-log-foundation/spec.md`
- `specs/138-managed-tenant-onboarding-draft-identity/spec.md`
## Goal
Turn the existing audit-derived problem clusters into exactly four proposed follow-up spec candidates.
The four candidate themes are:
1. queued execution reauthorization and scope continuity
2. tenant-owned query canon and wrong-tenant guards
3. findings workflow enforcement and audit backstop
4. Livewire context locking and trusted-state reduction
## Numbering rule
- Do not invent or reserve fixed spec numbers unless the current repository state proves they are available.
- If numbering is uncertain, use `Candidate A`, `Candidate B`, `Candidate C`, and `Candidate D`.
- Only recommend a numbering strategy; do not force numbering in the output when collisions are possible.
## Output requirements
Create exactly four spec candidates, one per problem class.
For each candidate provide:
1. Candidate label or confirmed spec number
2. Working title
3. Status: `Proposed`
4. Summary
5. Why this is needed now
6. Boundary to existing specs
7. Problem statement
8. Goals
9. Non-goals
10. Scope
11. Target model
12. Key requirements
13. Risks if not implemented
14. Dependencies and sequencing notes
15. Delivery recommendation: `hotfix`, `dedicated spec`, or `phased spec`
16. Suggested implementation priority: `Critical`, `High`, or `Medium`
17. Suggested slug
At the end provide:
A. Recommended implementation order
B. Which candidates can run in parallel
C. Which candidate should start first and why
D. A numbering strategy recommendation if active spec numbers are not yet known
## Writing rules
- Write in English.
- Use formal enterprise spec language.
- Be concrete and opinionated.
- Focus on structural integrity, not patch-level fixes.
- Treat the audit constitution as binding.
- Explicitly say when UI-only authorization is insufficient.
- Explicitly say when Livewire public state must be treated as untrusted input.
- Explicitly say when negative-path regression tests are required.
- Explicitly say when `OperationRun` or audit semantics must be extended or hardened.
- Do not duplicate adjacent specs; state the boundary clearly.
- Do not collapse all four themes into one umbrella spec.
## Candidate-specific direction
### Candidate A — queued execution reauthorization and scope continuity
- Treat this as an execution trust problem, not a simple `authorize()` omission.
- Cover dispatch-time actor and context capture, handle-time scope revalidation, capability reauthorization, execution denial semantics, and audit visibility.
- Define what happens when authorization or tenant operability changes between dispatch and execution.
### Candidate B — tenant-owned query canon and wrong-tenant guards
- Treat this as canonical data-access hardening.
- Cover tenant-owned and workspace-owned query rules, route model binding safety, canonical query paths, anti-pattern elimination, and required wrong-tenant regression tests.
- Focus on ownership enforcement, not generic repository-pattern advice.
### Candidate C — findings workflow enforcement and audit backstop
- Treat this as a workflow-truth problem.
- Cover formal lifecycle enforcement, invalid transition prevention, reopen and recurrence semantics, and audit backstop requirements.
- Make clear how this extends but does not duplicate Spec 111.
### Candidate D — Livewire context locking and trusted-state reduction
- Treat this as a UI/server trust-boundary hardening problem.
- Cover locked identifiers, untrusted public state, server-side reconstruction of workflow truth, sensitive-state reduction, and misuse regression tests.
- Make clear how this complements but does not duplicate Spec 138.

View File

@ -1,44 +1,16 @@
node_modules/
apps/platform/node_modules/
apps/website/node_modules/
apps/website/.astro/
apps/website/dist/
apps/website/playwright-report/
apps/website/test-results/
apps/website/blob-report/
dist/
build/
vendor/
apps/platform/vendor/
coverage/
.git/
.DS_Store
Thumbs.db
.env
.env.*
*.log
*.log*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
Dockerfile*
.dockerignore
*.tmp
*.swp
public/build/
apps/platform/public/build/
public/hot/
apps/platform/public/hot/
public/storage/
apps/platform/public/storage/
storage/framework/
apps/platform/storage/framework/
storage/logs/
apps/platform/storage/logs/
storage/debugbar/
apps/platform/storage/debugbar/
storage/*.key
apps/platform/storage/*.key
/references/
.idea/
.vscode/

65
.env.example Normal file
View File

@ -0,0 +1,65 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
# PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=tenantatlas
DB_USERNAME=root
DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
# CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

View File

@ -1,50 +0,0 @@
description = "Auto-commit changes after a Spec Kit command completes"
# Source: git
prompt = """
# Auto-Commit Changes
Automatically stage and commit all changes after a Spec Kit command completes.
## Behavior
This command is invoked as a hook after (or before) core commands. It:
1. Determines the event name from the hook context (e.g., if invoked as an `after_specify` hook, the event is `after_specify`; if `before_plan`, the event is `before_plan`)
2. Checks `.specify/extensions/git/git-config.yml` for the `auto_commit` section
3. Looks up the specific event key to see if auto-commit is enabled
4. Falls back to `auto_commit.default` if no event-specific key exists
5. Uses the per-command `message` if configured, otherwise a default message
6. If enabled and there are uncommitted changes, runs `git add .` + `git commit`
## Execution
Determine the event name from the hook that triggered this command, then run the script:
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name>`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name>`
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`).
## Configuration
In `.specify/extensions/git/git-config.yml`:
```yaml
auto_commit:
default: false # Global toggle — set true to enable for all commands
after_specify:
enabled: true # Override per-command
message: "[Spec Kit] Add specification"
after_plan:
enabled: false
message: "[Spec Kit] Add implementation plan"
```
## Graceful Degradation
- If Git is not available or the current directory is not a repository: skips with a warning
- If no config file exists: skips (disabled by default)
- If no changes to commit: skips with a message
"""

View File

@ -1,69 +0,0 @@
description = "Create a feature branch with sequential or timestamp numbering"
# Source: git
prompt = """
# Create Feature Branch
Create and switch to a new git feature branch for the given specification. This command handles **branch creation only** the spec directory and files are created by the core `/speckit.specify` workflow.
## User Input
```text
{{args}}
```
You **MUST** consider the user input before proceeding (if not empty).
## Environment Variable Override
If the user explicitly provided `GIT_BRANCH_NAME` (e.g., via environment variable, argument, or in their request), pass it through to the script by setting the `GIT_BRANCH_NAME` environment variable before invoking the script. When `GIT_BRANCH_NAME` is set:
- The script uses the exact value as the branch name, bypassing all prefix/suffix generation
- `--short-name`, `--number`, and `--timestamp` flags are ignored
- `FEATURE_NUM` is extracted from the name if it starts with a numeric prefix, otherwise set to the full branch name
## Prerequisites
- Verify Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, warn the user and skip branch creation
## Branch Numbering Mode
Determine the branch numbering strategy by checking configuration in this order:
1. Check `.specify/extensions/git/git-config.yml` for `branch_numbering` value
2. Check `.specify/init-options.json` for `branch_numbering` value (backward compatibility)
3. Default to `sequential` if neither exists
## Execution
Generate a concise short name (2-4 words) for the branch:
- Analyze the feature description and extract the most meaningful keywords
- Use action-noun format when possible (e.g., "add-user-auth", "fix-payment-bug")
- Preserve technical terms and acronyms (OAuth2, API, JWT, etc.)
Run the appropriate script based on your platform:
- **Bash**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --short-name "<short-name>" "<feature description>"`
- **Bash (timestamp)**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --timestamp --short-name "<short-name>" "<feature description>"`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -ShortName "<short-name>" "<feature description>"`
- **PowerShell (timestamp)**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -Timestamp -ShortName "<short-name>" "<feature description>"`
**IMPORTANT**:
- Do NOT pass `--number` the script determines the correct next number automatically
- Always include the JSON flag (`--json` for Bash, `-Json` for PowerShell) so the output can be parsed reliably
- You must only ever run this script once per feature
- The JSON output will contain `BRANCH_NAME` and `FEATURE_NUM`
## Graceful Degradation
If Git is not installed or the current directory is not a Git repository:
- Branch creation is skipped with a warning: `[specify] Warning: Git repository not detected; skipped branch creation`
- The script still outputs `BRANCH_NAME` and `FEATURE_NUM` so the caller can reference them
## Output
The script outputs JSON with:
- `BRANCH_NAME`: The branch name (e.g., `003-user-auth` or `20260319-143022-user-auth`)
- `FEATURE_NUM`: The numeric or timestamp prefix used
"""

View File

@ -1,51 +0,0 @@
description = "Initialize a Git repository with an initial commit"
# Source: git
prompt = """
# Initialize Git Repository
Initialize a Git repository in the current project directory if one does not already exist.
## Execution
Run the appropriate script from the project root:
- **Bash**: `.specify/extensions/git/scripts/bash/initialize-repo.sh`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/initialize-repo.ps1`
If the extension scripts are not found, fall back to:
- **Bash**: `git init && git add . && git commit -m "Initial commit from Specify template"`
- **PowerShell**: `git init; git add .; git commit -m "Initial commit from Specify template"`
The script handles all checks internally:
- Skips if Git is not available
- Skips if already inside a Git repository
- Runs `git init`, `git add .`, and `git commit` with an initial commit message
## Customization
Replace the script to add project-specific Git initialization steps:
- Custom `.gitignore` templates
- Default branch naming (`git config init.defaultBranch`)
- Git LFS setup
- Git hooks installation
- Commit signing configuration
- Git Flow initialization
## Output
On success:
- ` Git repository initialized`
## Graceful Degradation
If Git is not installed:
- Warn the user
- Skip repository initialization
- The project continues to function without Git (specs can still be created under `specs/`)
If Git is installed but `git init`, `git add .`, or `git commit` fails:
- Surface the error to the user
- Stop this command rather than continuing with a partially initialized repository
"""

View File

@ -1,47 +0,0 @@
description = "Detect Git remote URL for GitHub integration"
# Source: git
prompt = """
# Detect Git Remote URL
Detect the Git remote URL for integration with GitHub services (e.g., issue creation).
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and return empty:
```
[specify] Warning: Git repository not detected; cannot determine remote URL
```
## Execution
Run the following command to get the remote URL:
```bash
git config --get remote.origin.url
```
## Output
Parse the remote URL and determine:
1. **Repository owner**: Extract from the URL (e.g., `github` from `https://github.com/github/spec-kit.git`)
2. **Repository name**: Extract from the URL (e.g., `spec-kit` from `https://github.com/github/spec-kit.git`)
3. **Is GitHub**: Whether the remote points to a GitHub repository
Supported URL formats:
- HTTPS: `https://github.com/<owner>/<repo>.git`
- SSH: `git@github.com:<owner>/<repo>.git`
> [!CAUTION]
> ONLY report a GitHub repository if the remote URL actually points to github.com.
> Do NOT assume the remote is GitHub if the URL format doesn't match.
## Graceful Degradation
If Git is not installed, the directory is not a Git repository, or no remote is configured:
- Return an empty result
- Do NOT error other workflows should continue without Git remote information
"""

View File

@ -1,51 +0,0 @@
description = "Validate current branch follows feature branch naming conventions"
# Source: git
prompt = """
# Validate Feature Branch
Validate that the current Git branch follows the expected feature branch naming conventions.
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and skip validation:
```
[specify] Warning: Git repository not detected; skipped branch validation
```
## Validation Rules
Get the current branch name:
```bash
git rev-parse --abbrev-ref HEAD
```
The branch name must match one of these patterns:
1. **Sequential**: `^[0-9]{3,}-` (e.g., `001-feature-name`, `042-fix-bug`, `1000-big-feature`)
2. **Timestamp**: `^[0-9]{8}-[0-9]{6}-` (e.g., `20260319-143022-feature-name`)
## Execution
If on a feature branch (matches either pattern):
- Output: ` On feature branch: <branch-name>`
- Check if the corresponding spec directory exists under `specs/`:
- For sequential branches, look for `specs/<prefix>-*` where prefix matches the numeric portion
- For timestamp branches, look for `specs/<prefix>-*` where prefix matches the `YYYYMMDD-HHMMSS` portion
- If spec directory exists: ` Spec directory found: <path>`
- If spec directory missing: ` No spec directory found for prefix <prefix>`
If NOT on a feature branch:
- Output: ` Not on a feature branch. Current branch: <branch-name>`
- Output: `Feature branches should be named like: 001-feature-name or 20260319-143022-feature-name`
## Graceful Degradation
If Git is not installed or the directory is not a Git repository:
- Check the `SPECIFY_FEATURE` environment variable as a fallback
- If set, validate that value against the naming patterns
- If not set, skip validation with a warning
"""

View File

@ -1,14 +1,5 @@
{
"general": {
"previewFeatures": false
},
"mcpServers": {
"laravel-boost": {
"command": "vendor/bin/sail",
"args": [
"artisan",
"boost:mcp"
]
}
}
"general": {
"previewFeatures": false
}
}

View File

@ -1,80 +0,0 @@
name: Browser Lane
on:
workflow_dispatch:
schedule:
- cron: '43 4 * * 1-5'
permissions:
actions: read
contents: read
jobs:
browser:
if: ${{ github.event_name != 'schedule' || vars.TENANTATLAS_ENABLE_BROWSER_SCHEDULE == '1' }}
runs-on: ubuntu-latest
env:
SAIL_TTY: 'false'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
tools: composer:v2
- name: Install platform dependencies
run: |
cd apps/platform
if [[ ! -f .env ]]; then
cp .env.example .env
fi
composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Boot Sail
run: |
cd apps/platform
./vendor/bin/sail up -d
./vendor/bin/sail artisan key:generate --force --no-interaction
- name: Resolve Browser context
id: context
run: |
if [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "workflow_id=browser-scheduled" >> "$GITHUB_OUTPUT"
echo "trigger_class=scheduled" >> "$GITHUB_OUTPUT"
else
echo "workflow_id=browser-manual" >> "$GITHUB_OUTPUT"
echo "trigger_class=manual" >> "$GITHUB_OUTPUT"
fi
- name: Run Browser lane
run: ./scripts/platform-test-lane browser --workflow-id=${{ steps.context.outputs.workflow_id }} --trigger-class=${{ steps.context.outputs.trigger_class }}
- name: Refresh Browser report
if: always()
env:
TENANTATLAS_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: ./scripts/platform-test-report browser --workflow-id=${{ steps.context.outputs.workflow_id }} --trigger-class=${{ steps.context.outputs.trigger_class }} --fetch-latest-history
- name: Stage Browser artifacts
if: always()
run: ./scripts/platform-test-artifacts browser .gitea-artifacts/browser --workflow-id=${{ steps.context.outputs.workflow_id }} --trigger-class=${{ steps.context.outputs.trigger_class }}
- name: Upload Browser artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: browser-artifacts
path: .gitea-artifacts/browser
if-no-files-found: error
- name: Stop Sail
if: always()
run: |
cd apps/platform
./vendor/bin/sail stop

View File

@ -1,80 +0,0 @@
name: Heavy Governance Lane
on:
workflow_dispatch:
schedule:
- cron: '17 4 * * 1-5'
permissions:
actions: read
contents: read
jobs:
heavy-governance:
if: ${{ github.event_name != 'schedule' || vars.TENANTATLAS_ENABLE_HEAVY_GOVERNANCE_SCHEDULE == '1' }}
runs-on: ubuntu-latest
env:
SAIL_TTY: 'false'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
tools: composer:v2
- name: Install platform dependencies
run: |
cd apps/platform
if [[ ! -f .env ]]; then
cp .env.example .env
fi
composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Boot Sail
run: |
cd apps/platform
./vendor/bin/sail up -d
./vendor/bin/sail artisan key:generate --force --no-interaction
- name: Resolve Heavy Governance context
id: context
run: |
if [[ "${{ github.event_name }}" == "schedule" ]]; then
echo "workflow_id=heavy-governance-scheduled" >> "$GITHUB_OUTPUT"
echo "trigger_class=scheduled" >> "$GITHUB_OUTPUT"
else
echo "workflow_id=heavy-governance-manual" >> "$GITHUB_OUTPUT"
echo "trigger_class=manual" >> "$GITHUB_OUTPUT"
fi
- name: Run Heavy Governance lane
run: ./scripts/platform-test-lane heavy-governance --workflow-id=${{ steps.context.outputs.workflow_id }} --trigger-class=${{ steps.context.outputs.trigger_class }}
- name: Refresh Heavy Governance report
if: always()
env:
TENANTATLAS_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: ./scripts/platform-test-report heavy-governance --workflow-id=${{ steps.context.outputs.workflow_id }} --trigger-class=${{ steps.context.outputs.trigger_class }} --fetch-latest-history
- name: Stage Heavy Governance artifacts
if: always()
run: ./scripts/platform-test-artifacts heavy-governance .gitea-artifacts/heavy-governance --workflow-id=${{ steps.context.outputs.workflow_id }} --trigger-class=${{ steps.context.outputs.trigger_class }}
- name: Upload Heavy Governance artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: heavy-governance-artifacts
path: .gitea-artifacts/heavy-governance
if-no-files-found: error
- name: Stop Sail
if: always()
run: |
cd apps/platform
./vendor/bin/sail stop

View File

@ -1,68 +0,0 @@
name: Main Confidence
on:
push:
branches:
- dev
permissions:
actions: read
contents: read
jobs:
confidence:
runs-on: ubuntu-latest
env:
SAIL_TTY: 'false'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
tools: composer:v2
- name: Install platform dependencies
run: |
cd apps/platform
if [[ ! -f .env ]]; then
cp .env.example .env
fi
composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Boot Sail
run: |
cd apps/platform
./vendor/bin/sail up -d
./vendor/bin/sail artisan key:generate --force --no-interaction
- name: Run Confidence lane
run: ./scripts/platform-test-lane confidence --workflow-id=main-confidence --trigger-class=mainline-push
- name: Refresh Confidence report
if: always()
env:
TENANTATLAS_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: ./scripts/platform-test-report confidence --workflow-id=main-confidence --trigger-class=mainline-push --fetch-latest-history
- name: Stage Confidence artifacts
if: always()
run: ./scripts/platform-test-artifacts confidence .gitea-artifacts/main-confidence --workflow-id=main-confidence --trigger-class=mainline-push
- name: Upload Confidence artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: confidence-artifacts
path: .gitea-artifacts/main-confidence
if-no-files-found: error
- name: Stop Sail
if: always()
run: |
cd apps/platform
./vendor/bin/sail stop

View File

@ -1,70 +0,0 @@
name: PR Fast Feedback
on:
pull_request:
types:
- opened
- reopened
- synchronize
permissions:
actions: read
contents: read
jobs:
fast-feedback:
runs-on: ubuntu-latest
env:
SAIL_TTY: 'false'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
tools: composer:v2
- name: Install platform dependencies
run: |
cd apps/platform
if [[ ! -f .env ]]; then
cp .env.example .env
fi
composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Boot Sail
run: |
cd apps/platform
./vendor/bin/sail up -d
./vendor/bin/sail artisan key:generate --force --no-interaction
- name: Run Fast Feedback lane
run: ./scripts/platform-test-lane fast-feedback --workflow-id=pr-fast-feedback --trigger-class=pull-request
- name: Refresh Fast Feedback report
if: always()
env:
TENANTATLAS_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: ./scripts/platform-test-report fast-feedback --workflow-id=pr-fast-feedback --trigger-class=pull-request --fetch-latest-history
- name: Stage Fast Feedback artifacts
if: always()
run: ./scripts/platform-test-artifacts fast-feedback .gitea-artifacts/pr-fast-feedback --workflow-id=pr-fast-feedback --trigger-class=pull-request
- name: Upload Fast Feedback artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: fast-feedback-artifacts
path: .gitea-artifacts/pr-fast-feedback
if-no-files-found: error
- name: Stop Sail
if: always()
run: |
cd apps/platform
./vendor/bin/sail stop

View File

@ -2,318 +2,31 @@ # TenantAtlas Development Guidelines
Auto-generated from all feature plans. Last updated: 2025-12-22
## Relocation override
- The authoritative Laravel application root is `apps/platform`.
- Human-facing commands should use `cd apps/platform && ...`.
- Repo-root tooling may delegate via `./scripts/platform-sail` when it cannot set a nested working directory.
- Repo-root JavaScript orchestration uses `corepack pnpm install`, `corepack pnpm dev:platform`, `corepack pnpm dev:website`, `corepack pnpm dev`, `corepack pnpm build:website`, and `corepack pnpm build:platform`.
- `corepack pnpm dev:platform` starts the platform Sail stack and the Laravel panel Vite watcher. `corepack pnpm dev` starts that platform watcher plus the website dev server.
- `apps/website` is a standalone Astro app, not a second Laravel runtime, so Boost MCP remains platform-only.
- If any generated technology note below conflicts with the current repo, trust `apps/platform/composer.json`, `apps/platform/package.json`, and the live Laravel application metadata over stale generated entries.
## Active Technologies
- PHP 8.4.15 + Laravel 12, Filament v4, Livewire v3 (feat/005-bulk-operations)
- PostgreSQL (app), SQLite in-memory (tests) (feat/005-bulk-operations)
- PostgreSQL (Sail locally) (feat/032-backup-scheduling-mvp)
- PHP 8.4.x + Laravel 12, Filament v4, Livewire v3 (feat/042-inventory-dependencies-graph)
- PostgreSQL (JSONB) (feat/042-inventory-dependencies-graph)
- PHP 8.4.x (Laravel 12) + Laravel 12, Filament v4, Livewire v3 (feat/047-inventory-foundations-nodes)
- PostgreSQL (JSONB for `InventoryItem.meta_jsonb`) (feat/047-inventory-foundations-nodes)
- PostgreSQL (JSONB in `operation_runs.context`, `operation_runs.summary_counts`) (056-remove-legacy-bulkops)
- PHP 8.4.15 (Laravel 12.47.0) + Filament v5.0.0, Livewire v4.0.1 (058-tenant-ui-polish)
- PHP 8.4 (per repo guidelines) + Laravel 12, Filament v5, Livewire v4 (067-rbac-troubleshooting)
- PostgreSQL (via Laravel Sail) (067-rbac-troubleshooting)
- PHP 8.4.x (Composer constraint: `^8.2`) + Laravel 12, Filament 5, Livewire 4+, Pest 4, Sail 1.x (073-unified-managed-tenant-onboarding-wizard)
- PostgreSQL (Sail) + SQLite in tests where applicable (073-unified-managed-tenant-onboarding-wizard)
- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, Filament Infolists (schema-based) (078-operations-tenantless-canonical)
- PostgreSQL (no new migrations — read-only model changes) (078-operations-tenantless-canonical)
- PHP 8.4.15 (Laravel 12) + Filament v5, Livewire v4, Tailwind v4 (080-workspace-managed-tenant-admin)
- PostgreSQL (via Sail) (080-workspace-managed-tenant-admin)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Socialite v5 (081-provider-connection-cutover)
- PHP 8.4.x + Laravel 12, Filament v5, Livewire v4 (082-action-surface-contract)
- PHP 8.4 (Laravel 12) + Filament v5 (Livewire v4), Queue/Jobs (Laravel), Microsoft Graph via `GraphClientInterface` (084-verification-surfaces-unification)
- PostgreSQL (JSONB-backed `OperationRun.context`) (084-verification-surfaces-unification)
- PHP 8.4.15 (Laravel 12) + Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, Laravel Sail (085-tenant-operate-hub)
- PostgreSQL (primary) + session (workspace context + last-tenant memory) (085-tenant-operate-hub)
- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, Laravel Sail, Tailwind CSS v4 (085-tenant-operate-hub)
- PostgreSQL (Sail), SQLite in tests (087-legacy-runs-removal)
- PHP 8.4.x + Laravel 12, Filament v5, Livewire v4, Microsoft Graph integration via `GraphClientInterface` (095-graph-contracts-registry-completeness)
- PHP 8.4.15 (Laravel 12) + Filament v5, Livewire v4, Laravel Queue, Laravel Notifications (100-alert-target-test-actions)
- PostgreSQL (Sail locally); SQLite is used in some tests (101-golden-master-baseline-governance-v1)
- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, `OperateHubShell` support class (103-ia-scope-filter-semantics)
- PostgreSQL — no schema changes (103-ia-scope-filter-semantics)
- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, Pest v4 (104-provider-permission-posture)
- PostgreSQL (via Sail), JSONB for stored report payloads and finding evidence (104-provider-permission-posture)
- PHP 8.4 / Laravel 12 + Filament v5, Livewire v4, Tailwind CSS v4 (107-workspace-chooser)
- PostgreSQL (existing tables: `workspaces`, `workspace_memberships`, `users`, `audit_logs`) (107-workspace-chooser)
- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, Laravel Framework v12 (109-review-pack-export)
- PostgreSQL (jsonb columns for summary/options), local filesystem (`exports` disk) for ZIP artifacts (109-review-pack-export)
- PHP 8.4 + Laravel 12, Filament v5, Livewire v4 (116-baseline-drift-engine)
- PHP 8.4, Laravel 12, Filament v5, Livewire v4 + Laravel framework, Filament admin panels, Livewire, PostgreSQL JSONB persistence, Laravel Sail (120-secret-redaction-integrity)
- PostgreSQL (`policy_versions`, `operation_runs`, `audit_logs`, related evidence tables) (120-secret-redaction-integrity)
- PHP 8.4.15 / Laravel 12 + Filament v5 + Livewire v4.0+ + Tailwind CSS v4 (121-workspace-switch-fix)
- PostgreSQL + session-backed workspace context; no schema changes (121-workspace-switch-fix)
- PHP 8.4.15 / Laravel 12 + Filament v5, Livewire v4.0+, Tailwind CSS v4, Pest v4 (122-empty-state-consistency)
- PostgreSQL + existing workspace/tenant session context; no schema changes (122-empty-state-consistency)
- PHP 8.4 runtime target on Laravel 12 code conventions; Composer constraint `php:^8.2` + Laravel 12, Filament v5.2.1, Livewire v4, Pest v4, Laravel Sail (123-operations-auto-refresh)
- PostgreSQL primary app database (123-operations-auto-refresh)
- PHP 8.4.15 + Laravel 12, Filament 5, Livewire 4, Tailwind CSS 4, existing `CoverageCapabilitiesResolver`, `InventoryPolicyTypeMeta`, `BadgeCatalog`, and `TagBadgeCatalog` (124-inventory-coverage-table)
- N/A for this feature; page remains read-only and uses registry/config-derived runtime data while PostgreSQL remains unchanged (124-inventory-coverage-table)
- PHP 8.4.15 + Laravel 12, Filament 5, Livewire 4, Tailwind CSS 4, existing `BadgeCatalog` / `BadgeRenderer`, existing UI enforcement helpers, existing Filament resources, relation managers, widgets, and Livewire table components (125-table-ux-standardization)
- PostgreSQL remains unchanged; this feature is presentation-layer and behavior-layer only (125-table-ux-standardization)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4.0+, Tailwind CSS v4, Pest v4, existing `BadgeCatalog` / `BadgeRenderer`, existing `TagBadgeCatalog` / `TagBadgeRenderer`, existing Filament resource tables (126-filter-ux-standardization)
- PostgreSQL remains unchanged; session persistence uses Filament-native session keys and existing workspace/tenant contex (126-filter-ux-standardization)
- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, Laravel Sail, Microsoft Graph provider stack (127-rbac-inventory-backup)
- PostgreSQL for tenant-owned inventory, backup items, versions, verification outcomes, and operation runs (127-rbac-inventory-backup)
- PostgreSQL via Laravel Sail (128-rbac-baseline-compare)
- PostgreSQL via Laravel Sail plus session-backed workspace and tenant contex (129-workspace-admin-home)
- PostgreSQL via Laravel Sail using existing `baseline_snapshots`, `baseline_snapshot_items`, and JSONB presentation source fields (130-structured-snapshot-rendering)
- PostgreSQL via Laravel Sail, plus existing session-backed workspace and tenant contex (131-cross-resource-navigation)
- PostgreSQL via Laravel Sail plus existing workspace and tenant context, existing Eloquent relations, and provider-derived identifiers already stored in domain records (132-guid-context-resolver)
- PostgreSQL via Laravel Sail; no schema change expected (133-detail-page-template)
- PostgreSQL via Laravel Sail; existing `audit_logs` table expanded in place; JSON context payload remains application-shaped rather than raw archival payloads (134-audit-log-foundation)
- PHP 8.4 on Laravel 12 + Filament v5, Livewire v4, Pest v4, Laravel Sail (135-canonical-tenant-context-resolution)
- PostgreSQL application database (135-canonical-tenant-context-resolution)
- PostgreSQL application database and session-backed Filament table state (136-admin-canonical-tenant)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Laravel Sail, Pest v4, PHPUnit v12 (137-platform-provider-identity)
- PostgreSQL via Laravel migrations and encrypted model casts (137-platform-provider-identity)
- PHP 8.4 (Laravel 12) + Filament v5 (Livewire v4), Laravel Blade, existing onboarding/verification support classes (139-verify-access-permissions-assist)
- PostgreSQL; existing JSON-backed onboarding draft state and `OperationRun.context.verification_report` (139-verify-access-permissions-assist)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, PostgreSQL, Laravel Sail, Pest v4, existing `OperationRunService`, `ProviderOperationStartGate`, onboarding services, workspace audit logging (140-onboarding-lifecycle-operation-checkpoints-concurrency-mvp)
- PostgreSQL tables including `managed_tenant_onboarding_sessions`, `operation_runs`, `tenants`, and provider-connection-backed tenant records (140-onboarding-lifecycle-operation-checkpoints-concurrency-mvp)
- PostgreSQL via Laravel Sail for existing source records and JSON payloads; no new persistence introduced (141-shared-diff-presentation-foundation)
- PHP 8.4.15 / Laravel 12 + Filament v5, Livewire v4.0+, Tailwind CSS v4, shared `App\Support\Diff` foundation from Spec 141 (142-rbac-role-definition-diff-ux-upgrade)
- PostgreSQL via Laravel Sail for existing `findings.evidence_jsonb`; no schema or persistence changes (142-rbac-role-definition-diff-ux-upgrade)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4 (143-tenant-lifecycle-operability-context-semantics)
- PostgreSQL via Laravel Eloquent models and workspace/tenant scoped tables (143-tenant-lifecycle-operability-context-semantics)
- PHP 8.4 (Laravel 12) + Filament v5, Livewire v4, Laravel Gates and Policies, `OperateHubShell`, `OperationRunLinks` (144-canonical-operation-viewer-context-decoupling)
- PostgreSQL plus session-backed workspace and remembered tenant context (no schema changes) (144-canonical-operation-viewer-context-decoupling)
- PHP 8.4.15 with Laravel 12, Filament v5, Livewire v4.0+ + Filament Actions/Tables/Infolists, Laravel Gates/Policies, `UiEnforcement`, `WorkspaceUiEnforcement`, `ActionSurfaceDeclaration`, `BadgeCatalog`, `TenantOperabilityService`, `OnboardingLifecycleService` (145-tenant-action-taxonomy-lifecycle-safe-visibility)
- PostgreSQL for tenants, onboarding sessions, audit logs, operation runs, and workspace membership data (145-tenant-action-taxonomy-lifecycle-safe-visibility)
- PostgreSQL (existing tenant and operation records only; no schema changes planned) (146-central-tenant-status-presentation)
- PostgreSQL plus existing session-backed workspace and remembered-tenant context; no schema change planned (147-tenant-selector-remembered-context-enforcement)
- PHP 8.4.15 + Laravel 12, Filament 5, Livewire 4, Pest 4, existing support-layer helpers such as `UiEnforcement`, `CapabilityResolver`, `WorkspaceContext`, `OperateHubShell`, `TenantOperabilityService`, and `TenantActionPolicySurface` (148-central-tenant-operability-policy)
- PostgreSQL plus existing session-backed workspace and remembered-tenant context; no schema change planned for the first implementation slice (148-central-tenant-operability-policy)
- PHP 8.4.15 + Laravel 12, Filament 5, Livewire 4, Pest 4, existing `OperationRunService`, `TrackOperationRun`, `ProviderOperationStartGate`, `TenantOperabilityService`, `CapabilityResolver`, and `WriteGateInterface` seams (149-queued-execution-reauthorization)
- PostgreSQL-backed application data plus queue-serialized `OperationRun` context; no schema migration planned for the first implementation slice (149-queued-execution-reauthorization)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, PostgreSQL, Pest 4 (150-tenant-owned-query-canon-and-wrong-tenant-guards)
- PostgreSQL with existing `findings` and `audit_logs` tables; no new storage engine or external log store (151-findings-workflow-backstop)
- PostgreSQL with existing workspace-, tenant-, onboarding-, and audit-related tables; no new persistent storage planned for the first slice (152-livewire-context-locking)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `StoredReport`, `Finding`, `OperationRun`, and `AuditLog` infrastructure (153-evidence-domain-foundation)
- PostgreSQL with JSONB-backed snapshot metadata; existing private storage remains a downstream-consumer concern, not a primary evidence-foundation store (153-evidence-domain-foundation)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing Finding, AuditLog, EvidenceSnapshot, CapabilityResolver, WorkspaceCapabilityResolver, and UiEnforcement patterns (001-finding-risk-acceptance)
- PostgreSQL with new tenant-owned exception tables and JSONB-backed supporting metadata (001-finding-risk-acceptance)
- PHP 8.4, Laravel 12, Livewire 4, Filament 5 + Filament resources/pages/actions, Eloquent models, queued Laravel jobs, existing `EvidenceSnapshotService`, existing `ReviewPackService`, capability registry, `OperationRunService` (155-tenant-review-layer)
- PostgreSQL with JSONB-backed summary payloads and tenant/workspace ownership columns (155-tenant-review-layer)
- PostgreSQL-backed existing domain records; no new business-domain table is required for the first slice; shared taxonomy reference will live in repository documentation and code-level metadata (156-operator-outcome-taxonomy)
- PostgreSQL-backed existing records such as `operation_runs`, tenant governance records, onboarding workflow state, and provider connection state; no new business-domain table is required for the first slice (157-reason-code-translation)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `BadgeCatalog` / `BadgeRenderer` / `OperatorOutcomeTaxonomy`, `ReasonPresenter`, `OperationRunService`, `TenantReviewReadinessGate`, existing baseline/evidence/review/review-pack resources and canonical pages (158-artifact-truth-semantics)
- PostgreSQL with existing JSONB-backed `summary`, `summary_jsonb`, and `context` payloads on baseline snapshots, evidence snapshots, tenant reviews, review packs, and operation runs; no new primary storage required for the first slice (158-artifact-truth-semantics)
- PHP 8.4.15 + Laravel 12, Filament 5, Livewire 4, Pest 4, Laravel queue workers, existing `OperationRunService`, `TrackOperationRun`, `OperationUxPresenter`, `ReasonPresenter`, `BadgeCatalog` domain badges, and current Operations Monitoring pages (160-operation-lifecycle-guarantees)
- PostgreSQL for `operation_runs`, `jobs`, and `failed_jobs`; JSONB-backed `context`, `summary_counts`, and `failure_summary`; configuration in `config/queue.php` and `config/tenantpilot.php` (160-operation-lifecycle-guarantees)
- PostgreSQL (via Sail) plus existing read models persisted in application tables (161-operator-explanation-layer)
- PHP 8.4 / Laravel 12, Blade, Alpine via Filament, Tailwind CSS v4 + Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `OperationRun` and baseline compare services (162-baseline-gap-details)
- PostgreSQL with JSONB-backed `operation_runs.context`; no new tables required (162-baseline-gap-details)
- PostgreSQL via existing application tables, especially `operation_runs.context` and baseline snapshot summary JSON (163-baseline-subject-resolution)
- PHP 8.4, Laravel 12, Blade views, Alpine via Filament v5 / Livewire v4 + Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `OperationRunResource`, `TenantlessOperationRunViewer`, `EnterpriseDetailBuilder`, `ArtifactTruthPresenter`, `OperationUxPresenter`, and `SummaryCountsNormalizer` (164-run-detail-hardening)
- PostgreSQL with existing `operation_runs` JSONB-backed `context`, `summary_counts`, and `failure_summary`; no schema change planned (164-run-detail-hardening)
- PHP 8.4, Laravel 12, Blade, Filament v5, Livewire v4 + Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `BaselineCompareStats`, `BaselineCompareExplanationRegistry`, `ReasonPresenter`, `BadgeCatalog` or `BadgeRenderer`, `UiEnforcement`, and `OperationRunLinks` (165-baseline-summary-trust)
- PostgreSQL with existing baseline, findings, and `operation_runs` tables plus JSONB-backed compare context; no schema change planned (165-baseline-summary-trust)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `Finding`, `FindingException`, `FindingRiskGovernanceResolver`, `BadgeCatalog`, `BadgeRenderer`, `FilterOptionCatalog`, and tenant dashboard widgets (166-finding-governance-health)
- PostgreSQL using existing `findings`, `finding_exceptions`, related decision tables, and existing DB-backed summary sources; no schema changes required (166-finding-governance-health)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `ArtifactTruthPresenter`, `OperationUxPresenter`, `RelatedNavigationResolver`, `AppServiceProvider`, `BadgeCatalog`, `BadgeRenderer`, and current Filament resource/page seams (167-derived-state-memoization)
- PostgreSQL unchanged; feature adds no persistence and relies on request-local in-memory state only (167-derived-state-memoization)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `BaselineCompareStats`, `BaselineCompareSummaryAssessor`, `BaselineCompareLanding`, `BaselineCompareNow`, `NeedsAttention`, `BaselineCompareCoverageBanner`, and `RequestScopedDerivedStateStore` from Spec 167 (168-tenant-governance-aggregate-contract)
- PostgreSQL unchanged; no new persistence, cache store, or durable summary artifac (168-tenant-governance-aggregate-contract)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `ActionSurfaceDeclaration`, `ActionSurfaceValidator`, `ActionSurfaceDiscovery`, `ActionSurfaceExemptions`, and Filament Tables / Actions APIs (169-action-surface-v11)
- PostgreSQL unchanged; no new persistence, cache store, queue payload, or durable artifac (169-action-surface-v11)
- PHP 8.4, Laravel 12, Livewire v4, Filament v5 + `laravel/framework`, `filament/filament`, `livewire/livewire`, `pestphp/pest` (170-system-operations-surface-alignment)
- PostgreSQL with existing `operation_runs` and `audit_logs` tables; no schema changes (170-system-operations-surface-alignment)
- PHP 8.4, Laravel 12, Livewire v4, Filament v5, Tailwind CSS v4 + `laravel/framework`, `filament/filament`, `livewire/livewire`, `pestphp/pest` (171-operations-naming-consolidation)
- PostgreSQL with existing `operation_runs`, notification payloads, workspace records, and tenant records; no schema changes (171-operations-naming-consolidation)
- PostgreSQL with existing `operation_runs`, `managed_tenant_onboarding_sessions`, tenant records, and workspace records; no schema changes (172-deferred-operator-surfaces-retrofit)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `TenantDashboard`, `DashboardKpis`, `NeedsAttention`, `BaselineCompareNow`, `RecentDriftFindings`, `RecentOperations`, `TenantGovernanceAggregateResolver`, `BaselineCompareStats`, `BaselineCompareSummaryAssessor`, `FindingResource`, `OperationRunLinks`, and canonical admin Operations page (173-tenant-dashboard-truth-alignment)
- PostgreSQL unchanged; no new persistence, cache store, or durable dashboard summary artifac (173-tenant-dashboard-truth-alignment)
- PHP 8.4, Laravel 12, Filament v5, Livewire v4, Blade + Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `ArtifactTruthPresenter`, `ArtifactTruthEnvelope`, `TenantReviewReadinessGate`, `EvidenceSnapshotService`, `TenantReviewRegisterService`, and current evidence/review/review-pack resources and pages (174-evidence-freshness-publication-trust)
- PostgreSQL with existing `evidence_snapshots`, `evidence_snapshot_items`, `tenant_reviews`, and `review_packs` tables using current summary JSON and timestamps; no schema change planned (174-evidence-freshness-publication-trust)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `WorkspaceOverviewBuilder`, `TenantGovernanceAggregateResolver`, `BaselineCompareStats`, `BaselineCompareSummaryAssessor`, `WorkspaceSummaryStats`, `WorkspaceNeedsAttention`, `WorkspaceRecentOperations`, `FindingResource`, `BaselineCompareLanding`, `EvidenceSnapshotResource`, `TenantReviewResource`, and canonical admin Operations routes (175-workspace-governance-attention)
- PostgreSQL unchanged; no new persistence, cache table, or materialized aggregate is introduced (175-workspace-governance-attention)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `TenantResource`, `ProviderConnectionResource`, `TenantVerificationReport`, `BadgeCatalog`, `BadgeRenderer`, `TenantOperabilityService`, `ProviderConsentStatus`, `ProviderVerificationStatus`, and shared provider-state Blade partials (179-provider-truth-cleanup)
- PostgreSQL unchanged; no new table, column, or persisted artifact is introduced (179-provider-truth-cleanup)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `InventoryItem`, `OperationRun`, `InventoryCoverage`, `InventoryPolicyTypeMeta`, `CoverageCapabilitiesResolver`, `InventoryKpiHeader`, `InventoryCoverage` page, and `OperationRunResource` enterprise-detail stack (177-inventory-coverage-truth)
- PostgreSQL; existing `inventory_items` rows and `operation_runs.context` / `operation_runs.summary_counts` JSONB are reused with no schema change (177-inventory-coverage-truth)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `OperationRun`, `OperationLifecyclePolicy`, `OperationRunFreshnessState`, `OperationUxPresenter`, `OperationRunLinks`, `ActiveRuns`, `StuckRunClassifier`, `WorkspaceOverviewBuilder`, dashboard widgets, workspace widgets, and system ops pages (178-ops-truth-alignment)
- PostgreSQL unchanged; existing `operation_runs` JSONB-backed `context`, `summary_counts`, and `failure_summary`; no schema change (178-ops-truth-alignment)
- PHP 8.4, Laravel 12, Blade, Filament v5, Livewire v4 + Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `RestoreRunResource`, `RestoreService`, `RestoreRiskChecker`, `RestoreDiffGenerator`, `OperationRunResource`, `TenantlessOperationRunViewer`, shared badge infrastructure, and existing RBAC or write-gate helpers (181-restore-safety-integrity)
- PostgreSQL with existing `restore_runs` and `operation_runs` records plus JSON or array-backed `metadata`, `preview`, `results`, and `context`; no schema change planned (181-restore-safety-integrity)
- PHP 8.4, Laravel 12, Blade, Filament v5, Livewire v4 + Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `BackupSetResource`, `BackupItemsRelationManager`, `PolicyVersionResource`, `RestoreRunResource`, `CreateRestoreRun`, `AssignmentBackupService`, `VersionService`, `PolicySnapshotService`, `RestoreRiskChecker`, `BadgeRenderer`, `PolicySnapshotModeBadge`, `EnterpriseDetailBuilder`, and existing RBAC helpers (176-backup-quality-truth)
- PostgreSQL with existing tenant-owned `backup_sets`, `backup_items`, `policy_versions`, and restore wizard input state; JSON-backed `metadata`, `snapshot`, `assignments`, and `scope_tags`; no schema change planned (176-backup-quality-truth)
- PHP 8.4, Laravel 12, Blade, Filament v5, Livewire v4 + Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `DashboardKpis`, `NeedsAttention`, `BackupSetResource`, `BackupScheduleResource`, `BackupQualityResolver`, `BackupQualitySummary`, `ScheduleTimeService`, shared badge infrastructure, and existing RBAC helpers (180-tenant-backup-health)
- PostgreSQL with existing tenant-owned `backup_sets`, `backup_items`, and `backup_schedules` records plus existing JSON-backed backup metadata; no schema change planned (180-tenant-backup-health)
- PHP 8.4.15, Laravel 12, Blade, Livewire v4, Filament v5.2.x, Tailwind CSS v4, Vite 7 + `laravel/framework`, `filament/filament`, `livewire/livewire`, `laravel/sail`, `laravel-vite-plugin`, `tailwindcss`, `vite`, `pestphp/pest`, `drizzle-kit`, PostgreSQL, Redis, Docker Compose (182-platform-relocation)
- PostgreSQL, Redis, filesystem storage under the Laravel app `storage/` tree, plus existing Vite build artifacts in `public/build`; no new database persistence planned (182-platform-relocation)
- PHP 8.4.15 and Laravel 12 for `apps/platform`; Node.js 20+ with pnpm 10 workspace tooling; Astro v6 for `apps/website`; Bash and Docker Compose for root orchestration + `laravel/framework`, `filament/filament`, `livewire/livewire`, `laravel/sail`, `vite`, `tailwindcss`, `pnpm` workspaces, Astro, existing `./scripts/platform-sail` wrapper, repo-root Docker Compose (183-website-workspace-foundation)
- Existing PostgreSQL, Redis, and filesystem storage for `apps/platform`; static build artifacts for `apps/website`; repository-managed workspace manifests and docs; no new database persistence (183-website-workspace-foundation)
- PHP 8.4, Laravel 12, Blade, Filament v5, Livewire v4 + Filament v5 widgets and resources, Livewire v4, Pest v4, existing `TenantDashboard`, `DashboardKpis`, `NeedsAttention`, `TenantBackupHealthResolver`, `TenantBackupHealthAssessment`, `RestoreRunResource`, `RestoreSafetyResolver`, `RestoreResultAttention`, `OperationRunLinks`, and existing RBAC helpers (184-dashboard-recovery-honesty)
- PostgreSQL with existing tenant-owned `backup_sets`, `restore_runs`, and linked `operation_runs`; no schema change planned (184-dashboard-recovery-honesty)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `WorkspaceOverviewBuilder`, `WorkspaceSummaryStats`, `WorkspaceNeedsAttention`, `TenantBackupHealthResolver`, `TenantBackupHealthAssessment`, `RestoreSafetyResolver`, tenant dashboard widgets, `WorkspaceCapabilityResolver`, `CapabilityResolver`, and the current workspace overview Blade surfaces (185-workspace-recovery-posture-visibility)
- PostgreSQL unchanged; no schema change, new cache table, or persisted workspace recovery artifact is planned (185-workspace-recovery-posture-visibility)
- PHP 8.4, Laravel 12, Blade, Filament v5, Livewire v4 + Filament v5 resources and table filters, Livewire v4 `ListRecords`, Pest v4, Laravel Sail, existing `TenantResource`, `ListTenants`, `WorkspaceOverviewBuilder`, `TenantBackupHealthResolver`, `TenantBackupHealthAssessment`, `RestoreSafetyResolver`, `RecoveryReadiness`, and shared badge infrastructure (186-tenant-registry-recovery-triage)
- PostgreSQL with existing tenant-owned `tenants`, `backup_sets`, `backup_items`, `restore_runs`, `policies`, and membership records; no schema change planned (186-tenant-registry-recovery-triage)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `WorkspaceOverviewBuilder`, `TenantResource`, `TenantDashboard`, `CanonicalAdminTenantFilterState`, `TenantBackupHealthAssessment`, `RestoreSafetyResolver`, and continuity-aware backup or restore list pages (187-portfolio-triage-arrival-context)
- PostgreSQL unchanged; no new tables, caches, or durable workflow artifacts (187-portfolio-triage-arrival-context)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `ProviderConnection` model, `ProviderConnectionResolver`, `ProviderConnectionStateProjector`, `ProviderConnectionMutationService`, `ProviderConnectionHealthCheckJob`, `StartVerification`, `ProviderConnectionResource`, `TenantResource`, system directory pages, `BadgeCatalog`, `BadgeRenderer`, and shared provider-state Blade entries (188-provider-connection-state-cleanup)
- PostgreSQL with one narrow schema addition (`is_enabled`) followed by final removal of legacy `status` and `health_status` columns and their indexes (188-provider-connection-state-cleanup)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `WorkspaceOverviewBuilder`, `TenantResource`, `TenantDashboard`, `PortfolioArrivalContext`, `TenantBackupHealthResolver`, `RestoreSafetyResolver`, `BadgeCatalog`, `UiEnforcement`, and `AuditRecorder` patterns (189-portfolio-triage-review-state)
- PostgreSQL via Laravel Eloquent with one new table `tenant_triage_reviews` and no new external caches or background stores (189-portfolio-triage-review-state)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing `BaselineCompareService`, `BaselineSnapshotTruthResolver`, `BaselineCompareStats`, `RelatedNavigationResolver`, `CanonicalNavigationContext`, `BadgeCatalog`, and `UiEnforcement` patterns (190-baseline-compare-matrix)
- PostgreSQL via existing `baseline_profiles`, `baseline_snapshots`, `baseline_snapshot_items`, `baseline_tenant_assignments`, `operation_runs`, and `findings` tables; no new persistence planned (190-baseline-compare-matrix)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `BaselineCompareMatrixBuilder`, `BadgeCatalog`, `CanonicalNavigationContext`, and `UiEnforcement` patterns (191-baseline-compare-operator-mode)
- PostgreSQL via existing baseline, assignment, compare-run, and finding tables; no new persistence planned (191-baseline-compare-operator-mode)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `UiEnforcement`, `RelatedNavigationResolver`, `ActionSurfaceValidator`, and page-local Filament action builders (192-record-header-discipline)
- PostgreSQL through existing workspace-owned and tenant-owned resource models; no schema change planned (192-record-header-discipline)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `OperateHubShell`, `CanonicalNavigationContext`, `CanonicalAdminTenantFilterState`, `UiEnforcement`, `ActionSurfaceValidator`, and Filament page or resource action builders (193-monitoring-action-hierarchy)
- PostgreSQL through existing workspace-owned and tenant-owned models; no schema change planned (193-monitoring-action-hierarchy)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `UiEnforcement`, existing audit loggers (`AuditLogger`, `WorkspaceAuditLogger`, `SystemConsoleAuditLogger`), existing mutation services (`FindingExceptionService`, `FindingWorkflowService`, `TenantReviewLifecycleService`, `EvidenceSnapshotService`, `OperationRunTriageService`) (194-governance-friction-hardening)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `ActionSurfaceDiscovery`, `ActionSurfaceValidator`, `ActionSurfaceExemptions`, `GovernanceActionCatalog`, `UiEnforcement`, `WorkspaceContext`, and existing system/onboarding/auth helpers (195-action-surface-closure)
- PostgreSQL through existing workspace-owned, tenant-owned, and system-visible models; no schema change planned (195-action-surface-closure)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `DependencyQueryService`, `DependencyTargetResolver`, `TenantRequiredPermissionsViewModelBuilder`, `ArtifactTruthPresenter`, `WorkspaceContext`, Filament `InteractsWithTable`, Filament `TableComponent`, and existing badge and action-surface helpers (196-hard-filament-nativity-cleanup)
- PostgreSQL through existing tenant-owned and workspace-context models (`InventoryItem`, `InventoryLink`, `TenantPermission`, `EvidenceSnapshot`, `TenantReview`); no schema change planned (196-hard-filament-nativity-cleanup)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, Laravel Sail, existing `BaselineScope`, `InventoryPolicyTypeMeta`, `BaselineSupportCapabilityGuard`, `BaselineCaptureService`, and `BaselineCompareService` (202-governance-subject-taxonomy)
- PostgreSQL via existing `baseline_profiles.scope_jsonb`, `baseline_tenant_assignments.override_scope_jsonb`, and `operation_runs.context`; no new tables planned (202-governance-subject-taxonomy)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `BaselineCompareService`, `CompareBaselineToTenantJob`, `SubjectResolver`, `CurrentStateHashResolver`, `DriftHasher`, `BaselineCompareSummaryAssessor`, and finding lifecycle services (203-baseline-compare-strategy)
- PostgreSQL via existing baseline snapshots, baseline snapshot items, `operation_runs`, findings, and baseline scope JSON; no new top-level tables planned (203-baseline-compare-strategy)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `GovernanceSubjectTaxonomyRegistry`, `BaselineScope`, `CompareStrategyRegistry`, `OperationCatalog`, `OperationRunType`, `ReasonTranslator`, `ReasonResolutionEnvelope`, `ProviderReasonTranslator`, and current Filament monitoring or review surfaces (204-platform-core-vocabulary-hardening)
- PostgreSQL via existing `operation_runs.type`, `operation_runs.context`, `baseline_profiles.scope_jsonb`, `baseline_snapshot_items`, findings, evidence payloads, and current config-backed registries; no new top-level tables planned (204-platform-core-vocabulary-hardening)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Laravel Sail, existing `BaselineCompareService`, `CompareBaselineToTenantJob`, `CompareStrategyRegistry`, `IntuneCompareStrategy`, `CurrentStateHashResolver`, and current finding lifecycle services (205-compare-job-cleanup)
- PostgreSQL via existing baseline snapshots, baseline snapshot items, inventory items, `operation_runs`, findings, and current run-context JSON; no new storage planned (205-compare-job-cleanup)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `VerificationReportViewer`, `VerificationReportChangeIndicator`, `PolicyNormalizer`, `VersionDiff`, `DriftFindingDiffBuilder`, and `SettingsCatalogSettingsTable` (197-shared-detail-contract)
- PostgreSQL unchanged; no new persistence, cache store, or durable UI artifact (197-shared-detail-contract)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `CanonicalAdminTenantFilterState`, `CanonicalNavigationContext`, `OperateHubShell`, Filament `InteractsWithTable`, and page-local Livewire state on the affected Filament pages (198-monitoring-page-state)
- PostgreSQL plus existing Laravel session-backed table filter, search, and sort persistence; no schema change planned (198-monitoring-page-state)
- PHP 8.4.15 + Laravel 12, Pest v4, PHPUnit 12, Pest Browser plugin, Filament v5, Livewire v4, Laravel Sail (206-test-suite-governance)
- SQLite `:memory:` for the default test configuration, dedicated PostgreSQL config for the schema-level `Pgsql` suite, and local runner artifacts under `apps/platform/storage/logs/test-lanes` (206-test-suite-governance)
- PHP 8.4.15 + Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail (207-shared-test-fixture-slimming)
- SQLite `:memory:` for the default test environment, isolated PostgreSQL coverage via the existing dedicated suite, and lane-measurement artifacts under the app-root contract path `storage/logs/test-lanes` (207-shared-test-fixture-slimming)
- SQLite `:memory:` for the default test environment, existing lane artifacts under the app-root contract path `storage/logs/test-lanes`, and no new product persistence (208-heavy-suite-segmentation)
- SQLite `:memory:` for the default test environment, mixed database strategy for some heavy-governance families as declared in `TestLaneManifest`, and existing lane artifacts under the app-root contract path `storage/logs/test-lanes` (209-heavy-governance-cost)
- PHP 8.4.15 for repo-truth test governance, Bash for repo-root wrappers, and GitHub-compatible Gitea Actions workflow YAML under `.gitea/workflows/` + Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail, Gitea Actions backed by `act_runner`, and the existing `Tests\Support\TestLaneManifest`, `TestLaneBudget`, and `TestLaneReport` seams (210-ci-matrix-budget-enforcement)
- SQLite `:memory:` for default lane execution, filesystem artifacts under the app-root contract path `storage/logs/test-lanes`, checked-in workflow YAML under `.gitea/workflows/`, and no new product database persistence (210-ci-matrix-budget-enforcement)
- PHP 8.4.15 for repo-truth governance logic, Bash for repo-root wrappers, GitHub-compatible Gitea Actions workflow YAML under `.gitea/workflows/`, plus JSON Schema and logical OpenAPI for repository contracts + Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail, Gitea Actions backed by `act_runner`, uploaded artifact bundles, and the existing `Tests\Support\TestLaneManifest`, `TestLaneBudget`, and `TestLaneReport` seams (211-runtime-trend-recalibration)
- SQLite `:memory:` for lane execution, filesystem artifacts under `apps/platform/storage/logs/test-lanes`, staged CI bundles under `.gitea-artifacts/<workflow-profile>`, bounded derived trend/history artifacts adjacent to current lane artifacts, and no new product database persistence (211-runtime-trend-recalibration)
- Markdown for repository governance artifacts, JSON Schema plus logical OpenAPI for planning contracts, and Bash-backed SpecKit scripts already present in the repo + `.specify/memory/constitution.md`, `.specify/templates/spec-template.md`, `.specify/templates/plan-template.md`, `.specify/templates/tasks-template.md`, `.specify/templates/checklist-template.md`, `.specify/README.md`, `README.md`, and the existing Specs 206 through 211 governance vocabulary (212-test-authoring-guardrails)
- Repository-owned markdown and contract artifacts under `.specify/`, `specs/212-test-authoring-guardrails/`, and root documentation files; no product database persistence (212-test-authoring-guardrails)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing `WorkspaceContext`, `OperateHubShell`, `EnsureFilamentTenantSelected`, `WorkspaceRedirectResolver`, `WorkspaceIntendedUrl`, `TenantPageCategory`, and `ResolvesPanelTenantContext` (199-global-context-shell-contract)
- PostgreSQL unchanged plus existing Laravel session keys `current_workspace_id`, `workspace_intended_url`, and `workspace_last_tenant_ids`; no schema change planned (199-global-context-shell-contract)
- Markdown governance artifacts in a PHP 8.4.15 / Laravel 12 / Filament v5 / Livewire v4 repository + `.specify/memory/constitution.md`, `docs/ui/operator-ux-surface-standards.md`, adjacent Specs 196 through 199, existing UI rule IDs `UI-SURF-001`, `ACTSURF-001`, `UI-HARD-001`, `UI-EX-001`, `UI-FIL-001`, `DECIDE-001`, and `UX-001` (200-filament-surface-rules)
- Astro 6.0.0 templates + TypeScript 5.x (explicit setup in `apps/website`) + Astro 6, Tailwind CSS v4, custom Astro component primitives (shadcn-inspired), lightweight Playwright browser smoke tests (213-website-foundation-v0)
- Static filesystem content, styles, and assets under `apps/website/src` and `apps/website/public`; no database (213-website-foundation-v0)
- Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro component primitives, Playwright browser smoke tests (214-website-visual-foundation)
- Static filesystem content, styles, assets, and content collections under `apps/website/src` and `apps/website/public`; no database (214-website-visual-foundation)
- Markdown governance artifacts, JSON Schema plus logical OpenAPI planning contracts, and Bash-backed SpecKit scripts inside a PHP 8.4.15 / Laravel 12 / Filament v5 / Livewire v4 repository + `.specify/memory/constitution.md`, `.specify/templates/spec-template.md`, `.specify/templates/plan-template.md`, `.specify/templates/tasks-template.md`, `.specify/templates/checklist-template.md`, `.specify/README.md`, `docs/ui/operator-ux-surface-standards.md`, and Specs 196 through 200 (201-enforcement-review-guardrails)
- Repository-owned markdown and contract artifacts under `.specify/` and `/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/201-enforcement-review-guardrails/`; no product database persistence (201-enforcement-review-guardrails)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + Filament v5, Livewire v4, Pest v4, Laravel Sail, `ArtifactTruthPresenter`, `ArtifactTruthEnvelope`, `OperatorExplanationBuilder`, `BaselineSnapshotPresenter`, `BadgeCatalog`, `BadgeRenderer`, existing governance Filament resources/pages, and current Enterprise Detail builders (214-governance-outcome-compression)
- PostgreSQL via existing `baseline_snapshots`, `evidence_snapshots`, `evidence_snapshot_items`, `tenant_reviews`, `review_packs`, and `operation_runs` tables; no schema change planned (214-governance-outcome-compression)
- Astro 6.0.0 templates + TypeScript 5.9 strict + Astro 6, Tailwind CSS v4 via `@tailwindcss/vite`, Astro content collections, local Astro layout/primitive/content helpers, Playwright smoke tests (215-website-core-pages)
- Static filesystem pages, content modules, and Astro content collections under `apps/website/src` and `apps/website/public`; no database (215-website-core-pages)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + Filament Resources/Pages/Actions, Livewire 4, Pest 4, `ProviderOperationStartGate`, `ProviderOperationRegistry`, `ProviderConnectionResolver`, `OperationRunService`, `ProviderNextStepsRegistry`, `ReasonPresenter`, `OperationUxPresenter`, `OperationRunLinks` (216-provider-dispatch-gate)
- PostgreSQL via existing `operation_runs`, `provider_connections`, `managed_tenant_onboarding_sessions`, `restore_runs`, and tenant-owned runtime records; no new tables planned (216-provider-dispatch-gate)
- Astro 6.0.0 templates + TypeScript 5.9.x + Astro 6, Tailwind CSS v4, local Astro layout/section primitives, Astro content collections, Playwright browser smoke tests (217-homepage-structure)
- Static filesystem content, Astro content collections, and assets under `apps/website/src` and `apps/website/public`; no database (217-homepage-structure)
- Astro 6.0.0 templates + TypeScript 5.9.x + Astro 6, Tailwind CSS v4, existing Astro content modules and section primitives, Playwright browser smoke tests (218-homepage-hero)
- Static filesystem content and assets under `apps/website/src` and `apps/website/public`; no database (218-homepage-hero)
- PHP 8.4.15 / Laravel 12 + Filament v5, Livewire v4.0+, Pest v4, Tailwind CSS v4 (219-finding-ownership-semantics)
- PostgreSQL via Sail; existing `findings.owner_user_id`, `findings.assignee_user_id`, and `finding_exceptions.owner_user_id` fields; no schema changes planned (219-finding-ownership-semantics)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + Filament v5, Livewire v4, Pest v4, Laravel Sail, `TenantlessOperationRunViewer`, `OperationRunResource`, `ArtifactTruthPresenter`, `OperatorExplanationBuilder`, `ReasonPresenter`, `OperationUxPresenter`, `SummaryCountsNormalizer`, and the existing enterprise-detail builders (220-governance-run-summaries)
- PostgreSQL via existing `operation_runs` plus related `baseline_snapshots`, `evidence_snapshots`, `tenant_reviews`, and `review_packs`; no schema changes planned (220-governance-run-summaries)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + Filament admin panel pages, `Finding`, `FindingResource`, `WorkspaceOverviewBuilder`, `WorkspaceContext`, `WorkspaceCapabilityResolver`, `CapabilityResolver`, `CanonicalAdminTenantFilterState`, and `CanonicalNavigationContext` (221-findings-operator-inbox)
- PostgreSQL via existing `findings`, `tenants`, `tenant_memberships`, and workspace context session state; no schema changes planned (221-findings-operator-inbox)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + Filament admin pages/tables/actions/notifications, `Finding`, `FindingResource`, `FindingWorkflowService`, `FindingPolicy`, `CapabilityResolver`, `CanonicalAdminTenantFilterState`, `CanonicalNavigationContext`, `WorkspaceContext`, and `UiEnforcement` (222-findings-intake-team-queue)
- PostgreSQL via existing `findings`, `tenants`, `tenant_memberships`, `audit_logs`, and workspace session context; no schema changes planned (222-findings-intake-team-queue)
- TypeScript 5.9, Astro 6, Node.js 20+ + Astro, astro-icon, Tailwind CSS v4, Playwright 1.59 (223-astrodeck-website-rebuild)
- File-based route files, Astro content collections under `src/content`, public assets, and planning documents under `specs/223-astrodeck-website-rebuild`; no database (223-astrodeck-website-rebuild)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + Laravel notifications (`database` channel), Filament database notifications, `Finding`, `FindingWorkflowService`, `FindingSlaPolicy`, `AlertRule`, `AlertDelivery`, `AlertDispatchService`, `EvaluateAlertsJob`, `CapabilityResolver`, `WorkspaceContext`, `TenantMembership`, `FindingResource` (224-findings-notifications-escalation)
- PostgreSQL via existing `findings`, `alert_rules`, `alert_deliveries`, `notifications`, `tenant_memberships`, and `audit_logs`; no schema changes planned (224-findings-notifications-escalation)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + `Finding`, `FindingResource`, `MyFindingsInbox`, `FindingsIntakeQueue`, `WorkspaceOverviewBuilder`, `EnsureFilamentTenantSelected`, `FindingWorkflowService`, `AuditLog`, `TenantMembership`, Filament page and table primitives (225-assignment-hygiene)
- PostgreSQL via existing `findings`, `audit_logs`, `tenant_memberships`, and `users`; no schema changes planned (225-assignment-hygiene)
- Markdown artifacts + Astro 6.0.0 + TypeScript 5.9 context for source discovery + Repository spec workflow (`.specify`), Astro website source tree under `apps/website/src`, existing component taxonomy (`primitives`, `content`, `sections`, `layout`) (226-astrodeck-inventory-planning)
- Filesystem only (`specs/226-astrodeck-inventory-planning/*`) (226-astrodeck-inventory-planning)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4, Blade + `App\Models\Finding`, `App\Filament\Resources\FindingResource`, `App\Services\Findings\FindingWorkflowService`, `App\Services\Baselines\BaselineAutoCloseService`, `App\Services\EntraAdminRoles\EntraAdminRolesFindingGenerator`, `App\Services\PermissionPosture\PermissionPostureFindingGenerator`, `App\Jobs\CompareBaselineToTenantJob`, `App\Filament\Pages\Reviews\ReviewRegister`, `App\Filament\Resources\TenantReviewResource`, `BadgeCatalog`, `BadgeRenderer`, `AuditLog` metadata via `AuditLogger` (231-finding-outcome-taxonomy)
- PostgreSQL via existing `findings`, `finding_exceptions`, `tenant_reviews`, `stored_reports`, and audit-log tables; no schema changes planned (231-finding-outcome-taxonomy)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + Filament Resources/Pages/Widgets, Pest v4, `App\Support\OperationRunLinks`, `App\Support\System\SystemOperationRunLinks`, `App\Support\Navigation\CanonicalNavigationContext`, `App\Support\Navigation\RelatedNavigationResolver`, existing workspace and tenant authorization helpers (232-operation-run-link-contract)
- PostgreSQL-backed existing `operation_runs`, `tenants`, and `workspaces` records plus current session-backed canonical navigation state; no new persistence (232-operation-run-link-contract)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + Filament widgets/resources/pages, Pest v4, `App\Models\OperationRun`, `App\Support\Operations\OperationRunFreshnessState`, `App\Services\Operations\OperationLifecycleReconciler`, `App\Support\OpsUx\OperationUxPresenter`, `App\Support\OpsUx\ActiveRuns`, `App\Support\Badges\BadgeCatalog` / `BadgeRenderer`, `App\Support\Workspaces\WorkspaceOverviewBuilder`, `App\Support\OperationRunLinks` (233-stale-run-visibility)
- Existing PostgreSQL `operation_runs` records and current session/query-backed monitoring navigation state; no new persistence (233-stale-run-visibility)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + `App\Models\BaselineProfile`, `App\Support\Baselines\BaselineProfileStatus`, `App\Support\Badges\BadgeCatalog`, `App\Support\Badges\BadgeDomain`, `Database\Factories\TenantFactory`, `App\Console\Commands\SeedBackupHealthBrowserFixture`, existing tenant-truth and baseline-profile Pest tests (234-dead-transitional-residue)
- Existing PostgreSQL `baseline_profiles` and `tenants` tables; no new persistence and no schema migration in this slice (234-dead-transitional-residue)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + `BaselineCaptureService`, `CaptureBaselineSnapshotJob`, `BaselineReasonCodes`, `BaselineCompareStats`, `ReasonTranslator`, `GovernanceRunDiagnosticSummaryBuilder`, `OperationRunService`, `BaselineProfile`, `BaselineSnapshot`, `OperationRunOutcome`, existing Filament capture/compare surfaces (235-baseline-capture-truth)
- Existing PostgreSQL tables only; no new table or schema migration is planned in the mainline slice (235-baseline-capture-truth)
- PHP 8.4.15 + Laravel 12, Filament v5, Livewire v4, Pest v4, existing governance domain models and builders, existing Evidence Snapshot and Tenant Review infrastructure (236-canonical-control-catalog-foundation)
- PostgreSQL for existing downstream governance artifacts plus a product-seeded in-repo canonical control registry; no new DB-backed control authoring table in the first slice (236-canonical-control-catalog-foundation)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + existing provider seams under `App\Services\Providers` and `App\Services\Graph`, especially `ProviderGateway`, `ProviderIdentityResolver`, `ProviderIdentityResolution`, `PlatformProviderIdentityResolver`, `ProviderConnectionResolver`, `ProviderConnectionResolution`, `MicrosoftGraphOptionsResolver`, `ProviderOperationRegistry`, `ProviderOperationStartGate`, `GraphClientInterface`, Pest v4 (237-provider-boundary-hardening)
- Existing PostgreSQL tables such as `provider_connections` and `operation_runs`; one new in-repo config catalog for provider-boundary ownership; no new database tables (237-provider-boundary-hardening)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + `ProviderConnectionResource`, `ManagedTenantOnboardingWizard`, `ProviderConnection`, `ProviderConnectionResolver`, `ProviderConnectionResolution`, `ProviderConnectionMutationService`, `ProviderConnectionStateProjector`, `ProviderIdentityResolver`, `ProviderIdentityResolution`, `PlatformProviderIdentityResolver`, `BadgeRenderer`, Pest v4 (238-provider-identity-target-scope)
- Existing PostgreSQL tables such as `provider_connections`, `provider_credentials`, and existing audit tables; no new database tables planned (238-provider-identity-target-scope)
- PHP 8.4.15, Laravel 12, Filament v5, Livewire v4 + existing `App\Support\OperationCatalog`, `App\Support\OperationRunType`, `App\Services\OperationRunService`, `App\Services\Providers\ProviderOperationRegistry`, `App\Services\Providers\ProviderOperationStartGate`, `App\Filament\Resources\OperationRunResource`, `App\Filament\Pages\Workspaces\ManagedTenantOnboardingWizard`, `App\Support\Filament\FilterOptionCatalog`, `App\Support\OpsUx\OperationUxPresenter`, `App\Support\References\Resolvers\OperationRunReferenceResolver`, `App\Services\Audit\AuditEventBuilder`, Pest v4 (239-canonical-operation-type-source-of-truth)
- PostgreSQL via existing `operation_runs.type` and `managed_tenant_onboarding_sessions.state->bootstrap_operation_types`, plus config-backed `tenantpilot.operations.lifecycle.covered_types` and `tenantpilot.platform_vocabulary`; no new tables (239-canonical-operation-type-source-of-truth)
- PHP 8.4 (Laravel 12) + Laravel 12 + Filament v5 + Livewire v4 + Pest; existing onboarding services (`OnboardingLifecycleService`, `OnboardingDraftStageResolver`), provider connection summary, verification assist, and Ops-UX helpers (240-tenant-onboarding-readiness)
- PostgreSQL via existing `managed_tenant_onboarding_sessions`, `provider_connections`, `operation_runs`, and stored permission-posture data; no new persistence planned (240-tenant-onboarding-readiness)
- PHP 8.4 (Laravel 12) + Laravel 12 + Filament v5 + Livewire v4 + Pest; existing `OperationRunLinks`, `GovernanceRunDiagnosticSummaryBuilder`, `ProviderReasonTranslator`, `RelatedNavigationResolver`, `RedactionIntegrity`, `WorkspaceAuditLogger` (241-support-diagnostic-pack)
- PostgreSQL via existing `operation_runs`, `provider_connections`, `findings`, `stored_reports`, `tenant_reviews`, `review_packs`, and `audit_logs`; no new persistence planned (241-support-diagnostic-pack)
- PHP 8.4, Laravel 12 + Filament v5, Livewire v4, Pest v4, existing review/evidence/review-pack/audit/RBAC support services (249-customer-review-workspace)
- PostgreSQL via existing `tenant_reviews`, `review_packs`, `evidence_snapshots`, findings / finding-exception truth, workspace memberships, and `audit_logs`; no new persistence planned (249-customer-review-workspace)
- PHP 8.4 (Laravel 12) + Filament v5 + Livewire v4, existing workspace settings stack (`SettingsRegistry`, `SettingsResolver`, `SettingsWriter`), `WorkspaceEntitlementResolver`, `ReviewPackService`, system directory detail page (251-commercial-entitlements-billing-state)
- PostgreSQL via existing `workspace_settings` rows plus existing audit log records; no new table or billing/account model (251-commercial-entitlements-billing-state)
- PHP 8.4 (Laravel 12) + Laravel 12 + Filament v5 + Livewire v4 + Pest; existing `UiEnforcement`, `OperationUxPresenter`, `OperationRunService`, `OperationCatalog`, `SystemOperationRunLinks`, `OperationRunLinks`, `AuditRecorder`, `WorkspaceAuditLogger`, and `PlatformCapabilities` (253-remove-findings-backfill-runtime-surfaces)
- PostgreSQL existing `findings`, `operation_runs`, `audit_logs`, and related runtime tables only; no new persistence, migration, or data backfill is planned (253-remove-findings-backfill-runtime-surfaces)
- PHP 8.4.15 (feat/005-bulk-operations)
## Project Structure
```text
apps/
platform/
website/
docs/
specs/
scripts/
src/
tests/
```
## Commands
- Root workspace:
- `corepack pnpm install`
- `corepack pnpm dev:platform`
- `corepack pnpm dev:website`
- `corepack pnpm dev`
- `corepack pnpm build:website`
- `corepack pnpm build:platform`
- Platform app:
- `cd apps/platform && ./vendor/bin/sail up -d`
- `cd apps/platform && ./vendor/bin/sail pnpm dev`
- `cd apps/platform && ./vendor/bin/sail pnpm build`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact`
# Add commands for PHP 8.4.15
## Code Style
PHP 8.4.15: Follow standard conventions
## Recent Changes
- 253-remove-findings-backfill-runtime-surfaces: Added PHP 8.4 (Laravel 12) + Laravel 12 + Filament v5 + Livewire v4 + Pest; existing `UiEnforcement`, `OperationUxPresenter`, `OperationRunService`, `OperationCatalog`, `SystemOperationRunLinks`, `OperationRunLinks`, `AuditRecorder`, `WorkspaceAuditLogger`, and `PlatformCapabilities`
- 251-commercial-entitlements-billing-state: Added PHP 8.4 (Laravel 12) + Filament v5 + Livewire v4, existing workspace settings stack (`SettingsRegistry`, `SettingsResolver`, `SettingsWriter`), `WorkspaceEntitlementResolver`, `ReviewPackService`, system directory detail page
- 249-customer-review-workspace: Added PHP 8.4, Laravel 12 + Filament v5, Livewire v4, Pest v4, existing review/evidence/review-pack/audit/RBAC support services
- feat/005-bulk-operations: Added PHP 8.4.15 + Laravel 12, Filament v4, Livewire v3
- feat/005-bulk-operations: Added PHP 8.4.15
<!-- MANUAL ADDITIONS START -->
### Pre-production compatibility check
Before adding aliases, fallback readers, dual-write logic, migration shims, or legacy fixtures, verify all of the following:
1. Do live production data exist?
2. Is shared staging migration-relevant?
3. Does an external contract depend on the old shape?
4. Does the spec explicitly require compatibility behavior?
If all answers are no, replace the old shape and remove the compatibility path.
<!-- MANUAL ADDITIONS END -->

View File

@ -1,51 +0,0 @@
---
description: Auto-commit changes after a Spec Kit command completes
---
<!-- Extension: git -->
<!-- Config: .specify/extensions/git/ -->
# Auto-Commit Changes
Automatically stage and commit all changes after a Spec Kit command completes.
## Behavior
This command is invoked as a hook after (or before) core commands. It:
1. Determines the event name from the hook context (e.g., if invoked as an `after_specify` hook, the event is `after_specify`; if `before_plan`, the event is `before_plan`)
2. Checks `.specify/extensions/git/git-config.yml` for the `auto_commit` section
3. Looks up the specific event key to see if auto-commit is enabled
4. Falls back to `auto_commit.default` if no event-specific key exists
5. Uses the per-command `message` if configured, otherwise a default message
6. If enabled and there are uncommitted changes, runs `git add .` + `git commit`
## Execution
Determine the event name from the hook that triggered this command, then run the script:
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name>`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name>`
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`).
## Configuration
In `.specify/extensions/git/git-config.yml`:
```yaml
auto_commit:
default: false # Global toggle — set true to enable for all commands
after_specify:
enabled: true # Override per-command
message: "[Spec Kit] Add specification"
after_plan:
enabled: false
message: "[Spec Kit] Add implementation plan"
```
## Graceful Degradation
- If Git is not available or the current directory is not a repository: skips with a warning
- If no config file exists: skips (disabled by default)
- If no changes to commit: skips with a message

View File

@ -1,70 +0,0 @@
---
description: Create a feature branch with sequential or timestamp numbering
---
<!-- Extension: git -->
<!-- Config: .specify/extensions/git/ -->
# Create Feature Branch
Create and switch to a new git feature branch for the given specification. This command handles **branch creation only** — the spec directory and files are created by the core `/speckit.specify` workflow.
## User Input
```text
$ARGUMENTS
```
You **MUST** consider the user input before proceeding (if not empty).
## Environment Variable Override
If the user explicitly provided `GIT_BRANCH_NAME` (e.g., via environment variable, argument, or in their request), pass it through to the script by setting the `GIT_BRANCH_NAME` environment variable before invoking the script. When `GIT_BRANCH_NAME` is set:
- The script uses the exact value as the branch name, bypassing all prefix/suffix generation
- `--short-name`, `--number`, and `--timestamp` flags are ignored
- `FEATURE_NUM` is extracted from the name if it starts with a numeric prefix, otherwise set to the full branch name
## Prerequisites
- Verify Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, warn the user and skip branch creation
## Branch Numbering Mode
Determine the branch numbering strategy by checking configuration in this order:
1. Check `.specify/extensions/git/git-config.yml` for `branch_numbering` value
2. Check `.specify/init-options.json` for `branch_numbering` value (backward compatibility)
3. Default to `sequential` if neither exists
## Execution
Generate a concise short name (2-4 words) for the branch:
- Analyze the feature description and extract the most meaningful keywords
- Use action-noun format when possible (e.g., "add-user-auth", "fix-payment-bug")
- Preserve technical terms and acronyms (OAuth2, API, JWT, etc.)
Run the appropriate script based on your platform:
- **Bash**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --short-name "<short-name>" "<feature description>"`
- **Bash (timestamp)**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --timestamp --short-name "<short-name>" "<feature description>"`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -ShortName "<short-name>" "<feature description>"`
- **PowerShell (timestamp)**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -Timestamp -ShortName "<short-name>" "<feature description>"`
**IMPORTANT**:
- Do NOT pass `--number` — the script determines the correct next number automatically
- Always include the JSON flag (`--json` for Bash, `-Json` for PowerShell) so the output can be parsed reliably
- You must only ever run this script once per feature
- The JSON output will contain `BRANCH_NAME` and `FEATURE_NUM`
## Graceful Degradation
If Git is not installed or the current directory is not a Git repository:
- Branch creation is skipped with a warning: `[specify] Warning: Git repository not detected; skipped branch creation`
- The script still outputs `BRANCH_NAME` and `FEATURE_NUM` so the caller can reference them
## Output
The script outputs JSON with:
- `BRANCH_NAME`: The branch name (e.g., `003-user-auth` or `20260319-143022-user-auth`)
- `FEATURE_NUM`: The numeric or timestamp prefix used

View File

@ -1,52 +0,0 @@
---
description: Initialize a Git repository with an initial commit
---
<!-- Extension: git -->
<!-- Config: .specify/extensions/git/ -->
# Initialize Git Repository
Initialize a Git repository in the current project directory if one does not already exist.
## Execution
Run the appropriate script from the project root:
- **Bash**: `.specify/extensions/git/scripts/bash/initialize-repo.sh`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/initialize-repo.ps1`
If the extension scripts are not found, fall back to:
- **Bash**: `git init && git add . && git commit -m "Initial commit from Specify template"`
- **PowerShell**: `git init; git add .; git commit -m "Initial commit from Specify template"`
The script handles all checks internally:
- Skips if Git is not available
- Skips if already inside a Git repository
- Runs `git init`, `git add .`, and `git commit` with an initial commit message
## Customization
Replace the script to add project-specific Git initialization steps:
- Custom `.gitignore` templates
- Default branch naming (`git config init.defaultBranch`)
- Git LFS setup
- Git hooks installation
- Commit signing configuration
- Git Flow initialization
## Output
On success:
- `✓ Git repository initialized`
## Graceful Degradation
If Git is not installed:
- Warn the user
- Skip repository initialization
- The project continues to function without Git (specs can still be created under `specs/`)
If Git is installed but `git init`, `git add .`, or `git commit` fails:
- Surface the error to the user
- Stop this command rather than continuing with a partially initialized repository

View File

@ -1,48 +0,0 @@
---
description: Detect Git remote URL for GitHub integration
---
<!-- Extension: git -->
<!-- Config: .specify/extensions/git/ -->
# Detect Git Remote URL
Detect the Git remote URL for integration with GitHub services (e.g., issue creation).
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and return empty:
```
[specify] Warning: Git repository not detected; cannot determine remote URL
```
## Execution
Run the following command to get the remote URL:
```bash
git config --get remote.origin.url
```
## Output
Parse the remote URL and determine:
1. **Repository owner**: Extract from the URL (e.g., `github` from `https://github.com/github/spec-kit.git`)
2. **Repository name**: Extract from the URL (e.g., `spec-kit` from `https://github.com/github/spec-kit.git`)
3. **Is GitHub**: Whether the remote points to a GitHub repository
Supported URL formats:
- HTTPS: `https://github.com/<owner>/<repo>.git`
- SSH: `git@github.com:<owner>/<repo>.git`
> [!CAUTION]
> ONLY report a GitHub repository if the remote URL actually points to github.com.
> Do NOT assume the remote is GitHub if the URL format doesn't match.
## Graceful Degradation
If Git is not installed, the directory is not a Git repository, or no remote is configured:
- Return an empty result
- Do NOT error — other workflows should continue without Git remote information

View File

@ -1,52 +0,0 @@
---
description: Validate current branch follows feature branch naming conventions
---
<!-- Extension: git -->
<!-- Config: .specify/extensions/git/ -->
# Validate Feature Branch
Validate that the current Git branch follows the expected feature branch naming conventions.
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and skip validation:
```
[specify] Warning: Git repository not detected; skipped branch validation
```
## Validation Rules
Get the current branch name:
```bash
git rev-parse --abbrev-ref HEAD
```
The branch name must match one of these patterns:
1. **Sequential**: `^[0-9]{3,}-` (e.g., `001-feature-name`, `042-fix-bug`, `1000-big-feature`)
2. **Timestamp**: `^[0-9]{8}-[0-9]{6}-` (e.g., `20260319-143022-feature-name`)
## Execution
If on a feature branch (matches either pattern):
- Output: `✓ On feature branch: <branch-name>`
- Check if the corresponding spec directory exists under `specs/`:
- For sequential branches, look for `specs/<prefix>-*` where prefix matches the numeric portion
- For timestamp branches, look for `specs/<prefix>-*` where prefix matches the `YYYYMMDD-HHMMSS` portion
- If spec directory exists: `✓ Spec directory found: <path>`
- If spec directory missing: `⚠ No spec directory found for prefix <prefix>`
If NOT on a feature branch:
- Output: `✗ Not on a feature branch. Current branch: <branch-name>`
- Output: `Feature branches should be named like: 001-feature-name or 20260319-143022-feature-name`
## Graceful Degradation
If Git is not installed or the directory is not a Git repository:
- Check the `SPECIFY_FEATURE` environment variable as a fallback
- If set, validate that value against the naming patterns
- If not set, skip validation with a warning

View File

@ -1,680 +0,0 @@
<laravel-boost-guidelines>
=== .ai/filament-v5-blueprint rules ===
## Source of Truth
If any Filament behavior is uncertain, lookup the exact section in:
- docs/research/filament-v5-notes.md
and prefer that over guesses.
# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES)
# Filament Blueprint (v5)
## 1) Non-negotiables
- Filament v5 requires Livewire v4.0+.
- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`).
- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results.
- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions).
- Prefer render hooks + CSS hook classes over publishing Filament internal views.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/render-hooks
- https://filamentphp.com/docs/5.x/styling/css-hooks
## 2) Directory & naming conventions
- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture.
- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`.
Sources:
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/advanced/modular-architecture
## 3) Panel setup defaults
- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels.
- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel.
- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review.
- Assets policy:
- Panel-only assets: register via panel config.
- Shared/plugin assets: register via `FilamentAsset::register()`.
- Deployment must include `cd apps/platform && php artisan filament:assets`.
Sources:
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/advanced/assets
## 4) Navigation & information architecture
- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately.
- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows.
- Treat cluster code structure as a recommendation (organizational benefit), not a required rule.
- User menu:
- Configure via `userMenuItems()` with Action objects.
- Never put destructive actions there without confirmation + authorization.
Sources:
- https://filamentphp.com/docs/5.x/navigation/overview
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/navigation/user-menu
## 5) Resource patterns
- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows.
- Global search:
- If a resource is intended for global search: ensure Edit/View page exists.
- Otherwise disable global search for that resource (dont “expect it to work”).
- If global search renders relationship-backed details: eager-load via global search query override.
- For very large datasets: consider disabling term splitting (only when needed).
Sources:
- https://filamentphp.com/docs/5.x/resources/overview
- https://filamentphp.com/docs/5.x/resources/global-search
## 6) Page lifecycle & query rules
- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading.
- Prefer render hooks for layout injection; avoid publishing internal views.
Sources:
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 7) Infolists vs RelationManagers (decision tree)
- Interactive CRUD / attach / detach under owner record → RelationManager.
- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields.
- Inline CRUD inside owner form → Repeater.
- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists.
Sources:
- https://filamentphp.com/docs/5.x/resources/managing-relationships
- https://filamentphp.com/docs/5.x/infolists/overview
## 8) Form patterns (validation, reactivity, state)
- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side.
- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns).
- Custom field views must obey state binding modifiers.
Sources:
- https://filamentphp.com/docs/5.x/forms/overview
- https://filamentphp.com/docs/5.x/forms/custom-fields
## 9) Table & action patterns
- Tables: always define a meaningful empty state (and empty-state actions where appropriate).
- Actions:
- Execution actions use `->action(...)`.
- Destructive actions add `->requiresConfirmation()`.
- Navigation-only actions should use `->url(...)`.
- UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified.
Sources:
- https://filamentphp.com/docs/5.x/tables/empty-state
- https://filamentphp.com/docs/5.x/actions/modals
## 10) Authorization & security
- Enforce panel access in non-local environments as documented.
- UI visibility is not security; enforce policies/access checks in addition to hiding UI.
- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization.
Sources:
- https://filamentphp.com/docs/5.x/users/overview
- https://filamentphp.com/docs/5.x/resources/deleting-records
## 11) Notifications & UX feedback
- Default to explicit success/error notifications for user-triggered mutations that arent instantly obvious.
- Treat polling as a cost; set intervals intentionally where polling is used.
Sources:
- https://filamentphp.com/docs/5.x/notifications/overview
- https://filamentphp.com/docs/5.x/widgets/stats-overview
## 12) Performance defaults
- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies.
- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing.
Sources:
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/styling/css-hooks
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 13) Testing requirements
- Test pages/relation managers/widgets as Livewire components.
- Test actions using Filaments action testing guidance.
- Do not mount non-Livewire classes in Livewire tests.
Sources:
- https://filamentphp.com/docs/5.x/testing/overview
- https://filamentphp.com/docs/5.x/testing/testing-actions
## 14) Forbidden patterns
- Mixing Filament v3/v4 APIs into v5 code.
- Any mention of Livewire v3 for Filament v5.
- Registering panel providers in `bootstrap/app.php` on Laravel 11+.
- Destructive actions without `->requiresConfirmation()`.
- Shipping heavy assets globally when on-demand loading fits.
- Publishing Filament internal views as a default customization technique.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/assets
## 15) Agent output contract
For any implementation request, the agent must explicitly state:
1) Livewire v4.0+ compliance.
2) Provider registration location (Laravel 11+: `bootstrap/providers.php`).
3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled).
4) Which actions are destructive and how confirmation + authorization is handled.
5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy.
6) Testing plan: which pages/widgets/relation managers/actions are covered.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/testing/testing-actions
=== .ai/filament-v5-checklist rules ===
# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES)
## Version Safety
- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere).
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire”
- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs).
- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps.
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements”
## Panel & Navigation
- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel”
- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path”
- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments).
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster”
- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction”
- [ ] Cluster directory structure is treated as recommended, not mandatory.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters”
- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed.
- Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction”
## Resource Structure
- [ ] `$recordTitleAttribute` is set for any resource intended for global search.
- Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles”
- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles”
- [ ] Relationship-backed global search details are eager-loaded via the global search query override.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results”
## Infolists & Relations
- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job”
- [ ] RelationManagers remain lazy-loaded by default unless theres an explicit UX justification.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading”
## Forms
- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily.
- Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur”
- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers).
- Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers”
## Tables & Actions
- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate).
- Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions”
- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`.
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact).
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
## Authorization & Security
- [ ] Panel access is enforced for non-local environments as documented.
- Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel”
- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries.
- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required.
- Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization”
## UX & Notifications
- [ ] User-triggered mutations provide explicit success/error notifications when outcomes arent instantly obvious.
- Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction”
- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load.
- Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)”
## Performance
- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript”
- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default).
- Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes”
## Testing
- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes.
- Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?”
- [ ] Actions that mutate data are covered using Filaments action testing guidance.
- Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions”
## Deployment / Ops
- [ ] `cd apps/platform && php artisan filament:assets` is included in the deployment process when using registered assets.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade”
=== foundation rules ===
# Laravel Boost Guidelines
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications.
## Foundational Context
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
- php - 8.4.15
- filament/filament (FILAMENT) - v5
- laravel/framework (LARAVEL) - v12
- laravel/prompts (PROMPTS) - v0
- laravel/socialite (SOCIALITE) - v5
- livewire/livewire (LIVEWIRE) - v4
- laravel/mcp (MCP) - v0
- laravel/pint (PINT) - v1
- laravel/sail (SAIL) - v1
- pestphp/pest (PEST) - v4
- phpunit/phpunit (PHPUNIT) - v12
- tailwindcss (TAILWINDCSS) - v4
## Conventions
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
- Check for existing components to reuse before writing a new one.
## Verification Scripts
- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important.
## Application Structure & Architecture
- Stick to existing directory structure; don't create new base folders without approval.
- Do not change the application's dependencies without approval.
## Workspace Commands
- Repo-root JavaScript orchestration now uses `corepack pnpm install`, `corepack pnpm dev:platform`, `corepack pnpm dev:website`, `corepack pnpm dev`, `corepack pnpm build:website`, and `corepack pnpm build:platform`.
- `corepack pnpm dev:platform` starts the platform Sail stack and the Laravel panel Vite watcher. `corepack pnpm dev` starts that platform watcher plus the website dev server.
- `apps/website` is a standalone Astro app, not a second Laravel runtime, so Boost MCP remains platform-only.
## Frontend Bundling
- If the user doesn't see a platform frontend change reflected in the UI, it could mean they need to run `cd apps/platform && ./vendor/bin/sail pnpm build`, `cd apps/platform && ./vendor/bin/sail pnpm dev`, or `cd apps/platform && ./vendor/bin/sail composer run dev`. Ask them.
## Replies
- Be concise in your explanations - focus on what's important rather than explaining obvious details.
## Documentation Files
- You must only create documentation files if explicitly requested by the user.
=== boost rules ===
## Laravel Boost
- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them.
## Artisan
- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters.
## URLs
- Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port.
## Tinker / Debugging
- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
- Use the `database-query` tool when you only need to read from the database.
## Reading Browser Logs With the `browser-logs` Tool
- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost.
- Only recent browser logs will be useful - ignore old logs.
## Searching Documentation (Critically Important)
- Boost comes with a powerful `search-docs` tool you should use before any other approaches when dealing with Laravel or Laravel ecosystem packages. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
- The `search-docs` tool is perfect for all Laravel-related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc.
- You must use this tool to search for Laravel ecosystem documentation before falling back to other approaches.
- Search the documentation before making code changes to ensure we are taking the correct approach.
- Use multiple, broad, simple, topic-based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`.
- Do not add package names to queries; package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
### Available Search Syntax
- You can and should pass multiple queries at once. The most relevant results will be returned first.
1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'.
2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit".
3. Quoted Phrases (Exact Position) - query="infinite scroll" - words must be adjacent and in that order.
4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit".
5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms.
=== php rules ===
## PHP
- Always use curly braces for control structures, even if it has one line.
### Constructors
- Use PHP 8 constructor property promotion in `__construct()`.
- <code-snippet>public function __construct(public GitHub $github) { }</code-snippet>
- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private.
### Type Declarations
- Always use explicit return type declarations for methods and functions.
- Use appropriate PHP type hints for method parameters.
<code-snippet name="Explicit Return Types and Method Params" lang="php">
protected function isAccessible(User $user, ?string $path = null): bool
{
...
}
</code-snippet>
## Comments
- Prefer PHPDoc blocks over inline comments. Never use comments within the code itself unless there is something very complex going on.
## PHPDoc Blocks
- Add useful array shape type definitions for arrays when appropriate.
## Enums
- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
=== sail rules ===
## Laravel Sail
- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail.
- The canonical application working directory is `apps/platform`. Repo-root launchers such as MCP or VS Code tasks may use `./scripts/platform-sail`, but that helper is compatibility-only.
- Start services using `cd apps/platform && ./vendor/bin/sail up -d` and stop them with `cd apps/platform && ./vendor/bin/sail stop`.
- Open the application in the browser by running `cd apps/platform && ./vendor/bin/sail open`.
- Always prefix PHP, Artisan, Composer, and Node commands with `cd apps/platform && ./vendor/bin/sail`. Examples:
- Run Artisan Commands: `cd apps/platform && ./vendor/bin/sail artisan migrate`
- Install Composer packages: `cd apps/platform && ./vendor/bin/sail composer install`
- Execute Node commands: `cd apps/platform && ./vendor/bin/sail pnpm dev`
- Execute PHP scripts: `cd apps/platform && ./vendor/bin/sail php [script]`
- View all available Sail commands by running `cd apps/platform && ./vendor/bin/sail` without arguments.
=== tests rules ===
## Test Enforcement
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
- Run the minimum number of tests needed to ensure code quality and speed. Use `cd apps/platform && ./vendor/bin/sail artisan test --compact` with a specific filename or filter.
=== laravel/core rules ===
## Do Things the Laravel Way
- Use `cd apps/platform && ./vendor/bin/sail artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
- If you're creating a generic PHP class, use `cd apps/platform && ./vendor/bin/sail artisan make:class`.
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
### Database
- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins.
- Use Eloquent models and relationships before suggesting raw database queries.
- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them.
- Generate code that prevents N+1 query problems by using eager loading.
- Use Laravel's query builder for very complex database operations.
### Model Creation
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `cd apps/platform && ./vendor/bin/sail artisan make:model`.
### APIs & Eloquent Resources
- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
### Controllers & Validation
- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages.
- Check sibling Form Requests to see if the application uses array or string based validation rules.
### Queues
- Use queued jobs for time-consuming operations with the `ShouldQueue` interface.
### Authentication & Authorization
- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
### URL Generation
- When generating links to other pages, prefer named routes and the `route()` function.
### Configuration
- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`.
### Testing
- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
- When creating tests, make use of `cd apps/platform && ./vendor/bin/sail artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
### Vite Error
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `cd apps/platform && ./vendor/bin/sail pnpm build` or ask the user to run `cd apps/platform && ./vendor/bin/sail pnpm dev` or `cd apps/platform && ./vendor/bin/sail composer run dev`.
=== laravel/v12 rules ===
## Laravel 12
- Use the `search-docs` tool to get version-specific documentation.
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.
### Laravel 12 Structure
- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`.
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
- `bootstrap/providers.php` contains application specific service providers.
- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration.
### Database
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
### Models
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
=== livewire/core rules ===
## Livewire
- Use the `search-docs` tool to find exact version-specific documentation for how to write Livewire and Livewire tests.
- Use the `cd apps/platform && ./vendor/bin/sail artisan make:livewire [Posts\CreatePost]` Artisan command to create new components.
- State should live on the server, with the UI reflecting it.
- All Livewire requests hit the Laravel backend; they're like regular HTTP requests. Always validate form data and run authorization checks in Livewire actions.
## Livewire Best Practices
- Livewire components require a single root element.
- Use `wire:loading` and `wire:dirty` for delightful loading states.
- Add `wire:key` in loops:
```blade
@foreach ($items as $item)
<div wire:key="item-{{ $item->id }}">
{{ $item->name }}
</div>
@endforeach
```
- Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects:
<code-snippet name="Lifecycle Hook Examples" lang="php">
public function mount(User $user) { $this->user = $user; }
public function updatedSearch() { $this->resetPage(); }
</code-snippet>
## Testing Livewire
<code-snippet name="Example Livewire Component Test" lang="php">
Livewire::test(Counter::class)
->assertSet('count', 0)
->call('increment')
->assertSet('count', 1)
->assertSee(1)
->assertStatus(200);
</code-snippet>
<code-snippet name="Testing Livewire Component Exists on Page" lang="php">
$this->get('/posts/create')
->assertSeeLivewire(CreatePost::class);
</code-snippet>
=== pint/core rules ===
## Laravel Pint Code Formatter
- You must run `cd apps/platform && ./vendor/bin/sail bin pint --dirty` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `cd apps/platform && ./vendor/bin/sail bin pint --test`, simply run `cd apps/platform && ./vendor/bin/sail bin pint` to fix any formatting issues.
=== pest/core rules ===
## Pest
### Testing
- If you need to verify a feature is working, write or update a Unit / Feature test.
### Pest Tests
- All tests must be written using Pest. Use `cd apps/platform && ./vendor/bin/sail artisan make:test --pest {name}`.
- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application.
- Tests should test all of the happy paths, failure paths, and weird paths.
- Tests live in the `tests/Feature` and `tests/Unit` directories.
- Pest tests look and behave like this:
<code-snippet name="Basic Pest Test Example" lang="php">
it('is true', function () {
expect(true)->toBeTrue();
});
</code-snippet>
### Running Tests
- Run the minimal number of tests using an appropriate filter before finalizing code edits.
- To run all tests: `cd apps/platform && ./vendor/bin/sail artisan test --compact`.
- To run all tests in a file: `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ExampleTest.php`.
- To filter on a particular test name: `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=testName` (recommended after making a change to a related file).
- When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing.
### Pest Assertions
- When asserting status codes on a response, use the specific method like `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or similar, e.g.:
<code-snippet name="Pest Example Asserting postJson Response" lang="php">
it('returns all', function () {
$response = $this->postJson('/api/docs', []);
$response->assertSuccessful();
});
</code-snippet>
### Mocking
- Mocking can be very helpful when appropriate.
- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always import it via `use function Pest\Laravel\mock;` before using it. Alternatively, you can use `$this->mock()` if existing tests do.
- You can also create partial mocks using the same import or self method.
### Datasets
- Use datasets in Pest to simplify tests that have a lot of duplicated data. This is often the case when testing validation rules, so consider this solution when writing tests for validation rules.
<code-snippet name="Pest Dataset Example" lang="php">
it('has emails', function (string $email) {
expect($email)->not->toBeEmpty();
})->with([
'james' => 'james@laravel.com',
'taylor' => 'taylor@laravel.com',
]);
</code-snippet>
=== pest/v4 rules ===
## Pest 4
- Pest 4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
- Browser testing is incredibly powerful and useful for this project.
- Browser tests should live in `tests/Browser/`.
- Use the `search-docs` tool for detailed guidance on utilizing these features.
### Browser Testing
- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest 4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test.
- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging when appropriate.
### Example Tests
<code-snippet name="Pest Browser Test Example" lang="php">
it('may reset the password', function () {
Notification::fake();
$this->actingAs(User::factory()->create());
$page = visit('/sign-in'); // Visit on a real browser...
$page->assertSee('Sign In')
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
->click('Forgot Password?')
->fill('email', 'nuno@laravel.com')
->click('Send Reset Link')
->assertSee('We have emailed your password reset link!')
Notification::assertSent(ResetPassword::class);
});
</code-snippet>
<code-snippet name="Pest Smoke Testing Example" lang="php">
$pages = visit(['/', '/about', '/contact']);
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
</code-snippet>
=== tailwindcss/core rules ===
## Tailwind CSS
- Use Tailwind CSS classes to style HTML; check and use existing Tailwind conventions within the project before writing your own.
- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc.).
- Think through class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child carefully to limit repetition, and group elements logically.
- You can use the `search-docs` tool to get exact examples from the official documentation when needed.
### Spacing
- When listing items, use gap utilities for spacing; don't use margins.
<code-snippet name="Valid Flex Gap Spacing Example" lang="html">
<div class="flex gap-8">
<div>Superior</div>
<div>Michigan</div>
<div>Erie</div>
</div>
</code-snippet>
### Dark Mode
- If existing pages and components support dark mode, new pages and components must support dark mode in a similar way, typically using `dark:`.
=== tailwindcss/v4 rules ===
## Tailwind CSS 4
- Always use Tailwind CSS v4; do not use the deprecated utilities.
- `corePlugins` is not supported in Tailwind v4.
- In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed.
<code-snippet name="Extending Theme in CSS" lang="css">
@theme {
--color-brand: oklch(0.72 0.11 178);
}
</code-snippet>
- In Tailwind v4, you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3:
<code-snippet name="Tailwind v4 Import Tailwind Diff" lang="diff">
- @tailwind base;
- @tailwind components;
- @tailwind utilities;
+ @import "tailwindcss";
</code-snippet>
### Replaced Utilities
- Tailwind v4 removed deprecated utilities. Do not use the deprecated option; use the replacement.
- Opacity values are still numeric.
| Deprecated | Replacement |
|------------+--------------|
| bg-opacity-* | bg-black/* |
| text-opacity-* | text-black/* |
| border-opacity-* | border-black/* |
| divide-opacity-* | divide-black/* |
| ring-opacity-* | ring-black/* |
| placeholder-opacity-* | placeholder-black/* |
| flex-shrink-* | shrink-* |
| flex-grow-* | grow-* |
| overflow-ellipsis | text-ellipsis |
| decoration-slice | box-decoration-slice |
| decoration-clone | box-decoration-clone |
</laravel-boost-guidelines>
<!-- SPECKIT START -->
For additional context about technologies to be used, project structure,
shell commands, and other important information, read the current plan
<!-- SPECKIT END -->

View File

@ -1,3 +0,0 @@
---
agent: speckit.git.commit
---

View File

@ -1,3 +0,0 @@
---
agent: speckit.git.feature
---

View File

@ -1,3 +0,0 @@
---
agent: speckit.git.initialize
---

View File

@ -1,3 +0,0 @@
---
agent: speckit.git.remote
---

View File

@ -1,3 +0,0 @@
---
agent: speckit.git.validate
---

View File

@ -1,76 +0,0 @@
---
description: Scan the TenantPilot repository for architecture and safety violations against the workspace-first, RBAC-first, audit-first governance model.
---
You are a Senior Staff Engineer and Enterprise SaaS Architecture Auditor reviewing TenantPilot / TenantAtlas.
This is not a generic code review. Audit the repository against the TenantPilot audit constitution at `docs/audits/tenantpilot-architecture-audit-constitution.md`.
## Audit focus
Prioritize:
- workspace and tenant isolation
- route model binding safety
- Filament resources, pages, relation managers, widgets, and actions
- Livewire public properties and serialized state risks
- jobs, queue boundaries, and backend authorization rechecks
- provider access boundaries
- `OperationRun` consistency
- findings, exceptions, review, drift, and baseline workflow integrity
- audit trail completeness
- wrong-tenant regression coverage
- unauthorized action coverage
- workflow misuse and invalid transition coverage
## Output rules
Classify every finding as exactly one of:
- Constitutional Violation
- Architectural Drift
- Workflow Trust Gap
- Test Blind Spot
Assign one severity:
- Severity 1: Critical
- Severity 2: High
- Severity 3: Medium
- Severity 4: Low
Anything directly touching isolation, RBAC, secrets, or auditability must not be rated Low.
For each finding provide:
1. Title
2. Classification
3. Severity
4. Affected Area
5. Evidence with specific files, classes, methods, routes, or test gaps
6. Why this matters in TenantPilot
7. Recommended structural correction
8. Delivery recommendation: `hotfix`, `follow-up refactor`, or `dedicated spec required`
## Constraints
- Do not praise the codebase.
- Do not focus on style unless it affects architecture or safety.
- Do not suggest random patterns without proving fit.
- Group multiple symptoms under one deeper diagnosis when appropriate.
- Be explicit when a local fix is insufficient and a dedicated spec is required.
## Repository context
TenantPilot is an enterprise SaaS product for Intune and Microsoft 365 governance, backup, restore, inventory, drift detection, findings, exceptions, operations, and auditability.
The strategic priorities are:
- workspace-first context modeling
- capability-first RBAC
- strong auditability
- deterministic workflow semantics
- provider access through canonical boundaries
- minimal duplication of domain logic across UI surfaces
Return the audit as a concise but substantive findings report.

View File

@ -1,105 +0,0 @@
---
description: Turn TenantPilot architecture audit findings into bounded spec candidates without colliding with active spec numbering.
agent: speckit.specify
---
You are a Senior Staff Engineer and Enterprise SaaS Architect working on TenantPilot / TenantAtlas.
Your task is to produce spec candidates, not implementation code.
Before writing anything, read and use these repository files as binding context:
- `docs/audits/tenantpilot-architecture-audit-constitution.md`
- `docs/audits/2026-03-15-audit-spec-candidates.md`
- `specs/110-ops-ux-enforcement/spec.md`
- `specs/111-findings-workflow-sla/spec.md`
- `specs/134-audit-log-foundation/spec.md`
- `specs/138-managed-tenant-onboarding-draft-identity/spec.md`
## Goal
Turn the existing audit-derived problem clusters into exactly four proposed follow-up spec candidates.
The four candidate themes are:
1. queued execution reauthorization and scope continuity
2. tenant-owned query canon and wrong-tenant guards
3. findings workflow enforcement and audit backstop
4. Livewire context locking and trusted-state reduction
## Numbering rule
- Do not invent or reserve fixed spec numbers unless the current repository state proves they are available.
- If numbering is uncertain, use `Candidate A`, `Candidate B`, `Candidate C`, and `Candidate D`.
- Only recommend a numbering strategy; do not force numbering in the output when collisions are possible.
## Output requirements
Create exactly four spec candidates, one per problem class.
For each candidate provide:
1. Candidate label or confirmed spec number
2. Working title
3. Status: `Proposed`
4. Summary
5. Why this is needed now
6. Boundary to existing specs
7. Problem statement
8. Goals
9. Non-goals
10. Scope
11. Target model
12. Key requirements
13. Risks if not implemented
14. Dependencies and sequencing notes
15. Delivery recommendation: `hotfix`, `dedicated spec`, or `phased spec`
16. Suggested implementation priority: `Critical`, `High`, or `Medium`
17. Suggested slug
At the end provide:
A. Recommended implementation order
B. Which candidates can run in parallel
C. Which candidate should start first and why
D. A numbering strategy recommendation if active spec numbers are not yet known
## Writing rules
- Write in English.
- Use formal enterprise spec language.
- Be concrete and opinionated.
- Focus on structural integrity, not patch-level fixes.
- Treat the audit constitution as binding.
- Explicitly say when UI-only authorization is insufficient.
- Explicitly say when Livewire public state must be treated as untrusted input.
- Explicitly say when negative-path regression tests are required.
- Explicitly say when `OperationRun` or audit semantics must be extended or hardened.
- Do not duplicate adjacent specs; state the boundary clearly.
- Do not collapse all four themes into one umbrella spec.
## Candidate-specific direction
### Candidate A — queued execution reauthorization and scope continuity
- Treat this as an execution trust problem, not a simple `authorize()` omission.
- Cover dispatch-time actor and context capture, handle-time scope revalidation, capability reauthorization, execution denial semantics, and audit visibility.
- Define what happens when authorization or tenant operability changes between dispatch and execution.
### Candidate B — tenant-owned query canon and wrong-tenant guards
- Treat this as canonical data-access hardening.
- Cover tenant-owned and workspace-owned query rules, route model binding safety, canonical query paths, anti-pattern elimination, and required wrong-tenant regression tests.
- Focus on ownership enforcement, not generic repository-pattern advice.
### Candidate C — findings workflow enforcement and audit backstop
- Treat this as a workflow-truth problem.
- Cover formal lifecycle enforcement, invalid transition prevention, reopen and recurrence semantics, and audit backstop requirements.
- Make clear how this extends but does not duplicate Spec 111.
### Candidate D — Livewire context locking and trusted-state reduction
- Treat this as a UI/server trust-boundary hardening problem.
- Cover locked identifiers, untrusted public state, server-side reconstruction of workflow truth, sensitive-state reduction, and misuse regression tests.
- Make clear how this complements but does not duplicate Spec 138.

View File

@ -1,295 +0,0 @@
---
name: browsertest
description: Führe einen vollständigen Smoke-Browser-Test im Integrated Browser für das aktuelle Feature aus, inklusive Happy Path, zentraler Regressionen, Kontext-Prüfung und belastbarer Ergebniszusammenfassung.
license: MIT
metadata:
author: GitHub Copilot
---
# Browser Smoke Test
## What This Skill Does
Use this skill to validate the current feature end-to-end in the integrated browser.
This is a focused smoke test, not a full exploratory test session. The goal is to prove that the primary operator flow:
- loads in the correct auth, workspace, and tenant context
- exposes the expected controls and decision points
- completes the main happy path without blocking issues
- lands in the expected end state or canonical drilldown
- does not show obvious regressions such as broken navigation, missing data, or conflicting actions
The skill should produce a concrete pass or fail result with actionable evidence.
## When To Apply
Activate this skill when:
- the user asks to smoke test the current feature in the browser
- a new Filament page, dashboard signal, report, wizard, or detail flow was just added
- a UI regression fix needs confirmation in a real browser context
- the primary question is whether the feature works from an operator perspective
- you need a quick integration-level check without writing a full browser test suite first
## What Success Looks Like
A successful smoke test confirms all of the following:
- the target route opens successfully
- the visible context is correct
- the main flow is usable
- the expected result appears after interaction
- the route or drilldown destination is correct
- the surface does not obviously violate its intended interaction model
If the test cannot be completed, the output must clearly state whether the blocker is:
- authentication
- missing data or fixture state
- routing
- UI interaction failure
- server error
- an unclear expected behavior contract
Do not guess. If the route or state is blocked, report the blocker explicitly.
## Preconditions
Before running the browser smoke test, make sure you know:
- the canonical route or entry point for the feature
- the primary operator action or happy path
- the expected success state
- whether the feature depends on a specific tenant, workspace, or seeded record
When available, use the feature spec, quickstart, tasks, or current browser page as the source of truth.
## Standard Workflow
### 1. Define the smoke-test scope
Identify:
- the route to open
- the primary action to perform
- the expected end state
- one or two critical regressions that must not break
The smoke test should stay narrow. Prefer one complete happy path plus one critical boundary over broad exploratory clicking.
### 2. Establish the browser state
- Reuse the current browser page if it already matches the target feature.
- Otherwise open the canonical route.
- Confirm the current auth and scope context before interacting.
For this repo, that usually means checking whether the page is on:
- `/admin/...` for workspace-context surfaces
- `/admin/t/{tenant}/...` for tenant-context surfaces
### 3. Inspect before acting
- Use `read_page` before interacting so you understand the live controls, refs, headings, and route context.
- Prefer `read_page` over screenshots for actual interaction planning.
- Use screenshots only for visual evidence or when the user asks for them.
### 4. Execute the primary happy path
Run the smallest meaningful flow that proves the feature works.
Typical steps include:
- open the page
- verify heading or key summary text
- click the primary CTA or row
- fill the minimum required form fields
- confirm modal or dialog text when relevant
- submit or navigate
- verify the expected destination or changed state
After each meaningful action, re-read the page so the next step is based on current DOM state.
### 5. Validate the outcome
Check the exact result that matters for the feature.
Examples:
- a new row appears
- a status changes
- a success message appears
- a report filter changes the result set
- a row click lands on the canonical detail page
- a dashboard signal links to the correct report page
### 6. Check for obvious regressions
Even in a smoke test, verify a few core non-negotiables:
- the page is not blank or half-rendered
- the main action is present and usable
- the visible context is correct
- the drilldown destination is canonical
- no obviously duplicated primary actions exist
- no stuck modal, spinner, or blocked interaction remains onscreen
### 7. Capture evidence and summarize clearly
Your result should state:
- route tested
- context used
- steps executed
- pass or fail
- exact blocker or discrepancy if failed
Include a screenshot only when it adds value.
## Tool Usage Guidance
Use the browser tools in this order by default:
1. `read_page`
2. `click_element`
3. `type_in_page`
4. `handle_dialog` when needed
5. `navigate_page` or `open_browser_page` only when route changes are required
6. `run_playwright_code` only if the normal browser tools are insufficient
7. `screenshot_page` for evidence, not for primary navigation logic
## Repo-Specific Guidance For TenantPilot
### Workspace surfaces
For `/admin` pages and similar workspace-context surfaces:
- verify the page is reachable without forcing tenant-route assumptions
- confirm any summary signal or CTA lands on the canonical destination
- verify calm-state versus attention-state behavior when the feature defines both
### Tenant surfaces
For `/admin/t/{tenant}/...` pages:
- verify the tenant context is explicit and correct
- verify drilldowns stay in the intended tenant scope
- treat cross-tenant leakage or silent scope changes as failures
### Filament list or report surfaces
For Filament tables, reports, or registry-style pages:
- verify the heading and table shell render
- verify fixed filters or summary controls exist when the spec requires them
- verify row click or the primary inspect affordance behaves as designed
- verify empty-state messaging is specific rather than generic when the feature defines custom behavior
### Filament detail pages
For detail or view surfaces:
- verify the canonical record loads
- verify expected sections or summary content are present
- verify critical actions or drillbacks are usable
## Result Format
Use a compact result format like this:
```text
Browser smoke result: PASS
Route: /admin/findings/hygiene
Context: workspace member with visible hygiene issues
Steps: opened report -> verified filters -> clicked finding row -> landed on canonical finding detail
Verified: report rendered, primary interaction worked, drilldown route was correct
```
If the test fails:
```text
Browser smoke result: FAIL
Route: /admin/findings/hygiene
Context: authenticated workspace member
Failed step: clicking the summary CTA
Expected: navigate to /admin/findings/hygiene
Actual: remained on /admin with no route change
Blocker: CTA appears rendered but is not interactive
```
## Examples
### Example 1: Smoke test a new report page
Use this when the feature adds a new read-only report.
Steps:
- open the canonical report route
- verify the page heading and main controls
- confirm the table or defined empty state is visible
- click one row or primary inspect affordance
- verify navigation lands on the canonical detail route
Pass criteria:
- report loads
- intended controls exist
- primary inspect path works
### Example 2: Smoke test a dashboard signal
Use this when the feature adds a summary signal on `/admin`.
Steps:
- open `/admin`
- find the signal
- verify the visible count or summary text
- click the CTA
- confirm navigation lands on the canonical downstream surface
Pass criteria:
- signal is visible in the correct state
- CTA text is present
- CTA opens the correct route
### Example 3: Smoke test a tenant detail follow-up
Use this when a workspace-level surface should drill into a tenant-level detail page.
Steps:
- open the workspace-level surface
- trigger the drilldown
- verify the target route includes the correct tenant and record
- confirm the target page actually loads the expected detail content
Pass criteria:
- drilldown route is canonical
- tenant context is correct
- destination content matches the selected record
## Common Pitfalls
- Clicking before reading the page state and refs
- Treating a blocked auth session as a feature failure
- Confusing workspace-context routes with tenant-context routes
- Reporting visual impressions without validating the actual interaction result
- Forgetting to re-read the page after a modal opens or a route changes
- Claiming success without verifying the final destination or changed state
## Non-Goals
This skill does not replace:
- full exploratory QA
- formal Pest browser coverage
- accessibility review
- visual regression approval
- backend correctness tests
It is a fast, real-browser confidence pass for the current feature.

View File

@ -1,8 +0,0 @@
---
name: giteaflow
description: Describe what this skill does and when to use it. Include keywords that help agents identify relevant tasks.
---
<!-- Tip: Use /create-skill in chat to generate content with agent assistance -->
comit all changes, push to remote, and create a pull request against dev with gitea mcp

View File

@ -1,167 +0,0 @@
---
name: pest-testing
description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works."
license: MIT
metadata:
author: laravel
---
# Pest Testing 4
## When to Apply
Activate this skill when:
- Creating new tests (unit, feature, or browser)
- Modifying existing tests
- Debugging test failures
- Working with browser testing or smoke testing
- Writing architecture tests or visual regression tests
## Documentation
Use `search-docs` for detailed Pest 4 patterns and documentation.
## Basic Usage
### Creating Tests
All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
### Test Organization
- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
- Browser tests: `tests/Browser/` directory.
- Do NOT remove tests without approval - these are core application code.
### Basic Test Structure
<!-- Basic Pest Test Example -->
```php
it('is true', function () {
expect(true)->toBeTrue();
});
```
### Running Tests
- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
- Run all tests: `php artisan test --compact`.
- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.
## Assertions
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
<!-- Pest Response Assertion -->
```php
it('returns all', function () {
$this->postJson('/api/docs', [])->assertSuccessful();
});
```
| Use | Instead of |
|-----|------------|
| `assertSuccessful()` | `assertStatus(200)` |
| `assertNotFound()` | `assertStatus(404)` |
| `assertForbidden()` | `assertStatus(403)` |
## Mocking
Import mock function before use: `use function Pest\Laravel\mock;`
## Datasets
Use datasets for repetitive tests (validation rules, etc.):
<!-- Pest Dataset Example -->
```php
it('has emails', function (string $email) {
expect($email)->not->toBeEmpty();
})->with([
'james' => 'james@laravel.com',
'taylor' => 'taylor@laravel.com',
]);
```
## Pest 4 Features
| Feature | Purpose |
|---------|---------|
| Browser Testing | Full integration tests in real browsers |
| Smoke Testing | Validate multiple pages quickly |
| Visual Regression | Compare screenshots for visual changes |
| Test Sharding | Parallel CI runs |
| Architecture Testing | Enforce code conventions |
### Browser Test Example
Browser tests run in real browsers for full integration testing:
- Browser tests live in `tests/Browser/`.
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
- Use `RefreshDatabase` for clean state per test.
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging.
<!-- Pest Browser Test Example -->
```php
it('may reset the password', function () {
Notification::fake();
$this->actingAs(User::factory()->create());
$page = visit('/sign-in');
$page->assertSee('Sign In')
->assertNoJavaScriptErrors()
->click('Forgot Password?')
->fill('email', 'nuno@laravel.com')
->click('Send Reset Link')
->assertSee('We have emailed your password reset link!');
Notification::assertSent(ResetPassword::class);
});
```
### Smoke Testing
Quickly validate multiple pages have no JavaScript errors:
<!-- Pest Smoke Testing Example -->
```php
$pages = visit(['/', '/about', '/contact']);
$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
```
### Visual Regression Testing
Capture and compare screenshots to detect visual changes.
### Test Sharding
Split tests across parallel processes for faster CI runs.
### Architecture Testing
Pest 4 includes architecture testing (from Pest 3):
<!-- Architecture Test Example -->
```php
arch('controllers')
->expect('App\Http\Controllers')
->toExtendNothing()
->toHaveSuffix('Controller');
```
## Common Pitfalls
- Not importing `use function Pest\Laravel\mock;` before using mock
- Using `assertStatus(200)` instead of `assertSuccessful()`
- Forgetting datasets for repetitive validation tests
- Deleting tests without approval
- Forgetting `assertNoJavaScriptErrors()` in browser tests

View File

@ -1,625 +0,0 @@
---
name: platform-feature-finish
description: Commit, push, create a Gitea PR from a TenantPilot platform feature branch into platform-dev, and optionally refresh the platform-dev to dev integration PR by rebase.
---
# Skill: platform-feature-finish
## Purpose
Automate the TenantPilot platform feature completion workflow.
Trigger this skill when the user says something like:
- "alles committen pushen und PR gegen platform-dev"
- "feature fertig, bitte PR erstellen"
- "platform feature abschließen"
- "commit push PR mit Gitea MCP"
- "mach PR gegen platform-dev"
- "finish platform feature"
- "platform-dev nach dev vorbereiten"
- "platform-dev PR aktualisieren"
- "out-of-date mit dev beheben"
- "integration PR refresh"
- "platform-dev auf dev rebasen"
This skill handles:
1. Validate current Git branch
2. Commit all feature changes
3. Push current feature branch
4. Create a Gitea pull request into `platform-dev`
5. Refresh the `platform-dev``dev` integration PR when explicitly requested
6. Report the PR link and next integration step
---
## Branch Model
TenantPilot uses area branches:
```text
dev = shared integration branch
platform-dev = platform/application area integration branch
website-dev = website/marketing area integration branch
```
For platform features:
```text
platform-dev
feature branch
PR back to platform-dev
platform-dev → dev integration PR
```
Rules:
- Platform feature branches MUST target `platform-dev`.
- Do NOT target `dev` directly unless the user explicitly asks.
- Do NOT use `website-dev` for platform features.
- `platform-dev` is the default PR base for TenantPilot platform/application work.
- `dev` is the shared integration branch.
### Solo Workflow Rule
The user works alone on `platform-dev`.
For refreshing the integration branch before opening or updating the PR `platform-dev``dev`, prefer rebase over merge.
Do not repeatedly merge `origin/dev` into `platform-dev` for refresh.
Avoid creating repeated merge commits like:
```text
Merge remote-tracking branch 'origin/dev' into platform-dev
```
Use `--force-with-lease`, never plain `--force`.
If rebase conflicts occur, stop and report the conflict files.
---
## Preconditions
Before committing:
1. Confirm repository root.
2. Confirm current branch is not protected.
Protected branches:
```text
dev
platform-dev
website-dev
main
master
```
If the current branch is protected, STOP and report:
```text
Ich bin auf einem geschützten Branch. Bitte zuerst einen Feature-Branch auschecken.
```
3. Confirm remote exists.
4. Confirm there are local changes, untracked files, or unpushed commits.
5. Confirm there are no unresolved conflicts.
Do not ask for confirmation unless:
- The current branch is protected.
- Git status indicates unresolved conflicts.
- There is no remote configured.
- `.env` or other local secret/config files would be committed.
- Commit fails.
- Push fails.
- Gitea MCP PR creation fails.
---
## Required Tools
Use terminal for Git operations.
Use Gitea MCP for pull request creation.
Preferred Gitea MCP operation:
```text
create_pull_request
```
Required PR parameters:
```json
{
"owner": "ahmido",
"repo": "TenantAtlas",
"head": "<current-feature-branch>",
"base": "platform-dev",
"title": "<generated-title>",
"body": "<generated-body>"
}
```
---
## Workflow
### Step 1 — Inspect Git state
Run:
```bash
git rev-parse --show-toplevel
git rev-parse --abbrev-ref HEAD
git status --porcelain
git status -sb
git config --get remote.origin.url
git log --oneline --max-count=5
```
Determine:
- repository root
- current branch
- changed files
- untracked files
- remote URL
- whether there are unpushed commits
- whether unresolved conflicts exist
If the current branch is protected, stop.
If unresolved conflicts exist, stop.
If no remote exists, stop.
---
### Step 2 — Check for local environment files
Before `git add -A`, check whether local environment/config files are modified or untracked:
```bash
git status --porcelain | grep -E '(^.. \.env$|^.. apps/platform/\.env$|^.. .*\.env$)' || true
```
If `.env` or another environment file is included, STOP and report:
```text
Achtung: Eine .env-/Environment-Datei ist geändert oder untracked. Ich committe das nicht automatisch. Bitte prüfen oder aus dem Commit entfernen.
```
Do not commit secrets or local runtime configuration.
---
### Step 3 — Build commit message
Use the current branch name.
If branch starts with a spec number, for example:
```text
256-external-support-desk-handoff
```
Generate:
```text
feat(specs/256): external support desk handoff
```
If branch does not contain a spec number, generate:
```text
feat(platform): complete <branch-name>
```
Rules:
- Use lowercase subject.
- Use feature-style subject.
- Do not include `WIP`.
- Do not include `final`.
- Do not include overly generic `updates`.
Examples:
```text
feat(specs/256): external support desk handoff
feat(specs/252): platform localization v1
feat(platform): improve tenant review workspace
```
---
### Step 4 — Commit all changes
Run:
```bash
git add -A
git commit -m "<commit-message>"
```
If there are no local changes to commit, continue only if the branch has unpushed commits.
Check unpushed commits with:
```bash
git status -sb
git log --oneline origin/<current-branch>..HEAD
```
If there are no local changes and no unpushed commits, report:
```text
Es gibt keine lokalen Änderungen und keine unpushed commits. Ich erstelle keinen leeren Commit.
```
Then continue to PR creation only if the branch already exists remotely or can be pushed.
---
### Step 5 — Push branch
Run:
```bash
git push --set-upstream origin <current-branch>
```
If the upstream already exists, this is acceptable.
Never force-push unless the user explicitly requests it.
---
### Step 6 — Create PR into platform-dev via Gitea MCP
Use Gitea MCP to create a pull request:
```json
{
"owner": "ahmido",
"repo": "TenantAtlas",
"head": "<current-feature-branch>",
"base": "platform-dev",
"title": "<commit-message>",
"body": "Implements platform feature branch `<current-feature-branch>`.\n\nTarget branch: `platform-dev`.\n\nFollow-up integration path after merge:\n\n`platform-dev``dev`."
}
```
If a PR already exists for the same branch and base, do not create a duplicate.
Report the existing PR if available.
---
## Optional Step — Check platform-dev to dev PR
After creating the feature PR, check whether an open integration PR exists:
```text
platform-dev → dev
```
If a Gitea MCP list/search pull request function is available, use it.
If one exists, report:
```text
Der Folge-PR `platform-dev``dev` existiert bereits: <url>
```
If none exists, report:
```text
Nach dem Merge dieses Feature-PRs sollte der Integrations-PR `platform-dev``dev` erstellt oder aktualisiert werden.
```
Do not automatically create the `platform-dev``dev` PR unless the user explicitly asks for it.
Reason: before the feature PR is merged into `platform-dev`, the integration PR may not include the new feature yet.
---
## Integration Refresh Mode
Use this mode when the user explicitly says one of the following:
- "platform-dev nach dev vorbereiten"
- "platform-dev PR aktualisieren"
- "out-of-date mit dev beheben"
- "integration PR refresh"
- "platform-dev auf dev rebasen"
- "auch platform-dev nach dev"
- "und danach platform-dev nach dev"
- "full integration"
- "kompletten platform-dev zu dev PR machen"
- "folge-pr erstellen"
This mode prepares or updates the integration PR:
```text
platform-dev → dev
```
Because the user works alone on `platform-dev`, prefer rebase over merge.
### Integration Refresh Preconditions
Before running this mode:
1. Ensure the working tree is clean.
2. Ensure there are no unresolved conflicts.
3. Fetch remote branches.
4. Ensure `origin/platform-dev` exists.
5. Ensure `origin/dev` exists.
If the working tree is dirty, STOP and report:
```text
Der Working Tree ist nicht sauber. Bitte erst Änderungen committen, stashen oder verwerfen, bevor `platform-dev` auf `dev` rebased wird.
```
If unresolved conflicts exist, STOP and report the conflict files.
### Integration Refresh Workflow
Run:
```bash
git fetch origin
git checkout platform-dev
git reset --hard origin/platform-dev
git rebase origin/dev
git push --force-with-lease origin platform-dev
```
After pushing, verify that `origin/dev` is now an ancestor of `origin/platform-dev`:
```bash
git fetch origin
git merge-base --is-ancestor origin/dev origin/platform-dev \
&& echo "OK: platform-dev contains dev" \
|| echo "OUTDATED: platform-dev does not contain dev"
```
If the verification prints `OUTDATED`, stop and report it. Do not claim the PR is up-to-date.
Rules:
- Do not merge `origin/dev` into `platform-dev` for this refresh.
- Do not create repeated merge commits from `origin/dev` into `platform-dev`.
- Use `git push --force-with-lease origin platform-dev` after a successful rebase.
- Never use plain `git push --force`.
- If `git rebase origin/dev` reports conflicts, stop immediately.
- Do not continue to PR creation while a rebase is unresolved.
- Do not auto-merge the PR.
- Do not claim Gitea will remove the out-of-date warning unless the ancestor check succeeds.
If rebase conflicts occur, report:
```text
Rebase-Konflikte erkannt. Ich habe gestoppt.
Konfliktdateien:
<files>
Bitte Konflikte lösen, dann `git rebase --continue` ausführen oder den Rebase mit `git rebase --abort` abbrechen.
```
### Create or Report Integration PR
After the rebase, push, and ancestor verification succeeded, use Gitea MCP to create or report the integration PR:
```json
{
"owner": "ahmido",
"repo": "TenantAtlas",
"head": "platform-dev",
"base": "dev",
"title": "chore(platform): merge platform-dev into dev",
"body": "Integrates latest TenantPilot platform changes from `platform-dev` into `dev`.\n\nThis PR was created by agent on user request; do not merge automatically."
}
```
If an open PR already exists for `platform-dev``dev`, do not create a duplicate. Report the existing PR.
### Integration Refresh Reporting Format
Final response for this mode must include:
```text
Fertig.
- Branch aktualisiert: platform-dev
- Refresh-Methode: rebase auf origin/dev
- Ancestor-Check: origin/dev ist Ancestor von origin/platform-dev
- Push: --force-with-lease origin/platform-dev
- Integration PR: <url>
- Base: dev
- Hinweis: PR wurde nicht automatisch gemerged.
```
Do not claim tests passed unless they were actually executed.
---
## Reporting Format
Final response must be concise and include:
```text
Fertig.
- Branch: <branch>
- Commit: <commit-sha or "keine neuen Änderungen">
- Push: origin/<branch>
- PR: <url>
- Base: platform-dev
- Nächster Schritt: Nach Merge `platform-dev``dev` PR aktualisieren/erstellen
```
If tests were not run, say:
```text
Tests wurden in diesem Skill nicht automatisch ausgeführt.
```
Do not claim tests passed unless the tool actually ran them.
---
## Safety Rules
- Never commit directly to `dev`, `platform-dev`, `website-dev`, `main`, or `master`.
- Never force-push unless explicitly requested.
- For Integration Refresh Mode only, `git push --force-with-lease origin platform-dev` is allowed because the user works alone on `platform-dev`; never use plain `--force`.
- Never auto-merge PRs unless explicitly requested.
- Never target `dev` directly for platform feature PRs unless explicitly requested.
- Never delete branches unless explicitly requested.
- Never claim tests were run unless the tool actually ran them.
- Never commit `.env`, secrets, local tokens, local mock-server configuration, or temporary runtime-only changes.
- If migrations were created, mention that the target environment needs migration execution after deployment.
- If unresolved conflicts exist, stop.
---
## Useful Commands
Inspect:
```bash
git rev-parse --show-toplevel
git rev-parse --abbrev-ref HEAD
git status --porcelain
git status -sb
git config --get remote.origin.url
```
Detect protected branch:
```bash
branch="$(git rev-parse --abbrev-ref HEAD)"
case "$branch" in
dev|platform-dev|website-dev|main|master)
echo "PROTECTED_BRANCH:$branch"
exit 2
;;
esac
```
Detect unresolved conflicts:
```bash
git diff --name-only --diff-filter=U
```
Detect `.env` changes:
```bash
git status --porcelain | grep -E '(^.. \.env$|^.. apps/platform/\.env$|^.. .*\.env$)' || true
```
Commit:
```bash
git add -A
git commit -m "<message>"
```
Push:
```bash
git push --set-upstream origin "$(git rev-parse --abbrev-ref HEAD)"
```
Latest commit:
```bash
git rev-parse --short HEAD
git log -1 --pretty=%s
```
Integration refresh:
```bash
git fetch origin
git checkout platform-dev
git reset --hard origin/platform-dev
git rebase origin/dev
git push --force-with-lease origin platform-dev
```
Verify integration refresh:
```bash
git fetch origin
git merge-base --is-ancestor origin/dev origin/platform-dev \
&& echo "OK: platform-dev contains dev" \
|| echo "OUTDATED: platform-dev does not contain dev"
```
Check rebase conflicts:
```bash
git diff --name-only --diff-filter=U
```
---
## Example User Request
User:
```text
alles committen pushen und pr gegen platform-dev mit gitea mcp
```
Assistant should:
1. Check current branch.
2. Stop if branch is protected.
3. Stop if `.env` or secrets would be committed.
4. Commit all changes.
5. Push current branch.
6. Create PR into `platform-dev` with Gitea MCP.
7. Report result.
Do not ask unnecessary follow-up questions.
---
## Example Integration Refresh Request
User:
```text
platform-dev PR aktualisieren
```
Assistant should:
1. Ensure the working tree is clean.
2. Fetch origin.
3. Checkout `platform-dev`.
4. Reset local `platform-dev` to `origin/platform-dev`.
5. Rebase `platform-dev` onto `origin/dev`.
6. Push with `--force-with-lease`.
7. Verify `origin/dev` is an ancestor of `origin/platform-dev`.
8. Create or report the PR `platform-dev``dev`.
9. Report result.
Do not merge the PR automatically.

View File

@ -1,447 +0,0 @@
---
name: spec-kit-implementation-loop
description: Implement an existing TenantPilot/TenantAtlas Spec Kit feature, run tests, browser smoke checks where applicable, post-implementation analysis, fix all confirmed in-scope findings when safe and bounded, and repeat until no in-scope findings remain or a stop condition is reached.
---
# Skill: Spec Kit Implementation Loop
## Purpose
Use this skill to implement an already prepared TenantPilot/TenantAtlas Spec Kit feature and verify it with a bounded implementation loop.
This skill assumes `spec.md`, `plan.md`, and `tasks.md` already exist and have passed preparation readiness or have been explicitly accepted by the user.
The intended workflow is:
```text
active or explicitly named spec
→ inspect repo truth, constitution, spec, plan, tasks, and relevant code/tests
→ evaluate implementation gates
→ implement strictly task-by-task
→ run relevant tests/checks
→ run browser smoke test when UI/user-facing flows are affected
→ run strict post-implementation analysis
→ fix confirmed in-scope findings
→ repeat test + browser smoke + analysis + fix loop until clean or bounded stop condition is reached
→ final implementation report
```
## When to Use
Use this skill when the user asks to:
- implement an active or explicitly named Spec Kit feature
- run Spec Kit implement
- analyze after implementation
- fix implementation findings
- repeat implementation verification until no confirmed in-scope findings remain
- run tests and browser smoke checks after implementation
Typical user prompts:
```text
Implementiere die aktive Spec und analysiere danach, ob alles passt.
```
```text
Implementiere specs/243-product-usage-adoption-telemetry streng nach tasks.md.
```
```text
Mach Spec Kit implement und danach analyse. Behebe alle Abweichungen und wiederhole bis sauber.
```
```text
Implementiere die vorbereitete Spec. Danach Tests, Browser Smoke Test falls UI betroffen ist, Analyse und Fix-Loop bis keine In-Scope Findings mehr offen sind.
```
## Hard Rules
- Work strictly repo-based.
- Implement only the active or explicitly named Spec Kit feature.
- Do not choose a new candidate.
- Do not create a new spec.
- Do not expand scope beyond `spec.md`, `plan.md`, and `tasks.md`.
- Do not silently add roadmap features, adjacent UX rewrites, speculative architecture, or unrelated refactors.
- Follow the repository constitution and existing Spec Kit conventions.
- Preserve TenantPilot/TenantAtlas terminology.
- Prefer small, reviewable patches over broad rewrites.
- Treat repository truth as authoritative over assumptions.
- If repository truth conflicts with implementation scope, stop and report the conflict unless there is an obvious minimal correction inside active spec scope.
- Fix only confirmed findings from tests, static checks, browser smoke checks, or post-implementation analysis.
- Fix all confirmed in-scope findings, regardless of severity, when they are safe and bounded.
- Do not leave Medium/Low findings open silently. If they are not fixed, document exactly why.
- Never hide failing tests, weaken assertions, delete meaningful coverage, or mark tasks complete without implementation evidence.
- Do not run destructive commands.
- Do not force checkout, reset, stash, rebase, merge, or delete branches.
- Do not perform database-destructive actions unless the repository test workflow explicitly requires isolated test database resets.
- Do not continue analysis/fix loops indefinitely.
- Do not move from implementation to final status unless the Test Gate, Browser Smoke Test Gate where applicable, and Post-Implementation Analysis Gate have been evaluated.
- Do not claim merge-readiness unless the Merge Readiness Gate passes.
## Required Inputs
The user should provide at least one of:
- explicit spec directory such as `specs/<number>-<slug>/`
- instruction to use the current active Spec Kit feature
- instruction to implement the prepared/current spec
If the active spec cannot be determined safely, inspect the repository Spec Kit context first. If it is still ambiguous, stop and ask for the specific spec directory.
## Required Repository Checks
Always check:
1. active Spec Kit context / current branch
2. git status
3. `.specify/memory/constitution.md`
4. the active spec directory
5. `spec.md`
6. `plan.md`
7. `tasks.md`
8. relevant templates or conventions under `.specify/templates/`
9. nearby existing specs with related terminology or scope
10. application code surfaces referenced by the active spec
11. existing tests related to the changed behavior
## Git and Branch Safety
Before making implementation changes:
1. Check the current branch.
2. Check whether the working tree is clean.
3. If there are unrelated uncommitted changes, stop and report them. Do not continue.
4. If the working tree only contains user-intended changes for this operation, continue cautiously.
5. Do not force checkout, reset, stash, rebase, merge, or delete branches.
6. Do not overwrite unrelated work.
## Quality Gates
### Gate 1: Spec Readiness Gate
Required before implementation starts.
Pass criteria:
- `spec.md`, `plan.md`, and `tasks.md` exist.
- The spec has clear problem statement, user value, functional requirements, out-of-scope boundaries, acceptance criteria, assumptions, and risks.
- The plan identifies likely affected repo surfaces and does not contradict repository architecture.
- The tasks are small, ordered, verifiable, and include test/validation tasks.
- RBAC, workspace/tenant isolation, auditability, OperationRun semantics, evidence/result-truth, and UX requirements are addressed where relevant.
- No open question blocks safe implementation.
- The scope is small enough for a bounded implementation loop.
Fail behavior:
- Stop before implementation.
- Report readiness gaps.
- Do not compensate for an unclear spec by inventing implementation scope.
### Gate 2: Implementation Scope Gate
Required before changing application code.
Pass criteria:
- The active spec directory is known.
- The implementation target is traceable to specific tasks in `tasks.md`.
- The affected files/surfaces are consistent with `plan.md` or clearly justified by repository truth.
- No required change would introduce unrelated product behavior.
- No required change conflicts with constitution, existing architecture, RBAC/isolation boundaries, or source-of-truth semantics.
Fail behavior:
- Stop before code changes and report the conflict or ambiguity.
- Suggest a minimal spec/plan/tasks correction if the issue is in the artifacts rather than the codebase.
### Gate 3: Test Gate
Required after implementation and after each fix iteration.
Pass criteria:
- Targeted tests for changed behavior pass.
- Relevant existing tests pass or failures are proven unrelated and documented.
- Static analysis, linting, formatting, or type checks used by the repository pass when applicable.
- Security/governance-relevant changes have backend, policy, or domain coverage; UI-only verification is not enough.
- Regression coverage exists for each fixed Blocker or High finding where practical.
Fail behavior:
- Fix in-scope failures before post-implementation analysis.
- If failures are unrelated or pre-existing, document evidence and continue only if they do not invalidate the active spec.
- Do not weaken tests to pass the gate.
### Gate 4: Browser Smoke Test Gate
Required before claiming implementation is ready for manual review/merge when the change affects Filament UI, Livewire interactions, navigation, forms, tables, actions, modals, dashboards, operation drilldowns, tenant/workspace context, or any user-facing flow.
Not required for backend-only, domain-only, enum-only, contract-only, or test-only changes unless those changes alter a user-facing flow.
Pass criteria:
- The relevant page or flow loads in a real browser or the repository's browser-testing harness.
- The primary action introduced or changed by the spec can be executed successfully.
- Expected UI states, labels, badges, actions, empty states, tables, forms, modals, and navigation are visible where relevant.
- Workspace/tenant context is preserved across the tested flow where relevant.
- RBAC/capability-dependent visibility behaves as expected where practical to verify.
- Livewire interactions complete without visible runtime errors.
- No relevant browser console errors occur.
- No failed network requests occur for the tested flow, except known unrelated development noise that is explicitly documented.
- OperationRun, audit, evidence, result, or support-diagnostic drilldowns work where relevant.
- The smoke-tested path is documented in the final response.
Fail behavior:
- Fix in-scope browser, UX, Livewire, navigation, or runtime failures before claiming merge-readiness.
- If a browser issue is unrelated existing debt, document evidence and residual risk.
- Do not treat a passing browser smoke test as a substitute for backend, policy, domain, security, feature, or integration tests.
- Do not expand the smoke test into a full E2E suite unless the user explicitly asks for that.
### Gate 5: Post-Implementation Analysis Gate
Required after implementation and after each fix iteration.
Pass criteria:
- The implementation has been checked against `spec.md`, `plan.md`, `tasks.md`, and constitution.
- All completed tasks have implementation evidence.
- No confirmed in-scope findings remain.
- Medium/Low findings are fixed when they are inside active spec scope, clearly bounded, and safe.
- Medium/Low findings that remain open are explicitly documented with one of these reasons:
- out of scope
- requires separate spec
- risky refactor
- existing unrelated debt
- not reproducible
- blocked by unclear product/architecture decision
- No scope expansion was introduced during fixes.
Fail behavior:
- Fix confirmed in-scope findings, regardless of severity, when the fix is safe and bounded.
- Stop instead of fixing when remediation would expand scope, contradict repo architecture, introduce risky refactors, or repeat the same failed fix twice.
### Gate 6: Merge Readiness Gate
Required before claiming the implementation is ready for manual review/merge.
Pass criteria:
- Spec Readiness Gate passed.
- Implementation Scope Gate passed.
- Test Gate passed.
- Browser Smoke Test Gate passed when applicable, or was explicitly marked not applicable with a reason.
- Post-Implementation Analysis Gate passed.
- `tasks.md` reflects actual completion status.
- No confirmed in-scope findings remain.
- All remaining findings are documented as out-of-scope, follow-up candidates, unrelated existing debt, or explicit residual risks.
- Final response includes changed files, tests/checks run, browser smoke result, iterations performed, residual risks, and follow-up candidates.
Fail behavior:
- Do not claim merge-readiness.
- Report the failed gate, remaining risks, and the smallest recommended next action.
## Implementation Loop
Execute the loop in bounded phases:
1. Evaluate the Spec Readiness Gate.
2. Evaluate the Implementation Scope Gate before changing application code.
3. Implement the active Spec Kit feature scope task-by-task.
4. Run targeted tests and relevant static/dynamic checks.
5. Evaluate the Test Gate.
6. Run a Browser Smoke Test when the change affects UI/user-facing flows.
7. Evaluate the Browser Smoke Test Gate as passed, failed, or not applicable with a reason.
8. Run strict post-implementation analysis against spec, plan, tasks, constitution, changed code, changed tests, browser smoke results where applicable, and relevant existing patterns.
9. Evaluate the Post-Implementation Analysis Gate.
10. Identify confirmed findings by severity: Blocker, High, Medium, Low.
11. Fix all confirmed in-scope findings regardless of severity when safe and bounded.
12. Do not fix findings that require scope expansion, risky unrelated refactors, or architectural/product decisions outside the active spec; document them as follow-up/residual risks with reasons.
13. Re-run relevant tests and browser smoke checks where applicable after fixes.
14. Repeat test + browser smoke + analysis + fix loop until no confirmed in-scope findings remain or a stop condition is reached.
15. Evaluate the Merge Readiness Gate.
16. Report final implementation status, changed files, tests, browser smoke result, residual risks, failed/passed gates, and manual review prompt.
## Stop Conditions
Stop the implementation loop when any of the following is true:
- No confirmed in-scope findings remain.
- The same finding appears twice after attempted fixes.
- A required fix conflicts with the spec, plan, constitution, or repository architecture.
- A required fix would expand scope beyond the active spec.
- A required fix would require a risky unrelated refactor.
- A required fix depends on an unresolved product or architecture decision.
- Tests reveal an unrelated pre-existing failure that cannot be safely fixed inside the active spec.
- Browser smoke testing reveals an unrelated pre-existing UI/runtime failure that cannot be safely fixed inside the active spec.
- Three analysis/fix iterations have already been completed.
- The repository state is ambiguous enough that continuing would risk damaging architecture or data semantics.
When stopping before full cleanliness, report exactly why the loop stopped and what remains.
## Post-Implementation Analysis Prompt
Use this prompt internally after implementation and after each fix iteration:
```markdown
Du bist ein Senior Staff Software Engineer, Software Architect und Enterprise SaaS Reviewer.
Analysiere die Implementierung der aktiven Spec streng repo-basiert.
Ziel:
Prüfe, ob die Umsetzung vollständig, konsistent, getestet und constitution-konform ist.
Prüfe gegen:
- spec.md
- plan.md
- tasks.md
- .specify/memory/constitution.md
- geänderte Anwendungscodes
- geänderte Tests
- Browser-Smoke-Test-Ergebnis, falls UI/user-facing Flows betroffen sind
- bestehende Repository-Patterns
Wichtig:
- Keine Spekulation ohne Repo-Beleg.
- Keine Scope-Erweiterung.
- Keine neuen Produktideen als Pflicht-Fixes.
- Findings nach Blocker, High, Medium, Low gruppieren.
- Für jedes Finding konkrete Datei-/Code-Belege nennen.
- Für jedes Finding eine minimale Remediation nennen.
- Separat ausweisen, welche Findings innerhalb der aktiven Spec behoben werden müssen.
- Medium/Low Findings innerhalb der aktiven Spec ebenfalls zur Behebung markieren, wenn sie sicher und bounded sind.
- Bei UI-/Filament-/Livewire-Änderungen prüfen, ob ein Browser Smoke Test durchgeführt wurde und ob der getestete Operator-Flow wirklich funktioniert.
- Findings, die nicht behoben werden sollen, nur als Follow-up/Residual Risk ausweisen, wenn sie out of scope, risky refactor, unrelated existing debt, not reproducible oder durch eine offene Produkt-/Architekturentscheidung blockiert sind.
- Wenn keine bestätigten In-Scope Findings verbleiben, klare Implementierungsfreigabe geben.
```
## Task Completion Rules
- Keep `tasks.md` aligned with actual implementation status.
- Check off tasks only after the implementation and test evidence exists.
- If a task is obsolete because repository truth proves a different path, update the task note with the reason instead of silently deleting it.
- If a task cannot be completed inside scope, leave it unchecked and report why.
## Testing Rules
- Add or update tests for all changed business behavior.
- Include RBAC and workspace/tenant isolation tests where relevant.
- Include OperationRun, audit, evidence, or result-truth tests where relevant.
- Prefer regression tests for every fixed Blocker or High finding.
- Add regression tests for Medium/Low findings when the behavior is important and testable without excessive churn.
- Do not weaken tests to pass the suite.
- Do not treat a green UI path as sufficient without backend or policy coverage when the behavior is security- or governance-relevant.
## Browser Smoke Test Rules
Apply these rules when the active spec changes Filament UI, Livewire interactions, navigation, forms, tables, actions, modals, dashboards, operation drilldowns, tenant/workspace context, or any user-facing flow.
The browser smoke test should be narrow and focused. It is not a full E2E suite unless explicitly requested.
Minimum smoke path:
1. Open the relevant page or entry point.
2. Confirm the expected workspace/tenant context where relevant.
3. Confirm the changed or newly introduced UI element is visible.
4. Execute the primary action or interaction changed by the spec.
5. Confirm the expected result state, notification, redirect, table update, modal state, operation link, or drilldown.
6. Check for relevant console errors.
7. Check for failed network requests related to the tested flow.
8. Document the tested path in the final response.
For TenantPilot/TenantAtlas, pay special attention to:
- Filament actions and header actions
- Livewire polling, modals, validation, and actions
- workspace/tenant context preservation
- RBAC/capability-dependent action visibility
- OperationRun links and drilldown continuity
- audit/evidence/result/support-diagnostic drilldowns where relevant
- empty states, badges, labels, and decision guidance where relevant
Browser smoke testing is required for UI/user-facing changes and optional for backend-only changes.
Do not treat browser smoke success as proof that backend security, policies, domain logic, auditability, or workspace/tenant isolation are correct. Those still require automated tests or repo-based verification.
## Failure Handling
If an implementation step, test phase, browser smoke phase, or post-implementation analysis fails:
1. Stop at the relevant gate or stop condition.
2. Report the failing command or phase.
3. Summarize the error.
4. Do not attempt unrelated implementation as a workaround.
5. Suggest the smallest safe next action.
If the branch or working tree state is unsafe:
1. Stop before implementation changes.
2. Report the current branch and relevant uncommitted files.
3. Ask the user to commit, stash, or move to a clean worktree.
## Final Response Requirements
Respond with:
1. Active spec directory
2. Summary of implemented changes
3. Tests/checks run and their results
4. Browser smoke test result, tested path, or not-applicable reason
5. Quality gates passed/failed and number of analysis/fix iterations performed
6. Remaining in-scope findings, if any
7. Residual risks and follow-up candidates, if relevant
8. Files changed
9. Explicit statement whether the Merge Readiness Gate passed and whether the implementation is ready for manual review/merge
Keep the final response concise, but include enough detail for the user to continue immediately.
## Manual Review Prompt
Provide a ready-to-copy prompt like this, adapted to the active spec number and slug:
```markdown
Du bist ein Senior Staff Software Architect und Enterprise SaaS Reviewer.
Führe eine finale manuelle Review der implementierten Spec `<spec-number>-<slug>` streng repo-basiert durch.
Ziel:
Prüfe, ob die Implementierung nach dem Agenten-Loop wirklich merge-ready ist.
Wichtig:
- Keine Implementierung.
- Keine Codeänderungen.
- Keine Scope-Erweiterung.
- Prüfe gegen spec.md, plan.md, tasks.md und constitution.md.
- Prüfe die geänderten Dateien, Tests, Browser-Smoke-Test-Ergebnis, RBAC, Workspace-/Tenant-Isolation, Auditability, UX und OperationRun-Semantik, soweit relevant.
- Benenne nur konkrete Findings mit Repo-Beleg.
- Gib am Ende eine klare Entscheidung: Merge-ready, merge-ready with notes, oder not merge-ready.
```
## Example Invocation
User:
```text
Nutze den Skill spec-kit-implementation-loop.
Implementiere die aktive Spec.
Danach Tests ausführen, Browser Smoke Test falls UI/user-facing betroffen ist, Post-Implementation Analyse durchführen und alle bestätigten In-Scope Findings unabhängig von Severity beheben, wenn safe und bounded.
Wiederhole test + browser smoke + analysis + fix bis keine In-Scope Findings mehr offen sind oder eine Stop Condition greift.
```
Expected behavior:
1. Inspect active Spec Kit context, constitution, spec, plan, tasks, relevant code, and relevant tests.
2. Evaluate the Spec Readiness Gate and Implementation Scope Gate.
3. Implement only the active spec scope.
4. Run targeted tests and relevant checks.
5. Evaluate the Test Gate.
6. Run and evaluate Browser Smoke Test when UI/user-facing flows are affected.
7. Run post-implementation analysis.
8. Fix all confirmed in-scope findings regardless of severity when safe and bounded.
9. Repeat test + browser smoke + analysis + fix loop up to the stop conditions.
10. Evaluate the Merge Readiness Gate.
11. Report final status, changed files, tests, browser smoke result, residual risks, gates, and manual review prompt.
```

View File

@ -1,562 +0,0 @@
---
name: spec-kit-next-best-prep
description: Select the next suitable TenantPilot/TenantAtlas spec candidate from roadmap/spec-candidates, run the repository's Spec Kit preparation flow, create or update spec.md/plan.md/tasks.md, run preparation analysis, fix preparation-artifact issues only, and stop before application implementation.
---
# Skill: Spec Kit Next-Best Preparation
## Purpose
Use this skill to prepare the next implementation-ready Spec Kit package for TenantPilot/TenantAtlas without implementing application code.
This skill supports preparation only:
1. Select or scope the next suitable feature from roadmap/spec-candidates.
2. Run the repository's real Spec Kit preparation workflow where available.
3. Create or update `spec.md`, `plan.md`, and `tasks.md`.
4. Run preparation `analyze` when supported.
5. Fix preparation-artifact issues only.
6. Evaluate preparation quality gates.
7. Stop before application implementation.
The intended workflow is:
```text
roadmap / spec-candidates / feature idea
→ inspect repo truth, constitution, roadmap, spec candidates, existing specs, and relevant code
→ select the next suitable candidate or scope the provided idea
→ run Spec Kit specify/plan/tasks/analyze where available
→ create or update spec.md + plan.md + tasks.md
→ fix preparation-artifact issues only
→ evaluate Candidate Selection Gate and Spec Readiness Gate
→ final preparation report
→ explicit implementation step later
```
## When to Use
Use this skill when the user asks to:
- select the next best spec candidate from `docs/product/spec-candidates.md` and roadmap sources
- turn a feature idea, roadmap item, or candidate into `spec.md`, `plan.md`, and `tasks.md`
- prepare Spec Kit artifacts in one pass
- run specify/plan/tasks/analyze without implementation
- fix preparation analysis issues in Spec Kit artifacts only
- prepare a feature package for a later implementation skill
Typical user prompts:
```text
Nimm den nächsten sinnvollen Spec Candidate aus Roadmap/spec-candidates und mach spec, plan und tasks.
```
```text
Mach daraus spec, plan und tasks in einem Rutsch, aber noch nicht implementieren.
```
```text
Wähle aus roadmap.md und spec-candidates.md die nächste sinnvollste Spec und führe specify, plan, tasks und analyze aus.
```
```text
Behebe alle analyze-Issues in den Spec-Kit-Artefakten. Keine Application-Implementierung.
```
## Hard Rules
- Work strictly repo-based.
- This is a preparation-only skill.
- Do not implement application code.
- Do not modify production code.
- Do not modify migrations, models, services, jobs, Filament resources, Livewire components, policies, commands, routes, views, tests, or runtime behavior.
- Use the repository's actual Spec Kit workflow, scripts, templates, branch naming rules, and generated paths when available.
- Do not manually invent spec numbers, branch names, or spec paths if Spec Kit provides a script or command for that.
- Do not bypass Spec Kit branch mechanics.
- Create or update only Spec Kit preparation artifacts unless repository conventions require additional documentation artifacts.
- Do not expand scope beyond the selected feature, `spec.md`, `plan.md`, and `tasks.md`.
- Do not silently add roadmap features, adjacent UX rewrites, speculative architecture, or unrelated refactors.
- Follow the repository constitution and existing Spec Kit conventions.
- Preserve TenantPilot/TenantAtlas terminology.
- Prefer small, reviewable, implementation-ready specs over broad rewrites.
- Treat repository truth as authoritative over assumptions.
- If repository truth conflicts with the user-provided draft or candidate wording, keep repository truth and document the deviation.
- Fix only confirmed preparation-artifact findings from Spec Kit preparation analysis.
- Do not leave preparation findings open silently. If they are not fixed, document exactly why.
- Do not run destructive commands.
- Do not force checkout, reset, stash, rebase, merge, or delete branches.
- Do not overwrite existing specs.
- Do not move from preparation to an implementation step inside this skill.
## Required Inputs
The user should provide at least one of:
- feature title and short goal
- full spec candidate
- roadmap item
- rough problem statement
- UX or architecture improvement idea
- instruction to choose the next best candidate from roadmap/spec-candidates
If the input is incomplete, proceed with the smallest reasonable interpretation and document assumptions.
If no suitable candidate can be selected safely, stop and report why.
## Required Repository Checks
Always check:
1. `.specify/memory/constitution.md`
2. `.specify/templates/`
3. `.specify/scripts/`
4. existing Spec Kit command usage or repository instructions, if present
5. current branch and git status
6. `specs/`
7. `docs/product/spec-candidates.md`
8. relevant roadmap documents under `docs/product/`, especially `roadmap.md` if present
9. nearby existing specs with related terminology or scope
10. application code only as needed to avoid wrong naming, wrong architecture, duplicate concepts, impossible tasks, duplicated specs, or already-completed candidates
Do not edit application code.
## Git and Branch Safety
Before running any Spec Kit command:
1. Check the current branch.
2. Check whether the working tree is clean.
3. If there are unrelated uncommitted changes, stop and report them. Do not continue.
4. If the working tree only contains user-intended planning edits for this operation, continue cautiously.
5. Let Spec Kit create or switch to the correct feature branch when that is how the repository workflow works.
6. Do not force checkout, reset, stash, rebase, merge, or delete branches.
7. Do not overwrite existing specs.
If the repo requires an explicit branch creation script for `specify`, use that script rather than manually creating the branch.
## Quality Gates
### Gate 1: Candidate Selection Gate
Required before creating a new spec from roadmap/spec-candidates.
Pass criteria:
- The selected candidate exists in roadmap/spec-candidate material or is directly provided by the user.
- The selected candidate is not already covered by an existing active or completed spec.
- The selected candidate aligns with current roadmap priorities or explicitly documented product direction.
- The candidate can be scoped as a small, reviewable, implementation-ready slice.
- Major adjacent concerns are listed as follow-up candidates instead of being hidden inside the primary scope.
Fail behavior:
- If no candidate satisfies the gate, stop and report the top candidates plus the reason none is ready.
- Do not invent a new roadmap direction to force progress.
### Gate 2: Spec Readiness Gate
Required before reporting that the package is ready for implementation.
Pass criteria:
- `spec.md`, `plan.md`, and `tasks.md` exist.
- The spec has clear problem statement, user value, functional requirements, out-of-scope boundaries, acceptance criteria, assumptions, and risks.
- The plan identifies likely affected repo surfaces and does not contradict repository architecture.
- The tasks are small, ordered, verifiable, and include test/validation tasks.
- RBAC, workspace/tenant isolation, auditability, OperationRun semantics, evidence/result-truth, and UX requirements are addressed where relevant.
- No open question blocks safe implementation.
- The scope is small enough for a bounded implementation loop in a later implementation skill.
- Required checklist artifacts exist when the constitution requires them.
Fail behavior:
- Fix preparation-artifact issues when they are safe and bounded.
- If readiness cannot be achieved without implementation or unresolved product decisions, stop and report the gap.
- Do not compensate for an unclear spec by inventing implementation scope.
## Candidate Selection Rules
When the user asks for the next best spec from roadmap/spec-candidates:
- Read `docs/product/spec-candidates.md`.
- Read relevant roadmap documents under `docs/product/`, especially `roadmap.md` if present.
- Check existing specs to avoid duplicates.
- Prefer candidates that align with current roadmap priorities, platform foundations, enterprise UX, RBAC/isolation, auditability, observability, and governance workflow maturity.
- Prefer candidates that unlock roadmap progress, reduce architectural drift, harden foundations, or remove known blockers.
- Prefer small, implementation-ready slices over broad platform rewrites.
- If multiple candidates are plausible, choose one primary candidate and document why it was selected.
- Add non-selected relevant candidates as follow-up spec candidates, not hidden scope.
- Do not invent a candidate if existing roadmap/spec-candidate material provides a suitable one.
- Do not pick a spec only because it is listed first.
- Evaluate the Candidate Selection Gate before creating the spec directory.
Evaluate candidates using these criteria:
1. **Roadmap Fit**: Does it support the current roadmap sequence or unlock the next roadmap layer?
2. **Foundation Value**: Does it strengthen reusable platform foundations such as RBAC, isolation, auditability, evidence, OperationRun observability, provider boundaries, vocabulary, baseline/control/finding semantics, or enterprise UX patterns?
3. **Dependency Unblocking**: Does it make future specs smaller, safer, or more consistent?
4. **Scope Size**: Can it be implemented as a narrow, testable slice?
5. **Repo Readiness**: Does the repo already have enough structure to implement the next slice safely?
6. **Risk Reduction**: Does it reduce current architectural or product risk?
7. **User/Product Value**: Does it produce visible operator value or make the platform more sellable without heavy scope?
## Required Selection Output Before Spec Kit Execution
Before running the Spec Kit flow, identify:
- selected candidate title
- source location in roadmap/spec-candidates
- why it was selected
- why close alternatives were deferred
- roadmap relationship
- smallest viable implementation slice
- proposed concise feature description to feed into `specify`
The feature description must be product- and behavior-oriented. It should not be a low-level implementation plan.
## Spec Kit Preparation Flow
### Step 1: Determine the repository's Spec Kit command pattern
Inspect repository instructions and scripts to identify how this repo expects Spec Kit to be run.
Common locations to inspect:
```text
.specify/scripts/
.specify/templates/
.specify/memory/constitution.md
.github/prompts/
.github/skills/
README.md
specs/
```
Use the repo-specific mechanism if present.
### Step 2: Run `specify`
Run the repository's `specify` flow using the selected candidate and the smallest viable slice.
The `specify` input should include:
- selected candidate title
- problem statement
- operator/user value
- roadmap relationship
- out-of-scope boundaries
- key acceptance criteria
- important enterprise constraints
Let Spec Kit create the correct branch and spec location if that is the repo's configured behavior.
### Step 3: Run `plan`
Run the repository's `plan` flow for the generated spec.
The `plan` input should keep the scope tight and should require repo-based alignment with:
- constitution
- existing architecture
- workspace/tenant isolation
- RBAC
- OperationRun/observability where relevant
- evidence/snapshot/truth semantics where relevant
- Filament/Livewire conventions where relevant
- test strategy
### Step 4: Run `tasks`
Run the repository's `tasks` flow for the generated plan.
The generated tasks must be:
- ordered
- small
- testable
- grouped by phase
- limited to the selected scope
- suitable for later implementation or manual analysis before implementation
### Step 5: Run preparation `analyze`
Run the repository's `analyze` flow against the generated Spec Kit artifacts when the repository supports it.
Analyze must check:
- consistency between `spec.md`, `plan.md`, and `tasks.md`
- constitution alignment
- roadmap alignment
- whether the selected candidate was narrowed safely
- whether tasks are complete enough for implementation
- whether tasks accidentally require scope not described in the spec
- whether plan details conflict with repository architecture or terminology
- whether implementation risks are documented instead of silently ignored
Do not use analyze as a trigger to implement application code.
### Step 6: Fix preparation-artifact issues only
If preparation analyze finds issues, fix only Spec Kit preparation artifacts such as:
- `spec.md`
- `plan.md`
- `tasks.md`
- `checklists/requirements.md` or other generated Spec Kit metadata files, if the repository uses them
Allowed fixes include:
- clarify requirements
- tighten scope
- move out-of-scope work into follow-up candidates
- correct terminology
- add missing tasks
- remove tasks not backed by the spec
- align plan language with repository architecture
- add missing acceptance criteria or validation tasks
- add missing checklist artifacts required by the constitution
Forbidden fixes include:
- modifying application code
- creating migrations
- editing models, services, jobs, policies, Filament resources, Livewire components, tests, commands, routes, or views
- running implementation or test-fix loops
- changing runtime behavior
### Step 7: Evaluate the Spec Readiness Gate
After preparation analyze has passed or preparation-artifact issues have been fixed, evaluate the Spec Readiness Gate.
Stop after this gate and do not implement.
## Spec Directory Rules
When creating a new spec directory, use the repository's Spec Kit-generated directory or path.
If the repository does not provide a command for spec setup, use the next valid spec number and a kebab-case slug:
```text
specs/<number>-<slug>/
```
The exact number must be derived from the current repository state and existing numbering conventions.
Create or update preparation artifacts inside the selected spec directory:
```text
specs/<number>-<slug>/spec.md
specs/<number>-<slug>/plan.md
specs/<number>-<slug>/tasks.md
```
If the repository templates require additional preparation files, create them only when this is consistent with existing Spec Kit conventions.
## `spec.md` Requirements
The spec must be product- and behavior-oriented. It should avoid premature implementation detail unless needed for correctness.
Include:
- Feature title
- Problem statement
- Business/product value
- Primary users/operators
- User stories
- Functional requirements
- Non-functional requirements
- UX requirements
- RBAC/security requirements
- Auditability/observability requirements
- Data/truth-source requirements where relevant
- Out of scope
- Acceptance criteria
- Success criteria
- Risks
- Assumptions
- Open questions
TenantPilot/TenantAtlas specs should preserve enterprise SaaS principles:
- workspace/tenant isolation
- capability-first RBAC
- auditability
- operation/result truth separation
- source-of-truth clarity
- calm enterprise operator UX
- progressive disclosure where useful
- no false positive calmness
## `plan.md` Requirements
The plan must be repo-aware and implementation-oriented, but it must not make code changes by itself.
Include:
- Technical approach
- Existing repository surfaces likely affected
- Domain/model implications
- UI/Filament implications
- Livewire implications where relevant
- OperationRun/monitoring implications where relevant
- RBAC/policy implications
- Audit/logging/evidence implications where relevant
- Data/migration implications where relevant
- Test strategy
- Rollout considerations
- Risk controls
- Implementation phases
The plan should clearly distinguish where relevant:
- execution truth
- artifact truth
- backup/snapshot truth
- recovery/evidence truth
- operator next action
## `tasks.md` Requirements
Tasks must be ordered, small, and verifiable.
Include:
- checkbox tasks
- phase grouping
- tests before or alongside implementation tasks where practical
- final validation tasks
- documentation/update tasks if needed
- explicit non-goals where useful
Avoid vague tasks such as:
```text
Clean up code
Refactor UI
Improve performance
Make it enterprise-ready
```
Prefer concrete tasks such as:
```text
- [ ] Add a feature test covering workspace isolation for <specific behavior>.
- [ ] Update <specific Filament page/resource> to display <specific state>.
- [ ] Add policy coverage for <specific capability>.
```
If exact file names are not known yet, phrase tasks as repo-verification tasks first rather than inventing file paths.
## Preparation Scope Control
If the requested feature implies multiple independent concerns, create one primary spec for the smallest valuable slice and add a `Follow-up spec candidates` section.
Examples of follow-up candidates:
- assigned findings
- pending approvals
- personal work queue
- notification delivery settings
- evidence pack export hardening
- operation monitoring refinements
- autonomous governance decision surfaces
Do not force all follow-up candidates into the primary spec.
## Failure Handling
If a Spec Kit command or preparation analyze phase fails:
1. Stop at the relevant gate.
2. Report the failing command or phase.
3. Summarize the error.
4. Do not attempt implementation as a workaround.
5. Suggest the smallest safe next action.
If the branch or working tree state is unsafe:
1. Stop before running Spec Kit commands.
2. Report the current branch and relevant uncommitted files.
3. Ask the user to commit, stash, or move to a clean worktree.
## Final Response Requirements
Respond with:
1. Selected candidate and why it was chosen
2. Why close alternatives were deferred
3. Current branch after Spec Kit execution, if changed
4. Generated spec path
5. Files created or updated by Spec Kit
6. Preparation analyze result summary
7. Preparation-artifact fixes applied after analyze
8. Assumptions made
9. Open questions, if any
10. Candidate Selection Gate result
11. Spec Readiness Gate result
12. Recommended next implementation prompt
13. Explicit statement that no application implementation was performed
Keep the final response concise, but include enough detail for the user to continue immediately.
## Manual Review and Next-Step Prompts
Provide a ready-to-copy manual artifact review prompt like this, adapted to the generated spec branch/path:
```markdown
Du bist ein Senior Staff Software Architect und Enterprise SaaS Reviewer.
Analysiere die neu erstellte Spec `<spec-branch-or-spec-path>` streng repo-basiert.
Ziel:
Prüfe, ob `spec.md`, `plan.md` und `tasks.md` vollständig, konsistent, implementierbar und constitution-konform sind.
Wichtig:
- Keine Implementierung.
- Keine Codeänderungen.
- Keine Scope-Erweiterung.
- Prüfe nur gegen Repo-Wahrheit.
- Benenne konkrete Konflikte mit Dateien, Patterns, Datenflüssen oder bestehenden Specs.
- Schlage nur minimale Korrekturen an `spec.md`, `plan.md` und `tasks.md` vor.
- Wenn alles passt, gib eine klare Implementierungsfreigabe.
```
Also provide a ready-to-copy implementation prompt for the separate implementation skill after analyze has passed or preparation-artifact issues have been fixed:
```markdown
/spec-kit-implementation-loop
Implementiere die vorbereitete Spec `<spec-branch-or-spec-path>` streng anhand von `tasks.md`.
Danach Tests ausführen, Browser Smoke Test falls UI/user-facing betroffen ist, Post-Implementation Analyse durchführen und alle bestätigten In-Scope Findings unabhängig von Severity beheben, wenn safe und bounded.
Wiederhole test + browser smoke + analysis + fix bis keine In-Scope Findings mehr offen sind oder eine Stop Condition greift.
```
## Example Invocation
User:
```text
Nutze den Skill spec-kit-next-best-prep.
Wähle aus roadmap.md und spec-candidates.md die nächste sinnvollste Spec.
Führe danach GitHub Spec Kit specify, plan, tasks und analyze in einem Rutsch aus.
Behebe alle analyze-Issues in den Spec-Kit-Artefakten.
Keine Application-Implementierung.
```
Expected behavior:
1. Inspect constitution, Spec Kit scripts/templates, specs, roadmap, and spec candidates.
2. Check branch and working tree safety.
3. Compare candidate suitability.
4. Select the next best candidate.
5. Evaluate the Candidate Selection Gate.
6. Run the repository's real Spec Kit `specify` flow, letting it handle branch/spec setup.
7. Run the repository's real Spec Kit `plan` flow.
8. Run the repository's real Spec Kit `tasks` flow.
9. Run the repository's real Spec Kit preparation `analyze` flow.
10. Fix analyze issues only in Spec Kit preparation artifacts.
11. Evaluate the Spec Readiness Gate.
12. Stop before application implementation.
13. Return selection rationale, branch/path summary, artifact summary, analyze summary, fixes applied, gates, and next implementation prompt.
```

View File

@ -1,129 +0,0 @@
---
name: tailwindcss-development
description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes."
license: MIT
metadata:
author: laravel
---
# Tailwind CSS Development
## When to Apply
Activate this skill when:
- Adding styles to components or pages
- Working with responsive design
- Implementing dark mode
- Extracting repeated patterns into components
- Debugging spacing or layout issues
## Documentation
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
## Basic Usage
- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
## Tailwind CSS v4 Specifics
- Always use Tailwind CSS v4 and avoid deprecated utilities.
- `corePlugins` is not supported in Tailwind v4.
### CSS-First Configuration
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
<!-- CSS-First Config -->
```css
@theme {
--color-brand: oklch(0.72 0.11 178);
}
```
### Import Syntax
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
<!-- v4 Import Syntax -->
```diff
- @tailwind base;
- @tailwind components;
- @tailwind utilities;
+ @import "tailwindcss";
```
### Replaced Utilities
Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
| Deprecated | Replacement |
|------------|-------------|
| bg-opacity-* | bg-black/* |
| text-opacity-* | text-black/* |
| border-opacity-* | border-black/* |
| divide-opacity-* | divide-black/* |
| ring-opacity-* | ring-black/* |
| placeholder-opacity-* | placeholder-black/* |
| flex-shrink-* | shrink-* |
| flex-grow-* | grow-* |
| overflow-ellipsis | text-ellipsis |
| decoration-slice | box-decoration-slice |
| decoration-clone | box-decoration-clone |
## Spacing
Use `gap` utilities instead of margins for spacing between siblings:
<!-- Gap Utilities -->
```html
<div class="flex gap-8">
<div>Item 1</div>
<div>Item 2</div>
</div>
```
## Dark Mode
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
<!-- Dark Mode -->
```html
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
Content adapts to color scheme
</div>
```
## Common Patterns
### Flexbox Layout
<!-- Flexbox Layout -->
```html
<div class="flex items-center justify-between gap-4">
<div>Left content</div>
<div>Right content</div>
</div>
```
### Grid Layout
<!-- Grid Layout -->
```html
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
</div>
```
## Common Pitfalls
- Using deprecated v3 utilities (bg-opacity-*, flex-shrink-*, etc.)
- Using `@tailwind` directives instead of `@import "tailwindcss"`
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
- Using margins for spacing between siblings instead of gap utilities
- Forgetting to add dark mode variants when the project uses dark mode

40
.gitignore vendored
View File

@ -1,12 +1,10 @@
*.log
.DS_Store
.env
.env.*
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
*.cache
/.fleet
/.idea
/.nova
@ -15,49 +13,13 @@
/.zed
/auth.json
/node_modules
/apps/platform/node_modules
/apps/website/node_modules
/.pnpm-store
/apps/website/.astro
/apps/website/playwright-report
/apps/website/test-results
/apps/website/blob-report
dist/
build/
coverage/
/public/build
/apps/platform/public/build
/apps/website/dist
/public/hot
/apps/platform/public/hot
/public/storage
/apps/platform/public/storage
/storage/*.key
/apps/platform/storage/*.key
/storage/pail
/apps/platform/storage/pail
/storage/framework
/apps/platform/storage/framework
/storage/logs
/apps/platform/storage/logs
/apps/platform/storage/logs/*
!/apps/platform/storage/logs/test-lanes/
/apps/platform/storage/logs/test-lanes/*
!/apps/platform/storage/logs/test-lanes/.gitignore
/storage/debugbar
/apps/platform/storage/debugbar
/vendor
/apps/platform/vendor
/bootstrap/cache
/apps/platform/bootstrap/cache
Homestead.json
Homestead.yaml
Thumbs.db
/references
/tests/Browser/Screenshots
*.tmp
*.swp
/apps/platform/.env
/apps/platform/.env.*
/apps/website/.env
/apps/website/.env.*
/references

View File

@ -1,17 +1,7 @@
dist/
build/
public/build/
apps/platform/public/build/
node_modules/
apps/platform/node_modules/
apps/website/node_modules/
apps/website/.astro/
apps/website/dist/
apps/website/playwright-report/
apps/website/test-results/
apps/website/blob-report/
vendor/
apps/platform/vendor/
*.log
.env
.env.*

View File

@ -2,22 +2,8 @@ node_modules/
dist/
build/
public/build/
apps/platform/public/build/
public/hot/
apps/platform/public/hot/
public/storage/
apps/platform/public/storage/
coverage/
vendor/
apps/platform/vendor/
apps/platform/node_modules/
apps/website/node_modules/
apps/website/.astro/
apps/website/dist/
storage/
apps/platform/storage/
bootstrap/cache/
apps/platform/bootstrap/cache/
package-lock.json
yarn.lock
pnpm-lock.yaml

View File

@ -1,56 +0,0 @@
# `.specify/` (Tooling)
This folder contains **SpecKit tooling** (templates, scripts, constitution, etc.).
## Important
- **Do not** create new feature specs in `.specify/spec.md`, `.specify/plan.md`, `.specify/tasks.md`.
- Active feature specs live under `specs/<NNN>-<slug>/`:
- `spec.md`
- `plan.md`
- `tasks.md`
- `checklists/requirements.md`
- Runtime-changing or test-affecting work MUST carry actual test-purpose classification (`Unit`, `Feature`, `Heavy-Governance`, `Browser`), affected lanes, fixture/default cost risks, heavy-family changes, escalation decisions, and minimal validation commands through the active `spec.md`, `plan.md`, and `tasks.md`.
- Review-oriented checklists MUST surface nativity, shared-family boundaries, state-layer ownership, exception spread, proof depth, and close-out targeting before merge.
- `.specify/` is the operational workflow. `docs/ui/operator-ux-surface-standards.md` remains a rule/reference document, not a second checklist or review process.
## Author Entry Point
Use the active feature's `spec.md`, `plan.md`, and `tasks.md` in that order.
1. Fill the spec's UI / Surface Guardrail Impact section once. If there is no operator-facing surface change, write a concise `N/A` and do not invent extra prose downstream.
2. Turn that classification into plan-level handling modes, repository-signal treatment, required proof depth, and the named active feature PR close-out entry.
3. Carry the same terms into tasks so implementation, review, definition-of-done, exception documentation, and smoke coverage all point at the same guardrail decision.
## Review Entry Point
Use the active feature's `spec.md`, `plan.md`, and `tasks.md` together with the generated checklist based on `.specify/templates/checklist-template.md`.
1. Confirm the spec names the guardrail impact once: native/custom classification, shared-family relevance, state-layer ownership, exception need, and any low-impact `N/A` path.
2. Confirm the plan turns that into handling modes, repository-signal treatment, required test or smoke depth, and the named active feature PR close-out entry.
3. Apply the checklist and end with both one review outcome class (`blocker`, `strong-warning`, `documentation-required-exception`, or `acceptable-special-case`) and one workflow outcome (`keep`, `split`, `document-in-feature`, `follow-up-spec`, or `reject-or-split`).
4. If a guarded surface or exception remains in scope, ensure the active feature PR close-out entry records the final note rather than leaving the decision in scattered review comments.
## Low-Impact Rule
- Docs-only or template-only work may answer `N/A` or `none`.
- Do not force fake surface, lane, or exception prose when no operator-facing change or runtime impact exists.
- Use the low-impact path to stay fast, not to hide a real guarded surface change.
## Escalation Rule
- Use `blocker` when fake-native drift, hidden host drift, unresolved state ownership, or missing exception boundaries remain.
- Use `strong-warning` when the change can proceed only after the active workflow records the guardrail risk explicitly.
- Use `documentation-required-exception` when default rules are intentionally relaxed and the bounded exception record is the remaining requirement.
- Use `acceptable-special-case` only when the change already fits the declared guardrail contract.
- Use `document-in-feature` for contained cost or drift that belongs in the active feature.
- Use `follow-up-spec` only for recurring pain or structural lane or family changes.
- Use `reject-or-split` when hidden test cost or wrong-lane scope is still unresolved.
## Close-Out Rule
- The named close-out target for guarded work is the active feature PR close-out entry `Guardrail / Exception / Smoke Coverage`.
- That entry records the low-impact or representative scenario used, the outcome class, handling mode, workflow outcome, required tests or manual smoke, any exception boundary, duplicate-prompt notes, and any deferred automation.
- If the change is genuinely low-impact, record a concise `N/A` note rather than fabricating guardrail spread.
The files `.specify/spec.md`, `.specify/plan.md`, `.specify/tasks.md` may exist as legacy references only.

View File

@ -1,148 +0,0 @@
installed: []
settings:
auto_execute_hooks: true
hooks:
before_constitution:
- extension: git
command: speckit.git.initialize
enabled: true
optional: false
prompt: Execute speckit.git.initialize?
description: Initialize Git repository before constitution setup
condition: null
before_specify:
- extension: git
command: speckit.git.feature
enabled: true
optional: false
prompt: Execute speckit.git.feature?
description: Create feature branch before specification
condition: null
before_clarify:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit outstanding changes before clarification?
description: Auto-commit before spec clarification
condition: null
before_plan:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit outstanding changes before planning?
description: Auto-commit before implementation planning
condition: null
before_tasks:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit outstanding changes before task generation?
description: Auto-commit before task generation
condition: null
before_implement:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit outstanding changes before implementation?
description: Auto-commit before implementation
condition: null
before_checklist:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit outstanding changes before checklist?
description: Auto-commit before checklist generation
condition: null
before_analyze:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit outstanding changes before analysis?
description: Auto-commit before analysis
condition: null
before_taskstoissues:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit outstanding changes before issue sync?
description: Auto-commit before tasks-to-issues conversion
condition: null
after_constitution:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit constitution changes?
description: Auto-commit after constitution update
condition: null
after_specify:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit specification changes?
description: Auto-commit after specification
condition: null
after_clarify:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit clarification changes?
description: Auto-commit after spec clarification
condition: null
after_plan:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit plan changes?
description: Auto-commit after implementation planning
condition: null
after_tasks:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit task changes?
description: Auto-commit after task generation
condition: null
after_implement:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit implementation changes?
description: Auto-commit after implementation
condition: null
after_checklist:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit checklist changes?
description: Auto-commit after checklist generation
condition: null
after_analyze:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit analysis results?
description: Auto-commit after analysis
condition: null
after_taskstoissues:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
prompt: Commit after syncing issues?
description: Auto-commit after tasks-to-issues conversion
condition: null

View File

@ -1,44 +0,0 @@
{
"schema_version": "1.0",
"extensions": {
"git": {
"version": "1.0.0",
"source": "local",
"manifest_hash": "sha256:9731aa8143a72fbebfdb440f155038ab42642517c2b2bdbbf67c8fdbe076ed79",
"enabled": true,
"priority": 10,
"registered_commands": {
"agy": [
"speckit.git.feature",
"speckit.git.validate",
"speckit.git.remote",
"speckit.git.initialize",
"speckit.git.commit"
],
"codex": [
"speckit.git.feature",
"speckit.git.validate",
"speckit.git.remote",
"speckit.git.initialize",
"speckit.git.commit"
],
"copilot": [
"speckit.git.feature",
"speckit.git.validate",
"speckit.git.remote",
"speckit.git.initialize",
"speckit.git.commit"
],
"gemini": [
"speckit.git.feature",
"speckit.git.validate",
"speckit.git.remote",
"speckit.git.initialize",
"speckit.git.commit"
]
},
"registered_skills": [],
"installed_at": "2026-04-22T21:58:03.029565+00:00"
}
}
}

View File

@ -1,100 +0,0 @@
# Git Branching Workflow Extension
Git repository initialization, feature branch creation, numbering (sequential/timestamp), validation, remote detection, and auto-commit for Spec Kit.
## Overview
This extension provides Git operations as an optional, self-contained module. It manages:
- **Repository initialization** with configurable commit messages
- **Feature branch creation** with sequential (`001-feature-name`) or timestamp (`20260319-143022-feature-name`) numbering
- **Branch validation** to ensure branches follow naming conventions
- **Git remote detection** for GitHub integration (e.g., issue creation)
- **Auto-commit** after core commands (configurable per-command with custom messages)
## Commands
| Command | Description |
|---------|-------------|
| `speckit.git.initialize` | Initialize a Git repository with a configurable commit message |
| `speckit.git.feature` | Create a feature branch with sequential or timestamp numbering |
| `speckit.git.validate` | Validate current branch follows feature branch naming conventions |
| `speckit.git.remote` | Detect Git remote URL for GitHub integration |
| `speckit.git.commit` | Auto-commit changes (configurable per-command enable/disable and messages) |
## Hooks
| Event | Command | Optional | Description |
|-------|---------|----------|-------------|
| `before_constitution` | `speckit.git.initialize` | No | Init git repo before constitution |
| `before_specify` | `speckit.git.feature` | No | Create feature branch before specification |
| `before_clarify` | `speckit.git.commit` | Yes | Commit outstanding changes before clarification |
| `before_plan` | `speckit.git.commit` | Yes | Commit outstanding changes before planning |
| `before_tasks` | `speckit.git.commit` | Yes | Commit outstanding changes before task generation |
| `before_implement` | `speckit.git.commit` | Yes | Commit outstanding changes before implementation |
| `before_checklist` | `speckit.git.commit` | Yes | Commit outstanding changes before checklist |
| `before_analyze` | `speckit.git.commit` | Yes | Commit outstanding changes before analysis |
| `before_taskstoissues` | `speckit.git.commit` | Yes | Commit outstanding changes before issue sync |
| `after_constitution` | `speckit.git.commit` | Yes | Auto-commit after constitution update |
| `after_specify` | `speckit.git.commit` | Yes | Auto-commit after specification |
| `after_clarify` | `speckit.git.commit` | Yes | Auto-commit after clarification |
| `after_plan` | `speckit.git.commit` | Yes | Auto-commit after planning |
| `after_tasks` | `speckit.git.commit` | Yes | Auto-commit after task generation |
| `after_implement` | `speckit.git.commit` | Yes | Auto-commit after implementation |
| `after_checklist` | `speckit.git.commit` | Yes | Auto-commit after checklist |
| `after_analyze` | `speckit.git.commit` | Yes | Auto-commit after analysis |
| `after_taskstoissues` | `speckit.git.commit` | Yes | Auto-commit after issue sync |
## Configuration
Configuration is stored in `.specify/extensions/git/git-config.yml`:
```yaml
# Branch numbering strategy: "sequential" or "timestamp"
branch_numbering: sequential
# Custom commit message for git init
init_commit_message: "[Spec Kit] Initial commit"
# Auto-commit per command (all disabled by default)
# Example: enable auto-commit after specify
auto_commit:
default: false
after_specify:
enabled: true
message: "[Spec Kit] Add specification"
```
## Installation
```bash
# Install the bundled git extension (no network required)
specify extension add git
```
## Disabling
```bash
# Disable the git extension (spec creation continues without branching)
specify extension disable git
# Re-enable it
specify extension enable git
```
## Graceful Degradation
When Git is not installed or the directory is not a Git repository:
- Spec directories are still created under `specs/`
- Branch creation is skipped with a warning
- Branch validation is skipped with a warning
- Remote detection returns empty results
## Scripts
The extension bundles cross-platform scripts:
- `scripts/bash/create-new-feature.sh` — Bash implementation
- `scripts/bash/git-common.sh` — Shared Git utilities (Bash)
- `scripts/powershell/create-new-feature.ps1` — PowerShell implementation
- `scripts/powershell/git-common.ps1` — Shared Git utilities (PowerShell)

View File

@ -1,48 +0,0 @@
---
description: "Auto-commit changes after a Spec Kit command completes"
---
# Auto-Commit Changes
Automatically stage and commit all changes after a Spec Kit command completes.
## Behavior
This command is invoked as a hook after (or before) core commands. It:
1. Determines the event name from the hook context (e.g., if invoked as an `after_specify` hook, the event is `after_specify`; if `before_plan`, the event is `before_plan`)
2. Checks `.specify/extensions/git/git-config.yml` for the `auto_commit` section
3. Looks up the specific event key to see if auto-commit is enabled
4. Falls back to `auto_commit.default` if no event-specific key exists
5. Uses the per-command `message` if configured, otherwise a default message
6. If enabled and there are uncommitted changes, runs `git add .` + `git commit`
## Execution
Determine the event name from the hook that triggered this command, then run the script:
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name>`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name>`
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`).
## Configuration
In `.specify/extensions/git/git-config.yml`:
```yaml
auto_commit:
default: false # Global toggle — set true to enable for all commands
after_specify:
enabled: true # Override per-command
message: "[Spec Kit] Add specification"
after_plan:
enabled: false
message: "[Spec Kit] Add implementation plan"
```
## Graceful Degradation
- If Git is not available or the current directory is not a repository: skips with a warning
- If no config file exists: skips (disabled by default)
- If no changes to commit: skips with a message

View File

@ -1,67 +0,0 @@
---
description: "Create a feature branch with sequential or timestamp numbering"
---
# Create Feature Branch
Create and switch to a new git feature branch for the given specification. This command handles **branch creation only** — the spec directory and files are created by the core `/speckit.specify` workflow.
## User Input
```text
$ARGUMENTS
```
You **MUST** consider the user input before proceeding (if not empty).
## Environment Variable Override
If the user explicitly provided `GIT_BRANCH_NAME` (e.g., via environment variable, argument, or in their request), pass it through to the script by setting the `GIT_BRANCH_NAME` environment variable before invoking the script. When `GIT_BRANCH_NAME` is set:
- The script uses the exact value as the branch name, bypassing all prefix/suffix generation
- `--short-name`, `--number`, and `--timestamp` flags are ignored
- `FEATURE_NUM` is extracted from the name if it starts with a numeric prefix, otherwise set to the full branch name
## Prerequisites
- Verify Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, warn the user and skip branch creation
## Branch Numbering Mode
Determine the branch numbering strategy by checking configuration in this order:
1. Check `.specify/extensions/git/git-config.yml` for `branch_numbering` value
2. Check `.specify/init-options.json` for `branch_numbering` value (backward compatibility)
3. Default to `sequential` if neither exists
## Execution
Generate a concise short name (2-4 words) for the branch:
- Analyze the feature description and extract the most meaningful keywords
- Use action-noun format when possible (e.g., "add-user-auth", "fix-payment-bug")
- Preserve technical terms and acronyms (OAuth2, API, JWT, etc.)
Run the appropriate script based on your platform:
- **Bash**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --short-name "<short-name>" "<feature description>"`
- **Bash (timestamp)**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --timestamp --short-name "<short-name>" "<feature description>"`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -ShortName "<short-name>" "<feature description>"`
- **PowerShell (timestamp)**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -Timestamp -ShortName "<short-name>" "<feature description>"`
**IMPORTANT**:
- Do NOT pass `--number` — the script determines the correct next number automatically
- Always include the JSON flag (`--json` for Bash, `-Json` for PowerShell) so the output can be parsed reliably
- You must only ever run this script once per feature
- The JSON output will contain `BRANCH_NAME` and `FEATURE_NUM`
## Graceful Degradation
If Git is not installed or the current directory is not a Git repository:
- Branch creation is skipped with a warning: `[specify] Warning: Git repository not detected; skipped branch creation`
- The script still outputs `BRANCH_NAME` and `FEATURE_NUM` so the caller can reference them
## Output
The script outputs JSON with:
- `BRANCH_NAME`: The branch name (e.g., `003-user-auth` or `20260319-143022-user-auth`)
- `FEATURE_NUM`: The numeric or timestamp prefix used

View File

@ -1,49 +0,0 @@
---
description: "Initialize a Git repository with an initial commit"
---
# Initialize Git Repository
Initialize a Git repository in the current project directory if one does not already exist.
## Execution
Run the appropriate script from the project root:
- **Bash**: `.specify/extensions/git/scripts/bash/initialize-repo.sh`
- **PowerShell**: `.specify/extensions/git/scripts/powershell/initialize-repo.ps1`
If the extension scripts are not found, fall back to:
- **Bash**: `git init && git add . && git commit -m "Initial commit from Specify template"`
- **PowerShell**: `git init; git add .; git commit -m "Initial commit from Specify template"`
The script handles all checks internally:
- Skips if Git is not available
- Skips if already inside a Git repository
- Runs `git init`, `git add .`, and `git commit` with an initial commit message
## Customization
Replace the script to add project-specific Git initialization steps:
- Custom `.gitignore` templates
- Default branch naming (`git config init.defaultBranch`)
- Git LFS setup
- Git hooks installation
- Commit signing configuration
- Git Flow initialization
## Output
On success:
- `✓ Git repository initialized`
## Graceful Degradation
If Git is not installed:
- Warn the user
- Skip repository initialization
- The project continues to function without Git (specs can still be created under `specs/`)
If Git is installed but `git init`, `git add .`, or `git commit` fails:
- Surface the error to the user
- Stop this command rather than continuing with a partially initialized repository

View File

@ -1,45 +0,0 @@
---
description: "Detect Git remote URL for GitHub integration"
---
# Detect Git Remote URL
Detect the Git remote URL for integration with GitHub services (e.g., issue creation).
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and return empty:
```
[specify] Warning: Git repository not detected; cannot determine remote URL
```
## Execution
Run the following command to get the remote URL:
```bash
git config --get remote.origin.url
```
## Output
Parse the remote URL and determine:
1. **Repository owner**: Extract from the URL (e.g., `github` from `https://github.com/github/spec-kit.git`)
2. **Repository name**: Extract from the URL (e.g., `spec-kit` from `https://github.com/github/spec-kit.git`)
3. **Is GitHub**: Whether the remote points to a GitHub repository
Supported URL formats:
- HTTPS: `https://github.com/<owner>/<repo>.git`
- SSH: `git@github.com:<owner>/<repo>.git`
> [!CAUTION]
> ONLY report a GitHub repository if the remote URL actually points to github.com.
> Do NOT assume the remote is GitHub if the URL format doesn't match.
## Graceful Degradation
If Git is not installed, the directory is not a Git repository, or no remote is configured:
- Return an empty result
- Do NOT error — other workflows should continue without Git remote information

View File

@ -1,49 +0,0 @@
---
description: "Validate current branch follows feature branch naming conventions"
---
# Validate Feature Branch
Validate that the current Git branch follows the expected feature branch naming conventions.
## Prerequisites
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
- If Git is not available, output a warning and skip validation:
```
[specify] Warning: Git repository not detected; skipped branch validation
```
## Validation Rules
Get the current branch name:
```bash
git rev-parse --abbrev-ref HEAD
```
The branch name must match one of these patterns:
1. **Sequential**: `^[0-9]{3,}-` (e.g., `001-feature-name`, `042-fix-bug`, `1000-big-feature`)
2. **Timestamp**: `^[0-9]{8}-[0-9]{6}-` (e.g., `20260319-143022-feature-name`)
## Execution
If on a feature branch (matches either pattern):
- Output: `✓ On feature branch: <branch-name>`
- Check if the corresponding spec directory exists under `specs/`:
- For sequential branches, look for `specs/<prefix>-*` where prefix matches the numeric portion
- For timestamp branches, look for `specs/<prefix>-*` where prefix matches the `YYYYMMDD-HHMMSS` portion
- If spec directory exists: `✓ Spec directory found: <path>`
- If spec directory missing: `⚠ No spec directory found for prefix <prefix>`
If NOT on a feature branch:
- Output: `✗ Not on a feature branch. Current branch: <branch-name>`
- Output: `Feature branches should be named like: 001-feature-name or 20260319-143022-feature-name`
## Graceful Degradation
If Git is not installed or the directory is not a Git repository:
- Check the `SPECIFY_FEATURE` environment variable as a fallback
- If set, validate that value against the naming patterns
- If not set, skip validation with a warning

View File

@ -1,62 +0,0 @@
# Git Branching Workflow Extension Configuration
# Copied to .specify/extensions/git/git-config.yml on install
# Branch numbering strategy: "sequential" (001, 002, ...) or "timestamp" (YYYYMMDD-HHMMSS)
branch_numbering: sequential
# Commit message used by `git commit` during repository initialization
init_commit_message: "[Spec Kit] Initial commit"
# Auto-commit before/after core commands.
# Set "default" to enable for all commands, then override per-command.
# Each key can be true/false. Message is customizable per-command.
auto_commit:
default: false
before_clarify:
enabled: false
message: "[Spec Kit] Save progress before clarification"
before_plan:
enabled: false
message: "[Spec Kit] Save progress before planning"
before_tasks:
enabled: false
message: "[Spec Kit] Save progress before task generation"
before_implement:
enabled: false
message: "[Spec Kit] Save progress before implementation"
before_checklist:
enabled: false
message: "[Spec Kit] Save progress before checklist"
before_analyze:
enabled: false
message: "[Spec Kit] Save progress before analysis"
before_taskstoissues:
enabled: false
message: "[Spec Kit] Save progress before issue sync"
after_constitution:
enabled: false
message: "[Spec Kit] Add project constitution"
after_specify:
enabled: false
message: "[Spec Kit] Add specification"
after_clarify:
enabled: false
message: "[Spec Kit] Clarify specification"
after_plan:
enabled: false
message: "[Spec Kit] Add implementation plan"
after_tasks:
enabled: false
message: "[Spec Kit] Add tasks"
after_implement:
enabled: false
message: "[Spec Kit] Implementation progress"
after_checklist:
enabled: false
message: "[Spec Kit] Add checklist"
after_analyze:
enabled: false
message: "[Spec Kit] Add analysis report"
after_taskstoissues:
enabled: false
message: "[Spec Kit] Sync tasks to issues"

View File

@ -1,140 +0,0 @@
schema_version: "1.0"
extension:
id: git
name: "Git Branching Workflow"
version: "1.0.0"
description: "Feature branch creation, numbering (sequential/timestamp), validation, and Git remote detection"
author: spec-kit-core
repository: https://github.com/github/spec-kit
license: MIT
requires:
speckit_version: ">=0.2.0"
tools:
- name: git
required: false
provides:
commands:
- name: speckit.git.feature
file: commands/speckit.git.feature.md
description: "Create a feature branch with sequential or timestamp numbering"
- name: speckit.git.validate
file: commands/speckit.git.validate.md
description: "Validate current branch follows feature branch naming conventions"
- name: speckit.git.remote
file: commands/speckit.git.remote.md
description: "Detect Git remote URL for GitHub integration"
- name: speckit.git.initialize
file: commands/speckit.git.initialize.md
description: "Initialize a Git repository with an initial commit"
- name: speckit.git.commit
file: commands/speckit.git.commit.md
description: "Auto-commit changes after a Spec Kit command completes"
config:
- name: "git-config.yml"
template: "config-template.yml"
description: "Git branching configuration"
required: false
hooks:
before_constitution:
command: speckit.git.initialize
optional: false
description: "Initialize Git repository before constitution setup"
before_specify:
command: speckit.git.feature
optional: false
description: "Create feature branch before specification"
before_clarify:
command: speckit.git.commit
optional: true
prompt: "Commit outstanding changes before clarification?"
description: "Auto-commit before spec clarification"
before_plan:
command: speckit.git.commit
optional: true
prompt: "Commit outstanding changes before planning?"
description: "Auto-commit before implementation planning"
before_tasks:
command: speckit.git.commit
optional: true
prompt: "Commit outstanding changes before task generation?"
description: "Auto-commit before task generation"
before_implement:
command: speckit.git.commit
optional: true
prompt: "Commit outstanding changes before implementation?"
description: "Auto-commit before implementation"
before_checklist:
command: speckit.git.commit
optional: true
prompt: "Commit outstanding changes before checklist?"
description: "Auto-commit before checklist generation"
before_analyze:
command: speckit.git.commit
optional: true
prompt: "Commit outstanding changes before analysis?"
description: "Auto-commit before analysis"
before_taskstoissues:
command: speckit.git.commit
optional: true
prompt: "Commit outstanding changes before issue sync?"
description: "Auto-commit before tasks-to-issues conversion"
after_constitution:
command: speckit.git.commit
optional: true
prompt: "Commit constitution changes?"
description: "Auto-commit after constitution update"
after_specify:
command: speckit.git.commit
optional: true
prompt: "Commit specification changes?"
description: "Auto-commit after specification"
after_clarify:
command: speckit.git.commit
optional: true
prompt: "Commit clarification changes?"
description: "Auto-commit after spec clarification"
after_plan:
command: speckit.git.commit
optional: true
prompt: "Commit plan changes?"
description: "Auto-commit after implementation planning"
after_tasks:
command: speckit.git.commit
optional: true
prompt: "Commit task changes?"
description: "Auto-commit after task generation"
after_implement:
command: speckit.git.commit
optional: true
prompt: "Commit implementation changes?"
description: "Auto-commit after implementation"
after_checklist:
command: speckit.git.commit
optional: true
prompt: "Commit checklist changes?"
description: "Auto-commit after checklist generation"
after_analyze:
command: speckit.git.commit
optional: true
prompt: "Commit analysis results?"
description: "Auto-commit after analysis"
after_taskstoissues:
command: speckit.git.commit
optional: true
prompt: "Commit after syncing issues?"
description: "Auto-commit after tasks-to-issues conversion"
tags:
- "git"
- "branching"
- "workflow"
config:
defaults:
branch_numbering: sequential
init_commit_message: "[Spec Kit] Initial commit"

View File

@ -1,62 +0,0 @@
# Git Branching Workflow Extension Configuration
# Copied to .specify/extensions/git/git-config.yml on install
# Branch numbering strategy: "sequential" (001, 002, ...) or "timestamp" (YYYYMMDD-HHMMSS)
branch_numbering: sequential
# Commit message used by `git commit` during repository initialization
init_commit_message: "[Spec Kit] Initial commit"
# Auto-commit before/after core commands.
# Set "default" to enable for all commands, then override per-command.
# Each key can be true/false. Message is customizable per-command.
auto_commit:
default: false
before_clarify:
enabled: false
message: "[Spec Kit] Save progress before clarification"
before_plan:
enabled: false
message: "[Spec Kit] Save progress before planning"
before_tasks:
enabled: false
message: "[Spec Kit] Save progress before task generation"
before_implement:
enabled: false
message: "[Spec Kit] Save progress before implementation"
before_checklist:
enabled: false
message: "[Spec Kit] Save progress before checklist"
before_analyze:
enabled: false
message: "[Spec Kit] Save progress before analysis"
before_taskstoissues:
enabled: false
message: "[Spec Kit] Save progress before issue sync"
after_constitution:
enabled: false
message: "[Spec Kit] Add project constitution"
after_specify:
enabled: false
message: "[Spec Kit] Add specification"
after_clarify:
enabled: false
message: "[Spec Kit] Clarify specification"
after_plan:
enabled: false
message: "[Spec Kit] Add implementation plan"
after_tasks:
enabled: false
message: "[Spec Kit] Add tasks"
after_implement:
enabled: false
message: "[Spec Kit] Implementation progress"
after_checklist:
enabled: false
message: "[Spec Kit] Add checklist"
after_analyze:
enabled: false
message: "[Spec Kit] Add analysis report"
after_taskstoissues:
enabled: false
message: "[Spec Kit] Sync tasks to issues"

View File

@ -1,140 +0,0 @@
#!/usr/bin/env bash
# Git extension: auto-commit.sh
# Automatically commit changes after a Spec Kit command completes.
# Checks per-command config keys in git-config.yml before committing.
#
# Usage: auto-commit.sh <event_name>
# e.g.: auto-commit.sh after_specify
set -e
EVENT_NAME="${1:-}"
if [ -z "$EVENT_NAME" ]; then
echo "Usage: $0 <event_name>" >&2
exit 1
fi
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
_find_project_root() {
local dir="$1"
while [ "$dir" != "/" ]; do
if [ -d "$dir/.specify" ] || [ -d "$dir/.git" ]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
return 1
}
REPO_ROOT=$(_find_project_root "$SCRIPT_DIR") || REPO_ROOT="$(pwd)"
cd "$REPO_ROOT"
# Check if git is available
if ! command -v git >/dev/null 2>&1; then
echo "[specify] Warning: Git not found; skipped auto-commit" >&2
exit 0
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "[specify] Warning: Not a Git repository; skipped auto-commit" >&2
exit 0
fi
# Read per-command config from git-config.yml
_config_file="$REPO_ROOT/.specify/extensions/git/git-config.yml"
_enabled=false
_commit_msg=""
if [ -f "$_config_file" ]; then
# Parse the auto_commit section for this event.
# Look for auto_commit.<event_name>.enabled and .message
# Also check auto_commit.default as fallback.
_in_auto_commit=false
_in_event=false
_default_enabled=false
while IFS= read -r _line; do
# Detect auto_commit: section
if echo "$_line" | grep -q '^auto_commit:'; then
_in_auto_commit=true
_in_event=false
continue
fi
# Exit auto_commit section on next top-level key
if $_in_auto_commit && echo "$_line" | grep -Eq '^[a-z]'; then
break
fi
if $_in_auto_commit; then
# Check default key
if echo "$_line" | grep -Eq "^[[:space:]]+default:[[:space:]]"; then
_val=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
[ "$_val" = "true" ] && _default_enabled=true
fi
# Detect our event subsection
if echo "$_line" | grep -Eq "^[[:space:]]+${EVENT_NAME}:"; then
_in_event=true
continue
fi
# Inside our event subsection
if $_in_event; then
# Exit on next sibling key (same indent level as event name)
if echo "$_line" | grep -Eq '^[[:space:]]{2}[a-z]' && ! echo "$_line" | grep -Eq '^[[:space:]]{4}'; then
_in_event=false
continue
fi
if echo "$_line" | grep -Eq '[[:space:]]+enabled:'; then
_val=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
[ "$_val" = "true" ] && _enabled=true
[ "$_val" = "false" ] && _enabled=false
fi
if echo "$_line" | grep -Eq '[[:space:]]+message:'; then
_commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//')
fi
fi
fi
done < "$_config_file"
# If event-specific key not found, use default
if [ "$_enabled" = "false" ] && [ "$_default_enabled" = "true" ]; then
# Only use default if the event wasn't explicitly set to false
# Check if event section existed at all
if ! grep -q "^[[:space:]]*${EVENT_NAME}:" "$_config_file" 2>/dev/null; then
_enabled=true
fi
fi
else
# No config file — auto-commit disabled by default
exit 0
fi
if [ "$_enabled" != "true" ]; then
exit 0
fi
# Check if there are changes to commit
if git diff --quiet HEAD 2>/dev/null && git diff --cached --quiet 2>/dev/null && [ -z "$(git ls-files --others --exclude-standard 2>/dev/null)" ]; then
echo "[specify] No changes to commit after $EVENT_NAME" >&2
exit 0
fi
# Derive a human-readable command name from the event
# e.g., after_specify -> specify, before_plan -> plan
_command_name=$(echo "$EVENT_NAME" | sed 's/^after_//' | sed 's/^before_//')
_phase=$(echo "$EVENT_NAME" | grep -q '^before_' && echo 'before' || echo 'after')
# Use custom message if configured, otherwise default
if [ -z "$_commit_msg" ]; then
_commit_msg="[Spec Kit] Auto-commit ${_phase} ${_command_name}"
fi
# Stage and commit
_git_out=$(git add . 2>&1) || { echo "[specify] Error: git add failed: $_git_out" >&2; exit 1; }
_git_out=$(git commit -q -m "$_commit_msg" 2>&1) || { echo "[specify] Error: git commit failed: $_git_out" >&2; exit 1; }
echo "[OK] Changes committed ${_phase} ${_command_name}" >&2

View File

@ -1,453 +0,0 @@
#!/usr/bin/env bash
# Git extension: create-new-feature.sh
# Adapted from core scripts/bash/create-new-feature.sh for extension layout.
# Sources common.sh from the project's installed scripts, falling back to
# git-common.sh for minimal git helpers.
set -e
JSON_MODE=false
DRY_RUN=false
ALLOW_EXISTING=false
SHORT_NAME=""
BRANCH_NUMBER=""
USE_TIMESTAMP=false
ARGS=()
i=1
while [ $i -le $# ]; do
arg="${!i}"
case "$arg" in
--json)
JSON_MODE=true
;;
--dry-run)
DRY_RUN=true
;;
--allow-existing-branch)
ALLOW_EXISTING=true
;;
--short-name)
if [ $((i + 1)) -gt $# ]; then
echo 'Error: --short-name requires a value' >&2
exit 1
fi
i=$((i + 1))
next_arg="${!i}"
if [[ "$next_arg" == --* ]]; then
echo 'Error: --short-name requires a value' >&2
exit 1
fi
SHORT_NAME="$next_arg"
;;
--number)
if [ $((i + 1)) -gt $# ]; then
echo 'Error: --number requires a value' >&2
exit 1
fi
i=$((i + 1))
next_arg="${!i}"
if [[ "$next_arg" == --* ]]; then
echo 'Error: --number requires a value' >&2
exit 1
fi
BRANCH_NUMBER="$next_arg"
if [[ ! "$BRANCH_NUMBER" =~ ^[0-9]+$ ]]; then
echo 'Error: --number must be a non-negative integer' >&2
exit 1
fi
;;
--timestamp)
USE_TIMESTAMP=true
;;
--help|-h)
echo "Usage: $0 [--json] [--dry-run] [--allow-existing-branch] [--short-name <name>] [--number N] [--timestamp] <feature_description>"
echo ""
echo "Options:"
echo " --json Output in JSON format"
echo " --dry-run Compute branch name without creating the branch"
echo " --allow-existing-branch Switch to branch if it already exists instead of failing"
echo " --short-name <name> Provide a custom short name (2-4 words) for the branch"
echo " --number N Specify branch number manually (overrides auto-detection)"
echo " --timestamp Use timestamp prefix (YYYYMMDD-HHMMSS) instead of sequential numbering"
echo " --help, -h Show this help message"
echo ""
echo "Environment variables:"
echo " GIT_BRANCH_NAME Use this exact branch name, bypassing all prefix/suffix generation"
echo ""
echo "Examples:"
echo " $0 'Add user authentication system' --short-name 'user-auth'"
echo " $0 'Implement OAuth2 integration for API' --number 5"
echo " $0 --timestamp --short-name 'user-auth' 'Add user authentication'"
echo " GIT_BRANCH_NAME=my-branch $0 'feature description'"
exit 0
;;
*)
ARGS+=("$arg")
;;
esac
i=$((i + 1))
done
FEATURE_DESCRIPTION="${ARGS[*]}"
if [ -z "$FEATURE_DESCRIPTION" ]; then
echo "Usage: $0 [--json] [--dry-run] [--allow-existing-branch] [--short-name <name>] [--number N] [--timestamp] <feature_description>" >&2
exit 1
fi
# Trim whitespace and validate description is not empty
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | xargs)
if [ -z "$FEATURE_DESCRIPTION" ]; then
echo "Error: Feature description cannot be empty or contain only whitespace" >&2
exit 1
fi
# Function to get highest number from specs directory
get_highest_from_specs() {
local specs_dir="$1"
local highest=0
if [ -d "$specs_dir" ]; then
for dir in "$specs_dir"/*; do
[ -d "$dir" ] || continue
dirname=$(basename "$dir")
# Match sequential prefixes (>=3 digits), but skip timestamp dirs.
if echo "$dirname" | grep -Eq '^[0-9]{3,}-' && ! echo "$dirname" | grep -Eq '^[0-9]{8}-[0-9]{6}-'; then
number=$(echo "$dirname" | grep -Eo '^[0-9]+')
number=$((10#$number))
if [ "$number" -gt "$highest" ]; then
highest=$number
fi
fi
done
fi
echo "$highest"
}
# Function to get highest number from git branches
get_highest_from_branches() {
git branch -a 2>/dev/null | sed 's/^[* ]*//; s|^remotes/[^/]*/||' | _extract_highest_number
}
# Extract the highest sequential feature number from a list of ref names (one per line).
_extract_highest_number() {
local highest=0
while IFS= read -r name; do
[ -z "$name" ] && continue
if echo "$name" | grep -Eq '^[0-9]{3,}-' && ! echo "$name" | grep -Eq '^[0-9]{8}-[0-9]{6}-'; then
number=$(echo "$name" | grep -Eo '^[0-9]+' || echo "0")
number=$((10#$number))
if [ "$number" -gt "$highest" ]; then
highest=$number
fi
fi
done
echo "$highest"
}
# Function to get highest number from remote branches without fetching (side-effect-free)
get_highest_from_remote_refs() {
local highest=0
for remote in $(git remote 2>/dev/null); do
local remote_highest
remote_highest=$(GIT_TERMINAL_PROMPT=0 git ls-remote --heads "$remote" 2>/dev/null | sed 's|.*refs/heads/||' | _extract_highest_number)
if [ "$remote_highest" -gt "$highest" ]; then
highest=$remote_highest
fi
done
echo "$highest"
}
# Function to check existing branches and return next available number.
check_existing_branches() {
local specs_dir="$1"
local skip_fetch="${2:-false}"
if [ "$skip_fetch" = true ]; then
local highest_remote=$(get_highest_from_remote_refs)
local highest_branch=$(get_highest_from_branches)
if [ "$highest_remote" -gt "$highest_branch" ]; then
highest_branch=$highest_remote
fi
else
git fetch --all --prune >/dev/null 2>&1 || true
local highest_branch=$(get_highest_from_branches)
fi
local highest_spec=$(get_highest_from_specs "$specs_dir")
local max_num=$highest_branch
if [ "$highest_spec" -gt "$max_num" ]; then
max_num=$highest_spec
fi
echo $((max_num + 1))
}
# Function to clean and format a branch name
clean_branch_name() {
local name="$1"
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//'
}
# ---------------------------------------------------------------------------
# Source common.sh for resolve_template, json_escape, get_repo_root, has_git.
#
# Search locations in priority order:
# 1. .specify/scripts/bash/common.sh under the project root (installed project)
# 2. scripts/bash/common.sh under the project root (source checkout fallback)
# 3. git-common.sh next to this script (minimal fallback — lacks resolve_template)
# ---------------------------------------------------------------------------
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Find project root by walking up from the script location
_find_project_root() {
local dir="$1"
while [ "$dir" != "/" ]; do
if [ -d "$dir/.specify" ] || [ -d "$dir/.git" ]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
return 1
}
_common_loaded=false
_PROJECT_ROOT=$(_find_project_root "$SCRIPT_DIR") || true
if [ -n "$_PROJECT_ROOT" ] && [ -f "$_PROJECT_ROOT/.specify/scripts/bash/common.sh" ]; then
source "$_PROJECT_ROOT/.specify/scripts/bash/common.sh"
_common_loaded=true
elif [ -n "$_PROJECT_ROOT" ] && [ -f "$_PROJECT_ROOT/scripts/bash/common.sh" ]; then
source "$_PROJECT_ROOT/scripts/bash/common.sh"
_common_loaded=true
elif [ -f "$SCRIPT_DIR/git-common.sh" ]; then
source "$SCRIPT_DIR/git-common.sh"
_common_loaded=true
fi
if [ "$_common_loaded" != "true" ]; then
echo "Error: Could not locate common.sh or git-common.sh. Please ensure the Specify core scripts are installed." >&2
exit 1
fi
# Resolve repository root
if type get_repo_root >/dev/null 2>&1; then
REPO_ROOT=$(get_repo_root)
elif git rev-parse --show-toplevel >/dev/null 2>&1; then
REPO_ROOT=$(git rev-parse --show-toplevel)
elif [ -n "$_PROJECT_ROOT" ]; then
REPO_ROOT="$_PROJECT_ROOT"
else
echo "Error: Could not determine repository root." >&2
exit 1
fi
# Check if git is available at this repo root
if type has_git >/dev/null 2>&1; then
if has_git "$REPO_ROOT"; then
HAS_GIT=true
else
HAS_GIT=false
fi
elif git -C "$REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
HAS_GIT=true
else
HAS_GIT=false
fi
cd "$REPO_ROOT"
SPECS_DIR="$REPO_ROOT/specs"
# Function to generate branch name with stop word filtering
generate_branch_name() {
local description="$1"
local stop_words="^(i|a|an|the|to|for|of|in|on|at|by|with|from|is|are|was|were|be|been|being|have|has|had|do|does|did|will|would|should|could|can|may|might|must|shall|this|that|these|those|my|your|our|their|want|need|add|get|set)$"
local clean_name=$(echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/ /g')
local meaningful_words=()
for word in $clean_name; do
[ -z "$word" ] && continue
if ! echo "$word" | grep -qiE "$stop_words"; then
if [ ${#word} -ge 3 ]; then
meaningful_words+=("$word")
elif echo "$description" | grep -qw -- "${word^^}"; then
meaningful_words+=("$word")
fi
fi
done
if [ ${#meaningful_words[@]} -gt 0 ]; then
local max_words=3
if [ ${#meaningful_words[@]} -eq 4 ]; then max_words=4; fi
local result=""
local count=0
for word in "${meaningful_words[@]}"; do
if [ $count -ge $max_words ]; then break; fi
if [ -n "$result" ]; then result="$result-"; fi
result="$result$word"
count=$((count + 1))
done
echo "$result"
else
local cleaned=$(clean_branch_name "$description")
echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
fi
}
# Check for GIT_BRANCH_NAME env var override (exact branch name, no prefix/suffix)
if [ -n "${GIT_BRANCH_NAME:-}" ]; then
BRANCH_NAME="$GIT_BRANCH_NAME"
# Extract FEATURE_NUM from the branch name if it starts with a numeric prefix
# Check timestamp pattern first (YYYYMMDD-HHMMSS-) since it also matches the simpler ^[0-9]+ pattern
if echo "$BRANCH_NAME" | grep -Eq '^[0-9]{8}-[0-9]{6}-'; then
FEATURE_NUM=$(echo "$BRANCH_NAME" | grep -Eo '^[0-9]{8}-[0-9]{6}')
BRANCH_SUFFIX="${BRANCH_NAME#${FEATURE_NUM}-}"
elif echo "$BRANCH_NAME" | grep -Eq '^[0-9]+-'; then
FEATURE_NUM=$(echo "$BRANCH_NAME" | grep -Eo '^[0-9]+')
BRANCH_SUFFIX="${BRANCH_NAME#${FEATURE_NUM}-}"
else
FEATURE_NUM="$BRANCH_NAME"
BRANCH_SUFFIX="$BRANCH_NAME"
fi
else
# Generate branch name
if [ -n "$SHORT_NAME" ]; then
BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME")
else
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
fi
# Warn if --number and --timestamp are both specified
if [ "$USE_TIMESTAMP" = true ] && [ -n "$BRANCH_NUMBER" ]; then
>&2 echo "[specify] Warning: --number is ignored when --timestamp is used"
BRANCH_NUMBER=""
fi
# Determine branch prefix
if [ "$USE_TIMESTAMP" = true ]; then
FEATURE_NUM=$(date +%Y%m%d-%H%M%S)
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
else
if [ -z "$BRANCH_NUMBER" ]; then
if [ "$DRY_RUN" = true ] && [ "$HAS_GIT" = true ]; then
BRANCH_NUMBER=$(check_existing_branches "$SPECS_DIR" true)
elif [ "$DRY_RUN" = true ]; then
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
BRANCH_NUMBER=$((HIGHEST + 1))
elif [ "$HAS_GIT" = true ]; then
BRANCH_NUMBER=$(check_existing_branches "$SPECS_DIR")
else
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
BRANCH_NUMBER=$((HIGHEST + 1))
fi
fi
FEATURE_NUM=$(printf "%03d" "$((10#$BRANCH_NUMBER))")
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
fi
fi
# GitHub enforces a 244-byte limit on branch names
MAX_BRANCH_LENGTH=244
_byte_length() { printf '%s' "$1" | LC_ALL=C wc -c | tr -d ' '; }
BRANCH_BYTE_LEN=$(_byte_length "$BRANCH_NAME")
if [ -n "${GIT_BRANCH_NAME:-}" ] && [ "$BRANCH_BYTE_LEN" -gt $MAX_BRANCH_LENGTH ]; then
>&2 echo "Error: GIT_BRANCH_NAME must be 244 bytes or fewer in UTF-8. Provided value is ${BRANCH_BYTE_LEN} bytes."
exit 1
elif [ "$BRANCH_BYTE_LEN" -gt $MAX_BRANCH_LENGTH ]; then
PREFIX_LENGTH=$(( ${#FEATURE_NUM} + 1 ))
MAX_SUFFIX_LENGTH=$((MAX_BRANCH_LENGTH - PREFIX_LENGTH))
TRUNCATED_SUFFIX=$(echo "$BRANCH_SUFFIX" | cut -c1-$MAX_SUFFIX_LENGTH)
TRUNCATED_SUFFIX=$(echo "$TRUNCATED_SUFFIX" | sed 's/-$//')
ORIGINAL_BRANCH_NAME="$BRANCH_NAME"
BRANCH_NAME="${FEATURE_NUM}-${TRUNCATED_SUFFIX}"
>&2 echo "[specify] Warning: Branch name exceeded GitHub's 244-byte limit"
>&2 echo "[specify] Original: $ORIGINAL_BRANCH_NAME (${#ORIGINAL_BRANCH_NAME} bytes)"
>&2 echo "[specify] Truncated to: $BRANCH_NAME (${#BRANCH_NAME} bytes)"
fi
if [ "$DRY_RUN" != true ]; then
if [ "$HAS_GIT" = true ]; then
branch_create_error=""
if ! branch_create_error=$(git checkout -q -b "$BRANCH_NAME" 2>&1); then
current_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if git branch --list "$BRANCH_NAME" | grep -q .; then
if [ "$ALLOW_EXISTING" = true ]; then
if [ "$current_branch" = "$BRANCH_NAME" ]; then
:
elif ! switch_branch_error=$(git checkout -q "$BRANCH_NAME" 2>&1); then
>&2 echo "Error: Failed to switch to existing branch '$BRANCH_NAME'. Please resolve any local changes or conflicts and try again."
if [ -n "$switch_branch_error" ]; then
>&2 printf '%s\n' "$switch_branch_error"
fi
exit 1
fi
elif [ "$USE_TIMESTAMP" = true ]; then
>&2 echo "Error: Branch '$BRANCH_NAME' already exists. Rerun to get a new timestamp or use a different --short-name."
exit 1
else
>&2 echo "Error: Branch '$BRANCH_NAME' already exists. Please use a different feature name or specify a different number with --number."
exit 1
fi
else
>&2 echo "Error: Failed to create git branch '$BRANCH_NAME'."
if [ -n "$branch_create_error" ]; then
>&2 printf '%s\n' "$branch_create_error"
else
>&2 echo "Please check your git configuration and try again."
fi
exit 1
fi
fi
else
>&2 echo "[specify] Warning: Git repository not detected; skipped branch creation for $BRANCH_NAME"
fi
printf '# To persist: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME" >&2
fi
if $JSON_MODE; then
if command -v jq >/dev/null 2>&1; then
if [ "$DRY_RUN" = true ]; then
jq -cn \
--arg branch_name "$BRANCH_NAME" \
--arg feature_num "$FEATURE_NUM" \
'{BRANCH_NAME:$branch_name,FEATURE_NUM:$feature_num,DRY_RUN:true}'
else
jq -cn \
--arg branch_name "$BRANCH_NAME" \
--arg feature_num "$FEATURE_NUM" \
'{BRANCH_NAME:$branch_name,FEATURE_NUM:$feature_num}'
fi
else
if type json_escape >/dev/null 2>&1; then
_je_branch=$(json_escape "$BRANCH_NAME")
_je_num=$(json_escape "$FEATURE_NUM")
else
_je_branch="$BRANCH_NAME"
_je_num="$FEATURE_NUM"
fi
if [ "$DRY_RUN" = true ]; then
printf '{"BRANCH_NAME":"%s","FEATURE_NUM":"%s","DRY_RUN":true}\n' "$_je_branch" "$_je_num"
else
printf '{"BRANCH_NAME":"%s","FEATURE_NUM":"%s"}\n' "$_je_branch" "$_je_num"
fi
fi
else
echo "BRANCH_NAME: $BRANCH_NAME"
echo "FEATURE_NUM: $FEATURE_NUM"
if [ "$DRY_RUN" != true ]; then
printf '# To persist in your shell: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME"
fi
fi

View File

@ -1,54 +0,0 @@
#!/usr/bin/env bash
# Git-specific common functions for the git extension.
# Extracted from scripts/bash/common.sh — contains only git-specific
# branch validation and detection logic.
# Check if we have git available at the repo root
has_git() {
local repo_root="${1:-$(pwd)}"
{ [ -d "$repo_root/.git" ] || [ -f "$repo_root/.git" ]; } && \
command -v git >/dev/null 2>&1 && \
git -C "$repo_root" rev-parse --is-inside-work-tree >/dev/null 2>&1
}
# Strip a single optional path segment (e.g. gitflow "feat/004-name" -> "004-name").
# Only when the full name is exactly two slash-free segments; otherwise returns the raw name.
spec_kit_effective_branch_name() {
local raw="$1"
if [[ "$raw" =~ ^([^/]+)/([^/]+)$ ]]; then
printf '%s\n' "${BASH_REMATCH[2]}"
else
printf '%s\n' "$raw"
fi
}
# Validate that a branch name matches the expected feature branch pattern.
# Accepts sequential (###-* with >=3 digits) or timestamp (YYYYMMDD-HHMMSS-*) formats.
# Logic aligned with scripts/bash/common.sh check_feature_branch after effective-name normalization.
check_feature_branch() {
local raw="$1"
local has_git_repo="$2"
# For non-git repos, we can't enforce branch naming but still provide output
if [[ "$has_git_repo" != "true" ]]; then
echo "[specify] Warning: Git repository not detected; skipped branch validation" >&2
return 0
fi
local branch
branch=$(spec_kit_effective_branch_name "$raw")
# Accept sequential prefix (3+ digits) but exclude malformed timestamps
# Malformed: 7-or-8 digit date + 6-digit time with no trailing slug (e.g. "2026031-143022" or "20260319-143022")
local is_sequential=false
if [[ "$branch" =~ ^[0-9]{3,}- ]] && [[ ! "$branch" =~ ^[0-9]{7}-[0-9]{6}- ]] && [[ ! "$branch" =~ ^[0-9]{7,8}-[0-9]{6}$ ]]; then
is_sequential=true
fi
if [[ "$is_sequential" != "true" ]] && [[ ! "$branch" =~ ^[0-9]{8}-[0-9]{6}- ]]; then
echo "ERROR: Not on a feature branch. Current branch: $raw" >&2
echo "Feature branches should be named like: 001-feature-name, 1234-feature-name, or 20260319-143022-feature-name" >&2
return 1
fi
return 0
}

View File

@ -1,54 +0,0 @@
#!/usr/bin/env bash
# Git extension: initialize-repo.sh
# Initialize a Git repository with an initial commit.
# Customizable — replace this script to add .gitignore templates,
# default branch config, git-flow, LFS, signing, etc.
set -e
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Find project root
_find_project_root() {
local dir="$1"
while [ "$dir" != "/" ]; do
if [ -d "$dir/.specify" ] || [ -d "$dir/.git" ]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
return 1
}
REPO_ROOT=$(_find_project_root "$SCRIPT_DIR") || REPO_ROOT="$(pwd)"
cd "$REPO_ROOT"
# Read commit message from extension config, fall back to default
COMMIT_MSG="[Spec Kit] Initial commit"
_config_file="$REPO_ROOT/.specify/extensions/git/git-config.yml"
if [ -f "$_config_file" ]; then
_msg=$(grep '^init_commit_message:' "$_config_file" 2>/dev/null | sed 's/^init_commit_message:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//')
if [ -n "$_msg" ]; then
COMMIT_MSG="$_msg"
fi
fi
# Check if git is available
if ! command -v git >/dev/null 2>&1; then
echo "[specify] Warning: Git not found; skipped repository initialization" >&2
exit 0
fi
# Check if already a git repo
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "[specify] Git repository already initialized; skipping" >&2
exit 0
fi
# Initialize
_git_out=$(git init -q 2>&1) || { echo "[specify] Error: git init failed: $_git_out" >&2; exit 1; }
_git_out=$(git add . 2>&1) || { echo "[specify] Error: git add failed: $_git_out" >&2; exit 1; }
_git_out=$(git commit --allow-empty -q -m "$COMMIT_MSG" 2>&1) || { echo "[specify] Error: git commit failed: $_git_out" >&2; exit 1; }
echo "✓ Git repository initialized" >&2

View File

@ -1,169 +0,0 @@
#!/usr/bin/env pwsh
# Git extension: auto-commit.ps1
# Automatically commit changes after a Spec Kit command completes.
# Checks per-command config keys in git-config.yml before committing.
#
# Usage: auto-commit.ps1 <event_name>
# e.g.: auto-commit.ps1 after_specify
param(
[Parameter(Position = 0, Mandatory = $true)]
[string]$EventName
)
$ErrorActionPreference = 'Stop'
function Find-ProjectRoot {
param([string]$StartDir)
$current = Resolve-Path $StartDir
while ($true) {
foreach ($marker in @('.specify', '.git')) {
if (Test-Path (Join-Path $current $marker)) {
return $current
}
}
$parent = Split-Path $current -Parent
if ($parent -eq $current) { return $null }
$current = $parent
}
}
$repoRoot = Find-ProjectRoot -StartDir $PSScriptRoot
if (-not $repoRoot) { $repoRoot = Get-Location }
Set-Location $repoRoot
# Check if git is available
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
Write-Warning "[specify] Warning: Git not found; skipped auto-commit"
exit 0
}
# Temporarily relax ErrorActionPreference so git stderr warnings
# (e.g. CRLF notices on Windows) do not become terminating errors.
$savedEAP = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
git rev-parse --is-inside-work-tree 2>$null | Out-Null
$isRepo = $LASTEXITCODE -eq 0
} finally {
$ErrorActionPreference = $savedEAP
}
if (-not $isRepo) {
Write-Warning "[specify] Warning: Not a Git repository; skipped auto-commit"
exit 0
}
# Read per-command config from git-config.yml
$configFile = Join-Path $repoRoot ".specify/extensions/git/git-config.yml"
$enabled = $false
$commitMsg = ""
if (Test-Path $configFile) {
# Parse YAML to find auto_commit section
$inAutoCommit = $false
$inEvent = $false
$defaultEnabled = $false
foreach ($line in Get-Content $configFile) {
# Detect auto_commit: section
if ($line -match '^auto_commit:') {
$inAutoCommit = $true
$inEvent = $false
continue
}
# Exit auto_commit section on next top-level key
if ($inAutoCommit -and $line -match '^[a-z]') {
break
}
if ($inAutoCommit) {
# Check default key
if ($line -match '^\s+default:\s*(.+)$') {
$val = $matches[1].Trim().ToLower()
if ($val -eq 'true') { $defaultEnabled = $true }
}
# Detect our event subsection
if ($line -match "^\s+${EventName}:") {
$inEvent = $true
continue
}
# Inside our event subsection
if ($inEvent) {
# Exit on next sibling key (2-space indent, not 4+)
if ($line -match '^\s{2}[a-z]' -and $line -notmatch '^\s{4}') {
$inEvent = $false
continue
}
if ($line -match '\s+enabled:\s*(.+)$') {
$val = $matches[1].Trim().ToLower()
if ($val -eq 'true') { $enabled = $true }
if ($val -eq 'false') { $enabled = $false }
}
if ($line -match '\s+message:\s*(.+)$') {
$commitMsg = $matches[1].Trim() -replace '^["'']' -replace '["'']$'
}
}
}
}
# If event-specific key not found, use default
if (-not $enabled -and $defaultEnabled) {
$hasEventKey = Select-String -Path $configFile -Pattern "^\s*${EventName}:" -Quiet
if (-not $hasEventKey) {
$enabled = $true
}
}
} else {
# No config file — auto-commit disabled by default
exit 0
}
if (-not $enabled) {
exit 0
}
# Check if there are changes to commit
# Relax ErrorActionPreference so CRLF warnings on stderr do not terminate.
$savedEAP = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
git diff --quiet HEAD 2>$null; $d1 = $LASTEXITCODE
git diff --cached --quiet 2>$null; $d2 = $LASTEXITCODE
$untracked = git ls-files --others --exclude-standard 2>$null
} finally {
$ErrorActionPreference = $savedEAP
}
if ($d1 -eq 0 -and $d2 -eq 0 -and -not $untracked) {
Write-Host "[specify] No changes to commit after $EventName" -ForegroundColor DarkGray
exit 0
}
# Derive a human-readable command name from the event
$commandName = $EventName -replace '^after_', '' -replace '^before_', ''
$phase = if ($EventName -match '^before_') { 'before' } else { 'after' }
# Use custom message if configured, otherwise default
if (-not $commitMsg) {
$commitMsg = "[Spec Kit] Auto-commit $phase $commandName"
}
# Stage and commit
# Relax ErrorActionPreference so CRLF warnings on stderr do not terminate,
# while still allowing redirected error output to be captured for diagnostics.
$savedEAP = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
$out = git add . 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) { throw "git add failed: $out" }
$out = git commit -q -m $commitMsg 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) { throw "git commit failed: $out" }
} catch {
Write-Warning "[specify] Error: $_"
exit 1
} finally {
$ErrorActionPreference = $savedEAP
}
Write-Host "[OK] Changes committed $phase $commandName"

View File

@ -1,403 +0,0 @@
#!/usr/bin/env pwsh
# Git extension: create-new-feature.ps1
# Adapted from core scripts/powershell/create-new-feature.ps1 for extension layout.
# Sources common.ps1 from the project's installed scripts, falling back to
# git-common.ps1 for minimal git helpers.
[CmdletBinding()]
param(
[switch]$Json,
[switch]$AllowExistingBranch,
[switch]$DryRun,
[string]$ShortName,
[Parameter()]
[long]$Number = 0,
[switch]$Timestamp,
[switch]$Help,
[Parameter(Position = 0, ValueFromRemainingArguments = $true)]
[string[]]$FeatureDescription
)
$ErrorActionPreference = 'Stop'
if ($Help) {
Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-DryRun] [-AllowExistingBranch] [-ShortName <name>] [-Number N] [-Timestamp] <feature description>"
Write-Host ""
Write-Host "Options:"
Write-Host " -Json Output in JSON format"
Write-Host " -DryRun Compute branch name without creating the branch"
Write-Host " -AllowExistingBranch Switch to branch if it already exists instead of failing"
Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the branch"
Write-Host " -Number N Specify branch number manually (overrides auto-detection)"
Write-Host " -Timestamp Use timestamp prefix (YYYYMMDD-HHMMSS) instead of sequential numbering"
Write-Host " -Help Show this help message"
Write-Host ""
Write-Host "Environment variables:"
Write-Host " GIT_BRANCH_NAME Use this exact branch name, bypassing all prefix/suffix generation"
Write-Host ""
exit 0
}
if (-not $FeatureDescription -or $FeatureDescription.Count -eq 0) {
Write-Error "Usage: ./create-new-feature.ps1 [-Json] [-DryRun] [-AllowExistingBranch] [-ShortName <name>] [-Number N] [-Timestamp] <feature description>"
exit 1
}
$featureDesc = ($FeatureDescription -join ' ').Trim()
if ([string]::IsNullOrWhiteSpace($featureDesc)) {
Write-Error "Error: Feature description cannot be empty or contain only whitespace"
exit 1
}
function Get-HighestNumberFromSpecs {
param([string]$SpecsDir)
[long]$highest = 0
if (Test-Path $SpecsDir) {
Get-ChildItem -Path $SpecsDir -Directory | ForEach-Object {
if ($_.Name -match '^(\d{3,})-' -and $_.Name -notmatch '^\d{8}-\d{6}-') {
[long]$num = 0
if ([long]::TryParse($matches[1], [ref]$num) -and $num -gt $highest) {
$highest = $num
}
}
}
}
return $highest
}
function Get-HighestNumberFromNames {
param([string[]]$Names)
[long]$highest = 0
foreach ($name in $Names) {
if ($name -match '^(\d{3,})-' -and $name -notmatch '^\d{8}-\d{6}-') {
[long]$num = 0
if ([long]::TryParse($matches[1], [ref]$num) -and $num -gt $highest) {
$highest = $num
}
}
}
return $highest
}
function Get-HighestNumberFromBranches {
param()
try {
$branches = git branch -a 2>$null
if ($LASTEXITCODE -eq 0 -and $branches) {
$cleanNames = $branches | ForEach-Object {
$_.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
}
return Get-HighestNumberFromNames -Names $cleanNames
}
} catch {
Write-Verbose "Could not check Git branches: $_"
}
return 0
}
function Get-HighestNumberFromRemoteRefs {
[long]$highest = 0
try {
$remotes = git remote 2>$null
if ($remotes) {
foreach ($remote in $remotes) {
$env:GIT_TERMINAL_PROMPT = '0'
$refs = git ls-remote --heads $remote 2>$null
$env:GIT_TERMINAL_PROMPT = $null
if ($LASTEXITCODE -eq 0 -and $refs) {
$refNames = $refs | ForEach-Object {
if ($_ -match 'refs/heads/(.+)$') { $matches[1] }
} | Where-Object { $_ }
$remoteHighest = Get-HighestNumberFromNames -Names $refNames
if ($remoteHighest -gt $highest) { $highest = $remoteHighest }
}
}
}
} catch {
Write-Verbose "Could not query remote refs: $_"
}
return $highest
}
function Get-NextBranchNumber {
param(
[string]$SpecsDir,
[switch]$SkipFetch
)
if ($SkipFetch) {
$highestBranch = Get-HighestNumberFromBranches
$highestRemote = Get-HighestNumberFromRemoteRefs
$highestBranch = [Math]::Max($highestBranch, $highestRemote)
} else {
try {
git fetch --all --prune 2>$null | Out-Null
} catch { }
$highestBranch = Get-HighestNumberFromBranches
}
$highestSpec = Get-HighestNumberFromSpecs -SpecsDir $SpecsDir
$maxNum = [Math]::Max($highestBranch, $highestSpec)
return $maxNum + 1
}
function ConvertTo-CleanBranchName {
param([string]$Name)
return $Name.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
}
# ---------------------------------------------------------------------------
# Source common.ps1 from the project's installed scripts.
# Search locations in priority order:
# 1. .specify/scripts/powershell/common.ps1 under the project root
# 2. scripts/powershell/common.ps1 under the project root (source checkout)
# 3. git-common.ps1 next to this script (minimal fallback)
# ---------------------------------------------------------------------------
function Find-ProjectRoot {
param([string]$StartDir)
$current = Resolve-Path $StartDir
while ($true) {
foreach ($marker in @('.specify', '.git')) {
if (Test-Path (Join-Path $current $marker)) {
return $current
}
}
$parent = Split-Path $current -Parent
if ($parent -eq $current) { return $null }
$current = $parent
}
}
$projectRoot = Find-ProjectRoot -StartDir $PSScriptRoot
$commonLoaded = $false
if ($projectRoot) {
$candidates = @(
(Join-Path $projectRoot ".specify/scripts/powershell/common.ps1"),
(Join-Path $projectRoot "scripts/powershell/common.ps1")
)
foreach ($candidate in $candidates) {
if (Test-Path $candidate) {
. $candidate
$commonLoaded = $true
break
}
}
}
if (-not $commonLoaded -and (Test-Path "$PSScriptRoot/git-common.ps1")) {
. "$PSScriptRoot/git-common.ps1"
$commonLoaded = $true
}
if (-not $commonLoaded) {
throw "Unable to locate common script file. Please ensure the Specify core scripts are installed."
}
# Resolve repository root
if (Get-Command Get-RepoRoot -ErrorAction SilentlyContinue) {
$repoRoot = Get-RepoRoot
} elseif ($projectRoot) {
$repoRoot = $projectRoot
} else {
throw "Could not determine repository root."
}
# Check if git is available
if (Get-Command Test-HasGit -ErrorAction SilentlyContinue) {
# Call without parameters for compatibility with core common.ps1 (no -RepoRoot param)
# and git-common.ps1 (has -RepoRoot param with default).
$hasGit = Test-HasGit
} else {
try {
git -C $repoRoot rev-parse --is-inside-work-tree 2>$null | Out-Null
$hasGit = ($LASTEXITCODE -eq 0)
} catch {
$hasGit = $false
}
}
Set-Location $repoRoot
$specsDir = Join-Path $repoRoot 'specs'
function Get-BranchName {
param([string]$Description)
$stopWords = @(
'i', 'a', 'an', 'the', 'to', 'for', 'of', 'in', 'on', 'at', 'by', 'with', 'from',
'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
'do', 'does', 'did', 'will', 'would', 'should', 'could', 'can', 'may', 'might', 'must', 'shall',
'this', 'that', 'these', 'those', 'my', 'your', 'our', 'their',
'want', 'need', 'add', 'get', 'set'
)
$cleanName = $Description.ToLower() -replace '[^a-z0-9\s]', ' '
$words = $cleanName -split '\s+' | Where-Object { $_ }
$meaningfulWords = @()
foreach ($word in $words) {
if ($stopWords -contains $word) { continue }
if ($word.Length -ge 3) {
$meaningfulWords += $word
} elseif ($Description -match "\b$($word.ToUpper())\b") {
$meaningfulWords += $word
}
}
if ($meaningfulWords.Count -gt 0) {
$maxWords = if ($meaningfulWords.Count -eq 4) { 4 } else { 3 }
$result = ($meaningfulWords | Select-Object -First $maxWords) -join '-'
return $result
} else {
$result = ConvertTo-CleanBranchName -Name $Description
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
return [string]::Join('-', $fallbackWords)
}
}
# Check for GIT_BRANCH_NAME env var override (exact branch name, no prefix/suffix)
if ($env:GIT_BRANCH_NAME) {
$branchName = $env:GIT_BRANCH_NAME
# Check 244-byte limit (UTF-8) for override names
$branchNameUtf8ByteCount = [System.Text.Encoding]::UTF8.GetByteCount($branchName)
if ($branchNameUtf8ByteCount -gt 244) {
throw "GIT_BRANCH_NAME must be 244 bytes or fewer in UTF-8. Provided value is $branchNameUtf8ByteCount bytes; please supply a shorter override branch name."
}
# Extract FEATURE_NUM from the branch name if it starts with a numeric prefix
# Check timestamp pattern first (YYYYMMDD-HHMMSS-) since it also matches the simpler ^\d+ pattern
if ($branchName -match '^(\d{8}-\d{6})-') {
$featureNum = $matches[1]
} elseif ($branchName -match '^(\d+)-') {
$featureNum = $matches[1]
} else {
$featureNum = $branchName
}
} else {
if ($ShortName) {
$branchSuffix = ConvertTo-CleanBranchName -Name $ShortName
} else {
$branchSuffix = Get-BranchName -Description $featureDesc
}
if ($Timestamp -and $Number -ne 0) {
Write-Warning "[specify] Warning: -Number is ignored when -Timestamp is used"
$Number = 0
}
if ($Timestamp) {
$featureNum = Get-Date -Format 'yyyyMMdd-HHmmss'
$branchName = "$featureNum-$branchSuffix"
} else {
if ($Number -eq 0) {
if ($DryRun -and $hasGit) {
$Number = Get-NextBranchNumber -SpecsDir $specsDir -SkipFetch
} elseif ($DryRun) {
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
} elseif ($hasGit) {
$Number = Get-NextBranchNumber -SpecsDir $specsDir
} else {
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
}
}
$featureNum = ('{0:000}' -f $Number)
$branchName = "$featureNum-$branchSuffix"
}
}
$maxBranchLength = 244
if ($branchName.Length -gt $maxBranchLength) {
$prefixLength = $featureNum.Length + 1
$maxSuffixLength = $maxBranchLength - $prefixLength
$truncatedSuffix = $branchSuffix.Substring(0, [Math]::Min($branchSuffix.Length, $maxSuffixLength))
$truncatedSuffix = $truncatedSuffix -replace '-$', ''
$originalBranchName = $branchName
$branchName = "$featureNum-$truncatedSuffix"
Write-Warning "[specify] Branch name exceeded GitHub's 244-byte limit"
Write-Warning "[specify] Original: $originalBranchName ($($originalBranchName.Length) bytes)"
Write-Warning "[specify] Truncated to: $branchName ($($branchName.Length) bytes)"
}
if (-not $DryRun) {
if ($hasGit) {
$branchCreated = $false
$branchCreateError = ''
try {
$branchCreateError = git checkout -q -b $branchName 2>&1 | Out-String
if ($LASTEXITCODE -eq 0) {
$branchCreated = $true
}
} catch {
$branchCreateError = $_.Exception.Message
}
if (-not $branchCreated) {
$currentBranch = ''
try { $currentBranch = (git rev-parse --abbrev-ref HEAD 2>$null).Trim() } catch {}
$existingBranch = git branch --list $branchName 2>$null
if ($existingBranch) {
if ($AllowExistingBranch) {
if ($currentBranch -eq $branchName) {
# Already on the target branch
} else {
$switchBranchError = git checkout -q $branchName 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) {
if ($switchBranchError) {
Write-Error "Error: Branch '$branchName' exists but could not be checked out.`n$($switchBranchError.Trim())"
} else {
Write-Error "Error: Branch '$branchName' exists but could not be checked out. Resolve any uncommitted changes or conflicts and try again."
}
exit 1
}
}
} elseif ($Timestamp) {
Write-Error "Error: Branch '$branchName' already exists. Rerun to get a new timestamp or use a different -ShortName."
exit 1
} else {
Write-Error "Error: Branch '$branchName' already exists. Please use a different feature name or specify a different number with -Number."
exit 1
}
} else {
if ($branchCreateError) {
Write-Error "Error: Failed to create git branch '$branchName'.`n$($branchCreateError.Trim())"
} else {
Write-Error "Error: Failed to create git branch '$branchName'. Please check your git configuration and try again."
}
exit 1
}
}
} else {
if ($Json) {
[Console]::Error.WriteLine("[specify] Warning: Git repository not detected; skipped branch creation for $branchName")
} else {
Write-Warning "[specify] Warning: Git repository not detected; skipped branch creation for $branchName"
}
}
$env:SPECIFY_FEATURE = $branchName
}
if ($Json) {
$obj = [PSCustomObject]@{
BRANCH_NAME = $branchName
FEATURE_NUM = $featureNum
HAS_GIT = $hasGit
}
if ($DryRun) {
$obj | Add-Member -NotePropertyName 'DRY_RUN' -NotePropertyValue $true
}
$obj | ConvertTo-Json -Compress
} else {
Write-Output "BRANCH_NAME: $branchName"
Write-Output "FEATURE_NUM: $featureNum"
Write-Output "HAS_GIT: $hasGit"
if (-not $DryRun) {
Write-Output "SPECIFY_FEATURE environment variable set to: $branchName"
}
}

View File

@ -1,51 +0,0 @@
#!/usr/bin/env pwsh
# Git-specific common functions for the git extension.
# Extracted from scripts/powershell/common.ps1 — contains only git-specific
# branch validation and detection logic.
function Test-HasGit {
param([string]$RepoRoot = (Get-Location))
try {
if (-not (Test-Path (Join-Path $RepoRoot '.git'))) { return $false }
if (-not (Get-Command git -ErrorAction SilentlyContinue)) { return $false }
git -C $RepoRoot rev-parse --is-inside-work-tree 2>$null | Out-Null
return ($LASTEXITCODE -eq 0)
} catch {
return $false
}
}
function Get-SpecKitEffectiveBranchName {
param([string]$Branch)
if ($Branch -match '^([^/]+)/([^/]+)$') {
return $Matches[2]
}
return $Branch
}
function Test-FeatureBranch {
param(
[string]$Branch,
[bool]$HasGit = $true
)
# For non-git repos, we can't enforce branch naming but still provide output
if (-not $HasGit) {
Write-Warning "[specify] Warning: Git repository not detected; skipped branch validation"
return $true
}
$raw = $Branch
$Branch = Get-SpecKitEffectiveBranchName $raw
# Accept sequential prefix (3+ digits) but exclude malformed timestamps
# Malformed: 7-or-8 digit date + 6-digit time with no trailing slug (e.g. "2026031-143022" or "20260319-143022")
$hasMalformedTimestamp = ($Branch -match '^[0-9]{7}-[0-9]{6}-') -or ($Branch -match '^(?:\d{7}|\d{8})-\d{6}$')
$isSequential = ($Branch -match '^[0-9]{3,}-') -and (-not $hasMalformedTimestamp)
if (-not $isSequential -and $Branch -notmatch '^\d{8}-\d{6}-') {
[Console]::Error.WriteLine("ERROR: Not on a feature branch. Current branch: $raw")
[Console]::Error.WriteLine("Feature branches should be named like: 001-feature-name, 1234-feature-name, or 20260319-143022-feature-name")
return $false
}
return $true
}

View File

@ -1,69 +0,0 @@
#!/usr/bin/env pwsh
# Git extension: initialize-repo.ps1
# Initialize a Git repository with an initial commit.
# Customizable — replace this script to add .gitignore templates,
# default branch config, git-flow, LFS, signing, etc.
$ErrorActionPreference = 'Stop'
# Find project root
function Find-ProjectRoot {
param([string]$StartDir)
$current = Resolve-Path $StartDir
while ($true) {
foreach ($marker in @('.specify', '.git')) {
if (Test-Path (Join-Path $current $marker)) {
return $current
}
}
$parent = Split-Path $current -Parent
if ($parent -eq $current) { return $null }
$current = $parent
}
}
$repoRoot = Find-ProjectRoot -StartDir $PSScriptRoot
if (-not $repoRoot) { $repoRoot = Get-Location }
Set-Location $repoRoot
# Read commit message from extension config, fall back to default
$commitMsg = "[Spec Kit] Initial commit"
$configFile = Join-Path $repoRoot ".specify/extensions/git/git-config.yml"
if (Test-Path $configFile) {
foreach ($line in Get-Content $configFile) {
if ($line -match '^init_commit_message:\s*(.+)$') {
$val = $matches[1].Trim() -replace '^["'']' -replace '["'']$'
if ($val) { $commitMsg = $val }
break
}
}
}
# Check if git is available
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
Write-Warning "[specify] Warning: Git not found; skipped repository initialization"
exit 0
}
# Check if already a git repo
try {
git rev-parse --is-inside-work-tree 2>$null | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Warning "[specify] Git repository already initialized; skipping"
exit 0
}
} catch { }
# Initialize
try {
$out = git init -q 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) { throw "git init failed: $out" }
$out = git add . 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) { throw "git add failed: $out" }
$out = git commit --allow-empty -q -m $commitMsg 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) { throw "git commit failed: $out" }
} catch {
Write-Warning "[specify] Error: $_"
exit 1
}
Write-Host "✓ Git repository initialized"

View File

@ -1,10 +0,0 @@
{
"ai": "copilot",
"branch_numbering": "sequential",
"context_file": ".github/copilot-instructions.md",
"here": true,
"integration": "copilot",
"preset": null,
"script": "sh",
"speckit_version": "0.7.4"
}

View File

@ -1,4 +0,0 @@
{
"integration": "copilot",
"version": "0.7.4"
}

View File

@ -1,25 +0,0 @@
{
"integration": "copilot",
"version": "0.7.4",
"installed_at": "2026-04-22T21:58:02.962169+00:00",
"files": {
".github/agents/speckit.analyze.agent.md": "699032fdd49afe31d23c7191f3fe7bcb1d14b081fbc94c2287e6ba3a57574fda",
".github/agents/speckit.checklist.agent.md": "d7d691689fe45427c868dcf18ade4df500f0c742a6c91923fefba405d6466dde",
".github/agents/speckit.clarify.agent.md": "0cc766dcc5cab233ccdf3bc4cfb5759a6d7d1e13e29f611083046f818f5812bb",
".github/agents/speckit.constitution.agent.md": "58d35eb026f56bb7364d91b8b0382d5dd1249ded6c1449a2b69546693afb85f7",
".github/agents/speckit.implement.agent.md": "83628415c86ba487b3a083c7a2c0f016c9073abd02c1c7f4a30cff949b6602c0",
".github/agents/speckit.plan.agent.md": "2ad128b81ccd8f5bfa78b3b43101f377dfddd8f800fa0856f85bf53b1489b783",
".github/agents/speckit.specify.agent.md": "5bbb5270836cc9a3286ce3ed96a500f3d383a54abb06aa11b01a2d2f76dbf39b",
".github/agents/speckit.tasks.agent.md": "a58886f29f75e1a14840007772ddd954742aafb3e03d9d1231bee033e6c1626b",
".github/agents/speckit.taskstoissues.agent.md": "e84794f7a839126defb364ca815352c5c2b2d20db2d6da399fa53e4ddbb7b3ee",
".github/prompts/speckit.analyze.prompt.md": "bb93dbbafa96d07b7cd07fc7061d8adb0c6b26cb772a52d0dce263b1ca2b9b77",
".github/prompts/speckit.checklist.prompt.md": "c3aea7526c5cbfd8665acc9508ad5a9a3f71e91a63c36be7bed13a834c3a683c",
".github/prompts/speckit.clarify.prompt.md": "ce79b3437ca918d46ac858eb4b8b44d3b0a02c563660c60d94c922a7b5d8d4f4",
".github/prompts/speckit.constitution.prompt.md": "38f937279de14387601422ddfda48365debdbaf47b2d513527b8f6d8a27d499d",
".github/prompts/speckit.implement.prompt.md": "5053a17fb9238338c63b898ee9c80b2cb4ad1a90c6071fe3748de76864ac6a80",
".github/prompts/speckit.plan.prompt.md": "2098dae6bd9277335f31cb150b78bfb1de539c0491798e5cfe382c89ab0bcd0e",
".github/prompts/speckit.specify.prompt.md": "7b2cc4dc6462da1c96df46bac4f60e53baba3097f4b24ac3f9b684194458aa98",
".github/prompts/speckit.tasks.prompt.md": "88fc57c289f99d5e9d35c255f3e2683f73ecb0a5155dcb4d886f82f52b11841f",
".github/prompts/speckit.taskstoissues.prompt.md": "2f9636d4f312a1470f000747cb62677fec0655d8b4e2357fa4fbf238965fa66d"
}
}

View File

@ -1,8 +0,0 @@
{
"integration": "speckit",
"version": "0.7.4",
"installed_at": "2026-04-22T21:58:02.965809+00:00",
"files": {
".specify/templates/constitution-template.md": "ce7549540fa45543cca797a150201d868e64495fdff39dc38246fb17bd4024b3"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,236 +0,0 @@
# TenantPilot Spec Approval Rubric (Anti-Overengineering Guardrails)
## Leitsatz
> Kein neuer Layer ohne klaren Operatorgewinn, und kein neuer Spec nur für interne semantische Schönheit.
Ein neuer Spec ist nur dann stark genug, wenn er **sichtbar mehr Produktwahrheit oder Operator-Wirkung** erzeugt als er dauerhafte Systemkomplexität importiert.
Jeder Spec muss zwei Dinge gleichzeitig beweisen:
1. Welches echte Problem wird gelöst?
2. Warum ist diese Lösung die kleinste enterprise-taugliche Form?
Wenn der Spec nur interne Eleganz, feinere Semantik oder mehr Konsistenz bringt, aber keinen klaren Workflow-, Trust- oder Audit-Gewinn, dann ist er **verdächtig**.
---
## 5 Pflichtfragen vor jeder Freigabe
Ein Spec darf nur weiterverfolgt werden, wenn diese 5 Fragen sauber beantwortet sind.
### A. Welcher konkrete Operator-Workflow wird besser?
Nicht abstrakt „Konsistenz verbessern", sondern konkret: welcher Nutzer, auf welcher Fläche, in welchem Schritt, mit welchem heutigen Schmerz, und was danach schneller, sicherer oder ehrlicher wird.
Wenn kein klarer Vorher/Nachher-Workflow benennbar ist → Spec ist zu abstrakt.
### B. Welche falsche oder gefährliche Produktaussage wird verhindert?
Legitime Antworten:
- Falscher „alles okay"-Eindruck
- Irreführende Recovery-Claims
- Unsaubere Ownership
- Fehlende nächste Aktion
- Fehlende Audit-Nachvollziehbarkeit
- Tenant/Workspace Leakage
- RBAC-Missverständnisse
Wenn ein Spec weder Workflow noch Trust verbessert → kaum zu rechtfertigen.
### C. Was ist die kleinste brauchbare Version?
Explizit benennen:
- Was ist die v1-Minimalversion?
- Welche Teile sind bewusst nicht enthalten?
- Welche Generalisierung wird absichtlich verschoben?
Wenn v1 wie ein Framework, eine Plattform oder eine universelle Taxonomie klingt → zu groß.
### D. Welche dauerhafte Komplexität entsteht?
Nicht nur Implementierungsaufwand, sondern Dauerfolgen:
- Neue Models / Tables?
- Neue Enums / Statusachsen?
- Neue UI-Semantik?
- Neue cross-surface Contracts?
- Neue Tests, die dauerhaft gepflegt werden müssen?
- Neue Begriffe, die jeder verstehen muss?
Wenn die Liste lang ist → Produktgewinn muss entsprechend hoch sein.
### E. Warum jetzt?
Legitime Gründe:
- Blockiert Kernworkflow
- Verhindert gefährliche Fehlinterpretation
- Ist Voraussetzung für unmittelbar folgende Hauptdomäne
- Beseitigt echten systemischen Widerspruch
- Wird bereits von mehreren Flächen schmerzhaft benötigt
Schwache Gründe:
- „wäre sauberer"
- „brauchen wir später bestimmt"
- „passt gut zur Architektur"
- „macht das Modell vollständiger"
---
## 4 Spec-Klassen
Jeden Kandidaten zwingend in genau eine Klasse einordnen.
### Klasse 1 — Core Enterprise Spec
Mindestens eins muss stimmen:
- Schützt echte System-/Tenant-/RBAC-Korrektheit
- Verhindert falsche Governance-/Recovery-/Audit-Aussagen
- Schließt klaren Workflow-Gap
- Beseitigt cross-surface Widerspruch mit realem Operator-Schaden
- Ist echte Voraussetzung für eine wichtige Produktfunktion
Dürfen Komplexität einführen, aber nur gezielt.
### Klasse 2 — Workflow Compression Spec
Gut, wenn sie:
- Klickpfade verkürzen
- Kontextverlust senken
- Return-/Drilldown-Kontinuität verbessern
- Triage-/Review-/Run-Bearbeitung beschleunigen
Nützlich, aber klein halten.
### Klasse 3 — Cleanup / Consolidation
- Vereinfachung, Zusammenführung, Entkopplung
- Entfernen von Legacy / Duplikaten
- Reduktion unnötiger Schichten
Explizit erwünscht als Gegengewicht zu Wachstum.
### Klasse 4 — Premature / Defer
Wenn der Kandidat hauptsächlich bringt:
- Neue Semantik, Frameworks, Taxonomien
- Generalisierung für künftige Fälle
- Infrastruktur ohne breite aktuelle Nutzung
→ Nicht freigeben. Verschieben oder brutal einkürzen.
---
## Rote Flaggen
Wenn **zwei oder mehr** zutreffen → Spec muss aktiv verteidigt werden.
| # | Rote Flagge | Prüffrage |
|---|---|---|
| 1 | **Neue Achsen** — neues Truth-Modell, Statusdimension, Taxonomie, Bewertungsachse | Braucht der Operator das wirklich, oder nur das Modell? |
| 2 | **Neue Meta-Infrastruktur** — Presenter, Resolver, Catalog, Matrix, Registry, Builder, Policy-Layer | Sehr hoher Beweiswert nötig. |
| 3 | **Viele Flächen, wenig Nutzerwert** — 6 Flächen „harmonisiert", kein klarer Nutzerflow besser | Architektur um ihrer selbst willen? |
| 4 | **Klingt nach Foundation** — foundation, framework, generalized, reusable, future-proof, canonical semantics | Fast immer erklärungsbedürftig. |
| 5 | **Mehr Begriffe als Outcomes** — lange semantische Erklärung, Nutzerverbesserung kaum in einem Satz | Verdächtig. |
| 6 | **Mehrere Mikrospecs für eine Domäne** — foundation + semantics + presentation + hardening + integration | Zu fein zerlegt. |
---
## Grüne Flaggen
- Löst klar beobachtbaren Operator-Schmerz
- Verbessert echte Entscheidungssituation
- Verhindert konkrete Fehlinterpretation
- Reduziert Navigation oder Denkaufwand
- Vereinfacht bereits existierende Komplexität
- Führt wenig neue Begriffe ein
- Hat klare Nicht-Ziele
- Ist in einer Sitzung gut erklärbar
- Braucht keine neue Meta-Schicht
- Macht mehrere Flächen einfacher statt abstrakter
---
## Bewertungsraster (02 pro Dimension)
| Dimension | 0 | 1 | 2 |
|---|---|---|---|
| **Nutzen** | unklar | lokal nützlich | klarer Workflow-/Trust-/Audit-Gewinn |
| **Dringlichkeit** | kann warten | sinnvoll bald | blockiert oder schützt Wichtiges jetzt |
| **Scope-Disziplin** | wirkt wie Framework/Plattform | etwas breit | klar begrenzte v1 |
| **Komplexitätslast** | hohe dauerhafte Last | mittel | niedrig / gut beherrschbar |
| **Produktnähe** | vor allem intern/architektonisch | gemischt | direkt spürbar für Operatoren |
| **Wiederverwendung belegt** | hypothetisch | wahrscheinlich | bereits an mehreren echten Stellen nötig |
### Auswertung
| Score | Entscheidung |
|---|---|
| **1012** | Freigabefähig |
| **79** | Nur freigeben wenn Scope enger gezogen wird |
| **46** | Verschieben oder zu Cleanup/Micro-Follow-up downgraden |
| **03** | Nicht freigeben |
---
## TenantPilot-spezifische Regeln
### Regel A — Keine neue semantische Achse ohne UI-Beweis
Wo wird sie sichtbar? Warum reichen bestehende Achsen nicht? Welche Fehlentscheidung bleibt ohne sie bestehen?
### Regel B — Keine neue Support-/Presentation-Schicht ohne ≥ 3 echte Verbraucher
Registry, Resolver, Catalog, Presenter, Matrix, Explanation-Layer → nur mit mindestens drei echten (nicht künstlich erzeugten) Verbrauchern. Sonst lokal lösen.
### Regel C — Keine Spec-Aufspaltung unterhalb Operator-Domäne
Wenn ein Thema nicht eigenständig als Operator-Problem beschrieben werden kann → kein eigener Spec.
### Regel D — Jeder neue Status braucht eine echte Folgehandlung
Neue Status/Outcome nur erlaubt wenn sie etwas Konkretes ändern: andere nächste Aktion, anderes Routing, andere Audit-Bedeutung, andere Workflow-Behandlung.
### Regel E — Consolidation ist ein legitimer Spec-Typ
Zusammenführen von Semantik, Reduktion von Komplexität, Entfernen von Parallelmodellen, Vereinfachung von Navigation/Resolvern, Rückbau unnötiger Zwischenlayer — aktiv Platz geben.
---
## Freigabe-Template (Pflichtabschnitt in spec.md)
```markdown
## Spec Candidate Check
- **Problem**: [Konkreter Operator-Schmerz oder Trust-Gap heute]
- **Today's failure**: [Welche Fehlentscheidung / Verlangsamung / irreführende Produktaussage passiert aktuell?]
- **User-visible improvement**: [Was wird konkret schneller, sicherer oder ehrlicher?]
- **Smallest enterprise-capable version**: [Kleinste Version die das Problem sauber löst]
- **Explicit non-goals**: [Was wird bewusst nicht modelliert/generalisiert/frameworkisiert?]
- **Permanent complexity imported**: [Neue Models, Status, Enums, Services, Support-Layer, Tests, UI-Konzepte, Begriffe]
- **Why now**: [Warum jetzt wichtiger als später?]
- **Why not local**: [Warum reicht keine lokale, schmale Lösung?]
- **Approval class**: [Core Enterprise / Workflow Compression / Cleanup / Defer]
- **Red flags triggered**: [Welche roten Flaggen treffen zu?]
- **Score**: [Nutzen: _ | Dringlichkeit: _ | Scope: _ | Komplexität: _ | Produktnähe: _ | Wiederverwendung: _ | **Gesamt: _/12**]
- **Decision**: [approve / shrink / merge / defer / reject]
```
---
## Erlaubt vs. Verdächtig (Schnellreferenz)
| Erlaubt | Verdächtig |
|---|---|
| Echte Workflow-Specs | Neue truth sub-axes |
| Governance-/Finding-/Review-Bearbeitbarkeit | Neue explanation frameworks |
| Trust-/Audit-/RBAC-Härtung | Neue presentation taxonomies |
| Portfolio-Operator-Durchsatzverbesserungen | Neue generalized support layers |
| Consolidation-Specs | Mikro-Specs für bereits stark zerlegte Domänen |

View File

@ -1,8 +1,4 @@
# Implementation Plan: TenantPilot v1 (LEGACY / DEPRECATED)
> DEPRECATED: Do not use `.specify/plan.md` for new work.
> Active feature plans live under `specs/<NNN>-<slug>/plan.md` on `feat/<NNN>-<slug>` branches.
> Legacy history lives under `spechistory/`.
# Implementation Plan: TenantPilot v1
**Branch**: `dev`
**Date**: 2026-01-03

View File

@ -1,13 +1,4 @@
# Research T186 — settings_apply capability verification (LEGACY / DEPRECATED)
> **Status:** Superseded
> **Last reviewed:** 2026-04-30
> **Use for:** Historical investigation context only if a later Settings Catalog write-path regression needs provenance
> **Do not use for:** Active feature research or current implementation truth
> DEPRECATED: Do not add new research notes under `.specify/`.
> Active feature research should live under `specs/<NNN>-<slug>/`.
> Legacy history lives under `spechistory/`.
# Research T186 — settings_apply capability verification
Objective
---------

View File

@ -40,13 +40,9 @@ mkdir -p "$FEATURE_DIR"
TEMPLATE="$REPO_ROOT/.specify/templates/plan-template.md"
if [[ -f "$TEMPLATE" ]]; then
cp "$TEMPLATE" "$IMPL_PLAN"
if ! $JSON_MODE; then
echo "Copied plan template to $IMPL_PLAN"
fi
echo "Copied plan template to $IMPL_PLAN"
else
if ! $JSON_MODE; then
echo "Warning: Plan template not found at $TEMPLATE"
fi
echo "Warning: Plan template not found at $TEMPLATE"
# Create a basic plan file if template doesn't exist
touch "$IMPL_PLAN"
fi

View File

@ -1,8 +1,4 @@
# Feature Specification: TenantPilot v1 (LEGACY / DEPRECATED)
> DEPRECATED: Do not use `.specify/spec.md` for new work.
> Active feature specs live under `specs/<NNN>-<slug>/spec.md` on `feat/<NNN>-<slug>` branches.
> Legacy history lives under `spechistory/`.
# Feature Specification: TenantPilot v1
**Feature Branch**: `dev`
**Created**: 2025-12-10

View File

@ -2,11 +2,7 @@
description: "Task list for TenantPilot v1 implementation"
---
# Tasks: TenantPilot v1 (LEGACY / DEPRECATED)
> DEPRECATED: Do not use `.specify/tasks.md` for new work.
> Active feature task lists live under `specs/<NNN>-<slug>/tasks.md` on `feat/<NNN>-<slug>` branches.
> Legacy history lives under `spechistory/`.
# Tasks: TenantPilot v1
**Input**: Design documents from `.specify/spec.md` and `.specify/plan.md`
**Prerequisites**: plan.md (complete), spec.md (complete)

View File

@ -5,79 +5,36 @@ # [CHECKLIST TYPE] Checklist: [FEATURE NAME]
**Feature**: [Link to spec.md or relevant documentation]
**Note**: This checklist is generated by the `/speckit.checklist` command based on feature context and requirements.
If the checklist covers UI or surface work, use it to reach both one review
outcome class (`blocker`, `strong-warning`,
`documentation-required-exception`, or `acceptable-special-case`) and one
workflow outcome (`keep`, `split`, `document-in-feature`,
`follow-up-spec`, or `reject-or-split`). Low-impact docs-only or
template-only work may mark runtime-only checks `N/A`, but should still
leave one explicit workflow outcome and one note explaining why no
guardrail spread exists.
## Applicability And Low-Impact Gate
<!--
============================================================================
IMPORTANT: The checklist items below are SAMPLE ITEMS for illustration only.
The /speckit.checklist command MUST replace these with actual items based on:
- User's specific checklist request
- Feature requirements from spec.md
- Technical context from plan.md
- Implementation details from tasks.md
DO NOT keep these sample items in the generated checklist file.
============================================================================
-->
- [ ] CHK001 The change explicitly says whether an operator-facing surface or guardrail workflow surface is affected; low-impact `N/A` handling is used once and not contradicted elsewhere.
- [ ] CHK002 The spec, plan, and task artifacts carry forward the same native/custom classification, shared-family relevance, state-layer ownership, and exception need without inventing second wording.
## [Category 1]
## Native, Shared-Family, And State Ownership
- [ ] CHK001 First checklist item with clear action
- [ ] CHK002 Second checklist item
- [ ] CHK003 Third checklist item
- [ ] CHK003 The surface remains native/shared-primitives first; fake-native controls, GET-form page-body interactions, and simple-overview replacements are not treated as harmless customization.
- [ ] CHK004 Any shared-detail or shared-family surface keeps one shared contract, and any host variation is either folded back into that contract or explicitly bounded as an exception.
- [ ] CHK005 Shell, page, detail, and URL/query state owners are named once and do not collapse into one another.
- [ ] CHK006 The likely next operator action and the primary inspect/open model stay coherent with the declared surface class.
## [Category 2]
## Shared Pattern Reuse
- [ ] CHK007 Any cross-cutting interaction class is explicitly marked, and the existing shared contract/presenter/builder/renderer path is named once.
- [ ] CHK008 The change extends the shared path where it is sufficient, or the deviation is explicitly documented with product reason, preserved consistency, ownership cost, and spread-control.
- [ ] CHK009 The change does not create a parallel operator-facing UX language for the same interaction class unless a bounded exception is recorded.
## OperationRun Start UX Contract
- [ ] CHK019 The change explicitly says whether it creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`, and the required `OperationRun UX Impact` section exists when applicable.
- [ ] CHK020 Queued toast/link/artifact-link/browser-event/dedupe-or-blocked messaging and tenant/workspace-safe operation URL resolution are delegated to the shared OperationRun UX contract instead of local surface code.
- [ ] CHK021 Any queued DB notification is explicit opt-in in the active spec or plan, running DB notifications remain absent, and terminal notifications still flow through the central lifecycle mechanism.
- [ ] CHK022 Any exception records the explicit spec decision, architecture note, test or guard-test rationale, and temporary migration follow-up decision.
## Provider Boundary And Vocabulary
- [ ] CHK010 The change states whether any touched shared seam is provider-owned, platform-core, or mixed, and provider-specific semantics do not silently spread into platform-core contracts, taxonomy, identifiers, compare semantics, or operator vocabulary.
- [ ] CHK011 Any retained provider-specific shared boundary is justified as a bounded current-release exception or an explicit follow-up-spec need instead of becoming permanent platform truth by default.
## Signals, Exceptions, And Test Depth
- [ ] CHK012 Any triggered repository signal is classified with one handling mode: `hard-stop-candidate`, `review-mandatory`, `exception-required`, or `report-only`.
- [ ] CHK013 Any deviation from default rules includes a bounded exception record naming the broken rule, product reason, standardized parts, spread-control rule, and the active feature PR close-out entry.
- [ ] CHK014 The required surface test profile is explicit: `shared-detail-family`, `monitoring-state-page`, `global-context-shell`, `exception-coded-surface`, or `standard-native-filament`.
- [ ] CHK015 The chosen test family/lane and any manual smoke are the narrowest honest proof for the declared surface class, and `standard-native-filament` relief is used when no special contract exists.
## Audience-Aware Disclosure And Decision Hierarchy
- [ ] CHK023 Default-visible content is decision-first and clearly separated from operator diagnostics and support/raw evidence.
- [ ] CHK024 Customer/read-only default paths do not expose raw JSON, copied context payloads, fingerprints, internal reason ownership, platform reason families, monitoring detail, or other debug semantics by default.
- [ ] CHK025 Exactly one dominant next action is primary; navigation or debug helpers such as `Open operation`, `Technical details`, or `Show JSON` do not compete at equal weight.
- [ ] CHK026 Duplicate visible status, blocker, reason, impact, or next-action summaries are removed or explicitly justified as non-duplicative evidence.
- [ ] CHK027 Support/raw sections are collapsed, lower-priority, or capability-gated where applicable, and any local Blade/Tailwind surface still preserves Filament visual language, dark mode correctness, progressive disclosure, and accessibility.
## Review Outcome
- [ ] CHK016 One review outcome class is chosen: `blocker`, `strong-warning`, `documentation-required-exception`, or `acceptable-special-case`.
- [ ] CHK017 One workflow outcome is chosen: `keep`, `split`, `document-in-feature`, `follow-up-spec`, or `reject-or-split`.
- [ ] CHK018 The final note location is explicit: the active feature PR close-out entry for guarded work, or a concise `N/A` note for low-impact changes.
- [ ] CHK004 Another category item
- [ ] CHK005 Item with specific criteria
- [ ] CHK006 Final item in this category
## Notes
- `blocker`: the change conflicts with the declared surface contract or guardrail and cannot proceed as proposed.
- `strong-warning`: the change may proceed only after the active workflow records the remaining guardrail risk explicitly.
- `documentation-required-exception`: the change is valid only once a bounded exception and close-out note exist.
- `acceptable-special-case`: the change is legitimate without extra escalation beyond ordinary documentation.
- `keep`: the current scope, guardrail handling, and proof depth are justified.
- `split`: the intent is valid, but the scope should narrow before merge.
- `document-in-feature`: the change is acceptable, but the active feature must record the exception, signal handling, or proof notes explicitly.
- `follow-up-spec`: the issue is recurring or structural and needs dedicated governance follow-up. For already-implemented historical drift, prefer a follow-up spec or active feature note instead of retroactively rewriting closed specs.
- `reject-or-split`: hidden drift, unresolved exception spread, or wrong proof depth blocks merge as proposed.
- Check items off as completed: `[x]`
- Add comments or findings inline
- Link to relevant resources or documentation
- Items are numbered sequentially for easy reference
- Reviewer-facing checklists SHOULD stop merge when nativity, shared-family boundaries, state ownership, exception spread, test depth, or escalation handling is unclear.

View File

@ -1,50 +0,0 @@
# [PROJECT_NAME] Constitution
<!-- Example: Spec Constitution, TaskFlow Constitution, etc. -->
## Core Principles
### [PRINCIPLE_1_NAME]
<!-- Example: I. Library-First -->
[PRINCIPLE_1_DESCRIPTION]
<!-- Example: Every feature starts as a standalone library; Libraries must be self-contained, independently testable, documented; Clear purpose required - no organizational-only libraries -->
### [PRINCIPLE_2_NAME]
<!-- Example: II. CLI Interface -->
[PRINCIPLE_2_DESCRIPTION]
<!-- Example: Every library exposes functionality via CLI; Text in/out protocol: stdin/args → stdout, errors → stderr; Support JSON + human-readable formats -->
### [PRINCIPLE_3_NAME]
<!-- Example: III. Test-First (NON-NEGOTIABLE) -->
[PRINCIPLE_3_DESCRIPTION]
<!-- Example: TDD mandatory: Tests written → User approved → Tests fail → Then implement; Red-Green-Refactor cycle strictly enforced -->
### [PRINCIPLE_4_NAME]
<!-- Example: IV. Integration Testing -->
[PRINCIPLE_4_DESCRIPTION]
<!-- Example: Focus areas requiring integration tests: New library contract tests, Contract changes, Inter-service communication, Shared schemas -->
### [PRINCIPLE_5_NAME]
<!-- Example: V. Observability, VI. Versioning & Breaking Changes, VII. Simplicity -->
[PRINCIPLE_5_DESCRIPTION]
<!-- Example: Text I/O ensures debuggability; Structured logging required; Or: MAJOR.MINOR.BUILD format; Or: Start simple, YAGNI principles -->
## [SECTION_2_NAME]
<!-- Example: Additional Constraints, Security Requirements, Performance Standards, etc. -->
[SECTION_2_CONTENT]
<!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. -->
## [SECTION_3_NAME]
<!-- Example: Development Workflow, Review Process, Quality Gates, etc. -->
[SECTION_3_CONTENT]
<!-- Example: Code review requirements, testing gates, deployment approval process, etc. -->
## Governance
<!-- Example: Constitution supersedes all other practices; Amendments require documentation, approval, migration plan -->
[GOVERNANCE_RULES]
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->

View File

@ -3,7 +3,7 @@ # Implementation Plan: [FEATURE]
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
## Summary
@ -21,165 +21,17 @@ ## Technical Context
**Primary Dependencies**: [e.g., FastAPI, UIKit, LLVM or NEEDS CLARIFICATION]
**Storage**: [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
**Testing**: [e.g., pytest, XCTest, cargo test or NEEDS CLARIFICATION]
**Validation Lanes**: [e.g., fast-feedback, confidence or NEEDS CLARIFICATION]
**Target Platform**: [e.g., Linux server, iOS 15+, WASM or NEEDS CLARIFICATION]
**Project Type**: [single/web/mobile - determines source structure]
**Performance Goals**: [domain-specific, e.g., 1000 req/s, 10k lines/sec, 60 fps or NEEDS CLARIFICATION]
**Constraints**: [domain-specific, e.g., <200ms p95, <100MB memory, offline-capable or NEEDS CLARIFICATION]
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]
## UI / Surface Guardrail Plan
> **Fill for operator-facing or guardrail-relevant workflow changes. Docs-only or template-only work may use concise `N/A`. Copy the spec classification forward; do not rename or expand it here.**
- **Guardrail scope**: [no operator-facing surface change / changed surfaces / workflow-only guardrail change]
- **Native vs custom classification summary**: [native / custom / mixed / N/A]
- **Shared-family relevance**: [none / list affected shared families]
- **State layers in scope**: [shell / page / detail / URL-query / none]
- **Audience modes in scope**: [customer/read-only / operator-MSP / support-platform / N/A]
- **Decision/diagnostic/raw hierarchy plan**: [decision-first / diagnostics-second / support-raw-third / N/A]
- **Raw/support gating plan**: [collapsed / capability-gated / role-gated / N/A]
- **One-primary-action / duplicate-truth control**: [how one dominant next action is preserved and repeated blockers are removed]
- **Handling modes by drift class or surface**: [hard-stop-candidate / review-mandatory / exception-required / report-only / N/A]
- **Repository-signal treatment**: [report-only / review-mandatory / exception-required / future hard-stop candidate / N/A]
- **Special surface test profiles**: [standard-native-filament / shared-detail-family / monitoring-state-page / global-context-shell / exception-coded-surface / N/A]
- **Required tests or manual smoke**: [functional-core / state-contract / exception-fallback / manual-smoke / N/A]
- **Exception path and spread control**: [none / describe the named exception boundary]
- **Active feature PR close-out entry**: [Guardrail / Exception / Smoke Coverage / N/A]
## Shared Pattern & System Fit
> **Fill when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, navigation entry points, alerts, evidence/report viewers, or any other shared interaction family. Docs-only or template-only work may use concise `N/A`. Carry the same decision forward from the spec instead of renaming it here.**
- **Cross-cutting feature marker**: [yes / no / N/A]
- **Systems touched**: [List the existing shared systems or `N/A`]
- **Shared abstractions reused**: [Named contracts / presenters / builders / renderers / helpers or `N/A`]
- **New abstraction introduced? why?**: [none / short explanation]
- **Why the existing abstraction was sufficient or insufficient**: [Short explanation tied to current-release truth]
- **Bounded deviation / spread control**: [none / describe the exception boundary and containment rule]
## OperationRun UX Impact
> **Fill when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`. Docs-only or template-only work may use concise `N/A`.**
- **Touches OperationRun start/completion/link UX?**: [yes / no / N/A]
- **Central contract reused**: [shared OperationRun UX layer / `N/A`]
- **Delegated UX behaviors**: [queued toast / run link / artifact link / run-enqueued browser event / queued DB-notification decision / dedupe-or-blocked messaging / tenant/workspace-safe URL resolution / `N/A`]
- **Surface-owned behavior kept local**: [initiation inputs only / none / short explanation]
- **Queued DB-notification policy**: [explicit opt-in / spec override / `N/A`]
- **Terminal notification path**: [central lifecycle mechanism / `N/A`]
- **Exception path**: [none / spec decision + architecture note + test rationale + temporary migration follow-up]
## Provider Boundary & Portability Fit
> **Fill when the feature touches shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth. Docs-only or template-only work may use concise `N/A`.**
- **Shared provider/platform boundary touched?**: [yes / no / N/A]
- **Provider-owned seams**: [List or `N/A`]
- **Platform-core seams**: [List or `N/A`]
- **Neutral platform terms / contracts preserved**: [List or `N/A`]
- **Retained provider-specific semantics and why**: [none / short explanation]
- **Bounded extraction or follow-up path**: [none / document-in-feature / follow-up-spec / N/A]
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Inventory-first: clarify what is “last observed” vs snapshots/backups
- Read/write separation: any writes require preview + confirmation + audit + tests
- Graph contract path: Graph calls only via `GraphClientInterface` + `config/graph_contracts.php`
- Deterministic capabilities: capability derivation is testable (snapshot/golden tests)
- RBAC-UX: two planes (/admin vs /system) remain separated; cross-plane is 404; tenant-context routes (/admin/t/{tenant}/...) are tenant-scoped; canonical workspace-context routes under /admin remain tenant-safe; non-member tenant/workspace access is 404; member-but-missing-capability is 403; authorization checks use Gates/Policies + capability registries (no raw strings, no role-string checks)
- Workspace isolation: non-member workspace access is 404; tenant-plane routes require an established workspace context; workspace context switching is separate from Filament Tenancy
- RBAC-UX: destructive-like actions require `->requiresConfirmation()` and clear warning text
- RBAC-UX: global search is tenant-scoped; non-members get no hints; inaccessible results are treated as not found (404 semantics)
- Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked
- Run observability: long-running/remote/queued work creates/reuses `OperationRun`; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log; auth handshake exception OPS-EX-AUTH-001 allows synchronous outbound HTTP on `/auth/*` without `OperationRun`
- OperationRun start UX: any feature that creates, queues, deduplicates, resumes, blocks, completes, or links `OperationRun` reuses the central OperationRun Start UX Contract; no local composition of queued toast/link/event/start-state messaging; `OperationRun UX Impact` is present in the active spec or plan
- Ops-UX 3-surface feedback: if `OperationRun` is used, default feedback is toast intent-only + progress surfaces + exactly-once terminal `OperationRunCompleted` (initiator-only); queued DB notifications remain explicit opt-in through the shared start UX contract; running DB notifications stay disallowed
- Ops-UX lifecycle: `OperationRun.status` / `OperationRun.outcome` transitions are service-owned (only via `OperationRunService`); context-only updates allowed outside
- Ops-UX summary counts: `summary_counts` keys come from `OperationSummaryKeys::all()` and values are flat numeric-only
- Ops-UX guards: CI has regression guards that fail with actionable output (file + snippet) when these patterns regress
- Ops-UX system runs: initiator-null runs emit no terminal DB notification; audit remains via Monitoring; tenant-wide alerting goes through Alerts (not OperationRun notifications)
- Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter
- Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens
- Test governance (TEST-GOV-001): actual test-purpose classification, affected lanes, fixture/helper/factory/seed/context cost risks, heavy-family visibility, review-stop points, reviewer handoff, and any budget/baseline/trend follow-up are explicit; the narrowest proving lane mix is planned and any structural cost change has an escalation path
- Proportionality (PROP-001): any new structure, layer, persisted truth, or semantic machinery is justified by current release truth, current operator workflow, and why a narrower solution is insufficient
- No premature abstraction (ABSTR-001): no new factories, registries, resolvers, strategy systems, interfaces, type registries, or orchestration pipelines before at least 2 real concrete cases exist, unless security, tenant isolation, auditability, compliance evidence, or queue correctness require it now
- Persisted truth (PERSIST-001): new tables/entities/artifacts represent independent product truth or lifecycle; convenience projections and UI helpers stay derived
- Behavioral state (STATE-001): new states/statuses/reason codes change behavior, routing, permissions, lifecycle, audit, retention, or retry handling; presentation-only distinctions stay derived
- UI semantics (UI-SEM-001): avoid turning badges, explanation text, trust/confidence labels, or detail summaries into mandatory interpretation frameworks; prefer direct domain-to-UI mapping
- Shared pattern first (XCUT-001): cross-cutting interaction classes reuse existing shared contracts/presenters/builders/renderers first; any deviation is explicit, bounded, and justified against current-release truth
- Provider boundary (PROV-001): shared provider/platform seams are classified as provider-owned vs platform-core; provider-specific semantics stay out of platform-core contracts, taxonomy, identifiers, compare semantics, and operator vocabulary unless explicitly justified; bounded extraction beats speculative multi-provider frameworks
- V1 explicitness / few layers (V1-EXP-001, LAYER-001): prefer direct implementation, local mappings, and small helpers; any new layer replaces an old one or proves the old one cannot serve
- Spec discipline / bloat check (SPEC-DISC-001, BLOAT-001): related semantic changes are grouped coherently, and any new enum, DTO/presenter, persisted entity, interface/registry/resolver, or taxonomy includes a proportionality review covering operator problem, insufficiency, narrowness, ownership cost, rejected alternative, and whether it is current-release truth
- Badge semantics (BADGE-001): status-like badges use `BadgeCatalog` / `BadgeRenderer`; no ad-hoc mappings; new values include tests
- Filament-native UI (UI-FIL-001): admin/operator surfaces use native Filament components or shared primitives first; no ad-hoc status UI, local semantic color/border decisions, or hand-built replacements when native/shared semantics exist; any exception is explicitly justified
- Filament-native UI (UI-FIL-001): if local Blade/Tailwind cards are
still necessary, they preserve dark mode correctness, spacing
consistency, badge semantics, action hierarchy, progressive
disclosure, accessibility, and Filament visual language
- UI/UX surface taxonomy (UI-CONST-001 / UI-SURF-001): every changed operator-facing surface is classified as exactly one allowed surface type; ad-hoc interaction models are forbidden
- Decision-first operating model (DECIDE-001): each changed
operator-facing surface is classified as Primary Decision,
Secondary Context, or Tertiary Evidence / Diagnostics; primary
surfaces justify the human-in-the-loop moment, default-visible info
is limited to first-decision needs, deep proof is progressive
disclosed, one governance case stays decidable in one context where
practical, navigation follows workflows not storage structures, and
automation / alerts reduce attention load instead of adding noise
- Audience-aware disclosure (DECIDE-AUD-001 / OPSURF-001): detail or
status surfaces separate customer-readable decision content,
operator diagnostics, and support/raw evidence; customer-readable
default paths hide raw JSON, copied context, fingerprints, internal
reason ownership, platform reason families, and debug semantics;
one dominant next action is explicit; duplicate visible truth is
removed
- UI/UX inspect model (UI-HARD-001): each list surface has exactly one primary inspect/open model; redundant View beside row click or identifier click is forbidden; edit-as-inspect is limited to Config-lite resources
- UI/UX action hierarchy (UI-HARD-001 / UI-EX-001): standard CRUD and Registry rows expose at most one inline safe shortcut; destructive actions are grouped or in the detail header; queue exceptions are catalogued, justified, and tested
- UI/UX scope, truth, and naming (UI-HARD-001 / UI-NAMING-001 / OPSURF-001): scope signals are truthful, canonical nouns stay stable across shells, critical operational truth is default-visible, and standard lists remain scanable
- UI/UX placeholder ban (UI-HARD-001): empty `ActionGroup` / `BulkActionGroup` placeholders and declaration-only UI conformance are forbidden
- UI naming (UI-NAMING-001): operator-facing labels use `Verb + Object`; scope (`Workspace`, `Tenant`) is never the primary action label; source/domain is secondary unless disambiguation is required; runs/toasts/audit prose use the same domain vocabulary; implementation-first terms do not appear in primary operator UI
- Operator surfaces (OPSURF-001): `/admin` defaults are operator-first; default-visible content avoids raw implementation detail; diagnostics are explicitly revealed secondarily
- Operator surfaces (OPSURF-001): execution outcome, data completeness, governance result, and lifecycle/readiness are modeled as distinct status dimensions when all apply; they are not collapsed into one ambiguous status
- Operator surfaces (OPSURF-001): every mutating action communicates whether it changes TenantPilot only, the Microsoft tenant, or simulation only before execution
- Operator surfaces (OPSURF-001): dangerous actions follow configuration → safety checks/simulation → preview → hard confirmation where required → execute, unless a spec documents an explicit exemption and replacement safeguards
- Operator surfaces (OPSURF-001): workspace and tenant context remain explicit in navigation, actions, and page semantics; tenant surfaces do not silently expose workspace-wide actions
- Operator surfaces (OPSURF-001): each new or materially refactored operator-facing page defines a page contract covering persona, surface type, operator question, default-visible info, diagnostics-only info, status dimensions, mutation scope, primary actions, and dangerous actions
- Filament UI Action Surface Contract: for any new/modified Filament Resource/RelationManager/Page, define Header/Row/Bulk/Empty-State actions, ensure every List/Table has a surface-appropriate inspect affordance, remove redundant View when row click or identifier click already opens the same destination, keep standard CRUD/Registry rows to inspect plus at most one inline safe shortcut, group or relocate the rest to “More” or detail header, forbid empty bulk/overflow groups, require confirmations for destructive actions, write audit logs for mutations, enforce RBAC via central helpers (non-member 404, member missing capability 403), and ensure CI blocks merges if the contract is violated or not explicitly exempted
- Filament UI UX-001 (Layout & IA): Create/Edit uses Main/Aside (3-col grid, Main=columnSpan(2), Aside=columnSpan(1)); all fields inside Sections/Cards (no naked inputs); View uses Infolists (not disabled edit forms); status badges use BADGE-001; empty states have specific title + explanation + 1 CTA; max 1 primary + 1 secondary header action (see HDR-001); tables provide search/sort/filters for core dimensions; shared layout builders preferred for consistency
- Action-surface discipline (ACTSURF-001 / HDR-001): every changed
surface declares one broad action-surface class; the spec names the
one likely next operator action; navigation is separated from
mutation; record/detail/edit pages keep at most one visible primary
header action; monitoring/workbench surfaces separate scope/context,
selection actions, navigation, and object actions; risky or rare
actions are grouped and ordered by meaning/frequency/risk; any special
type or workflow-hub exception is explicit and justified
- UI review workflow: native/custom classification, shared-family
relevance, state-layer ownership, repository-signal treatment,
exception path, and the active feature PR close-out entry stay
explicit without duplicating the same decision across spec, plan,
tasks, checklist, and close-out surfaces
## Test Governance Check
> **Fill for any runtime-changing or test-affecting feature. Docs-only or template-only work may state concise `N/A` or `none`.**
- **Test purpose / classification by changed surface**: [Unit / Feature / Heavy-Governance / Browser / N/A]
- **Affected validation lanes**: [fast-feedback / confidence / heavy-governance / browser / profiling / junit / N/A]
- **Why this lane mix is the narrowest sufficient proof**: [Why the chosen classification and lanes fit the actual proving purpose]
- **Narrowest proving command(s)**: [Exact commands reviewers should run before merge]
- **Fixture / helper / factory / seed / context cost risks**: [none / describe]
- **Expensive defaults or shared helper growth introduced?**: [no / describe explicit opt-in path]
- **Heavy-family additions, promotions, or visibility changes**: [none / describe]
- **Surface-class relief / special coverage rule**: [standard-native relief / named special profile / N/A]
- **Closing validation and reviewer handoff**: [What must be re-run, what reviewers should verify, and what exact proof command they should rely on]
- **Budget / baseline / trend follow-up**: [none / describe]
- **Review-stop questions**: [lane fit / breadth / hidden cost / heavy-family risk / escalation]
- **Escalation path**: [none / document-in-feature / follow-up-spec / reject-or-split]
- **Active feature PR close-out entry**: [Guardrail / Exception / Smoke Coverage / N/A]
- **Why no dedicated follow-up spec is needed**: [Routine upkeep stays inside this feature unless recurring pain or structural lane changes justify a separate spec]
[Gates determined based on constitution file]
## Project Structure
@ -244,20 +96,9 @@ # [REMOVE IF UNUSED] Option 3: Mobile + API (when "iOS/Android" detected)
## Complexity Tracking
> **Fill when Constitution Check has violations that must be justified OR when BLOAT-001 is triggered by new persistence, abstractions, states, or semantic frameworks.**
> **Fill ONLY if Constitution Check has violations that must be justified**
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
## Proportionality Review
> **Fill when the feature introduces a new enum/status family, DTO/presenter/envelope, persisted entity/table/artifact, interface/contract/registry/resolver, taxonomy/classification system, or cross-domain UI framework.**
- **Current operator problem**: [What present-day workflow or risk requires this?]
- **Existing structure is insufficient because**: [Why the current code cannot serve safely or clearly]
- **Narrowest correct implementation**: [Why this shape is the smallest viable one]
- **Ownership cost created**: [Maintenance, testing, cognitive load, migration, or review burden]
- **Alternative intentionally rejected**: [Simpler option and why it failed]
- **Release truth**: [Current-release truth or future-release preparation]

View File

@ -5,171 +5,6 @@ # Feature Specification: [FEATURE NAME]
**Status**: Draft
**Input**: User description: "$ARGUMENTS"
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
<!-- This section MUST be completed before the spec progresses beyond Draft.
See .specify/memory/spec-approval-rubric.md for the full rubric. -->
- **Problem**: [Konkreter Operator-Schmerz oder Trust-Gap heute]
- **Today's failure**: [Welche Fehlentscheidung / Verlangsamung / irreführende Produktaussage passiert aktuell?]
- **User-visible improvement**: [Was wird konkret schneller, sicherer oder ehrlicher?]
- **Smallest enterprise-capable version**: [Kleinste Version die das Problem sauber löst]
- **Explicit non-goals**: [Was wird bewusst nicht modelliert/generalisiert/frameworkisiert?]
- **Permanent complexity imported**: [Neue Models, Status, Enums, Services, Support-Layer, Tests, UI-Konzepte, Begriffe]
- **Why now**: [Warum jetzt wichtiger als später?]
- **Why not local**: [Warum reicht keine lokale, schmale Lösung?]
- **Approval class**: [Core Enterprise / Workflow Compression / Cleanup / Defer]
- **Red flags triggered**: [Welche roten Flaggen treffen zu? Wenn ≥ 2: explizite Verteidigung nötig]
- **Score**: [Nutzen: _ | Dringlichkeit: _ | Scope: _ | Komplexität: _ | Produktnähe: _ | Wiederverwendung: _ | **Gesamt: _/12**]
- **Decision**: [approve / shrink / merge / defer / reject]
## Spec Scope Fields *(mandatory)*
- **Scope**: [workspace | tenant | canonical-view]
- **Primary Routes**: [List the primary routes/pages affected]
- **Data Ownership**: [workspace-owned vs tenant-owned tables/records impacted]
- **RBAC**: [membership requirements + capability requirements]
For canonical-view specs, the spec MUST define:
- **Default filter behavior when tenant-context is active**: [e.g., prefilter to current tenant]
- **Explicit entitlement checks preventing cross-tenant leakage**: [Describe checks]
## Cross-Cutting / Shared Pattern Reuse *(mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write `N/A - no shared interaction family touched`)*
- **Cross-cutting feature?**: [yes/no]
- **Interaction class(es)**: [notifications / status messaging / header actions / dashboard signals / navigation / reports / etc.]
- **Systems touched**: [List shared systems, surfaces, or infrastructure paths]
- **Existing pattern(s) to extend**: [Name the existing shared path(s) or write `none`]
- **Shared contract / presenter / builder / renderer to reuse**: [Exact class, helper, or surface path, or `none`]
- **Why the existing shared path is sufficient or insufficient**: [Short explanation tied to current-release truth]
- **Allowed deviation and why**: [none / bounded exception + why]
- **Consistency impact**: [What must stay aligned across interaction structure, copy, status semantics, actions, and deep links]
- **Review focus**: [What reviewers must verify to prevent parallel local patterns]
## OperationRun UX Impact *(mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`; otherwise write `N/A - no OperationRun start or link semantics touched`)*
- **Touches OperationRun start/completion/link UX?**: [yes/no]
- **Shared OperationRun UX contract/layer reused**: [Name it or `N/A`]
- **Delegated start/completion UX behaviors**: [queued toast / `Open operation` or `View run` link / artifact link / run-enqueued browser event / queued DB-notification decision / dedupe-or-blocked messaging / tenant/workspace-safe URL resolution / `N/A`]
- **Local surface-owned behavior that remains**: [initiation inputs only / none / bounded explanation]
- **Queued DB-notification policy**: [explicit opt-in / spec override / `N/A`]
- **Terminal notification path**: [central lifecycle mechanism / `N/A`]
- **Exception required?**: [none / explicit spec decision + architecture note + test or guard-test rationale + temporary migration follow-up]
## Provider Boundary / Platform Core Check *(mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write `N/A - no shared provider/platform boundary touched`)*
- **Shared provider/platform boundary touched?**: [yes/no]
- **Boundary classification**: [provider-owned / platform-core / mixed / N/A]
- **Seams affected**: [contracts, models, taxonomies, query keys, labels, filters, compare strategy, etc.]
- **Neutral platform terms preserved or introduced**: [List them or `N/A`]
- **Provider-specific semantics retained and why**: [none / bounded current-release necessity]
- **Why this does not deepen provider coupling accidentally**: [Short explanation]
- **Follow-up path**: [none / document-in-feature / follow-up-spec]
## UI / Surface Guardrail Impact *(mandatory when operator-facing surfaces are changed; otherwise write `N/A`)*
Use this section to classify UI and surface risk once. If the feature does
not change an operator-facing surface, write `N/A - no operator-facing surface
change` here and do not invent duplicate prose in the downstream surface tables.
| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note |
|---|---|---|---|---|---|---|
| e.g. Tenant policies page | yes | Native Filament + shared primitives | none | page, detail | no | n/a |
| e.g. Docs-only change | no | N/A | none | none | no | `N/A - repository workflow only` |
## Decision-First Surface Role *(mandatory when operator-facing surfaces are changed)*
If this feature adds or materially changes an operator-facing surface,
fill out one row per affected surface. This role is orthogonal to the
Action Surface Class / Surface Type below. Reuse the exact surface names
and classifications from the UI / Surface Guardrail Impact section above.
| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction |
|---|---|---|---|---|---|---|---|
| e.g. Review inbox | Primary Decision Surface | Review and release queued governance work | Case summary, severity, recommendation, required action | Full evidence, raw payloads, audit trail, provider diagnostics | Primary because it is the queue where operators decide and clear work | Follows pending-decisions workflow, not storage objects | Removes search across runs, findings, and audit pages |
## Audience-Aware Disclosure *(mandatory when operator-facing surfaces are changed)*
If this feature adds or materially changes a detail or status surface,
fill out one row per affected surface. Reuse the same surface names
used above and make the disclosure hierarchy explicit instead of
assuming it.
| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention |
|---|---|---|---|---|---|---|---|
| e.g. Review inbox | customer-read-only, operator-MSP, support-platform | Current status, why it matters, impact, recommendation, next action | Review history, lifecycle, related evidence, related runs | Raw payloads, fingerprints, reason ownership, platform reason family | `Review evidence` | Raw/support detail hidden or capability-gated outside support mode | The top summary states the blocker once; later sections add evidence rather than restating it |
## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)*
If this feature adds or materially changes an operator-facing list, detail, queue, audit, config, or report surface,
fill out one row per affected surface. Declare the broad Action Surface
Class first, then the detailed Surface Type. Keep this table in sync
with the Decision-First Surface Role section above and avoid renaming the
same surface a second time.
| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| e.g. Tenant policies page | List / Table / Bulk | CRUD / List-first Resource | Open policy for review | Full-row click | required | One inline safe shortcut + More | More / detail header | /admin/t/{tenant}/policies | /admin/t/{tenant}/policies/{record} | Tenant chip scopes rows and actions | Policies / Policy | Policy health, drift, assignment coverage | none |
## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)*
If this feature adds a new operator-facing page or materially refactors
one, fill out one row per affected page/surface. The contract MUST show
how one governance case or operator task becomes decidable without
unnecessary cross-page reconstruction.
| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions |
|---|---|---|---|---|---|---|---|---|---|---|
| e.g. Tenant policies page | Tenant operator | Decide whether policy state needs follow-up | List/detail | What needs action right now? | Policy health, drift, assignment coverage | Raw payloads, provider IDs, low-level API details | lifecycle, data completeness, governance result | TenantPilot only / Microsoft tenant / simulation only | Sync policies, View policy | Restore policy |
## Proportionality Review *(mandatory when structural complexity is introduced)*
Fill this section if the feature introduces any of the following:
- a new source of truth
- a new persisted entity, table, or artifact
- a new abstraction (interface, contract, registry, resolver, strategy, factory, orchestration layer)
- a new enum, status family, reason code family, or lifecycle category
- a new cross-domain UI framework, taxonomy, or classification system
- **New source of truth?**: [yes/no]
- **New persisted entity/table/artifact?**: [yes/no]
- **New abstraction?**: [yes/no]
- **New enum/state/reason family?**: [yes/no]
- **New cross-domain UI framework/taxonomy?**: [yes/no]
- **Current operator problem**: [What present-day workflow or risk does this solve?]
- **Existing structure is insufficient because**: [Why the current implementation shape cannot safely or clearly solve it]
- **Narrowest correct implementation**: [Why this is the smallest viable solution]
- **Ownership cost**: [What maintenance, testing, review, migration, or conceptual cost this adds]
- **Alternative intentionally rejected**: [What simpler option was considered and why it was not sufficient]
- **Release truth**: [Current-release truth or future-release preparation]
### Compatibility posture
This feature assumes a pre-production environment.
Backward compatibility, legacy aliases, migration shims, historical fixtures, and compatibility-specific tests are out of scope unless explicitly required by this spec.
Canonical replacement is preferred over preservation.
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
For docs-only or template-only changes, state concise `N/A` or `none`. For runtime- or test-affecting work, classification MUST follow the proving purpose of the change rather than the file path or folder name.
- **Test purpose / classification**: [Unit / Feature / Heavy-Governance / Browser / N/A]
- **Validation lane(s)**: [fast-feedback / confidence / heavy-governance / browser / profiling / junit / N/A]
- **Why this classification and these lanes are sufficient**: [Why the narrowest listed lane(s) and chosen test type prove the change]
- **New or expanded test families**: [none / describe]
- **Fixture / helper cost impact**: [none / describe new defaults, factories, seeds, helpers, browser setup, provider setup, workspace or membership context, session state, etc.]
- **Heavy-family visibility / justification**: [none / explain any heavy-governance or browser addition and how it remains explicit in naming, lane choice, and review]
- **Special surface test profile**: [standard-native-filament / shared-detail-family / monitoring-state-page / global-context-shell / exception-coded-surface / N/A]
- **Standard-native relief or required special coverage**: [ordinary feature coverage only / describe required tests or smoke checks]
- **Reviewer handoff**: [What reviewers must confirm about lane fit, hidden cost, heavy-family visibility, and the exact proof command]
- **Budget / baseline / trend impact**: [none / expected drift + follow-up]
- **Escalation needed**: [none / document-in-feature / follow-up-spec / reject-or-split]
- **Active feature PR close-out entry**: [Guardrail / Exception / Smoke Coverage / N/A]
- **Planned validation commands**: [Exact minimal commands reviewers should run]
## User Scenarios & Testing *(mandatory)*
<!--
@ -242,175 +77,6 @@ ### Edge Cases
## 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 (PROP-001 / ABSTR-001 / PERSIST-001 / STATE-001 / BLOAT-001):** If this feature introduces new persistence,
new abstractions, new states, or new semantic layers, the spec MUST explain:
- which current operator workflow or current product truth requires the addition now,
- why a narrower implementation is insufficient,
- whether the addition is current-release truth or future-release preparation,
- what ownership cost it creates,
- and how the choice follows the default bias of deriving before persisting, replacing before layering, and being explicit before generic.
If the feature introduces a new enum/status family, DTO/presenter/envelope, persisted entity/table, interface/contract/registry/resolver,
or taxonomy/classification system, the Proportionality Review section above is mandatory.
**Constitution alignment (XCUT-001):** If this feature touches a cross-cutting interaction class such as notifications, status messaging,
action links, header actions, dashboard signals/cards, alerts, navigation entry points, or evidence/report viewers, the spec MUST:
- state whether the feature is cross-cutting,
- name the existing shared pattern(s) and shared contract/presenter/builder/renderer to extend,
- explain why the existing shared path is sufficient or why it is insufficient for current-release truth,
- record any allowed deviation, the consistency it must preserve, and its ownership/spread-control cost,
- and make the reviewer focus explicit so parallel local UX paths do not appear silently.
**Constitution alignment (DECIDE-AUD-001 / OPSURF-001):** If this feature changes a detail or status surface, the spec MUST describe:
- how the surface separates customer-readable decision content, operator diagnostics, and support/raw evidence,
- which audience modes are in scope (`customer/read-only`, `operator/MSP`, `support/platform`),
- which content is hidden, collapsed, or capability-gated by default,
- how one dominant next action is preserved,
- and how duplicate visible truth is prevented.
**Constitution alignment (PROV-001):** If this feature touches a shared provider/platform seam, the spec MUST:
- classify each touched seam as provider-owned or platform-core,
- keep provider-specific semantics out of platform-core contracts, taxonomies, identifiers, compare semantics, and operator vocabulary unless explicitly justified,
- name the neutral platform terms or shared contracts being preserved,
- explain why any retained provider-specific semantics are the narrowest current-release truth,
- and state whether the remaining hotspot is resolved in-feature or escalated as a follow-up spec.
**Constitution alignment (TEST-GOV-001):** If this feature changes runtime behavior or tests, the spec MUST describe:
- the actual test-purpose classification (`Unit`, `Feature`, `Heavy-Governance`, or `Browser`) and why that classification matches the real proving purpose,
- the affected validation lane(s) and why they are the narrowest sufficient proof,
- any new or expanded heavy-governance or browser coverage,
- any fixture, helper, factory, seed, provider, workspace, membership, session, or default setup cost added or avoided,
- how any heavy family stays explicit rather than becoming accidental default breadth,
- the reviewer handoff for lane fit, hidden-cost checks, and the exact minimal validation commands,
- any expected budget, baseline, or trend impact,
- whether escalation stays inside this feature or resolves as `document-in-feature`, `follow-up-spec`, or `reject-or-split`,
- and the exact minimal validation commands reviewers should run.
**Constitution alignment (OPS-UX):** If this feature creates/reuses an `OperationRun`, the spec MUST:
- explicitly state compliance with the default Ops-UX 3-surface feedback contract (toast intent-only, progress surfaces, terminal DB notification) and whether any queued DB notification is explicitly opted into,
- state that `OperationRun.status` / `OperationRun.outcome` transitions are service-owned (only via `OperationRunService`),
- describe how `summary_counts` keys/values comply with `OperationSummaryKeys::all()` and numeric-only rules,
- clarify scheduled/system-run behavior (initiator null → no terminal DB notification; audit is via Monitoring),
- list which regression guard tests are added/updated to keep these rules enforceable in CI.
**Constitution alignment (OPS-UX-START-001):** If this feature creates, queues, deduplicates, resumes, blocks, completes, or links to an `OperationRun`, the spec MUST:
- include the `OperationRun UX Impact` section,
- name the shared OperationRun UX contract/layer being reused,
- delegate queued toast/link/artifact-link/browser-event/queued-DB-notification/dedupe-or-blocked messaging/tenant-safe URL resolution to that shared path,
- keep local surface code limited to initiation inputs and operation-specific data capture,
- keep queued DB notifications explicit opt-in unless the spec intentionally defines a different policy,
- route terminal notifications through the central lifecycle mechanism,
- and document any exception with an explicit spec decision, architecture note, test or guard-test rationale, and temporary follow-up migration decision.
**Constitution alignment (RBAC-UX):** If this feature introduces or changes authorization behavior, the spec MUST:
- state which authorization plane(s) are involved (tenant/admin `/admin` + tenant-context `/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 workspace scope OR 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.
**Constitution alignment (UI-FIL-001):** If this feature adds or changes Filament or Blade UI for admin/operator surfaces, the spec MUST describe:
- which native Filament components or shared UI primitives are used,
- whether any local replacement markup was avoided for badges, alerts, buttons, or status surfaces,
- how semantic emphasis is expressed through Filament props or central primitives rather than page-local color/border classes,
- how any required local Blade/Tailwind cards still preserve dark mode correctness, spacing consistency, badge semantics, action hierarchy, progressive disclosure, accessibility, and Filament visual language,
- and any exception where Filament or a shared primitive was insufficient, including why the exception is necessary and how it avoids introducing a new local status language.
**Constitution alignment (UI-NAMING-001):** If this feature adds or changes operator-facing buttons, header actions, run titles,
notifications, audit prose, or related helper copy, the spec MUST describe:
- the target object,
- the operator verb,
- whether source/domain disambiguation is actually needed,
- how the same domain vocabulary is preserved across button labels, modal titles, run titles, notifications, and audit prose,
- and how implementation-first terms are kept out of primary operator-facing labels.
**Constitution alignment (DECIDE-001):** If this feature adds or changes operator-facing surfaces, the spec MUST describe:
- whether each affected surface is a Primary Decision Surface,
Secondary Context Surface, or Tertiary Evidence / Diagnostics
Surface, and why,
- which human-in-the-loop moment each primary surface supports,
- what MUST be visible immediately for the first decision,
- what is preserved but only revealed on demand,
- why any new primary surface cannot live inside an existing decision
context,
- how navigation follows operator workflows rather than storage
structures,
- how one governance case remains decidable in one focused context,
- how any new automation, notifications, or autonomous governance logic
reduce search/review/click load,
- and how the resulting default experience is calmer and clearer rather
than merely larger.
**Constitution alignment (UI-CONST-001 / UI-SURF-001 / ACTSURF-001 / UI-HARD-001 / UI-EX-001 / UI-REVIEW-001 / HDR-001):** If this feature adds or changes an operator-facing surface, the spec MUST describe:
- the chosen broad action-surface class and why it is the correct classification,
- the chosen detailed surface type and why it is the correct refinement,
- the one most likely next operator action,
- the one and only primary inspect/open model,
- whether row click is required, allowed, or forbidden,
- whether explicit View or Inspect is present, and why it is present or forbidden,
- where pure navigation lives and why it is not competing with mutation,
- where secondary actions live,
- where destructive actions live,
- how grouped actions are ordered by meaning, frequency, and risk,
- the canonical collection route and canonical detail route,
- the scope signals shown to the operator and what real effect each one has,
- the canonical noun used across routes, labels, runs, notifications, and audit prose,
- which critical operational truth is visible by default,
- and any catalogued exception type, rationale, and dedicated test coverage.
**Constitution alignment (ACTSURF-001 - action hierarchy):** If this
feature adds or materially changes header actions, row actions, bulk
actions, or workbench controls, the spec MUST describe:
- how navigation, mutation, context signals, selection actions, and
dangerous actions are separated,
- why any visible secondary action deserves primary-plane placement,
- why any ActionGroup is structured rather than a mixed catch-all,
- and why any workflow-hub, wizard, system, or other special-type
exception is genuine rather than a convenience shortcut.
**Constitution alignment (OPSURF-001):** If this feature adds or materially refactors an operator-facing surface, the spec MUST describe:
- how the default-visible content stays operator-first on `/admin` and avoids raw implementation detail,
- which diagnostics are secondary and how they are explicitly revealed,
- how the dominant next action stays primary and how duplicate visible truth is avoided,
- which status dimensions are shown separately (execution outcome, data completeness, governance result, lifecycle/readiness) and why,
- how each mutating action communicates its mutation scope before execution (`TenantPilot only`, `Microsoft tenant`, or `simulation only`),
- how dangerous actions follow the safe-execution pattern (configuration, safety checks/simulation, preview, hard confirmation where required, execute),
- how workspace and tenant context remain explicit in navigation, action copy, and page semantics,
- and the page contract for each new or materially refactored operator-facing page.
**Constitution alignment (UI-SEM-001 / LAYER-001 / TEST-TRUTH-001):** If this feature adds UI semantics, presenters, explanation layers,
status taxonomies, or other interpretation layers, the spec MUST describe:
- why direct mapping from canonical domain truth to UI is insufficient,
- which existing layer is replaced or why no existing layer can serve,
- how the feature avoids creating redundant truth across models, service results, presenters, summaries, wrappers, and persisted mirrors,
- and how tests focus on business consequences rather than thin indirection alone.
**Constitution alignment (Filament Action Surfaces):** If this feature adds or modifies any Filament Resource / RelationManager / Page,
the spec MUST include a “UI Action Matrix” (see below) and explicitly state whether the Action Surface Contract is satisfied.
The same section MUST state that each affected surface has exactly one primary inspect/open model, that redundant View actions are absent,
that empty `ActionGroup` / `BulkActionGroup` placeholders are absent, and that destructive actions follow the required placement rules for the chosen surface type.
If the contract is not satisfied, the spec MUST include an explicit exemption with rationale.
The same section MUST also state whether UI-FIL-001 is satisfied and identify any approved exception.
**Constitution alignment (UX-001 — Layout & Information Architecture):** If this feature adds or modifies any Filament screen,
the spec MUST describe compliance with UX-001: Create/Edit uses Main/Aside layout (3-col grid), all fields inside Sections/Cards
(no naked inputs), View pages use Infolists (not disabled edit forms), status badges use BADGE-001, empty states have a specific
title + explanation + exactly 1 CTA, and tables provide search/sort/filters for core dimensions.
If UX-001 is not fully satisfied, the spec MUST include an explicit exemption with documented rationale.
<!--
ACTION REQUIRED: The content in this section represents placeholders.
Fill them out with the right functional requirements.
@ -429,17 +95,6 @@ ### Functional Requirements
- **FR-006**: System MUST authenticate users via [NEEDS CLARIFICATION: auth method not specified - email/password, SSO, OAuth?]
- **FR-007**: System MUST retain user data for [NEEDS CLARIFICATION: retention period not specified]
## UI Action Matrix *(mandatory when Filament is changed)*
If this feature adds/modifies any Filament Resource / RelationManager / Page, fill out the matrix below.
For each surface, list the exact action labels, whether they are destructive (confirmation? typed confirmation?),
RBAC gating (capability + enforcement helper), whether the mutation writes an audit log, and any exemption or exception used.
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|
| Resource/Page/RM | e.g. app/Filament/... | | e.g. `recordUrl()` / View action / linked column | | | | | | | |
### Key Entities *(include if feature involves data)*
- **[Entity 1]**: [What it represents, key attributes without implementation]

View File

@ -8,181 +8,9 @@ # Tasks: [FEATURE NAME]
**Input**: Design documents from `/specs/[###-feature-name]/`
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
**Tests**: For runtime behavior changes in this repo, tests are REQUIRED (Pest). Only docs-only changes may omit tests.
Runtime-changing features MUST also include tasks to:
- classify the actual test purpose (`Unit`, `Feature`, `Heavy-Governance`, `Browser`) and confirm the affected validation lane(s),
- keep fast or narrow lanes free of silent discovery, surface, workflow, or browser cost,
- keep new helpers, factories, seeds, providers, session state, and support defaults cheap by default or isolate expensive setup behind explicit opt-ins,
- make any new heavy-governance or browser family explicit in naming, lane assignment, and review notes,
- run the narrowest relevant lane before merge,
- record budget, baseline, or trend follow-up when runtime cost shifts materially,
- and document whether the change resolves as `document-in-feature`, `follow-up-spec`, or `reject-or-split`.
**Operations**: If this feature introduces long-running/remote/queued/scheduled work, include tasks to create/reuse and update a
canonical `OperationRun`, and ensure “View run” links route to the canonical Monitoring hub through the shared OperationRun start UX path rather than local surface composition.
If security-relevant DB-only actions skip `OperationRun`, include tasks for `AuditLog` entries (before/after + actor + tenant).
Auth handshake exception (OPS-EX-AUTH-001): OIDC/SAML login handshakes may perform synchronous outbound HTTP on `/auth/*` endpoints
without an `OperationRun`.
If this feature creates/reuses an `OperationRun`, tasks MUST also include:
- reusing the central OperationRun Start UX Contract instead of composing local queued toast/link/event/dedupe/blocked/start-failure semantics,
- delegating `Open operation` / `View run`, artifact links, run-enqueued browser event, queued DB-notification policy, dedupe / already-available / already-running messaging, blocked / failed-to-start messaging, and tenant/workspace-safe URL resolution to the shared OperationRun UX layer,
- enforcing the Ops-UX 3-surface feedback contract (toast intent-only via `OperationUxPresenter`, progress only in widget + run detail, terminal notification is `OperationRunCompleted` exactly-once, initiator-only),
- keeping queued DB notifications explicit opt-in in the active spec unless a different policy is intentionally approved, and ensuring running DB notifications do not exist,
- routing terminal notifications through the central lifecycle mechanism rather than feature-local notification code,
- ensuring `OperationRun.status` / `OperationRun.outcome` transitions happen only via `OperationRunService`,
- ensuring `summary_counts` keys come from `OperationSummaryKeys::all()` and values are flat numeric-only,
- adding/updating Ops-UX regression guards (Pest) that fail CI with actionable output (file + snippet) when these patterns regress,
- clarifying scheduled/system-run behavior (initiator null → no terminal DB notification; audit via Monitoring; tenant-wide alerting via Alerts system),
- documenting any exception with an explicit spec decision, architecture note, test or guard-test rationale, and temporary migration follow-up decision,
- and ensuring the active spec or plan contains an `OperationRun UX Impact` section.
**RBAC**: If this feature introduces or changes authorization, tasks MUST include:
- explicit Gate/Policy enforcement for all mutation endpoints/actions,
- explicit 404 vs 403 semantics:
- non-member / not entitled to workspace scope OR tenant scope → 404 (deny-as-not-found)
- member but missing capability → 403,
- capability registry usage (no raw capability strings; no role-string checks in feature code),
- stating which authorization plane(s) are involved (tenant/admin `/admin` + tenant-context `/admin/t/{tenant}/...` vs platform `/system`),
- tenant-safe global search scoping (no hints; inaccessible results treated as 404 semantics),
- destructive-like actions use `->requiresConfirmation()` (authorization still server-side),
- cross-plane deny-as-not-found (404) checks where applicable,
- at least one positive + one negative authorization test.
**UI Naming**: If this feature adds or changes operator-facing actions, run titles, notifications, audit prose, or helper copy, tasks MUST include:
- aligning primary action labels to `Verb + Object`,
- keeping scope terms (`Workspace`, `Tenant`) out of primary action labels unless they are the actual target object,
- using source/domain terms only where same-screen disambiguation is required,
- aligning button labels, modal titles, run titles, notifications, and audit prose to the same domain vocabulary,
- removing implementation-first wording from primary operator-facing copy.
**Cross-Cutting Shared Pattern Reuse (XCUT-001)**: If this feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, navigation entry points, alerts, evidence/report viewers, or another shared interaction family, tasks MUST include:
- identifying the existing shared contract/presenter/builder/renderer before local implementation begins,
- extending the shared path when it is sufficient for current-release truth,
- or recording a bounded exception task that documents why the shared path is insufficient, what consistency must still be preserved, and how spread is controlled,
- and ensuring reviewer proof covers whether the feature converged on the shared path or knowingly introduced a bounded exception.
**Provider Boundary / Platform Core (PROV-001)**: If this feature touches shared provider/platform seams, tasks MUST include:
- classifying each touched seam as provider-owned or platform-core,
- preventing provider-specific semantics from spreading into platform-core contracts, persistence truth, taxonomies, compare semantics, or operator vocabulary unless explicitly justified,
- implementing bounded normalization or extraction where a current hotspot is too provider-shaped, rather than introducing speculative multi-provider frameworks,
- and recording `document-in-feature` or `follow-up-spec` when a bounded provider-specific hotspot remains.
**UI / Surface Guardrails**: If this feature adds or changes operator-facing surfaces or the workflow that governs them, tasks MUST include:
- carrying forward the spec's native/custom classification, shared-family relevance, state-layer ownership, and exception need into implementation work without renaming the same decision,
- classifying any triggered repository signals with one handling mode (`hard-stop-candidate`, `review-mandatory`, `exception-required`, or `report-only`),
- adding explicit review or definition-of-done work when a guarded surface class, repository signal, or exception path is involved,
- adding required tests or manual smoke for `shared-detail-family`, `monitoring-state-page`, `global-context-shell`, or `exception-coded-surface`, OR recording `standard-native-filament` relief when no special contract exists,
- adding exception documentation and spread-control tasks whenever default surface rules are intentionally relaxed,
- recording the active feature PR close-out entry with guardrail class, exception status, required tests/manual smoke, low-impact `N/A` use, and any deferred automation.
**Operator Surfaces**: If this feature adds or materially refactors an operator-facing page or flow, tasks MUST include:
- classifying each affected surface as Primary Decision, Secondary
Context, or Tertiary Evidence / Diagnostics and keeping that role in
sync with the governing spec,
- defining the human-in-the-loop moment and justifying any new Primary
Decision Surface against existing decision contexts,
- filling the specs UI/UX Surface Classification for every affected surface,
- filling the specs Operator Surface Contract for every affected page,
- keeping default-visible content limited to first-decision needs and
moving proof, payloads, and diagnostics into progressive disclosure,
- implementing the three-tier disclosure hierarchy where applicable:
customer-readable decision content first, operator diagnostics
second, support/raw evidence third,
- making default-visible content operator-first and moving JSON payloads, raw IDs, internal field names, provider error details, and low-level metadata into explicitly revealed diagnostics surfaces,
- ensuring customer/read-only default paths do not expose raw JSON,
copied context payloads, fingerprints, internal reason ownership,
platform reason families, or debug semantics,
- keeping each governance case decidable in one focused context where
practical instead of forcing cross-page reconstruction,
- keeping exactly one dominant next action primary and demoting
navigation/debug helpers such as `Open operation`, `Technical
details`, or `Show JSON`,
- removing duplicate visible status, blocker, reason, impact, or
next-action summaries so later sections add evidence instead of
restating the same decision truth,
- modeling execution outcome, data completeness, governance result, and lifecycle/readiness as distinct status dimensions when applicable,
- making mutation scope legible before execution for every state-changing action (`TenantPilot only`, `Microsoft tenant`, or `simulation only`),
- implementing the safe-execution flow for dangerous actions (configuration, safety checks/simulation, preview, hard confirmation where required, execute) or documenting an approved exemption,
- keeping canonical nouns stable across routes, buttons, run titles, notifications, and audit prose,
- keeping navigation aligned to operator workflows rather than storage
structures,
- ensuring new automation, alerts, or autonomous flows reduce
search/review/click load instead of adding noise, extra lists, or
extra detail work,
- preserving a calm, prioritized default state that distinguishes
actionable work from worth-watching context and reference-only
information,
- keeping scope signals truthful and ensuring critical operational truth is visible by default,
- keeping standard CRUD / Registry rows scanable rather than prose-heavy,
- keeping workspace and tenant context explicit in navigation, actions, and page semantics so tenant pages do not silently expose workspace-wide actions.
**Filament UI Action Surfaces**: If this feature adds/modifies any Filament Resource / RelationManager / Page, tasks MUST include:
- filling the specs “UI Action Matrix” for all changed surfaces,
- assigning exactly one broad action-surface class to every changed
operator-facing surface and keeping the detailed surface type in sync
with the spec,
- identifying the one likely next operator action for each changed
surface and shaping the visible hierarchy around it,
- implementing required action surfaces (header/row/bulk/empty-state CTA for lists; header actions for view; consistent save/cancel on create/edit),
- ensuring every List/Table has exactly one primary inspect/open model with the correct surface-appropriate affordance,
- removing redundant View/Inspect actions when row click or identifier click already opens the same destination,
- keeping standard CRUD / Registry rows to inspect/open plus at most one inline safe shortcut,
- separating navigation from mutation so pure context changes do not
compete visually with state-changing actions,
- moving additional secondary actions into More or the detail header,
- ordering visible actions and grouped actions by meaning, frequency,
and risk rather than append order,
- placing destructive actions in More or the detail header for standard lists and using catalogued exceptions only where allowed,
- ensuring workbench and monitoring surfaces separate scope/context,
selection actions, navigation, and object actions instead of mixing
them into one flat header zone,
- grouping bulk actions via BulkActionGroup,
- preventing empty `ActionGroup` / `BulkActionGroup` placeholders,
- adding confirmations for destructive actions (and typed confirmation where required by scale),
- adding `AuditLog` entries for relevant mutations,
- using native Filament components or shared UI primitives before any local Blade/Tailwind assembly for badges, alerts, buttons, and semantic status surfaces,
- avoiding page-local semantic color, border, rounding, or highlight styling when Filament props or shared primitives can express the same state,
- documenting any workflow-hub, wizard, utility/system, or other
special-type exception in the spec/PR and adding dedicated test
coverage,
- documenting any catalogued UI exception in the spec/PR and adding dedicated test coverage,
- documenting any UI-FIL-001 exception with rationale in the spec/PR,
- adding/updated tests that enforce the contract and block merge on violations, OR documenting an explicit exemption with rationale.
- For any new or modified customer/operator-facing detail surface,
tests MUST prove default-visible status/reason/impact/next-action
content, exactly one dominant next action, diagnostics-secondary
ordering, hidden raw/support detail by default, capability-gated
support/raw sections where applicable, and the absence of duplicate
visible decision summaries.
**Filament UI UX-001 (Layout & IA)**: If this feature adds/modifies any Filament screen, tasks MUST include:
- ensuring Create/Edit pages use Main/Aside layout (3-col grid, Main=columnSpan(2), Aside=columnSpan(1)),
- ensuring all form fields are inside Sections/Cards (no naked inputs at root schema level),
- ensuring View pages use Infolists (not disabled edit forms); status badges use BADGE-001,
- ensuring empty states show a specific title + explanation + exactly 1 CTA; non-empty tables move CTA to header,
- enforcing ACTSURF-001 / HDR-001 action discipline: record/detail/edit
pages keep at most 1 visible primary header action; pure navigation
moves to contextual placement; destructive or governance-changing
actions are separated and require friction; monitoring/workbench
surfaces use their own layered hierarchy; rare actions live in
structured Action Groups; every affected surface passes the few-second
scan rule,
- using shared layout builders (e.g., `MainAsideForm`, `MainAsideInfolist`, `StandardTableDefaults`) where available,
- OR documenting an explicit exemption with rationale if UX-001 is not fully satisfied.
**Badges**: If this feature changes status-like badge semantics, tasks MUST use `BadgeCatalog` / `BadgeRenderer` (BADGE-001),
avoid ad-hoc mappings in Filament, and include mapping tests for any new/changed values.
**Proportionality / Anti-Bloat**: If this feature introduces a new enum/status family, DTO/presenter/envelope, persisted entity/table/artifact,
interface/contract/registry/resolver, taxonomy/classification system, or cross-domain UI framework, tasks MUST include:
- completing the specs Proportionality Review,
- implementing the narrowest correct shape justified by current-release truth,
- removing or replacing superseded layers where practical instead of stacking new ones on top,
- keeping convenience projections and UI helpers derived unless independent persistence is explicitly justified,
- and adding tests around business consequences, permissions, lifecycle behavior, isolation, or audit responsibilities rather than thin indirection alone.
**Tests**: The examples below include test tasks. Tests are OPTIONAL - only include them if explicitly requested in the feature specification.
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
Runtime behavior or test-surface changes MUST include at least one explicit task for lane validation or runtime-impact review so upkeep stays inside the feature instead of becoming separate cleanup.
## Test Governance Checklist
Include this short checklist in generated task lists for runtime-changing or test-affecting work. Docs-only or template-only work may mark the items `N/A`.
- [ ] Lane assignment is named and is the narrowest sufficient proof for the changed behavior.
- [ ] New or changed tests stay in the smallest honest family, and any heavy-governance or browser addition is explicit.
- [ ] Shared helpers, factories, seeds, fixtures, and context defaults stay cheap by default; any widening is isolated or documented.
- [ ] Planned validation commands cover the change without pulling in unrelated lane cost.
- [ ] The declared surface test profile or `standard-native-filament` relief is explicit.
- [ ] Any material budget, baseline, trend, or escalation note is recorded in the active spec or PR.
## Format: `[ID] [P?] [Story] Description`
@ -327,8 +155,6 @@ ## Phase N: Polish & Cross-Cutting Concerns
- [ ] TXXX Performance optimization across all stories
- [ ] TXXX [P] Additional unit tests (if requested) in tests/unit/
- [ ] TXXX Security hardening
- [ ] TXXX Proportionality cleanup: remove or collapse superseded layers introduced during implementation
- [ ] TXXX Record the active feature PR close-out entry with guardrail class, exception status, proof depth, and deferred automation
- [ ] TXXX Run quickstart.md validation
---

View File

@ -1,63 +0,0 @@
schema_version: "1.0"
workflow:
id: "speckit"
name: "Full SDD Cycle"
version: "1.0.0"
author: "GitHub"
description: "Runs specify → plan → tasks → implement with review gates"
requires:
speckit_version: ">=0.7.2"
integrations:
any: ["copilot", "claude", "gemini"]
inputs:
spec:
type: string
required: true
prompt: "Describe what you want to build"
integration:
type: string
default: "copilot"
prompt: "Integration to use (e.g. claude, copilot, gemini)"
scope:
type: string
default: "full"
enum: ["full", "backend-only", "frontend-only"]
steps:
- id: specify
command: speckit.specify
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"
- id: review-spec
type: gate
message: "Review the generated spec before planning."
options: [approve, reject]
on_reject: abort
- id: plan
command: speckit.plan
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"
- id: review-plan
type: gate
message: "Review the plan before generating tasks."
options: [approve, reject]
on_reject: abort
- id: tasks
command: speckit.tasks
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"
- id: implement
command: speckit.implement
integration: "{{ inputs.integration }}"
input:
args: "{{ inputs.spec }}"

View File

@ -1,13 +0,0 @@
{
"schema_version": "1.0",
"workflows": {
"speckit": {
"name": "Full SDD Cycle",
"version": "1.0.0",
"description": "Runs specify \u2192 plan \u2192 tasks \u2192 implement with review gates",
"source": "bundled",
"installed_at": "2026-04-22T21:58:03.039039+00:00",
"updated_at": "2026-04-22T21:58:03.039046+00:00"
}
}
}

823
Agents.md
View File

@ -25,14 +25,12 @@ ## Scope Reference
- Tenant-scoped RBAC and audit logs
## Workflow (Spec Kit)
1. Read `.specify/memory/constitution.md`
2. For new work: create/update `specs/<NNN>-<slug>/spec.md`
3. Produce `specs/<NNN>-<slug>/plan.md`
4. Break into `specs/<NNN>-<slug>/tasks.md`
1. Read `.specify/constitution.md`
2. For new work: create/update `.specify/spec.md`
3. Produce `.specify/plan.md`
4. Break into `.specify/tasks.md`
5. Implement changes in small PRs
Any spec that introduces a new persisted entity, abstraction, enum/status family, or taxonomy/framework must include the proportionality review required by the constitution before implementation starts.
If requirements change during implementation, update spec/plan before continuing.
## Workflow (SDD in diesem Repo)
@ -149,52 +147,6 @@ # Reset to before the conflict
# Or stash conflicting changes
git stash push -m "conflicting-agent-work-$(date +%s)"
```
## Solo + Copilot Workflow (Konflikte vermeiden)
Wenn du alleine arbeitest (du + Copilot), sind große Konflikt-Stürme fast immer „Branch drift“: `dev` bewegt sich weiter, das Feature hängt hinterher. Diese Regeln halten Feature-Branches mergebar.
### Regel 1: Vor jeder Troubleshooting-Änderung zuerst `dev` ins Feature holen
Bevor du einen kleinen Fix auf einem Feature-Branch machst (z.B. `config/`, `tests/`, shared Services), synchronisiere:
```bash
git fetch origin
git checkout feat/<NNN>-<slug>
git merge origin/dev
```
### Regel 2: Kurzlebige „Session Branches“ auch im Solo-Setup
Auch wenn du alleine bist: nutze Session-Branches für gezielte Fixes, damit du jederzeit sauber abbrechen kannst.
```bash
git checkout feat/<NNN>-<slug>
git checkout -b $(git branch --show-current)-session-$(date +%s)
```
Danach wie gewohnt committen, testen, zurück-merge:
```bash
SESSION_BRANCH=$(git branch --show-current)
ORIGINAL_BRANCH=$(git branch --show-current | sed 's/-session-[0-9]*$//')
git checkout $ORIGINAL_BRANCH
git merge $SESSION_BRANCH --no-ff -m "merge: agent session work"
```
### Regel 3: „Globale“ Fixes als Mini-PR nach `dev`
Wenn ein Fix nicht wirklich feature-spezifisch ist (z.B. `config/graph_contracts.php`, Test-Bootstrap, allgemeine Graph-Validation), dann:
- Mini-Branch von `dev` erstellen und PR → `dev` mergen.
- Danach im Feature-Branch einfach wieder `origin/dev` mergen.
Das reduziert Add/Add-Konflikte drastisch, weil `dev` die gemeinsame Wahrheit bleibt.
### Regel 4: Kein Rebase auf geteilten Branches
Wenn du und Copilot über längere Zeit auf demselben Feature-Branch arbeiten, bleib bei `merge origin/dev` (kein Rebase), damit die Historie stabil bleibt.
## Architecture Assumptions
- Backend: Laravel (latest stable)
- Admin UI: Filament
@ -272,7 +224,7 @@ ## Engineering Rules
- Keep Microsoft Graph integration isolated behind a dedicated abstraction layer.
- Use dependency injection and clear interfaces for Graph clients.
- No breaking changes to data structures or API contracts without updating:
- `specs/<NNN>-<slug>/spec.md`
- `.specify/spec.md`
- migration notes
- upgrade steps
- If a TypeScript/JS tooling package exists, use strict typing rules there too.
@ -318,13 +270,12 @@ ## Security
## Commands
### Sail (preferred locally)
- `cd apps/platform && ./vendor/bin/sail up -d`
- `cd apps/platform && ./vendor/bin/sail down`
- `cd apps/platform && ./vendor/bin/sail composer install`
- `cd apps/platform && ./vendor/bin/sail artisan migrate`
- `cd apps/platform && ./vendor/bin/sail artisan test`
- `cd apps/platform && ./vendor/bin/sail artisan` (general)
- Root helper for tooling only: `./scripts/platform-sail ...`
- `./vendor/bin/sail up -d`
- `./vendor/bin/sail down`
- `./vendor/bin/sail composer install`
- `./vendor/bin/sail artisan migrate`
- `./vendor/bin/sail artisan test`
- `./vendor/bin/sail artisan` (general)
### Drizzle (local DB tooling, if configured)
- Use only for local/dev workflows.
@ -336,10 +287,10 @@ ### Drizzle (local DB tooling, if configured)
(Agents should confirm the exact script names in `package.json` before suggesting them.)
### Non-Docker fallback (only if needed)
- `cd apps/platform && composer install`
- `cd apps/platform && php artisan serve`
- `cd apps/platform && php artisan migrate`
- `cd apps/platform && php artisan test`
- `composer install`
- `php artisan serve`
- `php artisan migrate`
- `php artisan test`
### Frontend/assets/tooling (if present)
- `pnpm install`
@ -353,11 +304,11 @@ ## Where to look first
- `.specify/`
- `AGENTS.md`
- `README.md`
- `apps/platform/app/`
- `apps/platform/database/`
- `apps/platform/routes/`
- `apps/platform/resources/`
- `apps/platform/config/`
- `app/`
- `database/`
- `routes/`
- `resources/`
- `config/`
---
@ -389,307 +340,20 @@ ## Reference Materials
===
<laravel-boost-guidelines>
=== .ai/filament-v5-blueprint rules ===
## Source of Truth
If any Filament behavior is uncertain, lookup the exact section in:
- docs/research/filament-v5-notes.md
and prefer that over guesses.
# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES)
# Filament Blueprint (v5)
## 1) Non-negotiables
- Filament v5 requires Livewire v4.0+.
- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`).
- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results.
- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions).
- Prefer render hooks + CSS hook classes over publishing Filament internal views.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/render-hooks
- https://filamentphp.com/docs/5.x/styling/css-hooks
## 2) Directory & naming conventions
- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture.
- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`.
Sources:
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/advanced/modular-architecture
## 3) Panel setup defaults
- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels.
- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel.
- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review.
- Assets policy:
- Panel-only assets: register via panel config.
- Shared/plugin assets: register via `FilamentAsset::register()`.
- Deployment must include `cd apps/platform && php artisan filament:assets`.
Sources:
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/advanced/assets
## 4) Navigation & information architecture
- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately.
- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows.
- Treat cluster code structure as a recommendation (organizational benefit), not a required rule.
- User menu:
- Configure via `userMenuItems()` with Action objects.
- Never put destructive actions there without confirmation + authorization.
Sources:
- https://filamentphp.com/docs/5.x/navigation/overview
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/navigation/user-menu
## 5) Resource patterns
- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows.
- Global search:
- If a resource is intended for global search: ensure Edit/View page exists.
- Otherwise disable global search for that resource (dont “expect it to work”).
- If global search renders relationship-backed details: eager-load via global search query override.
- For very large datasets: consider disabling term splitting (only when needed).
Sources:
- https://filamentphp.com/docs/5.x/resources/overview
- https://filamentphp.com/docs/5.x/resources/global-search
## 6) Page lifecycle & query rules
- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading.
- Prefer render hooks for layout injection; avoid publishing internal views.
Sources:
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 7) Infolists vs RelationManagers (decision tree)
- Interactive CRUD / attach / detach under owner record → RelationManager.
- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields.
- Inline CRUD inside owner form → Repeater.
- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists.
Sources:
- https://filamentphp.com/docs/5.x/resources/managing-relationships
- https://filamentphp.com/docs/5.x/infolists/overview
## 8) Form patterns (validation, reactivity, state)
- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side.
- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns).
- Custom field views must obey state binding modifiers.
Sources:
- https://filamentphp.com/docs/5.x/forms/overview
- https://filamentphp.com/docs/5.x/forms/custom-fields
## 9) Table & action patterns
- Tables: always define a meaningful empty state (and empty-state actions where appropriate).
- Actions:
- Execution actions use `->action(...)`.
- Destructive actions add `->requiresConfirmation()`.
- Navigation-only actions should use `->url(...)`.
- UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified.
Sources:
- https://filamentphp.com/docs/5.x/tables/empty-state
- https://filamentphp.com/docs/5.x/actions/modals
## 10) Authorization & security
- Enforce panel access in non-local environments as documented.
- UI visibility is not security; enforce policies/access checks in addition to hiding UI.
- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization.
Sources:
- https://filamentphp.com/docs/5.x/users/overview
- https://filamentphp.com/docs/5.x/resources/deleting-records
## 11) Notifications & UX feedback
- Default to explicit success/error notifications for user-triggered mutations that arent instantly obvious.
- Treat polling as a cost; set intervals intentionally where polling is used.
Sources:
- https://filamentphp.com/docs/5.x/notifications/overview
- https://filamentphp.com/docs/5.x/widgets/stats-overview
## 12) Performance defaults
- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies.
- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing.
Sources:
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/styling/css-hooks
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 13) Testing requirements
- Test pages/relation managers/widgets as Livewire components.
- Test actions using Filaments action testing guidance.
- Do not mount non-Livewire classes in Livewire tests.
Sources:
- https://filamentphp.com/docs/5.x/testing/overview
- https://filamentphp.com/docs/5.x/testing/testing-actions
## 14) Forbidden patterns
- Mixing Filament v3/v4 APIs into v5 code.
- Any mention of Livewire v3 for Filament v5.
- Registering panel providers in `bootstrap/app.php` on Laravel 11+.
- Destructive actions without `->requiresConfirmation()`.
- Shipping heavy assets globally when on-demand loading fits.
- Publishing Filament internal views as a default customization technique.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/assets
## 15) Agent output contract
For any implementation request, the agent must explicitly state:
1) Livewire v4.0+ compliance.
2) Provider registration location (Laravel 11+: `bootstrap/providers.php`).
3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled).
4) Which actions are destructive and how confirmation + authorization is handled.
5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy.
6) Testing plan: which pages/widgets/relation managers/actions are covered.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/testing/testing-actions
=== .ai/filament-v5-checklist rules ===
# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES)
## Version Safety
- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere).
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire”
- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs).
- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps.
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements”
## Panel & Navigation
- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel”
- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path”
- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments).
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster”
- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction”
- [ ] Cluster directory structure is treated as recommended, not mandatory.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters”
- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed.
- Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction”
## Resource Structure
- [ ] `$recordTitleAttribute` is set for any resource intended for global search.
- Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles”
- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles”
- [ ] Relationship-backed global search details are eager-loaded via the global search query override.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results”
## Infolists & Relations
- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job”
- [ ] RelationManagers remain lazy-loaded by default unless theres an explicit UX justification.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading”
## Forms
- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily.
- Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur”
- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers).
- Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers”
## Tables & Actions
- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate).
- Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions”
- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`.
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact).
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
## Authorization & Security
- [ ] Panel access is enforced for non-local environments as documented.
- Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel”
- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries.
- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required.
- Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization”
## UX & Notifications
- [ ] User-triggered mutations provide explicit success/error notifications when outcomes arent instantly obvious.
- Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction”
- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load.
- Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)”
## Performance
- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript”
- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default).
- Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes”
## Testing
- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes.
- Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?”
- [ ] Actions that mutate data are covered using Filaments action testing guidance.
- Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions”
## Deployment / Ops
- [ ] `cd apps/platform && php artisan filament:assets` is included in the deployment process when using registered assets.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade”
=== foundation rules ===
# Laravel Boost Guidelines
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications.
## Foundational Context
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
- php - 8.4.15
- filament/filament (FILAMENT) - v5
- filament/filament (FILAMENT) - v4
- laravel/framework (LARAVEL) - v12
- laravel/prompts (PROMPTS) - v0
- laravel/socialite (SOCIALITE) - v5
- livewire/livewire (LIVEWIRE) - v4
- livewire/livewire (LIVEWIRE) - v3
- laravel/mcp (MCP) - v0
- laravel/pint (PINT) - v1
- laravel/sail (SAIL) - v1
@ -697,251 +361,426 @@ ## Foundational Context
- phpunit/phpunit (PHPUNIT) - v12
- tailwindcss (TAILWINDCSS) - v4
## Skills Activation
This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.
- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes.
## Conventions
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, naming.
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
- Check for existing components to reuse before writing a new one.
- UI consistency: Prefer Filament components (`<x-filament::section>`, infolist/table entries, etc.) over custom HTML/Tailwind for admin UI; only roll custom markup when Filament cannot express the UI.
## Verification Scripts
- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.
- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important.
## Application Structure & Architecture
- Stick to existing directory structure; don't create new base folders without approval.
- Stick to existing directory structure - don't create new base folders without approval.
- Do not change the application's dependencies without approval.
## Frontend Bundling
- Repo-root JavaScript orchestration now uses `corepack pnpm install`, `corepack pnpm dev:platform`, `corepack pnpm dev:website`, `corepack pnpm dev`, `corepack pnpm build:website`, and `corepack pnpm build:platform`.
- `corepack pnpm dev:platform` starts the platform Sail stack and the Laravel panel Vite watcher. `corepack pnpm dev` starts that platform watcher plus the website dev server.
- `apps/website` is a standalone Astro app, not a second Laravel runtime, so Boost MCP remains platform-only.
- If the user doesn't see a platform frontend change reflected in the UI, it could mean they need to run `cd apps/platform && ./vendor/bin/sail pnpm build`, `cd apps/platform && ./vendor/bin/sail pnpm dev`, or `cd apps/platform && ./vendor/bin/sail composer run dev`. Ask them.
## Documentation Files
- You must only create documentation files if explicitly requested by the user.
- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.
## Replies
- Be concise in your explanations - focus on what's important rather than explaining obvious details.
## Documentation Files
- You must only create documentation files if explicitly requested by the user.
=== boost rules ===
# Laravel Boost
## Laravel Boost
- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them.
## Artisan
- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters.
- Use the `list-artisan-commands` tool when you need to call an Artisan command to double check the available parameters.
## URLs
- Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port.
- Whenever you share a project URL with the user you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain / IP, and port.
## Tinker / Debugging
- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
- Use the `database-query` tool when you only need to read from the database.
- Use the `database-schema` tool to inspect table structure before writing migrations or models.
## Reading Browser Logs With the `browser-logs` Tool
- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost.
- Only recent browser logs will be useful - ignore old logs.
## Searching Documentation (Critically Important)
- Boost comes with a powerful `search-docs` tool you should use before trying other approaches when working with Laravel or Laravel ecosystem packages. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
- Boost comes with a powerful `search-docs` tool you should use before any other approaches. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation specific for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
- The 'search-docs' tool is perfect for all Laravel related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc.
- You must use this tool to search for Laravel-ecosystem documentation before falling back to other approaches.
- Search the documentation before making code changes to ensure we are taking the correct approach.
- Use multiple, broad, simple, topic-based queries at once. For example: `['rate limiting', 'routing rate limiting', 'routing']`. The most relevant results will be returned first.
- Do not add package names to queries; package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
- Use multiple, broad, simple, topic based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`.
- Do not add package names to queries - package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
### Available Search Syntax
- You can and should pass multiple queries at once. The most relevant results will be returned first.
1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'
2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit"
3. Quoted Phrases (Exact Position) - query="infinite scroll" - Words must be adjacent and in that order
4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit"
5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms
1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'.
2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit".
3. Quoted Phrases (Exact Position) - query="infinite scroll" - words must be adjacent and in that order.
4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit".
5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms.
=== php rules ===
# PHP
## PHP
- Always use curly braces for control structures, even for single-line bodies.
## Constructors
- Always use curly braces for control structures, even if it has one line.
### Constructors
- Use PHP 8 constructor property promotion in `__construct()`.
- `public function __construct(public GitHub $github) { }`
- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private.
## Type Declarations
- <code-snippet>public function __construct(public GitHub $github) { }</code-snippet>
- Do not allow empty `__construct()` methods with zero parameters.
### Type Declarations
- Always use explicit return type declarations for methods and functions.
- Use appropriate PHP type hints for method parameters.
<!-- Explicit Return Types and Method Params -->
```php
<code-snippet name="Explicit Return Types and Method Params" lang="php">
protected function isAccessible(User $user, ?string $path = null): bool
{
...
}
```
## Enums
- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
</code-snippet>
## Comments
- Prefer PHPDoc blocks over inline comments. Never use comments within the code itself unless the logic is exceptionally complex.
- Prefer PHPDoc blocks over comments. Never use comments within the code itself unless there is something _very_ complex going on.
## PHPDoc Blocks
- Add useful array shape type definitions for arrays when appropriate.
- Add useful array shape type definitions when appropriate.
## Enums
- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
=== sail rules ===
# Laravel Sail
- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail.
- Start services using `cd apps/platform && ./vendor/bin/sail up -d` and stop them with `cd apps/platform && ./vendor/bin/sail stop`.
- Open the application in the browser by running `cd apps/platform && ./vendor/bin/sail open`.
- Always prefix PHP, Artisan, Composer, and Node commands with `cd apps/platform && ./vendor/bin/sail`. Examples:
- Run Artisan Commands: `cd apps/platform && ./vendor/bin/sail artisan migrate`
- Install Composer packages: `cd apps/platform && ./vendor/bin/sail composer install`
- Execute Node commands: `cd apps/platform && ./vendor/bin/sail pnpm dev`
- Execute PHP scripts: `cd apps/platform && ./vendor/bin/sail php [script]`
- View all available Sail commands by running `cd apps/platform && ./vendor/bin/sail` without arguments.
=== tests rules ===
# Test Enforcement
## Test Enforcement
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
- Run the minimum number of tests needed to ensure code quality and speed. Use `cd apps/platform && ./vendor/bin/sail artisan test --compact` with a specific filename or filter.
- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test` with a specific filename or filter.
=== laravel/core rules ===
# Do Things the Laravel Way
## Do Things the Laravel Way
- Use `cd apps/platform && ./vendor/bin/sail artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
- If you're creating a generic PHP class, use `cd apps/platform && ./vendor/bin/sail artisan make:class`.
- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
- If you're creating a generic PHP class, use `php artisan make:class`.
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
## Database
### Database
- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins.
- Use Eloquent models and relationships before suggesting raw database queries.
- Use Eloquent models and relationships before suggesting raw database queries
- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them.
- Generate code that prevents N+1 query problems by using eager loading.
- Use Laravel's query builder for very complex database operations.
### Model Creation
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `cd apps/platform && ./vendor/bin/sail artisan make:model`.
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`.
### APIs & Eloquent Resources
- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
## Controllers & Validation
### Controllers & Validation
- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages.
- Check sibling Form Requests to see if the application uses array or string based validation rules.
## Authentication & Authorization
- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
## URL Generation
- When generating links to other pages, prefer named routes and the `route()` function.
## Queues
### Queues
- Use queued jobs for time-consuming operations with the `ShouldQueue` interface.
## Configuration
### Authentication & Authorization
- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
### URL Generation
- When generating links to other pages, prefer named routes and the `route()` function.
### Configuration
- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`.
## Testing
### Testing
- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
- When creating tests, make use of `cd apps/platform && ./vendor/bin/sail artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
## Vite Error
### Vite Error
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `cd apps/platform && ./vendor/bin/sail pnpm build` or ask the user to run `cd apps/platform && ./vendor/bin/sail pnpm dev` or `cd apps/platform && ./vendor/bin/sail composer run dev`.
=== laravel/v12 rules ===
# Laravel 12
## Laravel 12
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
- Use the `search-docs` tool to get version specific documentation.
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.
## Laravel 12 Structure
- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`.
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
### Laravel 12 Structure
- No middleware files in `app/Http/Middleware/`.
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
- `bootstrap/providers.php` contains application specific service providers.
- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration.
## Database
- **No app\Console\Kernel.php** - use `bootstrap/app.php` or `routes/console.php` for console configuration.
- **Commands auto-register** - files in `app/Console/Commands/` are automatically available and do not require manual registration.
### Database
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
- Laravel 11 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
### Models
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
=== livewire/core rules ===
## Livewire Core
- Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests.
- Use the `php artisan make:livewire [Posts\CreatePost]` artisan command to create new components
- State should live on the server, with the UI reflecting it.
- All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions.
## Livewire Best Practices
- Livewire components require a single root element.
- Use `wire:loading` and `wire:dirty` for delightful loading states.
- Add `wire:key` in loops:
```blade
@foreach ($items as $item)
<div wire:key="item-{{ $item->id }}">
{{ $item->name }}
</div>
@endforeach
```
- Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects:
<code-snippet name="Lifecycle hook examples" lang="php">
public function mount(User $user) { $this->user = $user; }
public function updatedSearch() { $this->resetPage(); }
</code-snippet>
## Testing Livewire
<code-snippet name="Example Livewire component test" lang="php">
Livewire::test(Counter::class)
->assertSet('count', 0)
->call('increment')
->assertSet('count', 1)
->assertSee(1)
->assertStatus(200);
</code-snippet>
<code-snippet name="Testing a Livewire component exists within a page" lang="php">
$this->get('/posts/create')
->assertSeeLivewire(CreatePost::class);
</code-snippet>
=== livewire/v3 rules ===
## Livewire 3
### Key Changes From Livewire 2
- These things changed in Livewire 2, but may not have been updated in this application. Verify this application's setup to ensure you conform with application conventions.
- Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default.
- Components now use the `App\Livewire` namespace (not `App\Http\Livewire`).
- Use `$this->dispatch()` to dispatch events (not `emit` or `dispatchBrowserEvent`).
- Use the `components.layouts.app` view as the typical layout path (not `layouts.app`).
### New Directives
- `wire:show`, `wire:transition`, `wire:cloak`, `wire:offline`, `wire:target` are available for use. Use the documentation to find usage examples.
### Alpine
- Alpine is now included with Livewire, don't manually include Alpine.js.
- Plugins included with Alpine: persist, intersect, collapse, and focus.
### Lifecycle Hooks
- You can listen for `livewire:init` to hook into Livewire initialization, and `fail.status === 419` for the page expiring:
<code-snippet name="livewire:load example" lang="js">
document.addEventListener('livewire:init', function () {
Livewire.hook('request', ({ fail }) => {
if (fail && fail.status === 419) {
alert('Your session expired');
}
});
Livewire.hook('message.failed', (message, component) => {
console.error(message);
});
});
</code-snippet>
=== pint/core rules ===
# Laravel Pint Code Formatter
## Laravel Pint Code Formatter
- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues.
- You must run `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `cd apps/platform && ./vendor/bin/sail bin pint --test --format agent`, simply run `cd apps/platform && ./vendor/bin/sail bin pint --format agent` to fix any formatting issues.
=== pest/core rules ===
## Pest
### Testing
- If you need to verify a feature is working, write or update a Unit / Feature test.
### Pest Tests
- All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application.
- Tests should test all of the happy paths, failure paths, and weird paths.
- Tests live in the `tests/Feature` and `tests/Unit` directories.
- Pest tests look and behave like this:
<code-snippet name="Basic Pest Test Example" lang="php">
it('is true', function () {
expect(true)->toBeTrue();
});
</code-snippet>
### Running Tests
- Run the minimal number of tests using an appropriate filter before finalizing code edits.
- To run all tests: `php artisan test`.
- To run all tests in a file: `php artisan test tests/Feature/ExampleTest.php`.
- To filter on a particular test name: `php artisan test --filter=testName` (recommended after making a change to a related file).
- When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing.
### Pest Assertions
- When asserting status codes on a response, use the specific method like `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or similar, e.g.:
<code-snippet name="Pest Example Asserting postJson Response" lang="php">
it('returns all', function () {
$response = $this->postJson('/api/docs', []);
$response->assertSuccessful();
});
</code-snippet>
### Mocking
- Mocking can be very helpful when appropriate.
- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always import it via `use function Pest\Laravel\mock;` before using it. Alternatively, you can use `$this->mock()` if existing tests do.
- You can also create partial mocks using the same import or self method.
### Datasets
- Use datasets in Pest to simplify tests which have a lot of duplicated data. This is often the case when testing validation rules, so consider going with this solution when writing tests for validation rules.
<code-snippet name="Pest Dataset Example" lang="php">
it('has emails', function (string $email) {
expect($email)->not->toBeEmpty();
})->with([
'james' => 'james@laravel.com',
'taylor' => 'taylor@laravel.com',
]);
</code-snippet>
=== pest/v4 rules ===
## Pest 4
- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
- Browser testing is incredibly powerful and useful for this project.
- Browser tests should live in `tests/Browser/`.
- Use the `search-docs` tool for detailed guidance on utilizing these features.
### Browser Testing
- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest v4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test.
- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging when appropriate.
### Example Tests
<code-snippet name="Pest Browser Test Example" lang="php">
it('may reset the password', function () {
Notification::fake();
$this->actingAs(User::factory()->create());
$page = visit('/sign-in'); // Visit on a real browser...
$page->assertSee('Sign In')
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
->click('Forgot Password?')
->fill('email', 'nuno@laravel.com')
->click('Send Reset Link')
->assertSee('We have emailed your password reset link!')
Notification::assertSent(ResetPassword::class);
});
</code-snippet>
<code-snippet name="Pest Smoke Testing Example" lang="php">
$pages = visit(['/', '/about', '/contact']);
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
</code-snippet>
- This project uses Pest for testing. Create tests: `cd apps/platform && ./vendor/bin/sail artisan make:test --pest {name}`.
- Run tests: `cd apps/platform && ./vendor/bin/sail artisan test --compact` or filter: `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=testName`.
- Do NOT delete tests without approval.
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Pest documentation and updated code examples.
- IMPORTANT: Activate `pest-testing` every time you're working with a Pest or testing-related task.
=== tailwindcss/core rules ===
# Tailwind CSS
## Tailwind Core
- Always use existing Tailwind conventions; check project patterns before adding new ones.
- IMPORTANT: Always use `search-docs` tool for version-specific Tailwind CSS documentation and updated code examples. Never rely on training data.
- IMPORTANT: Activate `tailwindcss-development` every time you're working with a Tailwind CSS or styling-related task.
- Use Tailwind CSS classes to style HTML, check and use existing tailwind conventions within the project before writing your own.
- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc..)
- Think through class placement, order, priority, and defaults - remove redundant classes, add classes to parent or child carefully to limit repetition, group elements logically
- You can use the `search-docs` tool to get exact examples from the official documentation when needed.
### Spacing
- When listing items, use gap utilities for spacing, don't use margins.
<code-snippet name="Valid Flex Gap Spacing Example" lang="html">
<div class="flex gap-8">
<div>Superior</div>
<div>Michigan</div>
<div>Erie</div>
</div>
</code-snippet>
### Dark Mode
- If existing pages and components support dark mode, new pages and components must support dark mode in a similar way, typically using `dark:`.
=== tailwindcss/v4 rules ===
## Tailwind 4
- Always use Tailwind CSS v4 - do not use the deprecated utilities.
- `corePlugins` is not supported in Tailwind v4.
- In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed.
<code-snippet name="Extending Theme in CSS" lang="css">
@theme {
--color-brand: oklch(0.72 0.11 178);
}
</code-snippet>
- In Tailwind v4, you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3:
<code-snippet name="Tailwind v4 Import Tailwind Diff" lang="diff">
- @tailwind base;
- @tailwind components;
- @tailwind utilities;
+ @import "tailwindcss";
</code-snippet>
### Replaced Utilities
- Tailwind v4 removed deprecated utilities. Do not use the deprecated option - use the replacement.
- Opacity values are still numeric.
| Deprecated | Replacement |
|------------+--------------|
| bg-opacity-* | bg-black/* |
| text-opacity-* | text-black/* |
| border-opacity-* | border-black/* |
| divide-opacity-* | divide-black/* |
| ring-opacity-* | ring-black/* |
| placeholder-opacity-* | placeholder-black/* |
| flex-shrink-* | shrink-* |
| flex-grow-* | grow-* |
| overflow-ellipsis | text-ellipsis |
| decoration-slice | box-decoration-slice |
| decoration-clone | box-decoration-clone |
</laravel-boost-guidelines>
## Active Technologies
- PHP 8.4 (Laravel 12) + Filament v5 + Livewire v4
- PostgreSQL (Sail)
- Tailwind CSS v4
## Recent Changes
- 066-rbac-ui-enforcement-helper-v2-session-1769732329: Planned UiEnforcement v2 (spec + plan + design artifacts)

772
GEMINI.md
View File

@ -26,9 +26,9 @@ ## Scope Reference
## Workflow (Spec Kit)
1. Read `.specify/constitution.md`
2. For new work: create/update `specs/<NNN>-<slug>/spec.md`
3. Produce `specs/<NNN>-<slug>/plan.md`
4. Break into `specs/<NNN>-<slug>/tasks.md`
2. For new work: create/update `.specify/spec.md`
3. Produce `.specify/plan.md`
4. Break into `.specify/tasks.md`
5. Implement changes in small PRs
If requirements change during implementation, update spec/plan before continuing.
@ -110,7 +110,7 @@ ## Engineering Rules
- Keep Microsoft Graph integration isolated behind a dedicated abstraction layer.
- Use dependency injection and clear interfaces for Graph clients.
- No breaking changes to data structures or API contracts without updating:
- `specs/<NNN>-<slug>/spec.md`
- `.specify/spec.md`
- migration notes
- upgrade steps
- If a TypeScript/JS tooling package exists, use strict typing rules there too.
@ -156,13 +156,12 @@ ## Security
## Commands
### Sail (preferred locally)
- `cd apps/platform && ./vendor/bin/sail up -d`
- `cd apps/platform && ./vendor/bin/sail down`
- `cd apps/platform && ./vendor/bin/sail composer install`
- `cd apps/platform && ./vendor/bin/sail artisan migrate`
- `cd apps/platform && ./vendor/bin/sail artisan test`
- `cd apps/platform && ./vendor/bin/sail artisan` (general)
- Root helper for tooling only: `./scripts/platform-sail ...`
- `./vendor/bin/sail up -d`
- `./vendor/bin/sail down`
- `./vendor/bin/sail composer install`
- `./vendor/bin/sail artisan migrate`
- `./vendor/bin/sail artisan test`
- `./vendor/bin/sail artisan` (general)
### Drizzle (local DB tooling, if configured)
- Use only for local/dev workflows.
@ -174,10 +173,10 @@ ### Drizzle (local DB tooling, if configured)
(Agents should confirm the exact script names in `package.json` before suggesting them.)
### Non-Docker fallback (only if needed)
- `cd apps/platform && composer install`
- `cd apps/platform && php artisan serve`
- `cd apps/platform && php artisan migrate`
- `cd apps/platform && php artisan test`
- `composer install`
- `php artisan serve`
- `php artisan migrate`
- `php artisan test`
### Frontend/assets/tooling (if present)
- `pnpm install`
@ -191,11 +190,11 @@ ## Where to look first
- `.specify/`
- `AGENTS.md`
- `README.md`
- `apps/platform/app/`
- `apps/platform/database/`
- `apps/platform/routes/`
- `apps/platform/resources/`
- `apps/platform/config/`
- `app/`
- `database/`
- `routes/`
- `resources/`
- `config/`
---
@ -227,307 +226,20 @@ ## Reference Materials
===
<laravel-boost-guidelines>
=== .ai/filament-v5-blueprint rules ===
## Source of Truth
If any Filament behavior is uncertain, lookup the exact section in:
- docs/research/filament-v5-notes.md
and prefer that over guesses.
# SECTION B — FILAMENT V5 BLUEPRINT (EXECUTABLE RULES)
# Filament Blueprint (v5)
## 1) Non-negotiables
- Filament v5 requires Livewire v4.0+.
- Laravel 11+: register panel providers in `bootstrap/providers.php` (never `bootstrap/app.php`).
- Global search hard rule: If a Resource should appear in Global Search, it must have an Edit or View page; otherwise it will return no results.
- Destructive actions must execute via `Action::make(...)->action(...)` and include `->requiresConfirmation()` (no exceptions).
- Prefer render hooks + CSS hook classes over publishing Filament internal views.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/render-hooks
- https://filamentphp.com/docs/5.x/styling/css-hooks
## 2) Directory & naming conventions
- Default to Filament discovery conventions for Resources/Pages/Widgets unless you adopt modular architecture.
- Clusters: directory layout is recommended, not mandatory; functional behavior depends on `$cluster`.
Sources:
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/advanced/modular-architecture
## 3) Panel setup defaults
- Default to a single `/admin` panel unless multiple audiences/configs demand multiple panels.
- Verify provider registration (Laravel 11+: `bootstrap/providers.php`) when adding a panel.
- Use `path()` carefully; treat `path('')` as a high-risk change requiring route conflict review.
- Assets policy:
- Panel-only assets: register via panel config.
- Shared/plugin assets: register via `FilamentAsset::register()`.
- Deployment must include `cd apps/platform && php artisan filament:assets`.
Sources:
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/advanced/assets
## 4) Navigation & information architecture
- Use nav groups + sort order intentionally; apply conditional visibility for clarity, but enforce authorization separately.
- Use clusters to introduce hierarchy and sub-navigation when sidebar complexity grows.
- Treat cluster code structure as a recommendation (organizational benefit), not a required rule.
- User menu:
- Configure via `userMenuItems()` with Action objects.
- Never put destructive actions there without confirmation + authorization.
Sources:
- https://filamentphp.com/docs/5.x/navigation/overview
- https://filamentphp.com/docs/5.x/navigation/clusters
- https://filamentphp.com/docs/5.x/navigation/user-menu
## 5) Resource patterns
- Default to Resources for CRUD; use custom pages for non-CRUD tools/workflows.
- Global search:
- If a resource is intended for global search: ensure Edit/View page exists.
- Otherwise disable global search for that resource (dont “expect it to work”).
- If global search renders relationship-backed details: eager-load via global search query override.
- For very large datasets: consider disabling term splitting (only when needed).
Sources:
- https://filamentphp.com/docs/5.x/resources/overview
- https://filamentphp.com/docs/5.x/resources/global-search
## 6) Page lifecycle & query rules
- Treat relationship-backed rendering in aggregate contexts (global search details, list summaries) as requiring eager loading.
- Prefer render hooks for layout injection; avoid publishing internal views.
Sources:
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 7) Infolists vs RelationManagers (decision tree)
- Interactive CRUD / attach / detach under owner record → RelationManager.
- Pick existing related record(s) inside owner form → Select / CheckboxList relationship fields.
- Inline CRUD inside owner form → Repeater.
- Default performance stance: RelationManagers stay lazy-loaded unless explicit UX justification exists.
Sources:
- https://filamentphp.com/docs/5.x/resources/managing-relationships
- https://filamentphp.com/docs/5.x/infolists/overview
## 8) Form patterns (validation, reactivity, state)
- Default: minimize server-driven reactivity; only use it when schema/visibility/requirements must change server-side.
- Prefer “on blur” semantics for chatty inputs when using reactive behavior (per docs patterns).
- Custom field views must obey state binding modifiers.
Sources:
- https://filamentphp.com/docs/5.x/forms/overview
- https://filamentphp.com/docs/5.x/forms/custom-fields
## 9) Table & action patterns
- Tables: always define a meaningful empty state (and empty-state actions where appropriate).
- Actions:
- Execution actions use `->action(...)`.
- Destructive actions add `->requiresConfirmation()`.
- Navigation-only actions should use `->url(...)`.
- UNVERIFIED: do not assert modal/confirmation behavior for URL-only actions unless verified.
Sources:
- https://filamentphp.com/docs/5.x/tables/empty-state
- https://filamentphp.com/docs/5.x/actions/modals
## 10) Authorization & security
- Enforce panel access in non-local environments as documented.
- UI visibility is not security; enforce policies/access checks in addition to hiding UI.
- Bulk operations: explicitly decide between “Any” policy methods vs per-record authorization.
Sources:
- https://filamentphp.com/docs/5.x/users/overview
- https://filamentphp.com/docs/5.x/resources/deleting-records
## 11) Notifications & UX feedback
- Default to explicit success/error notifications for user-triggered mutations that arent instantly obvious.
- Treat polling as a cost; set intervals intentionally where polling is used.
Sources:
- https://filamentphp.com/docs/5.x/notifications/overview
- https://filamentphp.com/docs/5.x/widgets/stats-overview
## 12) Performance defaults
- Heavy assets: prefer on-demand loading (`loadedOnRequest()` + `x-load-css` / `x-load-js`) for heavy dependencies.
- Styling overrides use CSS hook classes; layout injection uses render hooks; avoid view publishing.
Sources:
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/styling/css-hooks
- https://filamentphp.com/docs/5.x/advanced/render-hooks
## 13) Testing requirements
- Test pages/relation managers/widgets as Livewire components.
- Test actions using Filaments action testing guidance.
- Do not mount non-Livewire classes in Livewire tests.
Sources:
- https://filamentphp.com/docs/5.x/testing/overview
- https://filamentphp.com/docs/5.x/testing/testing-actions
## 14) Forbidden patterns
- Mixing Filament v3/v4 APIs into v5 code.
- Any mention of Livewire v3 for Filament v5.
- Registering panel providers in `bootstrap/app.php` on Laravel 11+.
- Destructive actions without `->requiresConfirmation()`.
- Shipping heavy assets globally when on-demand loading fits.
- Publishing Filament internal views as a default customization technique.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/actions/modals
- https://filamentphp.com/docs/5.x/advanced/assets
## 15) Agent output contract
For any implementation request, the agent must explicitly state:
1) Livewire v4.0+ compliance.
2) Provider registration location (Laravel 11+: `bootstrap/providers.php`).
3) For each globally searchable resource: whether it has Edit/View page (or global search is disabled).
4) Which actions are destructive and how confirmation + authorization is handled.
5) Asset strategy: global vs on-demand and where `filament:assets` runs in deploy.
6) Testing plan: which pages/widgets/relation managers/actions are covered.
Sources:
- https://filamentphp.com/docs/5.x/upgrade-guide
- https://filamentphp.com/docs/5.x/panel-configuration
- https://filamentphp.com/docs/5.x/resources/global-search
- https://filamentphp.com/docs/5.x/advanced/assets
- https://filamentphp.com/docs/5.x/testing/testing-actions
=== .ai/filament-v5-checklist rules ===
# SECTION C — AI REVIEW CHECKLIST (STRICT CHECKBOXES)
## Version Safety
- [ ] Filament v5 explicitly targets Livewire v4.0+ (no Livewire v3 references anywhere).
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “Upgrading Livewire”
- [ ] All references are Filament `/docs/5.x/` only (no v3/v4 docs, no legacy APIs).
- [ ] Upgrade assumptions match the v5 upgrade guide requirements and steps.
- Source: https://filamentphp.com/docs/5.x/upgrade-guide — “New requirements”
## Panel & Navigation
- [ ] Laravel 11+: panel providers are registered in `bootstrap/providers.php` (not `bootstrap/app.php`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Creating a new panel”
- [ ] Panel `path()` choices are intentional and do not conflict with existing routes (especially `path('')`).
- Source: https://filamentphp.com/docs/5.x/panel-configuration — “Changing the path”
- [ ] Cluster usage is correctly configured (discovery + `$cluster` assignments).
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Creating a cluster”
- [ ] Cluster semantics (sub-navigation + grouped navigation behavior) are understood and verified against the clusters docs.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Introduction”
- [ ] Cluster directory structure is treated as recommended, not mandatory.
- Source: https://filamentphp.com/docs/5.x/navigation/clusters — “Code structure recommendations for panels using clusters”
- [ ] User menu items are registered via `userMenuItems()` and permission-gated where needed.
- Source: https://filamentphp.com/docs/5.x/navigation/user-menu — “Introduction”
## Resource Structure
- [ ] `$recordTitleAttribute` is set for any resource intended for global search.
- Source: https://filamentphp.com/docs/5.x/resources/overview — “Record titles”
- [ ] Hard rule enforced: every globally searchable resource has an Edit or View page; otherwise global search is disabled for it.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Setting global search result titles”
- [ ] Relationship-backed global search details are eager-loaded via the global search query override.
- Source: https://filamentphp.com/docs/5.x/resources/global-search — “Adding extra details to global search results”
## Infolists & Relations
- [ ] Each relationship uses the correct tool (RelationManager vs Select/CheckboxList vs Repeater) based on required interaction.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Choosing the right tool for the job”
- [ ] RelationManagers remain lazy-loaded by default unless theres an explicit UX justification.
- Source: https://filamentphp.com/docs/5.x/resources/managing-relationships — “Disabling lazy loading”
## Forms
- [ ] Server-driven reactivity is minimal; chatty inputs do not trigger network requests unnecessarily.
- Source: https://filamentphp.com/docs/5.x/forms/overview — “Reactive fields on blur”
- [ ] Custom field views obey state binding modifiers (no hardcoded `wire:model` without modifiers).
- Source: https://filamentphp.com/docs/5.x/forms/custom-fields — “Obeying state binding modifiers”
## Tables & Actions
- [ ] Tables define a meaningful empty state (and empty-state actions where appropriate).
- Source: https://filamentphp.com/docs/5.x/tables/empty-state — “Adding empty state actions”
- [ ] All destructive actions execute via `->action(...)` and include `->requiresConfirmation()`.
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
- [ ] No checklist rule assumes confirmation/modals for `->url(...)` actions unless verified in docs (UNVERIFIED behavior must not be asserted as fact).
- Source: https://filamentphp.com/docs/5.x/actions/modals — “Confirmation modals”
## Authorization & Security
- [ ] Panel access is enforced for non-local environments as documented.
- Source: https://filamentphp.com/docs/5.x/users/overview — “Authorizing access to the panel”
- [ ] UI visibility is not treated as authorization; policies/access checks still enforce boundaries.
- [ ] Bulk operations intentionally choose between “Any” policy methods vs per-record authorization where required.
- Source: https://filamentphp.com/docs/5.x/resources/deleting-records — “Authorization”
## UX & Notifications
- [ ] User-triggered mutations provide explicit success/error notifications when outcomes arent instantly obvious.
- Source: https://filamentphp.com/docs/5.x/notifications/overview — “Introduction”
- [ ] Polling (widgets/notifications) is configured intentionally (interval set or disabled) to control load.
- Source: https://filamentphp.com/docs/5.x/widgets/stats-overview — “Live updating stats (polling)”
## Performance
- [ ] Heavy frontend assets are loaded on-demand using `loadedOnRequest()` + `x-load-css` / `x-load-js` where appropriate.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “Lazy loading CSS” / “Lazy loading JavaScript”
- [ ] Styling overrides use CSS hook classes discovered via DevTools (no brittle selectors by default).
- Source: https://filamentphp.com/docs/5.x/styling/css-hooks — “Discovering hook classes”
## Testing
- [ ] Livewire tests mount Filament pages/relation managers/widgets (Livewire components), not static resource classes.
- Source: https://filamentphp.com/docs/5.x/testing/overview — “What is a Livewire component when using Filament?”
- [ ] Actions that mutate data are covered using Filaments action testing guidance.
- Source: https://filamentphp.com/docs/5.x/testing/testing-actions — “Testing actions”
## Deployment / Ops
- [ ] `cd apps/platform && php artisan filament:assets` is included in the deployment process when using registered assets.
- Source: https://filamentphp.com/docs/5.x/advanced/assets — “The FilamentAsset facade”
=== foundation rules ===
# Laravel Boost Guidelines
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications.
## Foundational Context
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
- php - 8.4.15
- filament/filament (FILAMENT) - v5
- filament/filament (FILAMENT) - v4
- laravel/framework (LARAVEL) - v12
- laravel/prompts (PROMPTS) - v0
- laravel/socialite (SOCIALITE) - v5
- livewire/livewire (LIVEWIRE) - v4
- livewire/livewire (LIVEWIRE) - v3
- laravel/mcp (MCP) - v0
- laravel/pint (PINT) - v1
- laravel/sail (SAIL) - v1
@ -535,251 +247,425 @@ ## Foundational Context
- phpunit/phpunit (PHPUNIT) - v12
- tailwindcss (TAILWINDCSS) - v4
## Skills Activation
This project has domain-specific skills available. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.
- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes.
## Conventions
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, naming.
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
- Check for existing components to reuse before writing a new one.
## Verification Scripts
- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.
- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important.
## Application Structure & Architecture
- Stick to existing directory structure; don't create new base folders without approval.
- Stick to existing directory structure - don't create new base folders without approval.
- Do not change the application's dependencies without approval.
## Frontend Bundling
- Repo-root JavaScript orchestration now uses `corepack pnpm install`, `corepack pnpm dev:platform`, `corepack pnpm dev:website`, `corepack pnpm dev`, `corepack pnpm build:website`, and `corepack pnpm build:platform`.
- `corepack pnpm dev:platform` starts the platform Sail stack and the Laravel panel Vite watcher. `corepack pnpm dev` starts that platform watcher plus the website dev server.
- `apps/website` is a standalone Astro app, not a second Laravel runtime, so Boost MCP remains platform-only.
- If the user doesn't see a platform frontend change reflected in the UI, it could mean they need to run `cd apps/platform && ./vendor/bin/sail pnpm build`, `cd apps/platform && ./vendor/bin/sail pnpm dev`, or `cd apps/platform && ./vendor/bin/sail composer run dev`. Ask them.
## Documentation Files
- You must only create documentation files if explicitly requested by the user.
- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.
## Replies
- Be concise in your explanations - focus on what's important rather than explaining obvious details.
## Documentation Files
- You must only create documentation files if explicitly requested by the user.
=== boost rules ===
# Laravel Boost
## Laravel Boost
- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them.
## Artisan
- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters.
- Use the `list-artisan-commands` tool when you need to call an Artisan command to double check the available parameters.
## URLs
- Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port.
- Whenever you share a project URL with the user you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain / IP, and port.
## Tinker / Debugging
- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
- Use the `database-query` tool when you only need to read from the database.
- Use the `database-schema` tool to inspect table structure before writing migrations or models.
## Reading Browser Logs With the `browser-logs` Tool
- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost.
- Only recent browser logs will be useful - ignore old logs.
## Searching Documentation (Critically Important)
- Boost comes with a powerful `search-docs` tool you should use before trying other approaches when working with Laravel or Laravel ecosystem packages. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
- Boost comes with a powerful `search-docs` tool you should use before any other approaches. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation specific for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages.
- The 'search-docs' tool is perfect for all Laravel related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc.
- You must use this tool to search for Laravel-ecosystem documentation before falling back to other approaches.
- Search the documentation before making code changes to ensure we are taking the correct approach.
- Use multiple, broad, simple, topic-based queries at once. For example: `['rate limiting', 'routing rate limiting', 'routing']`. The most relevant results will be returned first.
- Do not add package names to queries; package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
- Use multiple, broad, simple, topic based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`.
- Do not add package names to queries - package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`.
### Available Search Syntax
- You can and should pass multiple queries at once. The most relevant results will be returned first.
1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'
2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit"
3. Quoted Phrases (Exact Position) - query="infinite scroll" - Words must be adjacent and in that order
4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit"
5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms
1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'.
2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit".
3. Quoted Phrases (Exact Position) - query="infinite scroll" - words must be adjacent and in that order.
4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit".
5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms.
=== php rules ===
# PHP
## PHP
- Always use curly braces for control structures, even for single-line bodies.
## Constructors
- Always use curly braces for control structures, even if it has one line.
### Constructors
- Use PHP 8 constructor property promotion in `__construct()`.
- `public function __construct(public GitHub $github) { }`
- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private.
## Type Declarations
- <code-snippet>public function __construct(public GitHub $github) { }</code-snippet>
- Do not allow empty `__construct()` methods with zero parameters.
### Type Declarations
- Always use explicit return type declarations for methods and functions.
- Use appropriate PHP type hints for method parameters.
<!-- Explicit Return Types and Method Params -->
```php
<code-snippet name="Explicit Return Types and Method Params" lang="php">
protected function isAccessible(User $user, ?string $path = null): bool
{
...
}
```
## Enums
- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
</code-snippet>
## Comments
- Prefer PHPDoc blocks over inline comments. Never use comments within the code itself unless the logic is exceptionally complex.
- Prefer PHPDoc blocks over comments. Never use comments within the code itself unless there is something _very_ complex going on.
## PHPDoc Blocks
- Add useful array shape type definitions for arrays when appropriate.
- Add useful array shape type definitions when appropriate.
## Enums
- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`.
=== sail rules ===
# Laravel Sail
- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail.
- Start services using `cd apps/platform && ./vendor/bin/sail up -d` and stop them with `cd apps/platform && ./vendor/bin/sail stop`.
- Open the application in the browser by running `cd apps/platform && ./vendor/bin/sail open`.
- Always prefix PHP, Artisan, Composer, and Node commands with `cd apps/platform && ./vendor/bin/sail`. Examples:
- Run Artisan Commands: `cd apps/platform && ./vendor/bin/sail artisan migrate`
- Install Composer packages: `cd apps/platform && ./vendor/bin/sail composer install`
- Execute Node commands: `cd apps/platform && ./vendor/bin/sail pnpm dev`
- Execute PHP scripts: `cd apps/platform && ./vendor/bin/sail php [script]`
- View all available Sail commands by running `cd apps/platform && ./vendor/bin/sail` without arguments.
=== tests rules ===
# Test Enforcement
## Test Enforcement
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
- Run the minimum number of tests needed to ensure code quality and speed. Use `cd apps/platform && ./vendor/bin/sail artisan test --compact` with a specific filename or filter.
- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test` with a specific filename or filter.
=== laravel/core rules ===
# Do Things the Laravel Way
## Do Things the Laravel Way
- Use `cd apps/platform && ./vendor/bin/sail artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
- If you're creating a generic PHP class, use `cd apps/platform && ./vendor/bin/sail artisan make:class`.
- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool.
- If you're creating a generic PHP class, use `php artisan make:class`.
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
## Database
### Database
- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins.
- Use Eloquent models and relationships before suggesting raw database queries.
- Use Eloquent models and relationships before suggesting raw database queries
- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them.
- Generate code that prevents N+1 query problems by using eager loading.
- Use Laravel's query builder for very complex database operations.
### Model Creation
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `cd apps/platform && ./vendor/bin/sail artisan make:model`.
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`.
### APIs & Eloquent Resources
- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
## Controllers & Validation
### Controllers & Validation
- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages.
- Check sibling Form Requests to see if the application uses array or string based validation rules.
## Authentication & Authorization
- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
## URL Generation
- When generating links to other pages, prefer named routes and the `route()` function.
## Queues
### Queues
- Use queued jobs for time-consuming operations with the `ShouldQueue` interface.
## Configuration
### Authentication & Authorization
- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.).
### URL Generation
- When generating links to other pages, prefer named routes and the `route()` function.
### Configuration
- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`.
## Testing
### Testing
- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
- When creating tests, make use of `cd apps/platform && ./vendor/bin/sail artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
## Vite Error
### Vite Error
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `cd apps/platform && ./vendor/bin/sail pnpm build` or ask the user to run `cd apps/platform && ./vendor/bin/sail pnpm dev` or `cd apps/platform && ./vendor/bin/sail composer run dev`.
=== laravel/v12 rules ===
# Laravel 12
## Laravel 12
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
- Use the `search-docs` tool to get version specific documentation.
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.
## Laravel 12 Structure
- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`.
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
### Laravel 12 Structure
- No middleware files in `app/Http/Middleware/`.
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
- `bootstrap/providers.php` contains application specific service providers.
- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration.
## Database
- **No app\Console\Kernel.php** - use `bootstrap/app.php` or `routes/console.php` for console configuration.
- **Commands auto-register** - files in `app/Console/Commands/` are automatically available and do not require manual registration.
### Database
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
- Laravel 11 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
### Models
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
=== livewire/core rules ===
## Livewire Core
- Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests.
- Use the `php artisan make:livewire [Posts\CreatePost]` artisan command to create new components
- State should live on the server, with the UI reflecting it.
- All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions.
## Livewire Best Practices
- Livewire components require a single root element.
- Use `wire:loading` and `wire:dirty` for delightful loading states.
- Add `wire:key` in loops:
```blade
@foreach ($items as $item)
<div wire:key="item-{{ $item->id }}">
{{ $item->name }}
</div>
@endforeach
```
- Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects:
<code-snippet name="Lifecycle hook examples" lang="php">
public function mount(User $user) { $this->user = $user; }
public function updatedSearch() { $this->resetPage(); }
</code-snippet>
## Testing Livewire
<code-snippet name="Example Livewire component test" lang="php">
Livewire::test(Counter::class)
->assertSet('count', 0)
->call('increment')
->assertSet('count', 1)
->assertSee(1)
->assertStatus(200);
</code-snippet>
<code-snippet name="Testing a Livewire component exists within a page" lang="php">
$this->get('/posts/create')
->assertSeeLivewire(CreatePost::class);
</code-snippet>
=== livewire/v3 rules ===
## Livewire 3
### Key Changes From Livewire 2
- These things changed in Livewire 2, but may not have been updated in this application. Verify this application's setup to ensure you conform with application conventions.
- Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default.
- Components now use the `App\Livewire` namespace (not `App\Http\Livewire`).
- Use `$this->dispatch()` to dispatch events (not `emit` or `dispatchBrowserEvent`).
- Use the `components.layouts.app` view as the typical layout path (not `layouts.app`).
### New Directives
- `wire:show`, `wire:transition`, `wire:cloak`, `wire:offline`, `wire:target` are available for use. Use the documentation to find usage examples.
### Alpine
- Alpine is now included with Livewire, don't manually include Alpine.js.
- Plugins included with Alpine: persist, intersect, collapse, and focus.
### Lifecycle Hooks
- You can listen for `livewire:init` to hook into Livewire initialization, and `fail.status === 419` for the page expiring:
<code-snippet name="livewire:load example" lang="js">
document.addEventListener('livewire:init', function () {
Livewire.hook('request', ({ fail }) => {
if (fail && fail.status === 419) {
alert('Your session expired');
}
});
Livewire.hook('message.failed', (message, component) => {
console.error(message);
});
});
</code-snippet>
=== pint/core rules ===
# Laravel Pint Code Formatter
## Laravel Pint Code Formatter
- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues.
- You must run `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `cd apps/platform && ./vendor/bin/sail bin pint --test --format agent`, simply run `cd apps/platform && ./vendor/bin/sail bin pint --format agent` to fix any formatting issues.
=== pest/core rules ===
## Pest
### Testing
- If you need to verify a feature is working, write or update a Unit / Feature test.
### Pest Tests
- All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application.
- Tests should test all of the happy paths, failure paths, and weird paths.
- Tests live in the `tests/Feature` and `tests/Unit` directories.
- Pest tests look and behave like this:
<code-snippet name="Basic Pest Test Example" lang="php">
it('is true', function () {
expect(true)->toBeTrue();
});
</code-snippet>
### Running Tests
- Run the minimal number of tests using an appropriate filter before finalizing code edits.
- To run all tests: `php artisan test`.
- To run all tests in a file: `php artisan test tests/Feature/ExampleTest.php`.
- To filter on a particular test name: `php artisan test --filter=testName` (recommended after making a change to a related file).
- When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing.
### Pest Assertions
- When asserting status codes on a response, use the specific method like `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or similar, e.g.:
<code-snippet name="Pest Example Asserting postJson Response" lang="php">
it('returns all', function () {
$response = $this->postJson('/api/docs', []);
$response->assertSuccessful();
});
</code-snippet>
### Mocking
- Mocking can be very helpful when appropriate.
- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always import it via `use function Pest\Laravel\mock;` before using it. Alternatively, you can use `$this->mock()` if existing tests do.
- You can also create partial mocks using the same import or self method.
### Datasets
- Use datasets in Pest to simplify tests which have a lot of duplicated data. This is often the case when testing validation rules, so consider going with this solution when writing tests for validation rules.
<code-snippet name="Pest Dataset Example" lang="php">
it('has emails', function (string $email) {
expect($email)->not->toBeEmpty();
})->with([
'james' => 'james@laravel.com',
'taylor' => 'taylor@laravel.com',
]);
</code-snippet>
=== pest/v4 rules ===
## Pest 4
- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
- Browser testing is incredibly powerful and useful for this project.
- Browser tests should live in `tests/Browser/`.
- Use the `search-docs` tool for detailed guidance on utilizing these features.
### Browser Testing
- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest v4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test.
- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
- Switch color schemes (light/dark mode) when appropriate.
- Take screenshots or pause tests for debugging when appropriate.
### Example Tests
<code-snippet name="Pest Browser Test Example" lang="php">
it('may reset the password', function () {
Notification::fake();
$this->actingAs(User::factory()->create());
$page = visit('/sign-in'); // Visit on a real browser...
$page->assertSee('Sign In')
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
->click('Forgot Password?')
->fill('email', 'nuno@laravel.com')
->click('Send Reset Link')
->assertSee('We have emailed your password reset link!')
Notification::assertSent(ResetPassword::class);
});
</code-snippet>
<code-snippet name="Pest Smoke Testing Example" lang="php">
$pages = visit(['/', '/about', '/contact']);
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
</code-snippet>
- This project uses Pest for testing. Create tests: `cd apps/platform && ./vendor/bin/sail artisan make:test --pest {name}`.
- Run tests: `cd apps/platform && ./vendor/bin/sail artisan test --compact` or filter: `cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=testName`.
- Do NOT delete tests without approval.
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Pest documentation and updated code examples.
- IMPORTANT: Activate `pest-testing` every time you're working with a Pest or testing-related task.
=== tailwindcss/core rules ===
# Tailwind CSS
## Tailwind Core
- Always use existing Tailwind conventions; check project patterns before adding new ones.
- IMPORTANT: Always use `search-docs` tool for version-specific Tailwind CSS documentation and updated code examples. Never rely on training data.
- IMPORTANT: Activate `tailwindcss-development` every time you're working with a Tailwind CSS or styling-related task.
- Use Tailwind CSS classes to style HTML, check and use existing tailwind conventions within the project before writing your own.
- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc..)
- Think through class placement, order, priority, and defaults - remove redundant classes, add classes to parent or child carefully to limit repetition, group elements logically
- You can use the `search-docs` tool to get exact examples from the official documentation when needed.
### Spacing
- When listing items, use gap utilities for spacing, don't use margins.
<code-snippet name="Valid Flex Gap Spacing Example" lang="html">
<div class="flex gap-8">
<div>Superior</div>
<div>Michigan</div>
<div>Erie</div>
</div>
</code-snippet>
### Dark Mode
- If existing pages and components support dark mode, new pages and components must support dark mode in a similar way, typically using `dark:`.
=== tailwindcss/v4 rules ===
## Tailwind 4
- Always use Tailwind CSS v4 - do not use the deprecated utilities.
- `corePlugins` is not supported in Tailwind v4.
- In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed.
<code-snippet name="Extending Theme in CSS" lang="css">
@theme {
--color-brand: oklch(0.72 0.11 178);
}
</code-snippet>
- In Tailwind v4, you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3:
<code-snippet name="Tailwind v4 Import Tailwind Diff" lang="diff">
- @tailwind base;
- @tailwind components;
- @tailwind utilities;
+ @import "tailwindcss";
</code-snippet>
### Replaced Utilities
- Tailwind v4 removed deprecated utilities. Do not use the deprecated option - use the replacement.
- Opacity values are still numeric.
| Deprecated | Replacement |
|------------+--------------|
| bg-opacity-* | bg-black/* |
| text-opacity-* | text-black/* |
| border-opacity-* | border-black/* |
| divide-opacity-* | divide-black/* |
| ring-opacity-* | ring-black/* |
| placeholder-opacity-* | placeholder-black/* |
| flex-shrink-* | shrink-* |
| flex-grow-* | grow-* |
| overflow-ellipsis | text-ellipsis |
| decoration-slice | box-decoration-slice |
| decoration-clone | box-decoration-clone |
</laravel-boost-guidelines>
## Recent Changes
- 065-tenant-rbac-v1: Added PHP 8.4+ + Laravel 12, Filament 5, Livewire 4, Pest 4
- 064-auth-structure: Added PHP 8.4 + Laravel 12, Filament v5, Livewire v4
- 063-entra-signin: Added PHP 8.4 + `laravel/framework:^12`, `livewire/livewire:^4`, `filament/filament:^5`, `laravel/socialite:^5.0`
## Active Technologies
- PHP 8.4+ + Laravel 12, Filament 5, Livewire 4, Pest 4 (065-tenant-rbac-v1)

257
README.md
View File

@ -1,168 +1,19 @@
# TenantPilot Workspace
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
TenantPilot is an Intune management platform built around a stable Laravel application in
`apps/platform` and, starting with Spec 183, a standalone public Astro website in
`apps/website`. The repository root is now the official JavaScript workspace entry point and
orchestrates app-local commands without becoming a runtime itself.
<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## Multi-App Topology
- `apps/platform`: the Laravel 12 + Filament v5 + Livewire v4 product runtime
- `apps/website`: the Astro v6 public website runtime
- repo root: workspace manifests, documentation, scripts, editor tooling, and `docker-compose.yml`
- `./scripts/platform-sail`: platform-only compatibility helper for tooling that cannot set `cwd`
Website-track guardrails for independent evolution live in
[`docs/strategy/website-working-contract.md`](docs/strategy/website-working-contract.md).
## Official Root Commands
- Install workspace-managed JavaScript dependencies: `corepack pnpm install`
- Start the platform stack and Laravel panel Vite watcher: `corepack pnpm dev:platform`
- Start the website dev server: `corepack pnpm dev:website`
- Start platform Vite + website together: `corepack pnpm dev`
- Build the website: `corepack pnpm build:website`
- Build platform frontend assets inside Sail: `corepack pnpm build:platform`
## App-Local Commands
### Platform
- Install PHP dependencies: `cd apps/platform && composer install`
- Start Sail: `cd apps/platform && ./vendor/bin/sail up -d`
- Generate the app key: `cd apps/platform && ./vendor/bin/sail artisan key:generate`
- Run migrations and seeders: `cd apps/platform && ./vendor/bin/sail artisan migrate --seed`
- Run frontend watch/build inside Sail: `corepack pnpm dev:platform`, `cd apps/platform && ./vendor/bin/sail pnpm dev`, or `cd apps/platform && ./vendor/bin/sail pnpm build`
- Run tests: `cd apps/platform && ./vendor/bin/sail artisan test --compact`
### Website
- Start the dev server: `cd apps/website && pnpm dev`
- Build the static site: `cd apps/website && pnpm build`
## Test Suite Governance
### Canonical Lane Commands
- Preferred repo-root wrappers:
- `./scripts/platform-test-lane fast-feedback`
- `./scripts/platform-test-lane confidence`
- `./scripts/platform-test-lane heavy-governance`
- `./scripts/platform-test-lane browser`
- `./scripts/platform-test-lane profiling`
- `./scripts/platform-test-lane junit`
- Regenerate the latest report artifacts without re-running the lane:
- `./scripts/platform-test-report fast-feedback`
- `./scripts/platform-test-report confidence`
- `./scripts/platform-test-report heavy-governance`
- `./scripts/platform-test-report browser`
- `./scripts/platform-test-report profiling`
- `./scripts/platform-test-report junit`
- Trend-aware report refresh options:
- `--history-file=/absolute/path/to/<lane>-latest.trend-history.json` seeds one prior comparable window explicitly.
- `--history-bundle=/absolute/path/to/bundle-or-zip` hydrates the newest matching `trend-history.json` from a staged artifact bundle.
- `--fetch-latest-history` asks the wrapper to download the most recent comparable bundle from Gitea when `TENANTATLAS_GITEA_TOKEN` or `GITEA_TOKEN` is available.
- `--skip-latest-history` keeps the run intentionally cold-start so the summary reports `unstable` instead of guessing at trend state.
- App-local equivalents remain available through Sail Composer scripts:
- `cd apps/platform && ./vendor/bin/sail composer run test`
- `cd apps/platform && ./vendor/bin/sail composer run test:confidence`
- `cd apps/platform && ./vendor/bin/sail composer run test:heavy`
- `cd apps/platform && ./vendor/bin/sail composer run test:browser`
- `cd apps/platform && ./vendor/bin/sail composer run test:profile`
- `cd apps/platform && ./vendor/bin/sail composer run test:junit`
- The root wrapper is the safer default for long lanes because it pins Composer to `--timeout=0`.
### Trend Summary Reading
- `healthy`: enough comparable samples exist, the lane is comfortably under budget, and recent variance stays inside the documented noise floor.
- `budget-near`: the lane is still within budget, but headroom has entered the lane's near-budget band and needs attention before it becomes a repeated blocker.
- `trending-worse`: multiple comparable samples are worsening above the lane variance floor even though the lane is not yet clearly over budget.
- `regressed`: the lane is over budget or repeatedly worsening enough that ordinary noise is no longer a credible explanation.
- `unstable`: the report intentionally refuses a stronger label because history is too short, the comparison fingerprint changed, or the recent window is noisy.
- Recalibration is separate from health. Reports can emit candidate, approved, or rejected baseline or budget decisions, but repository truth never moves automatically.
- Hotspot evidence may be unavailable on a given cycle. When that happens the summary must say so explicitly, and `profiling` or `junit` remain the preferred support-lane follow-up paths.
### Workflow Expectation
- Every runtime-changing or test-affecting spec, plan, and task set MUST record actual test-purpose classification, target validation lane(s), fixture-cost risks, any heavy-governance or browser expansion, any heavy-family visibility change, and any budget/baseline/trend follow-up.
- Test classification follows the real proving purpose of the change, not the filename or folder.
- Minimal fixtures and minimal infrastructure are the default; database, Livewire, Filament, provider, workspace, membership, or session-heavy setup must stay explicit and opt-in.
- Review treats wrong lane fit, hidden default cost, accidental heavy-family growth, or undocumented runtime drift as merge issues, not later cleanup.
- Routine lane recalibration belongs inside the affecting feature spec or PR; open a dedicated follow-up spec only when recurring pain or structural lane changes justify it.
### Authoring And Review Guardrails
- Start with the smallest honest surface: `Unit` for isolated logic, `Feature` for HTTP, Livewire, Filament, jobs, or non-browser integration, `heavy-governance` for intentionally expensive governance scans, and `Browser` only for end-to-end workflow coverage.
- Specs and plans must state the affected lanes or a deliberate `N/A`, the family impact, the setup-cost impact, and the narrowest reviewer command.
- If database, Livewire, Filament, provider setup, workspace or membership context, session state, capability context, or browser coverage is required, say why a narrower proof is insufficient.
- Keep shared helpers, factories, seeds, fixtures, and defaults cheap by default. Full-context setup should stay behind explicit opt-ins instead of becoming the default path.
- Extend an existing heavy or browser family only when the behavior truly matches it. New heavy families, new browser scope, revived expensive defaults, or material lane-cost shifts require explicit escalation.
- Low-impact docs-only or template-only work may answer the governance prompts with `N/A` or `none`; do not invent runtime impact where none exists.
- Review should end with one explicit outcome: `keep`, `split`, `document-in-feature`, `follow-up-spec`, or `reject-or-split`.
- Use `document-in-feature` for contained drift or cost that belongs in the active feature. Use `follow-up-spec` for recurring pain or structural lane-model changes. Use `reject-or-split` when hidden cost is still unresolved.
### CI Trigger Matrix
- Pull requests (`opened`, `reopened`, `synchronize`) run only `./scripts/platform-test-lane fast-feedback` through `.gitea/workflows/test-pr-fast-feedback.yml` and block on test, wrapper, artifact, and mature Fast Feedback budget failures.
- Pushes to `dev` run only `./scripts/platform-test-lane confidence` through `.gitea/workflows/test-main-confidence.yml`; test and artifact failures block, while budget drift remains warning-first.
- Heavy Governance runs live in `.gitea/workflows/test-heavy-governance.yml` and stay isolated to manual plus scheduled triggers. The schedule remains gated behind `TENANTATLAS_ENABLE_HEAVY_GOVERNANCE_SCHEDULE=1` until the first successful manual validation.
- Browser runs live in `.gitea/workflows/test-browser.yml` and stay isolated to manual plus scheduled triggers. The schedule remains gated behind `TENANTATLAS_ENABLE_BROWSER_SCHEDULE=1` until the first successful manual validation.
- Fast Feedback uses a documented CI variance allowance of `15s` before budget overrun becomes blocking. Confidence uses `30s`, Browser uses `20s`, and Heavy Governance keeps warning-first or trend-only handling while its CI baseline stabilizes.
### CI Artifact Bundles
- Lane-local artifacts are still generated in `apps/platform/storage/logs/test-lanes` as `*-latest.*` files.
- CI workflows stage deterministic upload bundles through `./scripts/platform-test-artifacts` into `.gitea-artifacts/<workflow-profile>` before upload.
- Every governed CI lane now publishes `summary.md`, `budget.json`, `report.json`, `junit.xml`, and `trend-history.json`. `profiling` may additionally publish `profile.txt`.
- The report refresh step hydrates the most recent comparable `trend-history.json` before regenerating the current summary when CI credentials allow it, then republishes the refreshed bounded history for the next run.
- Artifact publication failures are first-class blocking failures for pull request and `dev` workflows.
### Recorded Baselines
| Scope | Wall clock | Budget | Notes |
|-------|------------|--------|-------|
| Full suite baseline | `2624.60s` | reference only | Current broad-suite measurement used as the budget anchor |
| `fast-feedback` | `176.74s` | `200s` | More than 50% below the current full-suite baseline |
| `confidence` | `394.38s` | `450s` | Broader non-browser pre-merge lane |
| `heavy-governance` | `83.66s` | `120s` | Seed heavy family lane for architecture, deprecation, ops UX, and action-surface scans |
| `browser` | `128.87s` | `150s` | Dedicated browser smoke and workflow lane |
| `junit` | `380.14s` | `450s` | Parallel machine-readable report lane for the confidence scope |
| `profiling` | `2701.51s` | `3000s` | Serial slow-test drift lane with profile output |
Artifacts are written under `apps/platform/storage/logs/test-lanes` and kept out of git except for the checked-in skeleton `.gitignore`.
### Honest Taxonomy Rules
- `Unit`: isolated logic, helpers, and low-cost domain behavior.
- `Feature`: HTTP, Livewire, Filament, jobs, and non-browser integration slices.
- `Browser`: only end-to-end browser smoke and workflow coverage under `tests/Browser`.
- `heavy-governance`: intentionally expensive architecture, deprecation, ops UX, and wide contract scans. The first seeded batch is `tests/Architecture`, `tests/Deprecation`, `tests/Feature/078`, `tests/Feature/090`, `tests/Feature/144`, `tests/Feature/OpsUx`, `tests/Feature/Filament/Alerts/AlertsKpiHeaderTest.php`, `tests/Feature/Guards/ActionSurfaceContractTest.php`, `tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php`, and `tests/Feature/ProviderConnections/CredentialLeakGuardTest.php`.
### Fixture Cost Guidance
- `createUserWithTenant()` now defaults to the explicit cheap `minimal` profile.
- Use `createMinimalUserWithTenant()` in high-usage callers that only need tenant membership and workspace/session wiring.
- Use `createStandardUserWithTenant()` or `fixtureProfile: 'standard'` when a test needs a default Microsoft provider connection without credentials, cache resets, or UI context.
- Use `createFullUserWithTenant()` or `fixtureProfile: 'full'` when a test intentionally needs provider, credential, cache-reset, and UI-context side effects together.
- Use `OperationRun::factory()->minimal()` for system-style runs and `OperationRun::factory()->withUser($user)` only when the initiator identity is materially part of the assertion.
- Use `BackupSet::factory()->full()` only when the test really needs backup items; the default backup-set factory path now stays item-free.
- `provider-enabled`, `credential-enabled`, `ui-context`, and `heavy` remain available only as temporary transition aliases while the first migration packs are landing.
### DB Reset and Seed Rules
- Default lanes use SQLite `:memory:` with `RefreshDatabase` as the reset strategy.
- The isolated PostgreSQL coverage remains the `Pgsql` suite and is reserved for schema or foreign-key assertions.
- Keep seeds out of default lanes. Opt into seeded fixtures only inside the test that needs business-truth seed data.
- Schema-baseline or dump-based acceleration remains a follow-up investigation, not a default requirement for the current lane model.
## Port Overrides
- Platform HTTP and Vite ports: set `APP_PORT` and or `VITE_PORT` before `corepack pnpm dev:platform` or `cd apps/platform && ./vendor/bin/sail up -d`
- Website dev server port: set `WEBSITE_PORT` before `corepack pnpm dev:website` or pass `--port <port>` to `cd apps/website && pnpm dev`
- Parallel local development keeps both apps isolated, even when one or both ports are overridden
## Platform Setup Notes
## TenantPilot setup
- Local dev (Sail-first):
- Start stack: `./vendor/bin/sail up -d`
- Init DB: `./vendor/bin/sail artisan migrate --seed`
- Tests: `./vendor/bin/sail artisan test`
- Policy sync: `./vendor/bin/sail artisan intune:sync-policies`
- Filament admin: `/admin` (seed user `test@example.com`, set password via factory or `artisan tinker`).
- Microsoft Graph (Intune) env vars:
- `GRAPH_TENANT_ID`
@ -174,45 +25,16 @@ ## Platform Setup Notes
- **Missing permissions?** Scope tags will show as "Unknown (ID: X)" - add `DeviceManagementRBAC.Read.All`
- Deployment (Dokploy, staging → production):
- Containerized deploy; ensure Postgres + Redis are provisioned (see `docker-compose.yml` for local baseline).
- Run application commands from `apps/platform`, including `php artisan filament:assets`.
- Run migrations on staging first, validate backup/restore flows, then promote to production.
- Ensure queue workers are running for jobs (e.g., policy sync) after deploy.
- Keep secrets/env in Dokploy, never in code.
## Platform relocation rollout notes
- Open branches that still touch legacy root app paths should merge `dev` first, then remap file moves from `app/`, `bootstrap/`, `config/`, `database/`, `lang/`, `public/`, `resources/`, `routes/`, `storage/`, and `tests/` into `apps/platform/...`.
- Keep using merge-based catch-up on shared feature branches; do not rebase long-lived shared branches just to absorb the relocation.
- VS Code tasks expose the official root workspace commands, while MCP launchers remain platform-only and delegate through `./scripts/platform-sail`.
## Bulk operations (Feature 005)
- Bulk actions are available in Filament resource tables (Policies, Policy Versions, Backup Sets, Restore Runs).
- Destructive operations require type-to-confirm at higher thresholds (e.g. `DELETE`).
- Long-running bulk ops are queued; the bottom-right progress widget polls for active runs.
### Troubleshooting
- **Progress stuck on “Queued…”** usually means the queue worker is not running (or not processing the queue you expect).
- Prefer using the Sail/Docker worker (see `docker-compose.yml`) rather than starting an additional local `php artisan queue:work`.
- Check worker status/logs: `cd apps/platform && ./vendor/bin/sail ps` and `cd apps/platform && ./vendor/bin/sail logs -f queue`.
- **Exit code 137** for `queue:work` typically means the process was killed (often OOM). Increase Docker memory/limits or run the worker inside the container.
- **Moved app but old commands still fail** usually means the command is still being run from repo root. Switch to `cd apps/platform && ...` or use `./scripts/platform-sail ...` only for tooling that cannot set `cwd`.
## Rollback checklist
1. Revert the relocation commit or merge on your feature branch instead of hard-resetting shared history.
2. Preserve any local app env overrides before switching commits: `cp apps/platform/.env /tmp/tenantatlas.platform.env.backup` if needed.
3. Stop local containers and clean generated artifacts: `cd apps/platform && ./vendor/bin/sail down -v`, then remove `apps/platform/vendor`, `apps/platform/node_modules`, `apps/platform/public/build`, and `apps/platform/public/hot` if they need a clean rebuild.
4. After rollback, restore the matching env file for the restored topology and rerun the documented setup flow for that commit.
5. Notify owners of open feature branches that the topology changed so they can remap outstanding work before the next merge from `dev`.
## Deployment unknowns
- Dokploy build context for a repo-root compose file plus an app-root Laravel runtime still needs staging confirmation.
- Production web, queue, and scheduler working directories must be verified explicitly after the move; do not assume repo root and app root behave interchangeably.
- Any Dokploy volume mounts or storage persistence paths that previously targeted repo-root `storage/` must be reviewed against `apps/platform/storage/`.
### Configuration
- `TENANTPILOT_BULK_CHUNK_SIZE` (default `10`): job refresh/progress chunk size.
@ -235,7 +57,7 @@ ## Graph Contract Registry & Drift Guard
- Sanitizes `$select`/`$expand` to allowed fields; logs warnings on trim.
- Derived @odata.type values within the family are accepted for preview/restore routing.
- Capability fallback: on 400s related to select/expand, retries without those clauses and surfaces warnings.
- Drift check: `cd apps/platform && php artisan graph:contract:check [--tenant=]` runs lightweight probes against contract endpoints to detect capability/shape issues; useful in staging/CI (prod optional).
- Drift check: `php artisan graph:contract:check [--tenant=]` runs lightweight probes against contract endpoints to detect capability/shape issues; useful in staging/CI (prod optional).
- If Graph returns capability errors, TenantPilot downgrades safely, records warnings/audit entries, and avoids breaking preview/restore flows.
## Policy Settings Display
@ -260,3 +82,54 @@ ## Policy JSON Viewer (Feature 002)
- Scrollable container with max height to prevent page overflow
- **Usage**: See `specs/002-filament-json/quickstart.md` for detailed examples and configuration
- **Performance**: Optimized for payloads up to 1 MB; auto-collapse improves initial render for large snapshots
## About Laravel
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Learning Laravel
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application.
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
### Premium Partners
- **[Vehikl](https://vehikl.com)**
- **[Tighten Co.](https://tighten.co)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Redberry](https://redberry.international/laravel-development)**
- **[Active Logic](https://activelogic.com)**
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

View File

@ -3,7 +3,6 @@
namespace App\Console\Commands;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphContractRegistry;
use Illuminate\Console\Command;
class GraphContractCheck extends Command
@ -12,7 +11,7 @@ class GraphContractCheck extends Command
protected $description = 'Validate Graph contract registry against live endpoints (lightweight probes)';
public function handle(GraphClientInterface $graph, GraphContractRegistry $registry): int
public function handle(GraphClientInterface $graph): int
{
$contracts = config('graph_contracts.types', []);
@ -37,13 +36,11 @@ public function handle(GraphClientInterface $graph, GraphContractRegistry $regis
continue;
}
$queryInput = array_filter([
$query = array_filter([
'$top' => 1,
'$select' => $select,
'$expand' => $expand,
], static fn ($value): bool => $value !== null && $value !== '' && $value !== []);
$query = $registry->sanitizeQuery($type, $queryInput)['query'];
]);
$response = $graph->request('GET', $resource, [
'query' => $query,

View File

@ -34,6 +34,6 @@ private function resolveTenant(): Tenant
->firstOrFail();
}
return Tenant::currentOrFail();
return Tenant::current();
}
}

View File

@ -0,0 +1,395 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\BackupSetResource\Pages;
use App\Filament\Resources\BackupSetResource\RelationManagers\BackupItemsRelationManager;
use App\Jobs\BulkBackupSetDeleteJob;
use App\Jobs\BulkBackupSetForceDeleteJob;
use App\Jobs\BulkBackupSetRestoreJob;
use App\Models\BackupSet;
use App\Models\Tenant;
use App\Services\BulkOperationService;
use App\Services\Intune\AuditLogger;
use App\Services\Intune\BackupService;
use BackedEnum;
use Filament\Actions;
use Filament\Actions\ActionGroup;
use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup;
use Filament\Forms;
use Filament\Infolists;
use Filament\Notifications\Notification;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Collection;
use UnitEnum;
class BackupSetResource extends Resource
{
protected static ?string $model = BackupSet::class;
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-archive-box';
protected static string|UnitEnum|null $navigationGroup = 'Backups & Restore';
public static function form(Schema $schema): Schema
{
return $schema
->schema([
Forms\Components\TextInput::make('name')
->label('Backup name')
->default(fn () => now()->format('Y-m-d H:i:s').' backup')
->required(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')->searchable(),
Tables\Columns\TextColumn::make('status')->badge(),
Tables\Columns\TextColumn::make('item_count')->label('Items'),
Tables\Columns\TextColumn::make('created_by')->label('Created by'),
Tables\Columns\TextColumn::make('completed_at')->dateTime()->since(),
Tables\Columns\TextColumn::make('created_at')->dateTime()->since(),
])
->filters([
Tables\Filters\TrashedFilter::make()
->label('Archived')
->placeholder('Active')
->trueLabel('All')
->falseLabel('Archived'),
])
->actions([
Actions\ViewAction::make()
->url(fn (BackupSet $record) => static::getUrl('view', ['record' => $record]))
->openUrlInNewTab(false),
ActionGroup::make([
Actions\Action::make('restore')
->label('Restore')
->color('success')
->icon('heroicon-o-arrow-uturn-left')
->requiresConfirmation()
->visible(fn (BackupSet $record) => $record->trashed())
->action(function (BackupSet $record, AuditLogger $auditLogger) {
$record->restore();
$record->items()->withTrashed()->restore();
if ($record->tenant) {
$auditLogger->log(
tenant: $record->tenant,
action: 'backup.restored',
resourceType: 'backup_set',
resourceId: (string) $record->id,
status: 'success',
context: ['metadata' => ['name' => $record->name]]
);
}
Notification::make()
->title('Backup set restored')
->success()
->send();
}),
Actions\Action::make('archive')
->label('Archive')
->color('danger')
->icon('heroicon-o-archive-box-x-mark')
->requiresConfirmation()
->visible(fn (BackupSet $record) => ! $record->trashed())
->action(function (BackupSet $record, AuditLogger $auditLogger) {
$record->delete();
if ($record->tenant) {
$auditLogger->log(
tenant: $record->tenant,
action: 'backup.deleted',
resourceType: 'backup_set',
resourceId: (string) $record->id,
status: 'success',
context: ['metadata' => ['name' => $record->name]]
);
}
Notification::make()
->title('Backup set archived')
->success()
->send();
}),
Actions\Action::make('forceDelete')
->label('Force delete')
->color('danger')
->icon('heroicon-o-trash')
->requiresConfirmation()
->visible(fn (BackupSet $record) => $record->trashed())
->action(function (BackupSet $record, AuditLogger $auditLogger) {
if ($record->restoreRuns()->withTrashed()->exists()) {
Notification::make()
->title('Cannot force delete backup set')
->body('Backup sets referenced by restore runs cannot be removed.')
->danger()
->send();
return;
}
if ($record->tenant) {
$auditLogger->log(
tenant: $record->tenant,
action: 'backup.force_deleted',
resourceType: 'backup_set',
resourceId: (string) $record->id,
status: 'success',
context: ['metadata' => ['name' => $record->name]]
);
}
$record->items()->withTrashed()->forceDelete();
$record->forceDelete();
Notification::make()
->title('Backup set permanently deleted')
->success()
->send();
}),
])->icon('heroicon-o-ellipsis-vertical'),
])
->bulkActions([
BulkActionGroup::make([
BulkAction::make('bulk_delete')
->label('Archive Backup Sets')
->icon('heroicon-o-archive-box-x-mark')
->color('danger')
->requiresConfirmation()
->hidden(function (HasTable $livewire): bool {
$trashedFilterState = $livewire->getTableFilterState(TrashedFilter::class) ?? [];
$value = $trashedFilterState['value'] ?? null;
$isOnlyTrashed = in_array($value, [0, '0', false], true);
return $isOnlyTrashed;
})
->modalDescription('This archives backup sets (soft delete). Already archived backup sets will be skipped.')
->form(function (Collection $records) {
if ($records->count() >= 10) {
return [
Forms\Components\TextInput::make('confirmation')
->label('Type DELETE to confirm')
->required()
->in(['DELETE'])
->validationMessages([
'in' => 'Please type DELETE to confirm.',
]),
];
}
return [];
})
->action(function (Collection $records) {
$tenant = Tenant::current();
$user = auth()->user();
$count = $records->count();
$ids = $records->pluck('id')->toArray();
$service = app(BulkOperationService::class);
$run = $service->createRun($tenant, $user, 'backup_set', 'delete', $ids, $count);
if ($count >= 10) {
Notification::make()
->title('Bulk archive started')
->body("Archiving {$count} backup sets in the background. Check the progress bar in the bottom right corner.")
->icon('heroicon-o-arrow-path')
->iconColor('warning')
->info()
->duration(8000)
->sendToDatabase($user)
->send();
BulkBackupSetDeleteJob::dispatch($run->id);
} else {
BulkBackupSetDeleteJob::dispatchSync($run->id);
}
})
->deselectRecordsAfterCompletion(),
BulkAction::make('bulk_restore')
->label('Restore Backup Sets')
->icon('heroicon-o-arrow-uturn-left')
->color('success')
->requiresConfirmation()
->hidden(function (HasTable $livewire): bool {
$trashedFilterState = $livewire->getTableFilterState(TrashedFilter::class) ?? [];
$value = $trashedFilterState['value'] ?? null;
$isOnlyTrashed = in_array($value, [0, '0', false], true);
return ! $isOnlyTrashed;
})
->modalHeading(fn (Collection $records) => "Restore {$records->count()} backup sets?")
->modalDescription('Archived backup sets will be restored back to the active list. Active backup sets will be skipped.')
->action(function (Collection $records) {
$tenant = Tenant::current();
$user = auth()->user();
$count = $records->count();
$ids = $records->pluck('id')->toArray();
$service = app(BulkOperationService::class);
$run = $service->createRun($tenant, $user, 'backup_set', 'restore', $ids, $count);
if ($count >= 10) {
Notification::make()
->title('Bulk restore started')
->body("Restoring {$count} backup sets in the background. Check the progress bar in the bottom right corner.")
->icon('heroicon-o-arrow-path')
->iconColor('warning')
->info()
->duration(8000)
->sendToDatabase($user)
->send();
BulkBackupSetRestoreJob::dispatch($run->id);
} else {
BulkBackupSetRestoreJob::dispatchSync($run->id);
}
})
->deselectRecordsAfterCompletion(),
BulkAction::make('bulk_force_delete')
->label('Force Delete Backup Sets')
->icon('heroicon-o-trash')
->color('danger')
->requiresConfirmation()
->hidden(function (HasTable $livewire): bool {
$trashedFilterState = $livewire->getTableFilterState(TrashedFilter::class) ?? [];
$value = $trashedFilterState['value'] ?? null;
$isOnlyTrashed = in_array($value, [0, '0', false], true);
return ! $isOnlyTrashed;
})
->modalHeading(fn (Collection $records) => "Force delete {$records->count()} backup sets?")
->modalDescription('This is permanent. Only archived backup sets will be permanently deleted; active backup sets will be skipped.')
->form(function (Collection $records) {
if ($records->count() >= 10) {
return [
Forms\Components\TextInput::make('confirmation')
->label('Type DELETE to confirm')
->required()
->in(['DELETE'])
->validationMessages([
'in' => 'Please type DELETE to confirm.',
]),
];
}
return [];
})
->action(function (Collection $records) {
$tenant = Tenant::current();
$user = auth()->user();
$count = $records->count();
$ids = $records->pluck('id')->toArray();
$service = app(BulkOperationService::class);
$run = $service->createRun($tenant, $user, 'backup_set', 'force_delete', $ids, $count);
if ($count >= 10) {
Notification::make()
->title('Bulk force delete started')
->body("Force deleting {$count} backup sets in the background. Check the progress bar in the bottom right corner.")
->icon('heroicon-o-arrow-path')
->iconColor('warning')
->info()
->duration(8000)
->sendToDatabase($user)
->send();
BulkBackupSetForceDeleteJob::dispatch($run->id);
} else {
BulkBackupSetForceDeleteJob::dispatchSync($run->id);
}
})
->deselectRecordsAfterCompletion(),
]),
]);
}
public static function infolist(Schema $schema): Schema
{
return $schema
->schema([
Infolists\Components\TextEntry::make('name'),
Infolists\Components\TextEntry::make('status')->badge(),
Infolists\Components\TextEntry::make('item_count')->label('Items'),
Infolists\Components\TextEntry::make('created_by')->label('Created by'),
Infolists\Components\TextEntry::make('completed_at')->dateTime(),
Infolists\Components\TextEntry::make('metadata')
->label('Metadata')
->formatStateUsing(fn ($state) => json_encode($state ?? [], JSON_PRETTY_PRINT))
->copyable()
->copyMessage('Metadata copied'),
]);
}
public static function getRelations(): array
{
return [
BackupItemsRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListBackupSets::route('/'),
'create' => Pages\CreateBackupSet::route('/create'),
'view' => Pages\ViewBackupSet::route('/{record}'),
];
}
/**
* @return array{label:?string,category:?string,restore:?string,risk:?string}|array<string,mixed>
*/
private static function typeMeta(?string $type): array
{
if ($type === null) {
return [];
}
$types = array_merge(
config('tenantpilot.supported_policy_types', []),
config('tenantpilot.foundation_types', [])
);
return collect($types)
->firstWhere('type', $type) ?? [];
}
/**
* Create a backup set via the domain service instead of direct model mass-assignment.
*/
public static function createBackupSet(array $data): BackupSet
{
/** @var Tenant $tenant */
$tenant = Tenant::current();
/** @var BackupService $service */
$service = app(BackupService::class);
return $service->createBackupSet(
tenant: $tenant,
policyIds: $data['policy_ids'] ?? [],
name: $data['name'] ?? null,
actorEmail: auth()->user()?->email,
actorName: auth()->user()?->name,
includeAssignments: $data['include_assignments'] ?? false,
includeScopeTags: $data['include_scope_tags'] ?? false,
);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\BackupSetResource\Pages;
use App\Filament\Resources\BackupSetResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListBackupSets extends ListRecords
{
protected static string $resource = BackupSetResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\BackupSetResource\Pages;
use App\Filament\Resources\BackupSetResource;
use Filament\Resources\Pages\ViewRecord;
class ViewBackupSet extends ViewRecord
{
protected static string $resource = BackupSetResource::class;
}

View File

@ -0,0 +1,215 @@
<?php
namespace App\Filament\Resources\BackupSetResource\RelationManagers;
use App\Filament\Resources\PolicyResource;
use App\Models\BackupItem;
use App\Services\Intune\AuditLogger;
use Filament\Actions;
use Filament\Notifications\Notification;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
class BackupItemsRelationManager extends RelationManager
{
protected static string $relationship = 'items';
public function table(Table $table): Table
{
return $table
->modifyQueryUsing(fn (Builder $query) => $query->with('policyVersion'))
->columns([
Tables\Columns\TextColumn::make('policy.display_name')
->label('Item')
->sortable()
->searchable()
->getStateUsing(fn (BackupItem $record) => $record->resolvedDisplayName()),
Tables\Columns\TextColumn::make('policyVersion.version_number')
->label('Version')
->badge()
->default('—')
->getStateUsing(fn (BackupItem $record): ?int => $record->policyVersion?->version_number),
Tables\Columns\TextColumn::make('policy_type')
->label('Type')
->badge()
->formatStateUsing(fn (?string $state) => static::typeMeta($state)['label'] ?? $state),
Tables\Columns\TextColumn::make('restore_mode')
->label('Restore')
->badge()
->state(fn (BackupItem $record) => static::typeMeta($record->policy_type)['restore'] ?? 'enabled')
->color(fn (?string $state) => $state === 'preview-only' ? 'warning' : 'success'),
Tables\Columns\TextColumn::make('risk')
->label('Risk')
->badge()
->state(fn (BackupItem $record) => static::typeMeta($record->policy_type)['risk'] ?? 'n/a')
->color(fn (?string $state) => str_contains((string) $state, 'high') ? 'danger' : 'gray'),
Tables\Columns\TextColumn::make('policy_identifier')
->label('Policy ID')
->copyable(),
Tables\Columns\TextColumn::make('platform')->badge(),
Tables\Columns\TextColumn::make('assignments')
->label('Assignments')
->badge()
->color('info')
->getStateUsing(function (BackupItem $record): string {
$assignments = $record->policyVersion?->assignments ?? $record->assignments;
if (is_array($assignments)) {
return (string) count($assignments);
}
$assignmentsFetched = $record->policyVersion?->metadata['assignments_fetched']
?? $record->metadata['assignments_fetched']
?? false;
return $assignmentsFetched ? '0' : '—';
}),
Tables\Columns\TextColumn::make('scope_tags')
->label('Scope Tags')
->default('—')
->getStateUsing(function (BackupItem $record): array {
$tags = $record->policyVersion?->scope_tags['names']
?? $record->metadata['scope_tag_names']
?? [];
return is_array($tags) ? $tags : [];
})
->formatStateUsing(function ($state): string {
if (is_array($state)) {
return $state === [] ? '—' : implode(', ', $state);
}
if (is_string($state) && $state !== '') {
return $state;
}
return '—';
}),
Tables\Columns\TextColumn::make('captured_at')->dateTime(),
Tables\Columns\TextColumn::make('created_at')->since(),
])
->filters([])
->headerActions([
Actions\Action::make('addPolicies')
->label('Add Policies')
->icon('heroicon-o-plus')
->modalHeading('Add Policies')
->modalSubmitAction(false)
->modalCancelActionLabel('Close')
->modalContent(function (): View {
$backupSet = $this->getOwnerRecord();
return view('filament.modals.backup-set-policy-picker', [
'backupSetId' => $backupSet->getKey(),
]);
}),
])
->actions([
Actions\ActionGroup::make([
Actions\ViewAction::make()
->label('View policy')
->url(fn ($record) => $record->policy_id ? PolicyResource::getUrl('view', ['record' => $record->policy_id]) : null)
->hidden(fn ($record) => ! $record->policy_id)
->openUrlInNewTab(true),
Actions\Action::make('remove')
->label('Remove')
->color('danger')
->icon('heroicon-o-x-mark')
->requiresConfirmation()
->action(function (BackupItem $record, AuditLogger $auditLogger) {
$record->delete();
if ($record->backupSet) {
$record->backupSet->update([
'item_count' => $record->backupSet->items()->count(),
]);
}
if ($record->tenant) {
$auditLogger->log(
tenant: $record->tenant,
action: 'backup.item_removed',
resourceType: 'backup_set',
resourceId: (string) $record->backup_set_id,
status: 'success',
context: ['metadata' => ['policy_id' => $record->policy_id]]
);
}
Notification::make()
->title('Policy removed from backup')
->success()
->send();
}),
])->icon('heroicon-o-ellipsis-vertical'),
])
->bulkActions([
Actions\BulkActionGroup::make([
Actions\BulkAction::make('bulk_remove')
->label('Remove selected')
->icon('heroicon-o-x-mark')
->color('danger')
->requiresConfirmation()
->action(function (Collection $records, AuditLogger $auditLogger) {
if ($records->isEmpty()) {
return;
}
$backupSet = $this->getOwnerRecord();
$records->each(fn (BackupItem $record) => $record->delete());
$backupSet->update([
'item_count' => $backupSet->items()->count(),
]);
$tenant = $records->first()?->tenant;
if ($tenant) {
$auditLogger->log(
tenant: $tenant,
action: 'backup.items_removed',
resourceType: 'backup_set',
resourceId: (string) $backupSet->id,
status: 'success',
context: [
'metadata' => [
'removed_count' => $records->count(),
'policy_ids' => $records->pluck('policy_id')->filter()->values()->all(),
'policy_identifiers' => $records->pluck('policy_identifier')->filter()->values()->all(),
],
]
);
}
Notification::make()
->title('Policies removed from backup')
->success()
->send();
}),
]),
]);
}
/**
* @return array{label:?string,category:?string,restore:?string,risk:?string}|array<string,mixed>
*/
private static function typeMeta(?string $type): array
{
if ($type === null) {
return [];
}
$types = array_merge(
config('tenantpilot.supported_policy_types', []),
config('tenantpilot.foundation_types', [])
);
return collect($types)
->firstWhere('type', $type) ?? [];
}
}

Some files were not shown because too many files have changed in this diff Show More