diff --git a/docs/claude/README.md b/docs/claude/README.md
new file mode 100644
index 0000000..86c0e2c
--- /dev/null
+++ b/docs/claude/README.md
@@ -0,0 +1,1352 @@
+# Spec-Driven Development Documentation
+
+Documentation for Conduction's spec-driven development workflow, combining OpenSpec, GitHub Issues, and Claude Code.
+
+## Guides
+
+### [Getting Started](./getting-started.md)
+Step-by-step guide from installation to your first completed change. Start here if you're new to the workflow.
+
+### [Workflow Overview](./workflow.md)
+Architecture overview of the full system: how specs, GitHub Issues, plan.json, and Ralph Wiggum loops fit together. Includes the plan.json format and flow diagrams.
+
+### [Command Reference](./commands.md)
+Detailed reference for every skill — OpenSpec built-ins (`/opsx-new`, `/opsx-ff`, etc.), custom Conduction skills (`/opsx-plan-to-issues`, `/opsx-apply-loop`, `/opsx-pipeline`), and planned additions (`/opsx-ralph-start`, `/opsx-ralph-review` — not yet implemented). Includes expected output and usage tips.
+
+### [Writing Specs](./writing-specs.md)
+In-depth guide on writing effective specifications: RFC 2119 keywords, Gherkin scenarios, delta specs, shared spec references, task breakdown, and common mistakes to avoid.
+
+### [Writing Skills](./writing-skills.md)
+How to create and structure skills: folder layout (`templates/`, `references/`, `examples/`, `assets/`), SKILL.md format, naming conventions, common patterns, and the extraction threshold rule.
+
+### [Writing ADRs](./writing-adrs.md)
+How to write Architectural Decision Records: structure, format, when to create one, and how ADRs feed into the OpenSpec workflow.
+
+### [Writing Docs](./writing-docs.md)
+Guidelines for writing and maintaining documentation within a project: structure, tone, what to document, and how docs connect to the spec-driven workflow.
+
+### [App Lifecycle](./app-lifecycle.md)
+Creating and managing Nextcloud apps: design research (`/app-design`), bootstrapping from template or onboarding an existing repo (`/app-create`), thinking through goals and features (`/app-explore`), applying config to code (`/app-apply`), and auditing for drift (`/app-verify`). Includes `project.md` and `openspec/config.yaml` templates, and an onboarding checklist.
+
+### [Docker Environment](./docker.md)
+Available docker-compose profiles, reset instructions, and environment setup.
+
+### [Global Claude settings (`~/.claude`)](./global-claude-settings.md)
+**Mandatory** user-level settings enforcing a read-only Bash policy and write-approval hooks. Versioned — Claude warns you at session start when an update is available. Install once per machine; see the doc for the full guide and update instructions.
+
+### [Testing Reference](./testing.md)
+All testing commands and skills in one place — when to use each, typical workflows (pre-PR, regression sweep, smoke test), per-command "use when" guidance, test scenario integration, and browser pool rules.
+
+### [Parallel Agents & Subscription Cap](./parallel-agents.md)
+How parallel agent commands (like `/test-counsel`, `/test-app`, and `/feature-counsel`) consume your Claude subscription cap, guidelines for responsible use, and which files to keep lean to reduce token usage.
+
+### [Usage Tracker](../../usage-tracker/README.md)
+Real-time Claude token usage monitoring in VS Code — color-coded status, threshold notifications, and multi-model support (Haiku, Sonnet, Opus). Reads Claude Code session files directly; no log configuration needed. Run `bash usage-tracker/install.sh` to get started.
+
+### [End-to-End Walkthrough](./walkthrough.md)
+A complete worked example showing every phase of the flow on a realistic feature (adding a search endpoint to OpenCatalogi). Shows exactly what you type and what happens.
+
+---
+
+## Table of Contents
+
+- [Work Pipeline](#work-pipeline)
+ - [Stage 1: Obtain](#stage-1-obtain--discovery--requirements-gathering)
+ - [Stage 2: Specify](#stage-2-specify--writing-openspec-artifacts)
+ - [Stage 3: Build](#stage-3-build--configuration-not-code)
+ - [Stage 4: Validate](#stage-4-validate--quality-assurance--verification)
+- [Double Dutch (RAD Workflow)](#double-dutch-rad-workflow)
+- [Workstation Setup (Windows)](#workstation-setup-windows)
+- [Prerequisites (WSL)](#prerequisites-wsl)
+ - [Node.js](#nodejs-via-nvm)
+ - [PHP & Composer](#php-81--composer)
+ - [GitHub CLI](#github-cli)
+ - [PHP Quality Tools](#php-quality-tools-phpcs-phpmd-psalm-phpstan)
+ - [Playwright Browsers](#playwright-browsers)
+ - [OpenSpec CLI](#openspec-cli)
+ - [Claude Code CLI](#claude-code-cli-optional-for-terminal-use)
+ - [Ollama + Qwen (local LLM)](#ollama--qwen-coder-optional-local-llm)
+- [Local Configuration](#local-configuration)
+- [Playwright MCP Browser Setup](#playwright-mcp-browser-setup)
+- [Directory Structure](#directory-structure)
+- [Commands Reference](#commands-reference)
+- [Skills Reference](#skills-reference)
+- [Personas](#personas)
+- [Scripts](#scripts)
+- [Usage Tracker](#usage-tracker)
+- [Architectural Design Rules (ADRs)](#architectural-design-rules-adrs)
+- [Contributing](#contributing)
+- [Troubleshooting](#troubleshooting)
+
+---
+
+## Work Pipeline
+
+Claude follows a four-stage pipeline for all development work. Each stage has dedicated commands and tools. Claude operates as an **architect and orchestrator** — it defines, configures, and validates but delegates actual code generation to the platform's building blocks.
+
+```mermaid
+graph TD
+ subgraph "1. OBTAIN"
+ direction LR
+ O1[GitHub Issues]
+ O2[App Crawling]
+ O3[Documentation]
+ O4[Tenders / RFPs]
+ O5[App Store Scouting]
+ end
+
+ subgraph "2. SPECIFY"
+ direction TB
+ S1[proposal.md]
+ S2[specs.md]
+ S3[design.md]
+ S4[tasks.md]
+ S5[GitHub Issues]
+ S1 --> S2 --> S3 --> S4 --> S5
+ end
+
+ subgraph "3. BUILD"
+ direction LR
+ B1["Frontend
@conduction/nextcloud-vue"]
+ B2["Backend Data
OpenRegister Schemas"]
+ B3["Backend Logic
n8n Workflows"]
+ end
+
+ subgraph "4. VALIDATE"
+ direction TB
+ V1["Code Quality
PHPCS · PHPMD · Psalm"]
+ V2["Testing
API · Browser · Personas"]
+ V3["CI/CD
GitHub Actions"]
+ V1 --> V2 --> V3
+ end
+
+ O1 & O2 & O3 & O4 & O5 --> S1
+ S5 --> B1 & B2 & B3
+ B1 & B2 & B3 --> V1
+
+ style O1 fill:#e3f2fd,stroke:#1565c0
+ style O2 fill:#e3f2fd,stroke:#1565c0
+ style O3 fill:#e3f2fd,stroke:#1565c0
+ style O4 fill:#e3f2fd,stroke:#1565c0
+ style O5 fill:#e3f2fd,stroke:#1565c0
+ style S1 fill:#fff3e0,stroke:#e65100
+ style S2 fill:#fff3e0,stroke:#e65100
+ style S3 fill:#fff3e0,stroke:#e65100
+ style S4 fill:#fff3e0,stroke:#e65100
+ style S5 fill:#fff3e0,stroke:#e65100
+ style B1 fill:#e8f5e9,stroke:#2e7d32
+ style B2 fill:#e8f5e9,stroke:#2e7d32
+ style B3 fill:#e8f5e9,stroke:#2e7d32
+ style V1 fill:#fce4ec,stroke:#c62828
+ style V2 fill:#fce4ec,stroke:#c62828
+ style V3 fill:#fce4ec,stroke:#c62828
+```
+
+**Key commands per stage:**
+
+```mermaid
+graph TD
+ E["/opsx-explore"] --> N["/opsx-new"]
+ N --> FF["/opsx-ff"]
+ FF --> PI["/opsx-plan-to-issues"]
+ PI --> A["/opsx-apply"]
+ A --> V["/opsx-verify"]
+ V --> TC["/test-counsel"]
+ TC --> AR["/opsx-archive"]
+
+ style E fill:#e3f2fd,stroke:#1565c0
+ style N fill:#fff3e0,stroke:#e65100
+ style FF fill:#fff3e0,stroke:#e65100
+ style PI fill:#fff3e0,stroke:#e65100
+ style A fill:#e8f5e9,stroke:#2e7d32
+ style V fill:#fce4ec,stroke:#c62828
+ style TC fill:#fce4ec,stroke:#c62828
+ style AR fill:#fce4ec,stroke:#c62828
+```
+
+### Stage 1: Obtain — Discovery & Requirements Gathering
+
+Collect requirements, study existing solutions, and identify what to build. Claude explores without making changes.
+
+| Source | How | Commands / Tools |
+|--------|-----|------------------|
+| **GitHub issues** | Sync and analyze open issues from project repos | `/swc-update`, `gh issue list`, `gh issue view` |
+| **Other applications** | Crawl code or browse running apps to understand patterns | `/opsx-explore`, Playwright browsers (`browser-1`–`browser-7`) |
+| **Documentation** | Read docs from other platforms, APIs, standards | `WebFetch`, `WebSearch`, `/opsx-explore` |
+| **Tenders** | Scrape TenderNed, classify by category, analyze requirements and ecosystem gaps | `/tender-scan`, `/tender-status`, `/tender-gap-report`, `/ecosystem-investigate`, `Read` (PDF support) |
+| **App store scouting** | Spot interesting apps on WordPress plugin directory, GitHub trending, ArtifactHub, Nextcloud app store | `WebSearch`, `WebFetch`, Playwright browsers |
+
+**Typical discovery session:**
+
+```
+/opsx-explore # Investigate a topic or problem
+> "What calendar apps exist on ArtifactHub and WordPress that we could learn from?"
+> "Crawl the Nextcloud app store for document management apps"
+> "Analyze the GitHub issues for openregister and summarize themes"
+```
+
+### Stage 2: Specify — Writing OpenSpec Artifacts
+
+Turn discoveries into structured specifications. This stage produces the blueprint that guides implementation.
+
+| Phase | Artifact | Command |
+|-------|----------|---------|
+| **Start** | Change directory + `proposal.md` | `/opsx-new ` |
+| **Proposal → Tasks** | All artifacts in one go (proposal, specs, design, tasks) | `/opsx-ff` |
+| **Incremental** | One artifact at a time | `/opsx-continue` |
+| **Review** | Multi-perspective feature analysis | `/feature-counsel` |
+| **Architecture** | Architecture review of specs | `/team-architect` |
+| **Business value** | Acceptance criteria and prioritization | `/team-po` |
+| **New app** | Full app design from scratch (architecture, features, wireframes) | `/app-design` |
+| **Track** | Convert tasks to GitHub Issues with epic | `/opsx-plan-to-issues` |
+
+**Artifact progression:**
+
+```
+proposal.md ──► specs.md ──► design.md ──► tasks.md ──► plan.json
+ │
+ ▼
+ GitHub Issues
+```
+
+**Typical spec session:**
+
+```
+/opsx-new add-document-preview # Start the change
+/opsx-ff # Generate all artifacts
+/feature-counsel # Get 8-persona feedback
+# Human reviews and refines specs
+/opsx-plan-to-issues # Create trackable issues
+```
+
+### Stage 3: Build — Configuration, Not Code
+
+Claude acts as an **assembler**, not a coder. It defines schemas, configures workflows, and wires up components using the platform's three pillars:
+
+| Layer | Tool | Claude's Role |
+|-------|------|---------------|
+| **Frontend UI** | `@conduction/nextcloud-vue` | Select and configure components, define views and layouts, set up routing |
+| **Backend data** | OpenRegister | Define schemas, registers, and object structures; configure validation rules and relations |
+| **Backend logic** | n8n workflows | Design workflow logic, configure triggers, map data transformations |
+
+Claude does **not** write raw PHP business logic, custom Vue components from scratch, or manual SQL. Instead:
+- UI comes from the shared `@conduction/nextcloud-vue` component library
+- Data models are OpenRegister schemas (JSON-based configuration)
+- Business processes are n8n workflows (visual/JSON configuration)
+
+| Command | Purpose |
+|---------|---------|
+| `/opsx-apply` | Implement tasks from the change — assembles components per the specs |
+| `/opsx-sync` | Sync delta specs to main specs during implementation |
+
+**Typical build session:**
+
+```
+/opsx-apply # Implement tasks from specs
+# Claude configures schemas, wires components, sets up n8n flows
+/opsx-sync # Keep specs in sync
+```
+
+### Stage 4: Validate — Quality Assurance & Verification
+
+Verify that the implementation matches the specs, passes quality standards, and works for all user types.
+
+#### Code Quality
+
+| Tool | Command | Checks |
+|------|---------|--------|
+| **PHPCS** | `composer phpcs` | Coding standards (auto-fix: `composer cs:fix`) |
+| **PHPMD** | `composer phpmd` | Complexity, naming, unused code |
+| **PHPStan** | `composer phpstan` | Static type analysis |
+| **Psalm** | `composer psalm` | Type analysis (stricter) |
+| **phpmetrics** | `composer phpmetrics` | Code metrics + violations |
+| **ESLint** | `npm run lint` | JavaScript/Vue linting (auto-fix: `npm run lint -- --fix`) |
+| **Stylelint** | `npm run stylelint` | CSS/SCSS linting |
+
+> If `composer phpcs` fails due to a permissions error on `vendor/`, see [PHP Quality Tools setup](#php-quality-tools-phpcs-phpmd-psalm-phpstan) in Prerequisites.
+
+#### Testing
+
+| Type | Command | What it tests |
+|------|---------|---------------|
+| **Spec verification** | `/opsx-verify` | Implementation matches spec requirements (CRITICAL / WARNING / SUGGESTION) |
+| **Functional** | `/test-functional` | Feature correctness via browser |
+| **API** | `/test-api` | REST API + NLGov Design Rules compliance |
+| **Accessibility** | `/test-accessibility` | WCAG 2.1 AA compliance |
+| **Performance** | `/test-performance` | Load times, API response, network |
+| **Security** | `/test-security` | OWASP Top 10, BIO2, multi-tenancy |
+| **Regression** | `/test-regression` | Cross-app regression |
+| **Persona testing** | `/test-persona-*` | 8 user-perspective tests (Henk, Fatima, Sem, etc.) |
+| **Multi-perspective** | `/test-counsel` | All 8 personas test simultaneously |
+| **Browser testing** | `/test-app ` | Automated browser testing (single or 6 parallel agents) |
+
+#### CI/CD
+
+All apps have `code-quality.yml` GitHub Actions workflows that block PRs on:
+- PHPCS + PHPMD + Psalm (PHP quality)
+- ESLint (frontend quality)
+- PHPUnit (unit tests)
+
+#### Completion
+
+| Command | Purpose |
+|---------|---------|
+| `/opsx-verify` | Final verification against specs — generates `review.md` |
+| `/opsx-archive` | Archive the change, merge delta specs into main specs |
+| `/opsx-bulk-archive` | Archive multiple completed changes at once |
+
+**Typical validation session:**
+
+```
+composer phpcs && composer phpmd # Code quality gates
+/opsx-verify # Verify against specs
+/test-counsel # 8-persona test sweep
+/test-api # API compliance check
+/opsx-archive # Archive when everything passes
+```
+
+---
+
+## Double Dutch (RAD Workflow)
+
+A two-shift Rapid Application Development cycle that pairs Claude (daytime, fast, cloud) with Qwen (overnight, slow, local/free).
+
+```
+ 09:00 17:00 09:00
+ | | |
+ ┌────────┴────────────────────────┴───────────────────────┴──
+ │ REVIEW ◄── DAY SHIFT (Claude) ──► HANDOFF NIGHT SHIFT (Qwen)
+ │ Qwen's Specs, architecture, Prepare PHPCS fixes,
+ │ output complex logic, task files boilerplate,
+ │ code review, PRs bulk refactors,
+ │ test generation
+ └────────────────────────────────────────────────────────────
+```
+
+### Daily Cycle
+
+**Morning (09:00)** — Review Qwen's overnight output: code changes, test results, PHPCS fixes. Accept or reject changes, note issues for the day's work.
+
+**Day (09:00-17:00)** — Spec work with Claude: clarify requirements, write OpenSpec artifacts (`/opsx-ff`, `/opsx-new` → `/opsx-continue`), design architecture, solve hard problems, review PRs. Claude handles the thinking.
+
+**Evening (17:00)** — Hand off to Qwen: prepare self-contained task files (e.g., `qwen-phpcs-task.md`) with specific, mechanical work. Start Qwen batch and leave overnight.
+
+### Division of Labor
+
+| | Claude (Day) | Qwen (Night) |
+|---|---|---|
+| **Strengths** | Reasoning, architecture, specs, multi-file design | Mechanical fixes, repetitive changes, bulk ops |
+| **Speed** | ~3s/response (cloud API) | ~2min/response (local 14b on 8GB VRAM) |
+| **Cost** | API tokens (Max plan) | Free (local GPU) |
+| **Best for** | Complex logic, code review, client deliverables | PHPCS fixes, boilerplate, test scaffolding |
+
+### Task File Format
+
+Qwen works best with narrow, explicit task files. Example:
+
+```markdown
+# Task: Fix PHPCS Named Parameter Errors
+
+Working directory: `/path/to/app`
+
+## Files to fix
+1. `lib/Controller/FooController.php` (3 errors)
+2. `lib/Service/BarService.php` (1 error)
+
+## How to fix
+Find function calls without named parameters. Look up the method signature
+and add the parameter name:
+- BEFORE: `$this->setName('value')` where signature is `setName(string $name)`
+- AFTER: `$this->setName(name: 'value')`
+
+## Verification
+Run: `./vendor/bin/phpcs --standard=phpcs.xml `
+Expected: 0 errors
+```
+
+### Running Qwen Overnight
+
+```bash
+# Terminal 1 — start Qwen with Claude Code CLI
+ANTHROPIC_BASE_URL=http://localhost:11434 ANTHROPIC_API_KEY=ollama \
+ claude --model qwen3:14b
+
+# Then paste or reference the task file
+```
+
+> **Requires `qwen3:14b` or larger** for tool calling (file editing, shell commands). See [Ollama setup](#ollama--qwen-coder-optional-local-llm) for details.
+
+> **Known limitation:** Tool calling via CLI is unreliable with local models when system prompts are large. For now, Qwen works best on tasks where it can output code changes as text that you review and apply manually in the morning.
+
+---
+
+## Workstation Setup (Windows)
+
+Our development environment runs on **Windows + WSL2 + Docker Desktop + VS Code**. Follow these steps on a fresh Windows machine.
+
+### 1. Install WSL2
+
+Open PowerShell as Administrator:
+
+```powershell
+wsl --install -d Ubuntu-24.04
+```
+
+Restart your machine when prompted. After reboot, Ubuntu will ask you to create a Linux username and password.
+
+### 2. Install Docker Desktop
+
+Download and install [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop/).
+
+After installation:
+1. Open Docker Desktop > **Settings** > **Resources** > **WSL Integration**
+2. Enable integration with your Ubuntu distro
+3. Click **Apply & Restart**
+
+Verify in WSL:
+```bash
+docker --version
+docker compose version
+```
+
+### 3. Install VS Code
+
+Download and install [Visual Studio Code](https://code.visualstudio.com/).
+
+### 4. Install VS Code Extensions
+
+Open VS Code and install these extensions (`Ctrl+Shift+X`):
+
+**Required:**
+
+| Extension | ID | Purpose |
+| ---------------- | ---------------------------- | ---------------------------------------------------- |
+| Claude Code | anthropic.claude-code | AI coding assistant — core to this setup |
+| WSL | ms-vscode-remote.remote-wsl | Connect VS Code to WSL (Windows side) |
+| Docker | ms-azuretools.vscode-docker | Dockerfile syntax, container management |
+| PHP Intelephense | bmewburn.vscode-intelephense | PHP autocompletion, type checking, go-to-definition |
+| Volar | vue.volar | Vue 2/3 language support (templates, script, style) |
+| ESLint | dbaeumer.vscode-eslint | JavaScript/Vue linting |
+| Python | ms-python.python | Python language support (for ExApp sidecar wrappers) |
+
+**Recommended:**
+
+| Extension | ID | Purpose |
+| ------------------- | ---------------------------- | -------------------------------------------------------------- |
+| PowerShell | ms-vscode.powershell | PowerShell 7 scripting (`.ps1` files) |
+| GitLens | eamodio.gitlens | Advanced Git history, blame, line annotations |
+| GitHub Copilot Chat | github.copilot-chat | AI pair programmer (requires Copilot license) |
+| YAML | redhat.vscode-yaml | Syntax & validation for `docker-compose.yml` and OpenSpec YAML |
+| GitHub Actions | github.vscode-github-actions | View and validate CI/CD workflows |
+| Makefile Tools | ms-vscode.makefile-tools | Makefile support (`make check-strict`) |
+| Pylance | ms-python.vscode-pylance | Enhanced Python type checking and IntelliSense |
+
+**Optional:**
+
+| Extension | ID | Purpose |
+| ------------- | ----------------------- | ----------------------------------------------------------------------- |
+| Git Assistant | ivanhofer.git-assistant | Commit message suggestions + uncommitted changes warnings on branch switch |
+| Rainbow CSV | mechatroner.rainbow-csv | Color-coded CSV/TSV highlighting |
+| Live Preview | ms-vscode.live-server | Preview HTML files directly inside VS Code (right-click → Show Preview) |
+
+Or install all required + recommended at once from the CLI (run inside WSL terminal):
+
+```bash
+code --install-extension anthropic.claude-code
+code --install-extension ms-azuretools.vscode-docker
+code --install-extension bmewburn.vscode-intelephense
+code --install-extension vue.volar
+code --install-extension dbaeumer.vscode-eslint
+code --install-extension ms-python.python
+code --install-extension ms-vscode.powershell
+code --install-extension eamodio.gitlens
+code --install-extension github.copilot-chat
+code --install-extension redhat.vscode-yaml
+code --install-extension github.vscode-github-actions
+code --install-extension ms-vscode.makefile-tools
+code --install-extension ms-python.vscode-pylance
+```
+
+> **Note:** `ms-vscode-remote.remote-wsl` must be installed on the **Windows side** of VS Code, not inside WSL. Install it from VS Code on Windows before connecting to WSL.
+
+### 4a. Extension Setup After Installing
+
+Most extensions work immediately after install, but a few need a small configuration step.
+
+#### PHP Intelephense
+
+Intelephense indexes your PHP files automatically on first open. No configuration needed for basic usage. A few tips:
+
+- The free tier covers autocompletion, go-to-definition, and diagnostics — enough for Nextcloud app development.
+- If VS Code shows duplicate PHP suggestions, disable the built-in PHP extension: `Ctrl+Shift+P` → **"Extensions: Disable (Workspace)"** → search **PHP** → disable `vscode.php-language-features`.
+- Premium license (one-time purchase) adds rename, code folding, and type inference across files — optional.
+
+#### GitLens vs. GitKraken
+
+**GitLens** (VS Code extension) gives you inline blame, commit history, and file/line comparisons directly in the editor — no separate app needed.
+
+**GitKraken** is a standalone GUI Git client with a visual branch graph, interactive rebase, and team features. It runs alongside GitLens (they don't conflict). Install it **inside WSL** so it runs natively against your Linux repos — this avoids path translation issues that occur when a Windows-installed GitKraken tries to open `\\wsl$\` paths:
+
+```bash
+wget https://release.gitkraken.com/linux/gitkraken-amd64.deb
+sudo dpkg -i gitkraken-amd64.deb && rm gitkraken-amd64.deb
+# Fix any missing dependencies:
+sudo apt-get install -f
+# Launch:
+gitkraken &
+```
+
+> Requires **WSLg** (Windows 11 with WSL 2.0+) for the GUI window to appear. Run `wsl --version` in PowerShell to confirm — look for `WSLg version`.
+
+### 5. Connect VS Code to WSL
+
+1. Open VS Code
+2. Press `Ctrl+Shift+P` > **"WSL: Connect to WSL"**
+3. Choose your Ubuntu distro
+4. VS Code will install its server component in WSL automatically
+
+From here, all VS Code terminal work happens inside WSL.
+
+### 6. Claude Code Authentication
+
+After installing the Claude Code extension, authenticate:
+
+1. Open the Claude Code panel in VS Code (sidebar icon)
+2. Sign in with your Anthropic account
+3. Or from the terminal: `claude auth login`
+
+### 7. Configure Global Claude Settings
+
+Before using Claude in this workspace, set up user-level permissions and a safety hook that restricts which shell commands Claude can run automatically.
+
+See **[global-claude-settings.md](./global-claude-settings.md)** for the full guide, including copy-ready example files and a new-machine checklist.
+
+Quick install:
+
+```bash
+mkdir -p ~/.claude/hooks
+cp global-settings/settings.json ~/.claude/settings.json
+cp global-settings/block-write-commands.sh ~/.claude/hooks/block-write-commands.sh
+chmod +x ~/.claude/hooks/block-write-commands.sh
+```
+
+---
+
+## Local Configuration
+
+Claude Code uses three settings files that work together. Understanding the difference prevents confusion:
+
+| File | Scope | Committed? | Purpose |
+|------|-------|------------|---------|
+| `~/.claude/settings.json` | Machine-wide, all projects | No — installed per developer | Global read-only policy and safety hooks. Installed from [`global-settings/`](../../global-settings/) in step 7 above. |
+| `.claude/settings.json` | Project-wide, all developers | **Yes** | Shared team permissions — MCP server approvals, `enableAllProjectMcpServers`. Do not edit locally. |
+| `.claude/settings.local.json` | Project, per developer | No — gitignored | Your personal tool approvals on top of the shared settings. Auto-generated by Claude Code. |
+
+### settings.local.json
+
+This file is **auto-generated** by Claude Code the first time you approve a tool permission in a session — no manual setup needed. It stores your personal allow/deny rules on top of the shared project settings.
+
+Optionally, bootstrap it upfront with common permissions to avoid approval prompts during normal development:
+
+```json
+{
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
+ "permissions": {
+ "allow": [
+ "Bash(docker:*)",
+ "Bash(docker-compose:*)",
+ "Bash(composer:*)",
+ "Bash(git:*)",
+ "Bash(npm:*)",
+ "Bash(php:*)",
+ "Bash(curl:*)",
+ "Bash(bash:*)",
+ "Bash(ls:*)",
+ "Bash(mkdir:*)",
+ "Bash(cp:*)",
+ "Bash(mv:*)",
+ "Bash(rm:*)",
+ "WebFetch(domain:localhost)",
+ "WebFetch(domain:github.com)",
+ "WebFetch(domain:raw.githubusercontent.com)"
+ ],
+ "additionalDirectories": [
+ "/tmp"
+ ]
+ }
+}
+```
+
+Save this as `.claude/settings.local.json` in your project root. It is gitignored and will never be committed.
+
+### CLAUDE.local.md
+
+Contains environment-specific credentials and API tokens (passwords, keys, endpoints). **Never commit this file.**
+
+Copy the example template and fill in your own values:
+
+```bash
+cp .claude/CLAUDE.local.md.example .claude/CLAUDE.local.md
+# Edit with your credentials
+```
+
+---
+
+## Prerequisites (WSL)
+
+Run these commands inside WSL (the VS Code terminal after connecting to WSL).
+
+### Node.js (via nvm)
+
+```bash
+curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
+source ~/.bashrc
+nvm install 20
+```
+
+### PHP 8.1+ & Composer
+
+```bash
+sudo apt update
+sudo apt install -y php8.1-cli php8.1-xml php8.1-mbstring php8.1-curl php8.1-zip unzip
+curl -sS https://getcomposer.org/installer | php
+sudo mv composer.phar /usr/local/bin/composer
+```
+
+### GitHub CLI
+
+```bash
+sudo apt install -y gh
+gh auth login
+```
+
+### PHP Quality Tools (phpcs, phpmd, psalm, phpstan)
+
+Run `composer install` once after cloning an app to install PHP dev dependencies locally:
+
+```bash
+composer install
+```
+
+For commands, see [Code Quality](#code-quality) in the Stage 4 section above.
+
+If `composer install` fails because `vendor/` is owned by root (common when Docker first creates it):
+
+```bash
+# Option 1 — fix vendor ownership (requires sudo password)
+sudo chown -R $USER:$USER vendor/
+composer install
+
+# Option 2 — install phpcs globally (no sudo needed)
+composer global require "squizlabs/php_codesniffer:^3.9"
+~/.config/composer/vendor/bin/phpcs --standard=phpcs.xml
+```
+
+> **Important:** Use phpcs v3 (`^3.9`) — the CI uses v3. phpcs v4 is incompatible with the project's `phpcs.xml` config.
+
+### Playwright Browsers
+
+The Playwright MCP servers use the Playwright-managed Chromium binary. Install it once:
+
+```bash
+npx playwright install chromium
+```
+
+> If the MCP server reports a different revision is needed (e.g. after a `@playwright/mcp` update), run the install from the npx cache that the MCP server uses. You can find it at `~/.npm/_npx/` — look for the directory containing `@playwright/mcp`.
+
+### OpenSpec CLI
+
+Used by all `/opsx-*` commands for spec-driven development:
+
+```bash
+npm install -g @fission-ai/openspec
+```
+
+> **Do NOT run `openspec init`** in an existing Conduction project — it already has a customized `openspec/` directory with Conduction-specific schemas, shared specs, and project changes. Running `init` would overwrite them.
+
+**OpenSpec documentation:**
+- [Official site](https://openspec.dev/) — Getting started, concepts, customization
+- [GitHub](https://github.com/Fission-AI/OpenSpec) — Source, issues, releases
+- [npm](https://www.npmjs.com/package/@fission-ai/openspec) — Package info
+- [Workflow docs](./workflow.md) — Our workspace-specific workflow
+
+**Local docs:**
+
+| File | Content |
+|------|---------|
+| [getting-started.md](./getting-started.md) | First-time setup and orientation |
+| [global-claude-settings.md](./global-claude-settings.md) | User-level Claude permissions, hooks, and safety settings |
+| [workflow.md](./workflow.md) | Full spec-driven development workflow |
+| [writing-specs.md](./writing-specs.md) | How to write good specs |
+| [commands.md](./commands.md) | CLI command reference |
+| [walkthrough.md](./walkthrough.md) | Step-by-step example of a full cycle |
+| [testing.md](./testing.md) | All testing commands and skills — when to use each, recommended workflows |
+| [app-lifecycle.md](./app-lifecycle.md) | Creating and managing Nextcloud apps — design, bootstrap, onboarding, config, and drift detection |
+| [parallel-agents.md](./parallel-agents.md) | How parallel agents work, subscription cap implications, responsible use |
+| [frontend-standards.md](./frontend-standards.md) | Frontend standards enforced across all Conduction apps |
+| [docker.md](./docker.md) | Docker environment setup, profiles, and common operations |
+| [exapp-sidecar-status.md](./exapp-sidecar-status.md) | Status report for ExApp sidecar wrapper projects |
+
+### Claude Code CLI (optional, for terminal use)
+
+```bash
+npm install -g @anthropic-ai/claude-code
+```
+
+### Ollama + Qwen Coder (optional, local LLM)
+
+Claude Code can run with a **local LLM** instead of the Anthropic API, using [Ollama](https://ollama.com/) and Alibaba's [Qwen3-Coder](https://ollama.com/library/qwen3-coder) model. Ollama v0.14.0+ includes built-in Anthropic Messages API compatibility, so Claude Code connects to it without any proxy or adapter.
+
+#### When to use local vs Claude API
+
+| Use case | Recommendation |
+|----------|---------------|
+| **Data sovereignty** — code or data must stay in the EU / on-premise | Local Qwen |
+| **Security-sensitive work** — credentials, private APIs, client data | Local Qwen |
+| **Offline / air-gapped environments** | Local Qwen |
+| **Simple tasks** — formatting, renaming, small refactors, boilerplate | Local Qwen |
+| **Cost reduction** — high-volume, repetitive prompts | Local Qwen |
+| **Complex reasoning** — architecture, debugging, multi-file changes | Claude API |
+| **Large context** — analyzing entire codebases or long specs | Claude API |
+| **Quality-critical** — production code, specs, client deliverables | Claude API |
+
+> **Rule of thumb:** Use Qwen locally for work that is private, simple, or high-volume. Use Claude API when quality and reasoning depth matter most. You can switch between them freely — they use the same Claude Code interface, tools, and commands.
+
+#### Step 1: Install Ollama
+
+Install Ollama **natively on WSL** (not in Docker — native gives better GPU passthrough and performance):
+
+```bash
+curl -fsSL https://ollama.com/install.sh | sh
+```
+
+Ollama runs as a background service automatically. Verify it's running:
+
+```bash
+ollama --version # Should show 0.14.0+
+```
+
+#### Step 2: Pull a Qwen model
+
+Choose the right model for your GPU VRAM:
+
+| Model | Download | Size | Min VRAM | Speed (RTX 3070) | Tool calling? |
+|-------|----------|------|----------|-------------------|---------------|
+| `qwen3:8b` | `ollama pull qwen3:8b` | 5.2 GB | 8 GB (fits 100%) | ~12s | **No** (chat only) |
+| **`qwen3:14b`** | `ollama pull qwen3:14b` | **9.3 GB** | 12 GB | **~2min** (spills to CPU on 8GB) | **Yes** |
+| `qwen3-coder` | `ollama pull qwen3-coder` | 18 GB | 24 GB | ~6min (mostly CPU on 8GB) | Yes |
+
+**Recommended: `qwen3:14b`** — the smallest model that supports **tool calling** (reading files, editing code, running commands). On 8GB VRAM it's slow (~2min/response) but works as a batch/overnight agent. On 12GB+ VRAM it runs at interactive speed (~15s).
+
+```bash
+ollama pull qwen3:14b
+```
+
+> **Why not `qwen3:8b`?** It's faster but can only chat — it cannot use tools (file access, shell commands, code editing). The model is too small to reliably produce the structured function-call format that CLI agents require. It will show its thinking but won't execute anything.
+>
+> **Why not `qwen3-coder`?** It's the most capable (30B params) but requires 24GB+ VRAM. On an 8GB GPU it runs ~68% on CPU and takes ~6 minutes per response. Only use it with a workstation GPU (RTX 4090, A6000, etc).
+
+Check your available memory:
+
+```bash
+free -h # Look at the "available" column
+nvidia-smi # Check GPU VRAM
+```
+
+If you don't have enough system memory, increase the WSL allocation. On **Windows**, edit (or create) `%USERPROFILE%\.wslconfig`:
+
+```ini
+[wsl2]
+memory=24GB
+```
+
+Then restart WSL from PowerShell:
+
+```powershell
+wsl --shutdown
+```
+
+Reopen your Ubuntu terminal — the new memory limit is now active.
+
+#### Step 3: Run Claude Code with Qwen
+
+Open a **new terminal** and run (replace model name with whichever you pulled):
+
+```bash
+ANTHROPIC_BASE_URL=http://localhost:11434 ANTHROPIC_API_KEY=ollama claude --model qwen3:14b
+```
+
+This opens the full interactive Claude Code CLI — same interface, same tools, same commands — but powered by Qwen running locally on your machine. No data leaves your workstation.
+
+For a quick **one-shot prompt** (no interactive session):
+
+```bash
+ANTHROPIC_BASE_URL=http://localhost:11434 ANTHROPIC_API_KEY=ollama claude --model qwen3:14b --print "explain this function"
+```
+
+#### Running local and API side by side
+
+The env vars are **scoped to that single terminal window only**. This means you can run both simultaneously:
+
+- **Terminal 1** — Qwen locally (free, private, slower) doing a long-running task like a bulk refactor or code review
+- **Terminal 2 / VS Code** — Claude API (fast, powerful) for your main interactive development work
+
+This is the recommended workflow: **sidecar the free local model** for background tasks while you continue your normal work with Claude API at full speed. The local session won't affect your API session in any way — they're completely independent.
+
+```
+┌─────────────────────────┐ ┌─────────────────────────┐
+│ Terminal 1 (Qwen) │ │ VS Code / Terminal 2 │
+│ │ │ │
+│ Free, local, private │ │ Claude API (Opus) │
+│ Running: bulk refactor │ │ Fast interactive dev │
+│ Speed: ~15 tok/s │ │ Speed: ~50-80 tok/s │
+│ Cost: $0 │ │ Cost: normal API usage │
+│ │ │ │
+│ ► Background task │ │ ► Your main work │
+└─────────────────────────┘ └─────────────────────────┘
+```
+
+To go back to Claude API in any terminal, simply open a new terminal as normal — no env vars to unset.
+
+#### Performance expectations
+
+Benchmarked on an RTX 3070 (8GB VRAM) with 24GB WSL memory:
+
+| Model | Simple task | Tool calling | Fits in 8GB VRAM | Usable interactively? |
+|-------|-------------|-------------|------------------|----------------------|
+| qwen3:8b | ~12 seconds | No | Yes (100% GPU) | Chat only |
+| **qwen3:14b** | **~2 minutes** | **Yes** | No (spills to CPU) | **Batch/overnight** |
+| qwen3-coder (30B) | ~6 minutes | Yes | No (68% CPU) | No |
+| Claude API (Opus) | ~3 seconds | Yes | N/A (cloud) | Yes |
+
+**Be honest about the trade-off:** The recommended local model (`qwen3:14b`) is **~40x slower** than Claude API on 8GB VRAM hardware. It's not viable for interactive coding — but it **does support tool calling**, which makes it a real coding agent that can read files, edit code, and run commands. Use it for batch jobs you kick off and walk away from (e.g., overnight PHPCS fixes, bulk refactors, code reviews).
+
+**Where local shines:**
+- **Nightly / batch jobs** — automated code reviews, linting suggestions, documentation generation, bulk refactors where you kick it off and walk away
+- **Cost** — completely free, no API usage, no token limits, run it as much as you want
+- **Privacy** — nothing leaves your machine, ideal for client code under NDA or government data
+- **Simple interactive tasks** — quick renames, formatting, boilerplate generation where the speed difference barely matters
+
+#### Alternative: Qwen Code CLI (native Qwen experience)
+
+Qwen has its own dedicated CLI tool (v0.11+) with an interface similar to Claude Code, optimized for Qwen models:
+
+```bash
+sudo npm install -g @qwen-code/qwen-code@latest
+```
+
+**Configure it to use your local Ollama** by editing `~/.qwen/settings.json`:
+
+```json
+{
+ "modelProviders": {
+ "openai": [
+ {
+ "id": "qwen3:14b",
+ "name": "Qwen3 14B (Local Ollama)",
+ "envKey": "OLLAMA_API_KEY",
+ "baseUrl": "http://localhost:11434/v1"
+ }
+ ]
+ },
+ "security": {
+ "auth": {
+ "selectedType": "openai"
+ }
+ },
+ "env": {
+ "OLLAMA_API_KEY": "ollama"
+ },
+ "model": {
+ "name": "qwen3:14b"
+ }
+}
+```
+
+> Adjust the model `id` and `name` if you pulled a different model (e.g., `qwen3:8b` for chat-only, or `qwen3-coder` on 24GB+ VRAM).
+
+The key parts: `security.auth.selectedType: "openai"` bypasses the OAuth prompt, `modelProviders.openai` tells Qwen Code where your local Ollama lives, and `env.OLLAMA_API_KEY` provides the dummy API key that Ollama ignores but Qwen Code requires.
+
+**Launch it:**
+
+```bash
+cd /path/to/your-project
+qwen
+```
+
+**Tool calling requires `qwen3:14b` or larger.** The `qwen3:8b` model runs in chat-only mode — it can reason and answer questions but cannot use tools (no file access, no shell commands, no code editing). The `qwen3:14b` model supports structured tool calling and works as a full coding agent, though it's slow on 8GB VRAM (~2min/response). On 12GB+ VRAM it runs at interactive speed.
+
+**Sharing context with Claude Code:** Qwen Code reads `QWEN.md` instead of `CLAUDE.md`, but supports `@path/to/file.md` imports. You can create a `QWEN.md` in the workspace root that imports the Claude configuration:
+
+```markdown
+@CLAUDE.md
+@CLAUDE.local.md
+```
+
+This gives Qwen Code the same project context, coding standards, and credentials as Claude Code. However, Qwen Code does **not** support Claude's `/opsx-*` slash commands or skills — those are Claude Code-specific. For the full OpenSpec workflow, use Claude Code (with either API or local Qwen backend).
+
+#### Tips
+
+- **Don't close the terminal** where Ollama is running — if Ollama stops, your Claude Code session loses its backend
+- **One model at a time** — Ollama loads/unloads models automatically, but running two large models simultaneously will OOM
+- **VS Code extension** still uses Claude API — the env var trick only works for the CLI. This is fine: use VS Code for complex work (Claude API) and terminal for quick local tasks (Qwen)
+- **All Claude Code features work** — tools, file editing, git, commands, skills, browser MCP — because the interface is the same, only the model backend changes
+
+### Summary Checklist
+
+```bash
+# Verify everything is installed
+node --version # v20.x+
+php --version # 8.1+
+composer --version # 2.x
+docker --version # 24+
+gh --version # 2.x+
+openspec --version # 1.x
+npx playwright --version # 1.x
+```
+
+---
+
+## Playwright MCP Browser Setup
+
+The workspace uses **7 independent Playwright browser sessions** for parallel testing.
+
+### Browser Pool
+
+| Server | Mode | Purpose |
+|--------|------|---------|
+| `browser-1` | Headless | Main agent (default) |
+| `browser-2` | Headless | Sub-agent / parallel |
+| `browser-3` | Headless | Sub-agent / parallel |
+| `browser-4` | Headless | Sub-agent / parallel |
+| `browser-5` | Headless | Sub-agent / parallel |
+| `browser-6` | **Headed** | User observation (visible window) |
+| `browser-7` | Headless | Sub-agent / parallel |
+
+### VS Code Extension Setup
+
+The VS Code extension loads MCP servers from `.mcp.json` in the **project root**. The file defines 7 browser instances. Browsers 1–5 and 7 are headless:
+
+```json
+{
+ "mcpServers": {
+ "browser-1": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] },
+ "browser-2": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] },
+ "browser-3": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] },
+ "browser-4": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] },
+ "browser-5": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] },
+ "browser-6": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--isolated"] },
+ "browser-7": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"] }
+ }
+}
+```
+
+**Headed browser** (browser-6 — for watching tests live):
+
+```json
+{
+ "browser-6": {
+ "command": "npx",
+ "args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--isolated"]
+ }
+}
+```
+
+> `browser-6` omits `--headless` so the browser window is visible when you want to watch.
+
+The shared `settings.json` has two pre-approval entries:
+
+- **`"enableAllProjectMcpServers": true`** — auto-approves all servers from `.mcp.json` without prompting on each reload.
+- **All `mcp__browser-*` tool calls** — pre-approved for all 7 browsers so that parallel sub-agents (used by `/test-app` Full mode and `/test-counsel`) can use their assigned browser without needing an interactive permission prompt. Without this, background agents are silently denied and no testing occurs.
+
+Then **reload the VS Code window**: `Ctrl+Shift+P` → type `reload window` → Enter.
+
+### Verification
+
+After reload, open the MCP servers panel to verify all 7 browsers show **Connected**. You can do this two ways:
+- Type `/MCP servers` in the Claude Code chat input
+- Or `Ctrl+Shift+P` → search **"MCP servers"**
+
+
+
+If any server shows an error, check the output panel: `Ctrl+Shift+P` → **"Output: Focus on Output"** → select **"Claude VSCode"** from the dropdown.
+
+### CLI Alternative (terminal only)
+
+For the Claude Code CLI (`claude` terminal command, not VS Code), you can start servers as HTTP endpoints on fixed ports and reference them via URL:
+
+```bash
+# Start headless browsers
+for port in 9221 9222 9223 9224 9225 9227; do
+ npx -y @playwright/mcp@latest --headless --isolated --port $port &
+done
+
+# Start headed browser
+npx -y @playwright/mcp@latest --isolated --port 9226 &
+```
+
+> This is **not needed for VS Code** — the extension manages server processes automatically via `.mcp.json`. Only use this approach if you're running `claude` from the terminal without VS Code.
+
+### Usage Rules
+
+1. **Default**: Use `browser-1` for normal work
+2. **Parallel agents**: Assign sub-agents `browser-2` through `browser-5` and `browser-7`
+3. **User watching**: Switch to `browser-6` when the user wants to observe
+4. **Fallback**: If a browser errors, try the next numbered browser
+5. **Keep `browser-6` reserved**: Only for explicit user observation
+
+---
+
+## Directory Structure
+
+```
+/
+├── .mcp.json # Playwright browser MCP servers
+│
+└── .claude/ # Claude Code configuration (this repository)
+ ├── CLAUDE.md # Workflow rules, project context, Docker env
+ ├── CLAUDE.local.md # [GITIGNORED] Your credentials — copy from .example, never commit
+ ├── CLAUDE.local.md.example # Template — copy to CLAUDE.local.md on setup
+ ├── .mcp.json # Template — copy to project root on setup
+ ├── settings.json # [COMMITTED] Shared project permissions (MCP approvals, enableAllProjectMcpServers)
+ ├── settings.local.json # [GITIGNORED] Your personal tool permissions — auto-generated by Claude Code
+ │
+ ├── docs/ # This documentation
+ │
+ ├── global-settings/ # Source files for ~/.claude/ — install once per machine (see step 7)
+ │ ├── settings.json # → installed as ~/.claude/settings.json (global read-only policy)
+ │ ├── block-write-commands.sh # → installed as ~/.claude/hooks/block-write-commands.sh
+ │ ├── check-settings-version.sh # → installed as ~/.claude/hooks/check-settings-version.sh
+ │ └── VERSION # → version tracking for update checks
+ │
+ ├── skills/ # See docs/commands.md for full reference
+ │ ├── app-*/ # App lifecycle (create, design, apply, verify, explore)
+ │ ├── ecosystem-*/ # Ecosystem research (investigate, propose-app)
+ │ ├── opsx-*/ # OpenSpec workflow (new, ff, apply, verify, archive, …)
+ │ ├── swc-*/ # Softwarecatalogus (test, update)
+ │ ├── team-*/ # Scrum team agents (architect, backend, frontend, …)
+ │ ├── tender-*/ # Tender intelligence (scan, status, gap-report)
+ │ ├── test-*/ # Testing (counsel, app, personas, scenarios, …)
+ │ ├── clean-env/ # Reset Docker environment
+ │ ├── create-pr/ # Create a PR on GitHub
+ │ ├── feature-counsel/ # Multi-persona spec analysis
+ │ ├── intelligence-update/ # Sync external data sources
+ │ ├── sync-docs/ # Sync documentation to current state
+ │ └── verify-global-settings-version/
+ │
+ ├── personas/ # 8 Dutch government user personas
+ │ ├── henk-bakker.md
+ │ ├── fatima-el-amrani.md
+ │ ├── sem-de-jong.md
+ │ ├── noor-yilmaz.md
+ │ ├── annemarie-de-vries.md
+ │ ├── mark-visser.md
+ │ ├── priya-ganpat.md
+ │ └── janwillem-van-der-berg.md
+ │
+ └── usage-tracker/ # Claude token usage monitoring
+```
+
+---
+
+## Commands Reference
+
+Commands are invoked as `/namespace-command [args]` in Claude Code.
+
+### OpenSpec Workflow (`/opsx-*`)
+
+**Core lifecycle:**
+
+| Command | Description |
+|---------|-------------|
+| `/opsx-new ` | Start a new change |
+| `/opsx-ff` | Fast-forward: create all artifacts (proposal, specs, design, tasks) |
+| `/opsx-continue` | Create the next artifact incrementally |
+| `/opsx-apply` | Implement tasks from a change |
+| `/opsx-verify` | Check implementation against specs (includes test coverage + documentation checks) |
+| `/opsx-archive` | Archive a completed change, merge delta specs, update feature docs |
+| `/opsx-bulk-archive` | Archive multiple changes at once |
+| `/opsx-sync` | Sync delta specs to main specs |
+| `/opsx-plan-to-issues` | Convert tasks.md to GitHub Issues with tracking epic |
+| `/opsx-pipeline` | Process multiple changes in parallel — full lifecycle per change, up to 5 concurrent agents |
+
+**Discovery & design:**
+
+| Command | Description |
+|---------|-------------|
+| `/opsx-explore` | Read-only investigation mode |
+| `/opsx-onboard` | Guided walkthrough for new team members |
+
+**App lifecycle:**
+
+| Command | Description |
+|---------|-------------|
+| `/app-design [app-name]` | Full upfront design — architecture research, competitor analysis, feature matrix, ASCII wireframes, OpenSpec setup. Run **before** `/app-create` for new apps. |
+| `/app-create [app-id]` | Bootstrap a new app from template or onboard an existing repo — creates `openspec/`, scaffolds files, sets up GitHub repo |
+| `/app-explore [app-id]` | Think through and evolve app configuration — updates `openspec/app-config.json`, feature specs, and ADRs |
+| `/app-apply [app-id]` | Apply `openspec/app-config.json` changes to the actual app files (info.xml, CI workflows, PHP namespaces, etc.) |
+| `/app-verify [app-id]` | Read-only audit — reports drift between `openspec/app-config.json` and the actual files (CRITICAL / WARNING / INFO) |
+
+**Team role agents:**
+
+| Command | Role |
+|---------|------|
+| `/team-architect` | Architecture review (API, data models, cross-app) |
+| `/team-backend` | Backend implementation (PHP, entities, services) |
+| `/team-frontend` | Frontend implementation (Vue, state, UX) |
+| `/team-po` | Product Owner (business value, acceptance criteria) |
+| `/team-qa` | QA Engineer (test coverage, edge cases) |
+| `/team-reviewer` | Code review (standards, conventions) |
+| `/team-sm` | Scrum Master (progress, blockers) |
+
+**Testing agents:**
+
+| Command | Focus |
+|---------|-------|
+| `/test-functional` | Feature correctness via browser |
+| `/test-api` | REST API + NLGov Design Rules compliance |
+| `/test-accessibility` | WCAG 2.1 AA compliance |
+| `/test-performance` | Load times, API response, network |
+| `/test-security` | OWASP Top 10, BIO2, multi-tenancy |
+| `/test-regression` | Cross-app regression testing |
+| `/test-persona-*` | 8 persona-specific testing agents |
+
+**Typical workflow:**
+
+```
+/opsx-new add-search-filters # Define the change
+/opsx-ff # Generate all spec artifacts
+/opsx-plan-to-issues # Create GitHub issues (optional)
+/opsx-apply # Implement the tasks
+/opsx-verify # Verify against specs
+/opsx-archive # Archive when done
+```
+
+### Softwarecatalogus (`/swc-*`)
+
+| Command | Description |
+|---------|-------------|
+| `/swc-test [mode]` | Run tests — `api`, `browser`, `all`, or `personas` |
+| `/swc-update` | Sync GitHub issues and update test infrastructure |
+
+---
+
+## Skills Reference
+
+Skills are invoked as `/skill-name [args]`.
+
+### General
+
+| Skill | Description |
+|-------|-------------|
+| `/clean-env` | Full Docker environment reset (stop, remove volumes, restart, install apps) |
+| `/feature-counsel` | Analyze specs from 8 persona perspectives, suggest missing features |
+| `/test-app [appname]` | Automated browser testing — single agent or 6 parallel perspectives |
+| `/test-counsel` | Execute tests from 8 persona perspectives using browser + API |
+
+> These commands spawn multiple agents in parallel and consume your Claude usage cap faster than normal. See [parallel-agents.md](./parallel-agents.md) for cap impact, guidelines, and tips to reduce token usage.
+
+### Tender & Ecosystem Intelligence
+
+| Skill | Description |
+|-------|-------------|
+| `/tender-scan` | Scrape TenderNed for new tenders, import to SQLite, classify by software category using local Qwen |
+| `/tender-status` | Dashboard: tenders by source, category, status, gaps, and top integration systems |
+| `/tender-gap-report` | Generate gap analysis report — categories with tender demand but no Conduction product |
+| `/ecosystem-investigate ` | Deep-dive competitor research for a software category (GitHub, G2, Capterra, AlternativeTo) |
+| `/ecosystem-propose-app ` | Generate a structured app proposal from tender requirements and competitor research |
+| `/intelligence-update [source]` | Sync external data sources into `intelligence.db` (due sources by default, or specify `all`/``) |
+
+> Requires `concurrentie-analyse/intelligence.db` to exist. `/tender-scan` requires a local Qwen model via Ollama (`http://localhost:11434`).
+
+---
+
+## Personas
+
+Eight Dutch government user personas in `personas/` represent the full spectrum of public sector users:
+
+| Persona | Age | Role | Perspective |
+|---------|-----|------|-------------|
+| Henk Bakker | 78 | Retired citizen | Accessibility, simple Dutch |
+| Fatima El-Amrani | 52 | Low-literate migrant | Icons, mobile-first, B1 language |
+| Sem de Jong | 22 | Digital native student | Performance, keyboard, dark mode |
+| Noor Yilmaz | 36 | Municipal CISO | Security, BIO2, audit trails |
+| Annemarie de Vries | 38 | VNG standards architect | GEMMA, NLGov API, interoperability |
+| Mark Visser | 48 | MKB software vendor | CRUD efficiency, bulk operations |
+| Priya Ganpat | 34 | ZZP developer | API quality, OpenAPI, DX |
+| Jan-Willem van der Berg | 55 | Small business owner | Plain language, findability |
+
+Used by `/feature-counsel` and `/test-counsel` for multi-perspective analysis.
+
+---
+
+## Scripts
+
+Shell scripts in `scripts/` are shared utilities used by skills and developers.
+
+| Script | Description | Usage |
+|--------|-------------|-------|
+| `clean-env.sh` | Full Docker environment reset — stops containers, removes volumes, restarts, installs core apps | `bash scripts/clean-env.sh` or `/clean-env` |
+
+---
+
+## Usage Tracker
+
+Monitor your Claude token usage in real-time to avoid hitting subscription limits mid-session. The tracker reads Claude Code's JSONL session files directly — no extra configuration needed.
+
+```bash
+# Install
+bash usage-tracker/install.sh
+
+# Quick status (all models)
+python3 usage-tracker/claude-usage-tracker.py --status-bar --all-models
+
+# Live monitoring (refreshes every 5 min)
+python3 usage-tracker/claude-usage-tracker.py --monitor --all-models
+```
+
+See [usage-tracker/README.md](../../usage-tracker/README.md) for full documentation, VS Code task integration, and limit configuration.
+
+---
+
+## Architectural Design Rules (ADRs)
+
+ADRs define constraints that all OpenSpec artifacts must comply with. Company-wide ADRs live in `openspec/architecture/`; app-specific ADRs live in `{app}/openspec/architecture/`. They are enforced at two points:
+
+1. **During artifact creation** — `config.yaml` rules reference ADRs, which get injected into `openspec instructions` output
+2. **During verification** — `/opsx-verify` checks ADR compliance as a report dimension
+
+### Current ADRs
+
+| ADR | Title | Enforced In |
+|-----|-------|-------------|
+| 001 | OpenRegister as Universal Data Layer | design: no custom DB tables |
+| 002 | REST API Conventions | specs: URL patterns, error format |
+| 003 | NL Design System for All UI | design: CSS variables only |
+| 004 | Nextcloud App Framework Patterns | design: DI, annotations |
+| 005 | i18n — Dutch and English Required | tasks: translation tasks |
+| 006 | OpenRegister Schema Standards | specs: schema definitions |
+| 007 | Security and Authentication | specs: auth requirements |
+| 008 | Backend Layering (Controller -> Service -> Mapper) | design: layer structure |
+| 009 | Mandatory Test Coverage (75%) | tasks: test tasks |
+| 010 | Documentation with Screenshots | tasks: docs + Playwright screenshots |
+| 011 | Deduplication Check Against OR Core | proposal: check existing features |
+| 012 | Frontend Patterns (@conduction/nextcloud-vue) | design: component reuse |
+| 013 | Loadable Register Templates | specs: register JSON format |
+| 014 | Per-App Register Content i18n | specs: translatable fields |
+| 015 | Per-App Prometheus Metrics | specs: metrics endpoints |
+| 016 | Mandatory Seed Data for Testability | design: seed data section; tasks: seed data task |
+
+### How ADRs Flow Into Work
+
+```
+config.yaml rules -> openspec instructions -> artifact content -> verify checks
+```
+
+ADRs are referenced in each app's `openspec/config.yaml` under the `rules:` section per artifact type. When an agent creates a proposal, design, spec, or task list, the CLI injects these rules into the instructions. The agent MUST follow them.
+
+### Adding a New ADR
+
+1. Create `openspec/architecture/adr-NNN-title.md` (company-wide) or `{app}/openspec/architecture/adr-NNN-title.md` (app-specific) following the template in `openspec/architecture/README.md`
+2. Add reference rules to `config.yaml` for the relevant artifact types
+3. Update this table
+
+---
+
+## Contributing
+
+### Adding a Skill
+
+1. Create `skills//SKILL.md`
+2. Use frontmatter:
+ ```yaml
+ ---
+ name: skill-name
+ description: What this skill does
+ ---
+ ```
+3. Document instructions and expected behavior
+
+### Adding a Persona
+
+1. Create `personas/.md`
+2. Follow the existing format (see `henk-bakker.md`)
+3. Update skills that reference the persona list
+
+### PR Process
+
+1. Create a branch: `git checkout -b my-change`
+2. Make changes, commit, push
+3. Create PR against `main`
+
+---
+
+## Troubleshooting
+
+### `openspec: command not found`
+
+```bash
+npm install -g @fission-ai/openspec
+```
+
+### Playwright browser not launching
+
+```bash
+npx playwright install chromium
+# If permission errors:
+npx playwright install --with-deps chromium
+```
+
+### MCP servers not showing in VS Code / not connected
+
+1. Confirm `.mcp.json` exists in your **project root**
+2. Confirm `settings.json` contains `"enableAllProjectMcpServers": true`
+3. Reload the window: `Ctrl+Shift+P` → `reload window`
+4. Check the output panel for errors: `Ctrl+Shift+P` → **"Output: Focus on Output"** → select **"Claude VSCode"**
+
+You can verify the MCP binary itself starts correctly:
+
+```bash
+npx -y @playwright/mcp@latest --headless --isolated --port 9999
+# Should print: Listening on http://localhost:9999
+```
+
+### Claude Code doesn't see commands
+
+Ensure `.claude/` is at the workspace root and Claude Code is started from that directory.
+
+### `gh: not logged in`
+
+```bash
+gh auth login
+```
+
+### Ollama model won't load (out of memory)
+
+Increase WSL memory in `%USERPROFILE%\.wslconfig`:
+
+```ini
+[wsl2]
+memory=24GB
+```
+
+Then restart WSL from PowerShell: `wsl --shutdown`
+
+### Docker environment not starting
+
+```bash
+docker compose -f openregister/docker-compose.yml up -d
+# Full reset:
+/clean-env
+```
+
+### "You've hit your limit · resets 3pm (Europe/Amsterdam)"
+
+This means you've reached your Claude subscription's usage cap. It can happen after running commands that launch many agents in parallel (`/test-counsel`, `/feature-counsel`, `/test-app` in Full mode).
+
+See [parallel-agents.md](./parallel-agents.md) for an explanation of why parallel agents drain the cap, guidelines for careful use, and tips to reduce token usage (including always opening a fresh window before running these commands).
+
+To monitor your usage proactively before hitting the limit, use the [usage tracker](../../usage-tracker/README.md):
+```bash
+python3 usage-tracker/claude-usage-tracker.py --status-bar --all-models
+```
diff --git a/docs/claude/app-lifecycle.md b/docs/claude/app-lifecycle.md
new file mode 100644
index 0000000..ff02991
--- /dev/null
+++ b/docs/claude/app-lifecycle.md
@@ -0,0 +1,499 @@
+# App Lifecycle — Creating and Managing Nextcloud Apps
+
+App lifecycle commands live in the `app-` namespace. OpenSpec workflow commands (`opsx-`) handle feature implementation; `app-` commands handle the app itself — bootstrapping, configuration, and health checks.
+
+---
+
+## Flow Diagram
+
+```mermaid
+graph TD
+ AD["/app-design\n(optional pre-step)"] --> AC["/app-create"]
+ AC --> AE["/app-explore"]
+ AE --> AP["/app-apply"]
+ AP --> AV["/app-verify"]
+ AV -->|"Issues found"| AP
+ AV -->|"All passing"| DONE["Build features\n/opsx-ff"]
+ AE -->|"Revisit config"| AE
+
+ style AD fill:#f3e5f5,stroke:#6a1b9a
+ style AC fill:#e3f2fd,stroke:#1565c0
+ style AE fill:#fff3e0,stroke:#e65100
+ style AP fill:#e8f5e9,stroke:#2e7d32
+ style AV fill:#fce4ec,stroke:#c62828
+ style DONE fill:#e8f5e9,stroke:#1b5e20
+```
+
+```
+[/app-design] ← optional: architecture, features, wireframes
+ ↓
+/app-create ──► /app-explore ──► /app-apply ──► /app-verify
+ ▲ │
+ └──────────── revisit (if drift found) ───┘
+ ↓
+ openspec/app-config.json ◄── single source of truth
+```
+
+---
+
+## Commands
+
+### `/app-design [app-name]` _(optional pre-step)_
+
+**Full upfront design for a new app — architecture, features, wireframes, OpenSpec setup.**
+
+Run this **before** `/app-create` when building a brand new app from scratch. It does deep research and produces:
+
+| Artifact | Contents | Optional? |
+|----------|---------|-----------|
+| `docs/ARCHITECTURE.md` | Standards research, data model, entity definitions, OpenRegister schema, Vue component decisions | No |
+| `docs/FEATURES.md` | 10+ competitor analysis, feature matrix (MVP/V1/Enterprise), settings & notifications derived from features | No |
+| `docs/DESIGN-REFERENCES.md` | ASCII wireframes, UX inspiration, missing feature patterns | No |
+| `openspec/config.yaml` | OpenSpec CLI context — standards, architecture summary, rules for specs/design/tasks | No |
+| `img/app-store.svg` | Blue hexagon app logo | Yes _(asked during Phase 5)_ |
+| `docusaurus/` + `.github/workflows/documentation.yml` | Docusaurus documentation site, GitHub Pages deployment | Yes _(asked during Phase 5)_ |
+
+`/app-create` and `/app-explore` will automatically read these documents as context when they exist.
+
+**When to use:** Starting a brand new app and you want comprehensive design research before writing any code. Skip for simple apps or when onboarding an existing repo.
+
+**Re-running:** Safe to run again — prompts you to overwrite, update, or skip each existing document individually.
+
+**`openspec/config.yaml` — OpenSpec CLI configuration**
+
+`/app-design` generates this file as part of its output. The template uses the shared `conduction` schema; customise the `context` and `rules` blocks for your project:
+
+```yaml
+schema: conduction
+
+context: |
+ Project:
+ Repo: ConductionNL/
+ Type: Nextcloud App (PHP)
+ Description:
+ Key components:
+ Database: PostgreSQL (via OpenRegister's ObjectService)
+ Mount path: /var/www/html/custom_apps/
+
+ Shared specs: See ../openspec/specs/ for cross-project conventions
+ Project guidelines: See ../project.md for workspace-wide standards
+
+rules:
+ proposal:
+ - Reference shared nextcloud-app spec for app structure requirements
+ -
+ specs:
+ -
+ design:
+ -
+ tasks:
+ -
+```
+
+- **`context`**: Injected into every artifact Claude generates — include purpose, tech stack, and architecture
+- **`rules`**: Per-artifact-type rules that guide Claude when writing specs for this project
+
+If not using `/app-design`, create this file manually before running `openspec validate --all`.
+
+---
+
+### `/app-create [app-id]`
+
+**Bootstrap or onboard a Nextcloud app.**
+
+Starts the interactive setup process. Collects app identity, goal, dependency choices, and GitHub details — then either clones the template as a starting point or onboards an existing folder by comparing it against the template structure.
+
+Always creates an `openspec/` folder with:
+- `app-config.json` — the canonical config file for all decisions
+- `README.md` — documents the app's goal and structure
+
+If `/app-design` was run first, reads `docs/ARCHITECTURE.md` and `docs/FEATURES.md` to pre-populate fields. Never overwrites existing design artifacts.
+
+After `/app-create` you have a fully scaffolded app with all template placeholders replaced and both `main` + `development` branches on GitHub.
+
+**When to use:** Starting a brand new app, or onboarding an existing repo into the lifecycle flow for the first time.
+
+**`project.md` — project description for Claude context**
+
+Each app should have a `project.md` at its root. This file is loaded by Claude as context when working on the project. `/app-create` scaffolds a stub; complete it with the details below:
+
+```markdown
+#
+
+## Overview
+
+
+## Repository
+- **GitHub**: https://github.com/ConductionNL/
+- **Organization**: ConductionNL
+- **Container mount**: /var/www/html/custom_apps/
+
+## Architecture
+
+### Key Components
+- **** —
+- **** —
+
+### Important Patterns
+-
+
+### Directory Structure
+```
+lib/
+ Controller/
+ Service/
+ Db/
+appinfo/
+ info.xml
+ routes.php
+```
+
+## Dependencies
+- **Depends on**:
+- **Depended on by**:
+
+## API
+- Base URL: `/index.php/apps//api/`
+- Auth:
+- Format: JSON
+
+## Testing
+-
+```
+
+List known gotchas, dependencies in both directions, and any project-specific patterns that differ from the workspace-wide standards in `apps-extra/project.md`.
+
+---
+
+### `/app-explore [app-id]`
+
+**Think through and evolve the app configuration.**
+
+Opens an interactive exploration session. Acts as a thinking partner — questions assumptions, maps features, explores architecture, and helps you clarify what the app should do and how it should be built.
+
+Reads `openspec/app-config.json`, feature specs, ADRs, and — when present — `docs/ARCHITECTURE.md`, `docs/FEATURES.md`, and `docs/DESIGN-REFERENCES.md` from `/app-design`. Never writes application code.
+
+What you can do in explore mode:
+- Refine the app's goal or summary
+- Map out features and write `openspec/specs/{feature-name}/spec.md` files
+- Change category, OpenRegister dependency, CI settings
+- Sketch architecture with ASCII diagrams
+- Surface open questions and risks
+- Create app-specific ADRs in `openspec/architecture/`
+
+**When to use:** When starting to plan features, when the app's purpose has evolved, or when you need to think through a design decision before building.
+
+---
+
+### `/app-apply [app-id]`
+
+**Apply config changes from `openspec/app-config.json` to the actual app files.**
+
+Reads the config, compares it against all tracked files, shows you a diff of what would change, and applies approved changes. Updates `appinfo/info.xml`, all CI/CD workflows, `composer.json`, `package.json`, Vue files, README, and more.
+
+Always shows the change summary and asks for confirmation before modifying anything.
+
+**When to use:** After `/app-explore` has updated `openspec/app-config.json` and you're ready to push those changes into the codebase. Also use after manually editing `app-config.json` directly.
+
+---
+
+### `/app-verify [app-id]`
+
+**Audit the app files against `openspec/app-config.json`.**
+
+Read-only. Checks every tracked file and reports findings with severity:
+- **CRITICAL** — Identity mismatches that break CI or cause runtime errors
+- **WARNING** — Metadata drift that is incorrect but not breaking
+- **INFO** — Minor cosmetic differences
+
+Also checks for deprecated components (`CnDetailViewLayout`) and CHANGELOG version alignment.
+
+**When to use:** After `/app-apply` to confirm changes landed correctly. Also useful as a periodic health check to detect drift (e.g. after manual edits to CI files).
+
+---
+
+## The `openspec/` Folder
+
+Every app managed by this lifecycle has an `openspec/` folder at its root. This folder is the **single source of truth** for all configuration decisions — it is what `/app-apply` reads and what `/app-verify` checks against.
+
+```
+openspec/
+├── app-config.json # Core configuration (id, name, goal, dependencies, CI settings)
+├── README.md # Documents the app's goal and structure
+├── architecture/ # Architectural Decision Records (app-specific ADRs)
+│ └── adr-001-*.md
+└── specs/ # Feature specifications (created during /app-explore)
+ └── {feature-name}/
+ └── spec.md
+```
+
+### `app-config.json` Structure
+
+```json
+{
+ "id": "my-app",
+ "name": "My App",
+ "namespace": "MyApp",
+ "summary": "One-line description (~100 chars)",
+ "goal": "Full description of what this app does and the problem it solves.",
+ "category": "organization",
+ "version": "0.1.0",
+ "license": "EUPL-1.2",
+ "author": "Conduction B.V.",
+ "repository": "https://github.com/ConductionNL/my-app",
+ "dependencies": {
+ "requiresOpenRegister": true,
+ "additionalCiApps": [
+ {"repo": "ConductionNL/openregister", "app": "openregister", "ref": "main"}
+ ]
+ },
+ "cicd": {
+ "phpVersions": ["8.3", "8.4"],
+ "nextcloudRefs": ["stable31", "stable32"],
+ "enableNewman": false
+ },
+ "createdAt": "2026-01-01",
+ "updatedAt": "2026-03-20"
+}
+```
+
+### Feature Specs
+
+Feature specs live in `openspec/specs/{feature-name}/spec.md` and are created during `/app-explore` sessions:
+
+```markdown
+# {Feature Name} Specification
+
+**Status**: idea | planned | in-progress | done
+
+**OpenSpec changes:** _(links to openspec/changes/ when in-progress or done)_
+
+## Purpose
+
+What this feature does and why it matters to users.
+
+## Requirements
+
+### Requirement: {Requirement Name}
+The system MUST/SHOULD/MAY {requirement statement}.
+
+#### Scenario: {Scenario Name}
+- GIVEN {precondition}
+- WHEN {action}
+- THEN the system {MUST/SHOULD} {expected outcome}
+
+## User Stories
+
+- As a [role], I want to [action] so that [outcome]
+
+## Acceptance Criteria
+
+- [ ] ...
+
+## Notes
+
+Open questions, constraints, dependencies, related ADRs.
+```
+
+> For `idea` status, a lightweight spec (Purpose + User Stories + Acceptance Criteria) is fine. Fill in formal Requirements/Scenarios when moving to `planned` — that is what `/opsx-ff` reads to generate the change artifacts.
+
+---
+
+## Tracked Files
+
+`/app-apply` and `/app-verify` track the following files and keep them in sync with `app-config.json`:
+
+| File | What is tracked |
+|------|----------------|
+| `appinfo/info.xml` | App ID, name, summary, goal, namespace, category, GitHub URLs |
+| `lib/AppInfo/Application.php` | APP_ID constant, PHP namespace |
+| `composer.json` | Package name, PSR-4 namespace |
+| `package.json` | Package name |
+| `webpack.config.js` | App ID constant |
+| `src/App.vue` | OpenRegister gate (present/absent) |
+| `README.md` | Title, goal section |
+| `.github/workflows/code-quality.yml` | App name, PHP versions, NC refs, Newman flag, additional apps |
+| `.github/workflows/release-beta.yml` | App name |
+| `.github/workflows/release-stable.yml` | App name |
+| `.github/workflows/issue-triage.yml` | App name |
+| `.github/workflows/openspec-sync.yml` | App name |
+
+---
+
+## Typical Sessions
+
+### Starting a brand new app (full design-first flow)
+
+```
+/app-design my-new-app # Architecture, competitor research, wireframes, OpenSpec setup
+/app-create my-new-app # Scaffold from template, create GitHub repo
+/app-explore my-new-app # Think through goals and features
+# Approve saving changes to openspec/
+/app-apply my-new-app # Push config changes to app files
+/app-verify my-new-app # Confirm everything landed correctly
+openspec validate --all # Verify OpenSpec config is valid
+/opsx-onboard # Confirm Claude integration works
+```
+
+### Starting a new app (quick flow, no upfront design)
+
+```
+/app-create my-new-app # Scaffold from template, create GitHub repo
+/app-explore my-new-app # Think through goals and features
+/app-apply my-new-app # Push config changes to app files
+/app-verify my-new-app # Confirm everything landed correctly
+openspec validate --all # Verify OpenSpec config is valid
+/opsx-onboard # Confirm Claude integration works
+```
+
+### Onboarding checklist
+
+After completing the steps above, confirm:
+
+- [ ] `openspec/` directory initialized with `app-config.json`
+- [ ] `openspec/config.yaml` present and pointing to `conduction` schema with project context
+- [ ] `project.md` at app root — describes purpose, architecture, and dependencies
+- [ ] `openspec validate --all` passes
+- [ ] `/opsx-onboard` works in Claude Code
+
+### Evolving an existing app
+
+```
+/app-explore my-app # Revisit goals, add feature files
+# Update summary, category, or CI settings
+/app-apply my-app # Apply the changes
+/app-verify my-app # Verify
+```
+
+### Periodic health check
+
+```
+/app-verify my-app # Check for drift
+# If CRITICAL or WARNING issues:
+/app-apply my-app # Fix them
+```
+
+---
+
+## Architectural Decision Records (ADRs)
+
+App-specific ADRs live in `openspec/architecture/` and document why the app is built the way it is. They are created and explored during `/app-explore` sessions.
+
+> **Company-wide ADRs** (ADR-001 through ADR-015) live in `apps-extra/.claude/openspec/architecture/` and apply to all Conduction apps. Only create an app-specific ADR when the decision is unique to that app.
+
+Good candidates for app-specific ADRs:
+- Data storage approach (OpenRegister vs own tables)
+- Frontend architecture choices unique to this app
+- Authentication and authorization model
+- External API dependencies and coupling decisions
+- Standards compliance choices (ZGW, GEMMA, CMMN)
+
+**Format:** `openspec/architecture/adr-{NNN}-{slug}.md` with fields for Context, Decision, Consequences, and Alternatives Considered.
+
+---
+
+## Handoff to OpenSpec — The Cut-off Point
+
+The app lifecycle flow handles everything up to and including **"we know what to build"**. OpenSpec takes over when you are ready to **write the spec and implement**.
+
+The cut-off is driven by feature status:
+
+```
+openspec/specs/ openspec/changes/
+─────────────── ─────────────────
+idea (not here yet)
+planned ─────────────────► /opsx-ff creates the change
+in-progress proposal.md → specs.md → tasks.md
+done archived
+```
+
+**Trigger:** When a feature in `openspec/specs/` moves from `idea` to `planned` — meaning it has a clear goal, user stories, and acceptance criteria — it is ready for `/opsx-ff {feature-name}`.
+
+### One feature → multiple changes
+
+A single feature can result in **multiple OpenSpec changes**. This is intentional — OpenSpec changes should be independently deployable slices:
+
+```
+openspec/specs/document-upload/spec.md (planned)
+ │
+ ├──► openspec/changes/document-upload-backend/ (schema + API)
+ ├──► openspec/changes/document-upload-frontend/ (Vue upload UI)
+ └──► openspec/changes/document-upload-notify/ (notifications)
+```
+
+The feature spec tracks the overall concept; the OpenSpec changes track implementation.
+
+---
+
+## Preventing Duplication Between `openspec/` and `openspec/changes/`
+
+The two layers have distinct roles — duplication is avoided by keeping each layer at the right level of detail:
+
+| | `openspec/` (app config layer) | `openspec/changes/` (implementation layer) |
+|---|---|---|
+| **Question** | WHAT should this app do? | HOW should we build it? |
+| **Level** | Concept and goal | Technical specification |
+| **Feature detail** | User stories + acceptance criteria | Architecture, design, tasks |
+| **Decisions** | WHY (ADRs in `openspec/architecture/`) | HOW (design.md per change) |
+| **Output** | Input for OpenSpec | Output for development |
+
+**Rule of thumb:**
+- If you are deciding *what* the app needs → `openspec/specs/{feature}/spec.md`
+- If you are deciding *how* to implement a specific change → `openspec/changes/{change}/`
+- If a decision is about the overall architecture of the app → `openspec/architecture/adr-NNN-*.md`
+- If a decision is about the design of a specific feature → `openspec/changes/{change}/design.md`
+
+The `README.md` in the app root references the `openspec/` folder and links to key documents — it is the entry point, not a duplicate.
+
+---
+
+## Scope of `/app-apply`
+
+App Apply is deliberately narrow. It only touches scaffold and configuration files:
+
+✅ **Syncs:** `appinfo/info.xml`, CI/CD workflow parameters, PHP namespaces, package names, `README.md` header sections, `src/App.vue` OpenRegister gate.
+
+❌ **Does not touch:** Feature code, business logic, Vue components, controllers, OpenRegister schemas.
+
+If a user asks `/app-apply` to do something outside this scope, it redirects to the right tool:
+- Config decision → `/app-explore` to capture it, then `/app-apply`
+- Feature implementation → `/opsx-ff {feature-name}`
+
+---
+
+## `/app-design` vs `/app-explore` vs `/app-create` — When to Use Which
+
+These three commands can feel overlapping. Here's the clear distinction:
+
+| | `/app-design` | `/app-create` | `/app-explore` |
+|---|---|---|---|
+| **When** | Before the repo exists | Once, at bootstrap | Repeatedly throughout app lifetime |
+| **Does** | Research + document (architecture, competitors, wireframes) | Scaffold files + set up GitHub | Think + iterate on config and features |
+| **Writes to** | `docs/`, `openspec/config.yaml`, `openspec/specs/` | Template files, `openspec/app-config.json` | `openspec/app-config.json`, `openspec/specs/`, `openspec/architecture/` |
+| **Requires** | Nothing (pre-repo) | Git repo, optionally design docs | Existing app with `openspec/app-config.json` |
+| **Skip when** | Onboarding existing repo / simple app | Already done | Never — use it repeatedly |
+
+**Overlap note:** `/app-design` creates `openspec/config.yaml` and initial `openspec/specs/`. `/app-create` creates `openspec/app-config.json`. These are different files with different purposes — the design docs don't replace the machine-readable config file.
+
+---
+
+## Relationship to the OpenSpec Flow
+
+App lifecycle commands (`app-`) work alongside the OpenSpec workflow commands (`opsx-`), making the full workflow coherent:
+
+| | App Lifecycle commands | OpenSpec implementation commands |
+|---|---|---|
+| **Purpose** | Bootstrap, configure, and audit the app itself | Specify, implement, and validate features |
+| **Source of truth** | `openspec/app-config.json` | `openspec/changes/` directories |
+| **Writes to** | App files (metadata, CI, config) | Application code (PHP, Vue, schemas) |
+| **Start with** | A new or existing app repo | A specific feature or change to implement |
+| **ADR location** | `openspec/architecture/` (app-wide decisions) | `openspec/changes/*/design.md` (feature decisions) |
+
+Typical combined workflow:
+1. Design (optional): `/app-design`
+2. Bootstrap and configure: `/app-create` → `/app-explore` → `/app-apply`
+3. Define features and ADRs: `/app-explore` (repeated as the app evolves)
+4. Implement features: `/opsx-ff {feature-name}` → `/opsx-apply` → `/opsx-verify` → test (`/test-functional`, `/test-counsel`) → `/create-pr` → `/opsx-archive`
+5. Keep config in sync: `/app-verify` periodically, `/app-apply` when drift is found
+
+For guidance on testing commands and when to use each, see [testing.md](testing.md).
diff --git a/docs/claude/commands.md b/docs/claude/commands.md
new file mode 100644
index 0000000..be6df09
--- /dev/null
+++ b/docs/claude/commands.md
@@ -0,0 +1,1379 @@
+# Command Reference
+
+Complete reference for all commands available in the spec-driven development workflow.
+
+## OpenSpec Built-in Commands
+
+These commands are installed per-project when you run `openspec init`. They're available inside each project directory.
+
+---
+
+### `/opsx-new `
+
+**Phase:** Spec Building
+
+Start a new change. Creates the change directory with metadata.
+
+**Usage:**
+```
+/opsx-new add-publication-search
+```
+
+**What it creates:**
+```
+openspec/changes/add-publication-search/
+└── .openspec.yaml # Change metadata (schema, created date)
+```
+
+**Tips:**
+- Use descriptive kebab-case names: `add-dark-mode`, `fix-cors-headers`, `refactor-object-service`
+- The name becomes a GitHub Issue label, so keep it readable
+
+---
+
+### `/opsx-ff`
+
+**Phase:** Spec Building
+
+Fast-forward: generates ALL artifacts in dependency order (proposal → specs → design → tasks) in one go.
+
+**Usage:**
+```
+/opsx-ff
+```
+
+**What it creates:**
+```
+openspec/changes/add-publication-search/
+├── .openspec.yaml
+├── proposal.md # Why & what
+├── specs/ # Delta specs (ADDED/MODIFIED/REMOVED)
+│ └── search/
+│ └── spec.md
+├── design.md # How (technical approach)
+└── tasks.md # Implementation checklist
+```
+
+**When to use:** When you have a clear idea of what you want to build and want to generate everything quickly for review.
+
+**When NOT to use:** When you want to iterate on each artifact step by step, getting feedback between each. Use `/opsx-continue` instead.
+
+**Model:** Asked at run time — the command asks which model to use and spawns a subagent with that model for artifact generation. Artifact quality (specs, design, tasks) directly determines implementation quality downstream. **Sonnet** for most changes. **Opus** for complex or architectural changes where deeper reasoning improves the design.
+
+---
+
+### `/opsx-continue`
+
+**Phase:** Spec Building
+
+Creates the next artifact in the dependency chain. Run repeatedly to build specs incrementally.
+
+**Usage:**
+```
+/opsx-continue # Creates proposal.md (first time)
+/opsx-continue # Creates specs/ (second time)
+/opsx-continue # Creates design.md (third time)
+/opsx-continue # Creates tasks.md (fourth time)
+```
+
+**Dependency chain:**
+```
+proposal (root)
+ ├── discovery (optional — requires: proposal)
+ ├── contract (optional — requires: proposal)
+ ├── specs (requires: proposal)
+ ├── design (requires: proposal)
+ ├── migration (optional — requires: design)
+ ├── test-plan (optional — requires: specs)
+ └── tasks (requires: specs + design)
+```
+
+**When to use:** When you want to review and refine each artifact before proceeding to the next.
+
+---
+
+### `/opsx-explore`
+
+**Phase:** Pre-spec
+
+Think through ideas and investigate the codebase before starting a formal change. No artifacts are created.
+
+**Usage:**
+```
+/opsx-explore
+```
+
+**When to use:** When you're not sure what approach to take yet and want to investigate first.
+
+**Comparison with `/app-explore`:**
+
+| | `/opsx-explore` | `/app-explore` |
+|---|---|---|
+| **Scope** | Any topic — a change, a bug, an idea | A specific Nextcloud app's configuration |
+| **Output** | None — thinking only | Writes to `openspec/app-config.json` |
+| **When to use** | Before starting a change (`/opsx-new`) when requirements are unclear | When designing or refining an app's goals, architecture, and features |
+| **Phase** | Pre-spec | Design / Configuration |
+
+Use `/opsx-explore` to think through *what to build*. Use `/app-explore` to document *how an app is designed and configured*.
+
+**Model:** Checked at run time — stops on Haiku. Asks which model to use and explains how to switch if the choice differs from the active model. **Sonnet** for most exploration sessions. ✅ **Opus** recommended — complex analysis, architecture decisions, and strategic thinking benefit from stronger reasoning.
+
+---
+
+### `/opsx-apply`
+
+**Phase:** Implementation
+
+OpenSpec's built-in implementation command. Reads `tasks.md` and works through tasks.
+
+**Usage:**
+```
+/opsx-apply
+```
+
+**Note:** `/opsx-ralph-start` (not yet built) is planned as a dedicated implementation loop with minimal context loading and deeper GitHub Issues integration. For now, use this command — it already supports `plan.json` and GitHub Issues when a `plan.json` exists.
+
+**Model:** Checked at run time — stops if on Haiku. **Sonnet** for most implementation work. **Opus** for architecturally complex changes.
+
+---
+
+### `/opsx-verify`
+
+**Phase:** Review
+
+OpenSpec's built-in verification. Validates implementation against artifacts.
+
+**Usage:**
+```
+/opsx-verify
+```
+
+**Checks:**
+- **Completeness** — All tasks done, all requirements implemented
+- **Correctness** — Implementation matches spec intent
+- **Coherence** — Design decisions reflected in code
+- **Test coverage** — Every new PHP service/controller has a corresponding test file; every new Vue component has a test if the project uses Jest/Vitest
+- **Documentation** — New features and API endpoints are described in README.md or docs/
+
+**Note:** `/opsx-ralph-review` (not yet built) is planned as a dedicated review command that cross-references shared specs and creates GitHub Issues for findings. For now, use this command — it already supports GitHub Issues sync via `plan.json` when present.
+
+**Model:** Checked at run time — stops if on Haiku. **Sonnet** for most verification work. **Opus** for complex or large changes.
+
+---
+
+### `/opsx-sync`
+
+**Phase:** Archive
+
+Merges delta specs from the change into the main `openspec/specs/` directory.
+
+**Usage:**
+```
+/opsx-sync
+```
+
+**What it does:**
+- **ADDED** requirements → appended to main spec
+- **MODIFIED** requirements → replace existing in main spec
+- **REMOVED** requirements → deleted from main spec
+
+Usually done automatically during archive.
+
+---
+
+### `/sync-docs`
+
+**Phase:** Maintenance
+
+Check and sync documentation to reflect the current project state. Two targets: **app docs** (`{app}/docs/`) for a specific Nextcloud app's users and admins, and **dev docs** (`.claude/docs/`) for Claude and developers.
+
+**Usage:**
+```
+/sync-docs # prompts for target
+/sync-docs app # prompts for which app, then syncs its docs/
+/sync-docs app openregister # sync docs for a specific app
+/sync-docs dev # sync developer/Claude docs (.claude/docs/)
+```
+
+Before syncing, runs 4 preliminary checks in parallel — config.yaml rules vs writing-docs.md/writing-specs.md, Sources of Truth accuracy, writing-specs.md vs schema template alignment (`.claude/openspec/schemas/conduction/`), and forked schema drift from the upstream `spec-driven` schema. Reports gaps and asks whether to fix before proceeding.
+
+**App docs mode** (`{app}/docs/`) — checks the app's `README.md` (root), `docs/features/`, `docs/ARCHITECTURE.md`, `docs/FEATURES.md`, `docs/GOVERNMENT-FEATURES.md`, and any other user-facing `.md` files against the app's current specs. Also loads all company-wide ADRs from `apps-extra/.claude/openspec/architecture/` and any app-level ADRs as auditing context (never as link targets in app docs). Flags outdated descriptions, missing features, stale `[Future]` markers (with full removal checklist), broken links, duplicated content, writing anti-patterns, ADR compliance gaps (screenshots, i18n, API conventions), and missing GEMMA/ZGW/Forum Standaardisatie standards references. Never inserts links into `.claude/` paths. Always shows a diff and asks for confirmation before writing.
+
+**Dev docs mode** (`.claude/docs/`) — checks `commands.md`, `workflow.md`, `writing-specs.md`, `writing-docs.md`, `testing.md`, `getting-started.md`, `README.md`, plus the conduction schema (`.claude/openspec/schemas/conduction/schema.yaml`) and its `templates/spec.md` for alignment with `writing-specs.md`. Never changes intent without user confirmation. After syncing, runs a Phase 6 review of all commands and skills for stale references, outdated instructions, and redundant inline content — and asks whether to update them.
+
+Both modes enforce the [Documentation Principles](writing-docs.md) — duplication and wrong-audience content are flagged as issues, with direct links to the relevant writing-docs.md sections.
+
+**When to use:** After a significant batch of changes — new commands, archived features, updated specs, or structural changes to the project.
+
+---
+
+### `/opsx-archive`
+
+**Phase:** Archive
+
+Complete a change and preserve it for the historical record.
+
+**Usage:**
+```
+/opsx-archive
+```
+
+**What it does:**
+1. Checks artifact and task completion
+2. Syncs delta specs into main specs (if not already done)
+3. Moves the change to `openspec/changes/archive/YYYY-MM-DD-/`
+4. All artifacts are preserved for audit trail
+5. Updates or creates `docs/features/.md` — creates it if no matching feature doc exists
+6. Updates the feature overview table in `docs/features/README.md` (creates the file if it doesn't exist)
+7. Creates or updates `CHANGELOG.md` — completed tasks become versioned entries (version from `app-config.json`); uses [Keep a Changelog](https://keepachangelog.com/) format
+
+---
+
+### `/opsx-bulk-archive`
+
+**Phase:** Archive
+
+Archive multiple completed changes at once.
+
+**Usage:**
+```
+/opsx-bulk-archive
+```
+
+**When to use:** When you have several changes that are all complete and want to clean up.
+
+---
+
+### `/opsx-apply-loop`
+
+**Phase:** Full Lifecycle (experimental)
+
+Automated apply→verify loop for a single change in a specific app. Runs the implementation loop inside an isolated Docker container, optionally runs targeted tests on the host, then archives and syncs to GitHub.
+
+**Usage:**
+```
+/opsx-apply-loop # asks which app + change to run
+/opsx-apply-loop procest add-sla-tracking # run a specific app/change
+/opsx-apply-loop openregister seed-data # run in a different app
+```
+
+**What it does:**
+1. Selects app and change (scans across all apps, or uses provided arguments)
+2. Checks for a GitHub tracking issue (runs `/opsx-plan-to-issues` first if missing)
+3. Creates a `feature//` branch in the app's git repo
+4. Checks the Nextcloud environment is running
+5. Reads `test-plan.md` (if present) and classifies which test commands to include in the loop
+6. Asks whether to include a test cycle (tests run **outside the container** against the live Nextcloud app)
+7. Builds and starts an isolated Docker container — mounts the app directory + shared `.claude/` skills (read-only); no git, no GitHub
+8. Inside the container: runs `/opsx-apply` → `/opsx-verify` in a loop (max 5 iterations)
+ - CRITICAL issues retrigger the loop; WARNING issues also retrigger but never block archive
+ - At max iterations with only warnings remaining, archive still proceeds
+ - Seed data (ADR-016) is created/updated during apply as required
+9. Captures container logs to `.claude/logs/`, then removes container
+10. **If test cycle enabled:** runs targeted single-agent test commands on the host (max 3 test iterations); failures loop back into apply→verify
+11. **If test cycle enabled and deferred tests exist:** asks about multi-agent/broad tests from the test-plan that were excluded from the loop; runs them once if confirmed, with one final apply→verify if they fail
+12. Runs `/opsx-archive` on the host (after tests pass or tests skipped)
+13. Commits all changes in the app repo with a generated commit message
+14. Syncs GitHub: updates issue checkboxes, posts a completion comment, prompts to close
+15. Asks about test scenario conversion (deferred from archive)
+16. Shows a final report with iterations used, tasks completed, and what's next
+
+**When to use:** When you want hands-off implementation of a single change in one app. Prefer `/opsx-pipeline` for running multiple changes across apps in parallel.
+
+**Container design:** The container mounts the app directory at `/workspace` and the shared `.claude/` at `/workspace/.claude` (read-only). This gives the container's Claude session access to all shared skills without requiring git or GitHub. Each app is isolated — the container only touches one app directory.
+
+**Container limitations:** GitHub operations, `docker compose exec`, browser tests, and git commands are not available inside the container — all handled on the host after the container exits. Tests always run on the host against the live Nextcloud environment.
+
+**Cap impact:** High — runs apply + verify sequentially (up to 5 iterations), optionally followed by targeted tests (up to 3 test iterations). Each iteration is a full implementation + verification pass.
+
+**Model:** Sonnet recommended for most changes; Opus for complex architectural work. Asked at run time.
+
+**Requires:**
+- Docker running
+- `gh` CLI authenticated on the host
+- Nextcloud containers up (auto-started if not running — uses `docker compose -f` pointed at the docker-dev root's `.github/docker-compose.yml`)
+- **Container authentication** — the Docker container cannot use interactive OAuth, so it needs an explicit token. One of these environment variables must be set in your shell (see [Getting Started — Container authentication](getting-started.md#prerequisites) for full setup instructions):
+ 1. `CLAUDE_CODE_AUTH_TOKEN` (preferred) — uses your existing Claude Max/Pro subscription at no extra cost. Generate with `claude setup-token`, then `export CLAUDE_CODE_AUTH_TOKEN="..."` in `~/.bashrc`.
+ 2. `ANTHROPIC_API_KEY` (fallback) — uses prepaid API credits from console.anthropic.com (costs money). `export ANTHROPIC_API_KEY="sk-ant-api03-..."` in `~/.bashrc`.
+
+---
+
+### `/opsx-pipeline`
+
+**Phase:** Full Lifecycle (experimental)
+
+Process one or more OpenSpec changes through the full lifecycle in parallel — each change gets its own subagent, git worktree, feature branch, and PR.
+
+**Usage:**
+```
+/opsx-pipeline all # process all open proposals across all repos
+/opsx-pipeline procest # all open proposals in one app
+/opsx-pipeline sla-tracking routing # specific changes by name
+```
+
+**What it does:**
+1. Discovers open proposals (changes with `proposal.md` but not yet archived)
+2. Presents an execution plan and asks for confirmation
+3. Creates a git worktree and feature branch per change
+4. Launches up to 5 parallel subagents — each runs ff → apply → verify → archive
+5. Monitors progress and queues remaining changes as slots free up
+6. Creates a PR per completed change to `development`
+7. Reports full results including tasks completed, quality checks, and PR links
+
+**Subagent lifecycle per change:**
+```
+ff (artifacts) → plan-to-issues → apply (implement + tests + docs) → verify → archive → push + PR
+```
+
+**When to use:** When you have multiple open proposals ready to implement and want to run them hands-off.
+
+**Cap impact:** High — up to 5 agents running full implementations in parallel. Each agent may run for 10-30 minutes depending on change complexity.
+
+**Model:** Asked at run time with three options: one model for all sub-agents, choose per change, or auto-select by reading each proposal. **Haiku** for simple changes (config, text, minor fixes). **Sonnet** for standard feature work. **Opus** for complex architectural changes. The model applies per implementation sub-agent — choose based on change complexity and available quota.
+
+**Requires:** `gh` CLI authenticated; quality checks must pass per app (`composer check:strict` / `npm run lint`)
+
+---
+
+### `/opsx-onboard`
+
+**Phase:** Setup
+
+Get an overview of the current project's OpenSpec setup and active changes.
+
+**Usage:**
+```
+/opsx-onboard
+```
+
+---
+
+## App Management Commands
+
+Commands for creating, configuring, and maintaining Nextcloud apps. These work together in a lifecycle: `/app-design` → `/app-create` → `/app-explore` → `/app-apply` → `/app-verify`.
+
+For a full guide on the lifecycle, when to use each command, and how they relate to the OpenSpec workflow, see [App Lifecycle](app-lifecycle.md).
+
+---
+
+### `/app-design`
+
+**Phase:** Setup / Design
+
+Full upfront design for a new Nextcloud app — architecture research, competitor analysis, feature matrix, ASCII wireframes, and OpenSpec setup. Run this **before** `/app-create` for brand-new apps.
+
+**Usage:**
+```
+/app-design
+/app-design my-new-app
+```
+
+**What it does:**
+1. Researches the problem domain and existing solutions
+2. Produces architecture decisions, feature matrix, and ASCII wireframes
+3. Sets up the `openspec/` structure with initial design docs
+
+**Output:** Design documentation ready to hand off to `/app-create`.
+
+**Model:** Checked at run time — stops on Haiku. Asks which model to use and explains how to switch if the choice differs from the active model. **Sonnet** for general design sessions. ✅ **Opus** recommended — competitive research, architecture decisions, and full design document creation benefit from stronger reasoning.
+
+---
+
+### `/app-create`
+
+**Phase:** Setup
+
+Bootstrap a new Nextcloud app from the ConductionNL template, or onboard an existing repo. Always creates an `openspec/` configuration folder that tracks all app decisions.
+
+**Usage:**
+```
+/app-create
+/app-create my-new-app
+```
+
+**What it does:**
+1. Asks whether a local folder already exists — if yes, uses it as the base; if no, clones the template
+2. Collects basic identity: app ID, name, goal, one-line summary, Nextcloud category
+3. Asks about dependencies (OpenRegister, additional CI apps)
+4. Creates `openspec/app-config.json` and `openspec/README.md`
+5. Replaces all template placeholders (`AppTemplate`, `app-template`, etc.) across all files
+6. Creates the GitHub repository and branches (`main`, `development`, `beta`)
+7. Optionally sets branch protection and team access
+8. Optionally installs dependencies and enables the app in the local Nextcloud environment
+
+**Output:** Fully scaffolded app directory with correct identity, CI/CD workflows, and GitHub repo.
+
+**Requires:** `gh` CLI authenticated (`gh auth login`)
+
+---
+
+### `/app-explore`
+
+**Phase:** Design / Configuration
+
+Enter exploration mode for a Nextcloud app. Think through its goals, architecture, features, and Architectural Decision Records (ADRs). Updates `openspec/` files to capture decisions.
+
+**Usage:**
+```
+/app-explore
+/app-explore openregister
+```
+
+**What it does:**
+- Loads `openspec/app-config.json` for full context
+- Acts as a **thinking partner** — draws diagrams, asks questions, challenges assumptions
+- Captures decisions into `openspec/app-config.json`
+- Never writes application code — only `openspec/` files
+
+**Feature lifecycle:**
+```
+idea → planned → in-progress → done
+```
+When a feature moves to `planned` (has user stories + acceptance criteria), suggests `/opsx-ff {feature-name}` to create an OpenSpec change from it.
+
+**Important:** Run this before implementing anything. Features defined here become the inputs for `/opsx-ff`.
+
+**Model:** Checked at run time — stops on Haiku. Asks which model to use and explains how to switch if the choice differs from the active model. **Sonnet** for general app exploration. ✅ **Opus** recommended — feature strategy, ADRs, and competitive analysis benefit from stronger reasoning.
+
+---
+
+### `/app-apply`
+
+**Phase:** Configuration
+
+Applies `openspec/app-config.json` decisions back into the actual app files. The counterpart to `/app-explore`.
+
+**Usage:**
+```
+/app-apply
+/app-apply openregister
+```
+
+**What it does:**
+1. Loads `openspec/app-config.json`
+2. Compares current file values against config — builds a list of pending changes
+3. Shows a clear diff summary of what would change
+4. Asks for confirmation before applying any changes
+5. Updates only the tracked values in each file (IDs, names, namespaces, CI parameters) — never touches feature code
+6. Optionally runs `composer check:strict` to verify PHP changes are clean
+
+**In scope:** `appinfo/info.xml`, CI/CD workflow parameters, PHP namespaces and app ID constants, `composer.json`/`package.json` names, `webpack.config.js` app ID, `src/App.vue` OpenRegister gate, `README.md` header.
+
+**Out of scope:** Feature code, business logic, Vue components, PHP controllers. Use `/opsx-ff {feature-name}` for those.
+
+---
+
+### `/app-verify`
+
+**Phase:** Review / Audit
+
+Read-only audit. Checks every tracked app file against `openspec/app-config.json` and reports drift — without making any changes.
+
+**Usage:**
+```
+/app-verify
+/app-verify openregister
+```
+
+**What it does:**
+- Loads `openspec/app-config.json` and reads every tracked file
+- Reports each check as **CRITICAL** (will break CI or runtime), **WARNING** (wrong metadata), or **INFO** (cosmetic drift)
+- Shows exact current value vs expected value for every failing check
+- Recommends `/app-apply` if issues are found
+
+**When to use:** After `/app-apply` to confirm changes landed, or at any time to check for drift.
+
+---
+
+### `/clean-env`
+
+**Phase:** Setup / Reset
+
+Fully resets the OpenRegister Docker development environment.
+
+**Usage:**
+```
+/clean-env
+```
+
+**What it does:**
+1. Stops all Docker containers from the OpenRegister docker-compose
+2. Removes all containers and volumes (full data reset)
+3. Starts containers fresh
+4. Waits for Nextcloud to become ready
+5. Installs core apps: openregister, opencatalogi, softwarecatalog, nldesign, mydash
+
+**Important:** Destructive — removes all database data and volumes. Only use when a full reset is intended.
+
+After completion, verify at `http://localhost:8080` (admin/admin).
+
+**Model:** Checked at run time when invoked standalone — stops if on Opus (no reasoning needed, wastes quota), warns if on Sonnet and offers to switch. **Haiku** is the right fit for this task. Model check is skipped when this skill is called from within another skill.
+
+---
+
+## Team Role Commands
+
+Specialist agents representing different roles on the development team. Useful for getting a focused perspective on a change — architecture review, QA, product sign-off, etc.
+
+| Command | Role | Focus |
+|---------|------|-------|
+| `/team-architect` | Architect | API design, data models, cross-app dependencies |
+| `/team-backend` | Backend Developer | PHP implementation, entities, services, tests |
+| `/team-frontend` | Frontend Developer | Vue components, state management, UX |
+| `/team-po` | Product Owner | Business value, acceptance criteria, priority |
+| `/team-qa` | QA Engineer | Test coverage, edge cases, regression risk |
+| `/team-reviewer` | Code Reviewer | Standards, conventions, security, code quality |
+| `/team-sm` | Scrum Master | Progress tracking, blockers, sprint health |
+
+**Usage:**
+```
+/team-architect # review the API design for the active change
+/team-qa # get QA perspective on test coverage
+```
+
+**Model for `/team-architect`:** Checked at run time — stops if on Haiku. Asks which model to use and explains how to switch if the choice differs from the active model. **Opus** recommended — best multi-framework reasoning across NLGov, BIO2/NIS2, WCAG, Haven, AVG/GDPR. **Sonnet** not recommended — may miss nuances in complex compliance scenarios.
+
+---
+
+## Softwarecatalogus Commands (`/swc:*`)
+
+Commands specific to the VNG Softwarecatalogus client project. See `Softwarecatalogus/` (never commit to this repo).
+
+---
+
+### `/swc-test`
+
+**Phase:** Testing
+
+Run automated tests for the GEMMA Softwarecatalogus — API tests (Postman/Newman), browser tests (persona agents), or both.
+
+**Usage:**
+```
+/swc-test # choose mode interactively
+/swc-test api # API tests only
+/swc-test browser # browser tests only
+/swc-test personas # all 8 persona agents
+/swc-test all # everything
+```
+
+---
+
+### `/swc-update`
+
+**Phase:** Maintenance
+
+Sync GitHub issues from VNG-Realisatie/Softwarecatalogus, auto-generate acceptance criteria, and update test infrastructure to reflect current issue state.
+
+**Usage:**
+```
+/swc-update
+```
+
+---
+
+## Custom Conduction Commands
+
+These commands are workspace-level and available from any project within `apps-extra/`. They extend OpenSpec with GitHub Issues integration and Ralph Wiggum loops.
+
+---
+
+### `/create-pr`
+
+**Phase:** Git / Delivery
+
+Create a Pull Request from a branch in any repo. Handles the full flow interactively.
+
+**Usage:**
+```
+/create-pr
+```
+
+**What it does:**
+1. **Selects the repository** — scans for available git repos in the workspace, asks you to pick one (never assumes the current directory)
+2. **Confirms the source branch** — shows the current branch, lets you override
+3. **Recommends a target branch** based on the branching strategy; checks GitHub for an existing open PR on the same branch pair — if found, offers to view or update it instead
+4. **Checks for uncommitted or unpushed changes** — if any are found, offers to commit, stash, or continue; offers to push unpushed commits before continuing
+5. **Verifies global settings version** *(claude-code-config repo only)* — delegates to `/verify-global-settings-version`; pauses and offers a fix if a VERSION bump is missing
+6. **Discovers CI checks from `.github/workflows/`** — reads the repo's workflow files to determine exactly which checks CI will run, then mirrors them locally (never hardcodes a list)
+7. **Installs missing dependencies** (`vendor/`, `node_modules/`) if needed before running checks
+8. **Runs all discovered checks** — nothing skipped; slow checks (e.g. test suites) ask for confirmation first; shows a pass/fail table when done
+9. **Reads all commits and diffs** on the branch to draft a PR title and description from the actual changes
+10. **Shows the draft in chat** for review — you can ask to change or shorten it; the loop repeats until you approve
+11. **Pushes the branch and creates the PR** via `gh pr create`
+12. Reports the PR URL and next steps
+
+**Branching strategy:**
+
+| Source | Recommended target |
+|---|---|
+| `feature/*`, `bugfix/*` | `development` |
+| `development` | `beta` |
+| `beta` | `main` |
+| `hotfix/*` | `main` (or `beta`/`development`) |
+
+**Model:** Checked at run time — the command reads your active model from context and stops automatically if you're on Haiku (or anything weaker than Sonnet). Involves parsing CI workflows, detecting branch-protection rules, and reasoning about code diffs where mistakes have real consequences. **Sonnet** for most PRs. **Opus** when the repo uses reusable CI workflows, branch-protection rulesets, or a complex branching strategy — that's where it pays off most.
+
+**Requires:** `gh` CLI authenticated (`gh auth login`)
+
+---
+
+### `/verify-global-settings-version`
+
+**Phase:** Git / Delivery
+
+Checks whether `global-settings/VERSION` has been correctly bumped after any changes to files in the `global-settings/` directory. Run this before creating a PR on the `ConductionNL/claude-code-config` repo.
+
+**Usage:**
+```
+/verify-global-settings-version
+```
+
+**What it does:**
+1. Fetches `origin/main` to get the latest published version
+2. Diffs `global-settings/` between the current branch and `origin/main`
+3. Compares the branch `VERSION` against the `origin/main` `VERSION`
+4. Reports one of four outcomes:
+ - ✅ No changes to `global-settings/` — no bump needed
+ - ✅ Changes found and `VERSION` correctly bumped
+ - ❌ Changes found but `VERSION` not bumped — suggests the next semver and the command to apply it
+ - ⚠️ `VERSION` bumped but no other files changed — flags as unusual
+
+**When to use:**
+- Standalone: any time you modify a file in `global-settings/` and want to confirm the bump is in place before committing
+- Automatically: called by `/create-pr` when the selected repo is `ConductionNL/claude-code-config` — no need to run it separately in that flow
+
+**Semver rules for `global-settings/`:**
+- `1.0.0 → 1.1.0` — new permissions, guards, or behavior added
+- `1.0.0 → 2.0.0` — breaking change requiring manual migration
+
+**Model:** Checked at run time when invoked standalone — stops if on Opus (no reasoning needed, wastes quota), warns if on Sonnet and offers to switch. **Haiku** is the right fit for this task. Model check is skipped when this skill is called from within another skill.
+
+---
+
+### `/opsx-plan-to-issues`
+
+**Phase:** Planning → GitHub
+
+Converts an OpenSpec change's `tasks.md` into structured `plan.json` and creates corresponding GitHub Issues.
+
+**Usage:**
+```
+/opsx-plan-to-issues
+```
+
+**Prerequisites:**
+- A change with completed `tasks.md`
+- GitHub MCP server active or `gh` CLI authenticated
+- Git remote pointing to a ConductionNL repository
+
+**What it does:**
+
+1. **Finds the active change** in the current project's `openspec/changes/`
+2. **Detects the GitHub repo** from `git remote get-url origin`
+3. **Parses tasks.md** into structured JSON
+4. **Creates GitHub Issues:**
+ - One **tracking issue** (epic) with:
+ - Title: `[OpenSpec] `
+ - Body: proposal summary + task checklist
+ - Labels: `openspec`, `tracking`
+ - One **issue per task** with:
+ - Title: `[] `
+ - Body: description, acceptance criteria, spec ref, affected files
+ - Labels: `openspec`, ``
+5. **Saves `plan.json`** with all issue numbers linked
+
+**Output example:**
+```
+Created tracking issue: https://github.com/ConductionNL/opencatalogi/issues/42
+Created 5 task issues: #43, #44, #45, #46, #47
+Saved plan.json at: openspec/changes/add-search/plan.json
+
+Run /opsx-ralph-start to begin implementation.
+```
+
+**The plan.json it creates:**
+```json
+{
+ "change": "add-search",
+ "project": "opencatalogi",
+ "repo": "ConductionNL/opencatalogi",
+ "created": "2026-02-15T10:00:00Z",
+ "tracking_issue": 42,
+ "tasks": [
+ {
+ "id": 1,
+ "title": "Create SearchController",
+ "description": "Add new controller for search API endpoint",
+ "github_issue": 43,
+ "status": "pending",
+ "spec_ref": "openspec/specs/search/spec.md#requirement-search-api",
+ "acceptance_criteria": [
+ "GIVEN a search query WHEN GET /api/search?q=test THEN returns matching results"
+ ],
+ "files_likely_affected": [
+ "lib/Controller/SearchController.php"
+ ],
+ "labels": ["openspec", "add-search"]
+ }
+ ]
+}
+```
+
+---
+
+### `/opsx-ralph-start` *(not yet implemented)*
+
+**Phase:** Implementation
+
+Starts a Ralph Wiggum implementation loop driven by `plan.json`. This is the core of our minimal-context coding approach.
+
+**Usage:**
+```
+/opsx-ralph-start
+```
+
+**Prerequisites:**
+- A `plan.json` in the active change (created by `/opsx-plan-to-issues`)
+
+**What it does per iteration:**
+
+1. **Reads plan.json** — finds the next task with `"status": "pending"`
+2. **Sets status to `"in_progress"`** in plan.json
+3. **Reads ONLY the referenced spec section** — uses `spec_ref` to load just the relevant requirement, NOT the entire spec file
+4. **Implements the task** — following acceptance criteria and coding standards
+5. **Verifies** — checks acceptance criteria are met
+6. **Updates progress:**
+ - Sets task to `"completed"` in plan.json
+ - Checks off boxes in tasks.md
+ - Closes the GitHub issue with a summary comment
+ - Updates the tracking issue checklist
+7. **Loops** — picks up the next pending task, or stops if all done
+
+**Why minimal context matters:**
+
+Each iteration loads only:
+- `plan.json` (the task list — typically 1-2 KB)
+- One spec section via `spec_ref` (the specific requirement — a few paragraphs)
+- The affected files
+
+It does NOT load:
+- proposal.md
+- design.md
+- Other spec files
+- The full tasks.md
+
+This prevents context window bloat and keeps each iteration fast and focused.
+
+**Resuming after interruption:**
+
+If the loop is interrupted (context limit, error, etc.), simply run `/opsx-ralph-start` again. It reads `plan.json`, finds the first non-completed task, and continues from there.
+
+---
+
+### `/opsx-ralph-review` *(not yet implemented)*
+
+**Phase:** Review
+
+Verifies the completed implementation against all spec requirements and shared conventions. Creates a structured review report.
+
+**Usage:**
+```
+/opsx-ralph-review
+```
+
+**Prerequisites:**
+- All tasks in plan.json should be `"completed"`
+
+**What it does:**
+
+1. **Loads full context** — proposal, all delta specs, tasks, plan.json
+2. **Checks completeness:**
+ - All tasks completed?
+ - All GitHub issues closed?
+ - All task checkboxes checked?
+3. **Checks spec compliance:**
+ - For each ADDED requirement: does the implementation exist?
+ - For each MODIFIED requirement: is the old behavior changed?
+ - For each REMOVED requirement: is the deprecated code gone?
+ - Do GIVEN/WHEN/THEN scenarios match the code behavior?
+4. **Cross-references shared specs:**
+ - `nextcloud-app/spec.md` — correct app structure, DI, route ordering
+ - `api-patterns/spec.md` — URL patterns, CORS, error responses
+ - `nl-design/spec.md` — design tokens, accessibility
+ - `docker/spec.md` — environment compatibility
+5. **Categorizes findings:**
+ - **CRITICAL** — Spec MUST/SHALL requirement not met
+ - **WARNING** — SHOULD requirement not met or partial compliance
+ - **SUGGESTION** — Improvement opportunity
+6. **Generates `review.md`** in the change directory
+7. **Creates GitHub Issue** if CRITICAL/WARNING findings exist
+
+**Output example:**
+```
+Review: add-search
+Tasks completed: 5/5
+GitHub issues closed: 5/5
+Spec compliance: PASS (with warnings)
+
+Findings:
+- 0 CRITICAL
+- 2 WARNING
+ - Missing CORS headers on /api/search (api-patterns spec)
+ - No pagination metadata in response (api-patterns spec)
+- 1 SUGGESTION
+ - Consider adding rate limiting
+
+Review saved: openspec/changes/add-search/review.md
+GitHub issue created: #48 [Review] add-search: 0 critical, 2 warnings
+```
+
+---
+
+## OpenSpec CLI Commands
+
+These are terminal commands (not Claude slash commands) for managing specs directly.
+
+| Command | Description |
+|---------|-------------|
+| `openspec init --tools claude` | Initialize OpenSpec in a project |
+| `openspec list --changes` | List all active changes |
+| `openspec list --specs` | List all specs |
+| `openspec show ` | View details of a change or spec |
+| `openspec status --change ` | Show artifact completion status |
+| `openspec validate --all` | Validate all specs and changes |
+| `openspec validate --strict` | Strict validation (errors on warnings) |
+| `openspec update` | Regenerate AI tool config after CLI upgrade |
+| `openspec schema which` | Show which schema is being used |
+| `openspec config list` | Show all configuration |
+
+Add `--json` to any command for machine-readable output.
+
+---
+
+## Testing Commands
+
+For detailed guidance on when to use each command, typical testing workflows, and situational advice, see [testing.md](testing.md).
+
+> **Note on agentic browser testing:** `/test-app`, `/test-counsel`, and `/feature-counsel` use Playwright MCP browsers to explore live applications. Results may include false positives (elements not found due to timing) or false negatives (bugs missed due to exploration order). Always verify critical findings manually.
+
+---
+
+### `/test-app`
+
+**Phase:** Testing
+
+Run automated browser tests for any Nextcloud app in this workspace. Explores every page, button, and form guided by the app's documentation and specs.
+
+**Usage:**
+```
+/test-app
+/test-app procest
+```
+
+**Modes:**
+- **Quick (1 agent)** — One agent walks through the entire app. Fast, good for smoke testing. Low cap impact.
+- **Full (6 agents)** — Six parallel agents each with a different perspective: Functional, UX, Performance, Accessibility, Security, API. More thorough. High cap impact.
+
+**What it does:**
+1. Selects the app (from argument or prompt)
+2. Chooses Quick or Full mode
+3. Checks `{APP}/test-scenarios/` for active scenarios — asks whether to include them
+4. Reads `{APP}/docs/features/` to understand what to test
+5. Asks which model to use for agents (Haiku default, Sonnet, or Opus)
+6. Launches agents, each reading docs, logging in, and testing from their perspective
+7. Agents execute any included test scenario steps before free exploration
+8. Writes per-perspective results to `{APP}/test-results/` and a summary to `{APP}/test-results/README.md`
+
+**Model:** Asked at run time (applies to all sub-agents). **Haiku** (default) — fastest, lowest quota cost. **Sonnet** — more nuanced analysis, larger context window. **Opus** — deepest coverage; significant quota cost in Full mode. See [parallel-agents.md](parallel-agents.md) for context window sizes, subscription quota limits, and how they differ.
+
+**Cap impact:** See [parallel-agents.md](parallel-agents.md).
+
+---
+
+### `/test-counsel`
+
+**Phase:** Testing
+
+Test a Nextcloud app from 8 persona perspectives simultaneously: Henk, Fatima, Sem, Noor, Annemarie, Mark, Priya, Jan-Willem.
+
+**Usage:**
+```
+/test-counsel
+```
+
+**What it does:**
+- Launches 8 parallel browser agents — one per persona (model is user-selected at run time; Haiku is the default)
+- Each agent reads its persona card and relevant test scenarios before testing
+- Tests from the perspective of that persona's role, technical level, and priorities
+- Produces a combined report with findings per persona
+- Writes results to `{APP}/test-results/`
+
+**Model:** Asked at run time (applies to all 8 agents). **Haiku** (default) — fastest, lowest quota cost. **Sonnet** — more nuanced persona findings, larger context window. **Opus** — deepest analysis; significant quota cost with 8 agents. See [parallel-agents.md](parallel-agents.md) for context window sizes, subscription quota limits, and how they differ.
+
+**Cap impact:** Very high — 8 parallel agents. Open a fresh Claude window before running. See [parallel-agents.md](parallel-agents.md).
+
+---
+
+### `/feature-counsel`
+
+**Phase:** Discovery / Ideation
+
+Analyse a Nextcloud app's OpenSpec from 8 persona perspectives and suggest additional features or improvements.
+
+**Usage:**
+```
+/feature-counsel
+```
+
+**What it does:**
+- Reads the app's OpenSpec, specs, and existing features
+- Each of the 8 personas considers what's missing from their perspective
+- Produces a consolidated list of suggested features and improvements
+- Does not test the live app — reads specs and docs only
+
+**Model:** Asked at run time (applies to all 8 agents). No browser required — agents read specs and docs only. **Sonnet** (default) — recommended; no context window concern without browser snapshots, and better reasoning produces more useful suggestions. **Haiku** — faster, lower quota, good for a quick broad pass. **Opus** — deepest reasoning for complex architectural gaps; use with full mode (8 agents) sparingly.
+
+**Cap impact:** Very high — 8 parallel agents. See [parallel-agents.md](parallel-agents.md).
+
+---
+
+### Commands (Single-Agent)
+
+---
+
+### `/test-functional`
+
+**Phase:** Testing
+
+Feature correctness via browser — executes GIVEN/WHEN/THEN scenarios from specs against the live app.
+
+**Usage:**
+```
+/test-functional
+```
+
+---
+
+### `/test-api`
+
+**Phase:** Testing
+
+REST API endpoint testing. Checks endpoints, authentication, pagination, and error responses.
+
+**Usage:**
+```
+/test-api
+```
+
+---
+
+### `/test-accessibility`
+
+**Phase:** Testing
+
+WCAG 2.1 AA compliance using axe-core, plus manual keyboard and focus checks.
+
+**Usage:**
+```
+/test-accessibility
+```
+
+---
+
+### `/test-performance`
+
+**Phase:** Testing
+
+Load times, API response times, and network request analysis via browser timing APIs.
+
+**Usage:**
+```
+/test-performance
+```
+
+---
+
+### `/test-security`
+
+**Phase:** Testing
+
+OWASP Top 10, Nextcloud roles, authorization, XSS, CSRF, sensitive data exposure.
+
+**Usage:**
+```
+/test-security
+```
+
+---
+
+### `/test-regression`
+
+**Phase:** Testing
+
+Cross-feature regression — verifies changes don't break unrelated flows.
+
+**Usage:**
+```
+/test-regression
+```
+
+---
+
+### `/test-persona-*`
+
+**Phase:** Testing
+
+Single-persona deep dive. Use when you want one persona's full assessment without launching all eight:
+
+| Command | Persona | Role |
+|---------|---------|------|
+| `/test-persona-henk` | **Henk Bakker** | Elderly citizen — low digital literacy |
+| `/test-persona-fatima` | **Fatima El-Amrani** | Low-literate migrant citizen |
+| `/test-persona-sem` | **Sem de Jong** | Young digital native |
+| `/test-persona-noor` | **Noor Yilmaz** | Municipal CISO / functional admin |
+| `/test-persona-annemarie` | **Annemarie de Vries** | VNG standards architect |
+| `/test-persona-mark` | **Mark Visser** | MKB software vendor |
+| `/test-persona-priya` | **Priya Ganpat** | ZZP developer / integrator |
+| `/test-persona-janwillem` | **Jan-Willem van der Berg** | Small business owner |
+
+**Usage:**
+```
+/test-persona-henk
+/test-persona-priya
+```
+
+**Use when:** You know which persona is most affected by a change, or when you've run `/test-counsel` and want a deeper single-perspective follow-up. One agent instead of eight — lower cap cost.
+
+**Cap impact:** Low — single agent. See [parallel-agents.md](parallel-agents.md).
+
+---
+
+## Test Scenario Commands
+
+Test scenarios are reusable, Gherkin-style descriptions of user journeys that can be executed by any test command. They live in `{APP}/test-scenarios/TS-NNN-slug.md` and are automatically discovered by `/test-app`, `/test-counsel`, and `/test-persona-*` when they run.
+
+> **Test scenario vs test case**: A scenario is a high-level, user-centered description of *what* to verify and *for whom* — one concrete flow, written in Given-When-Then. It is broader than a click-by-click test case but more specific than a spec requirement.
+
+---
+
+### `/test-scenario-create`
+
+**Phase:** Testing
+
+Guided wizard for creating a well-structured test scenario for a Nextcloud app.
+
+**Usage:**
+```
+/test-scenario-create
+/test-scenario-create openregister
+```
+
+**What it does:**
+1. Determines the next ID (`TS-NNN`) by scanning existing scenarios
+2. Asks for title, goal, category (functional/api/security/accessibility/performance/ux/integration), and priority
+3. Shows relevant personas and asks which this scenario targets
+4. Suggests which test commands should automatically include it
+5. Auto-suggests tags based on category and title
+6. Guides through Gherkin steps (Given/When/Then), test data, and acceptance criteria
+7. Generates persona-specific notes for each linked persona
+8. Saves to `{APP}/test-scenarios/TS-NNN-slug.md`
+
+**Scenario categories and suggested personas:**
+
+| Category | Suggested personas |
+|---|---|
+| functional | Mark Visser, Sem de Jong |
+| api | Priya Ganpat, Annemarie de Vries |
+| security | Noor Yilmaz |
+| accessibility | Henk Bakker, Fatima El-Amrani |
+| ux | Henk Bakker, Jan-Willem, Mark Visser |
+| performance | Sem de Jong, Priya Ganpat |
+| integration | Priya Ganpat, Annemarie de Vries |
+
+---
+
+### `/test-scenario-run`
+
+**Phase:** Testing
+
+Execute one or more test scenarios against the live Nextcloud environment using a browser agent.
+
+**Usage:**
+```
+/test-scenario-run # list and choose
+/test-scenario-run TS-001 # run specific scenario
+/test-scenario-run openregister TS-001 # run from specific app
+/test-scenario-run --tag smoke # run all smoke-tagged scenarios
+/test-scenario-run --all openregister # run all scenarios for an app
+/test-scenario-run --persona priya-ganpat # run all Priya's scenarios
+```
+
+**What it does:**
+1. Discovers scenario files in `{APP}/test-scenarios/`
+2. Filters by tag, persona, or ID as specified
+3. Asks which environment to test against (local or custom URL)
+4. Asks whether to use Haiku (default, cost-efficient) or Sonnet (for complex flows)
+5. Launches a browser agent per scenario (parallelised up to 5 for multiple)
+6. Agent verifies preconditions, follows Given-When-Then steps, checks each acceptance criterion
+7. Writes results to `{APP}/test-results/scenarios/`
+8. Synthesises a summary report for multiple runs
+
+**Model:** Asked at run time. **Haiku** (default) — fast, cost-efficient. **Sonnet** — for complex multi-step flows or ambiguous UI states where Haiku may misread the interface. Cap cost scales with the number of scenarios run in parallel.
+
+**Cap impact:** Low for single scenario; medium for multiple. See [parallel-agents.md](parallel-agents.md).
+
+**Result statuses**: ✅ PASS / ❌ FAIL / ⚠️ PARTIAL / ⛔ BLOCKED
+
+---
+
+### `/test-scenario-edit`
+
+**Phase:** Testing
+
+Edit an existing test scenario — update any field (metadata or content) interactively.
+
+**Usage:**
+```
+/test-scenario-edit # list all scenarios, pick one
+/test-scenario-edit TS-001 # open specific scenario
+/test-scenario-edit openregister TS-001 # open from specific app
+```
+
+**What it does:**
+1. Locates the scenario file
+2. Shows a summary of current values (status, priority, category, personas, tags, spec refs)
+3. Asks what scope to edit: metadata only / content only / both / status only / tags only
+4. Walks through each field in scope, showing the current value and asking for the new one
+5. Supports `+tag` / `-tag` syntax for incremental tag changes, same for personas
+6. Regenerates persona notes if the personas list changed
+7. Optionally renames the file if the title changed
+8. Writes the updated file and shows a diff-style summary
+
+---
+
+### How existing test commands use scenarios
+
+| Command | Behaviour when scenarios exist |
+|---|---|
+| `/test-app` | Asks to include active scenarios before launching agents. Agents execute scenario steps before free exploration. |
+| `/test-counsel` | Asks to include scenarios, grouped by persona. Each persona agent receives only the scenarios tagged with their slug. |
+| `/test-persona-*` | Scans for scenarios matching that persona's slug. Asks to run them before free exploration in Step 2. |
+
+---
+
+## Tender & Ecosystem Intelligence Commands
+
+These commands support the competitive analysis and ecosystem gap-finding workflow. They operate on the `concurrentie-analyse/intelligence.db` SQLite database and require the database to exist before running.
+
+---
+
+### `/tender-scan`
+
+**Phase:** Intelligence Gathering
+
+Scrape TenderNed for new tenders, import them into SQLite, and classify unclassified tenders by software category using a local Qwen model.
+
+**Usage:**
+```
+/tender-scan
+```
+
+**What it does:**
+1. Runs `concurrentie-analyse/tenders/scrape_tenderned.py` to fetch fresh data
+2. Imports new tenders into the intelligence database
+3. Classifies unclassified tenders using Qwen via `localhost:11434`
+4. Reports new tenders found, classified, and any new gaps detected
+
+**Requires:** Local Qwen model running on Ollama (`http://localhost:11434`)
+
+---
+
+### `/tender-status`
+
+**Phase:** Intelligence Monitoring
+
+Show a dashboard of the tender intelligence database — totals by source, category, status, gaps, and recent activity.
+
+**Usage:**
+```
+/tender-status
+```
+
+**What it does:**
+- Queries `concurrentie-analyse/intelligence.db` for live stats
+- Shows tenders by source, status, and category (top 15)
+- Highlights categories with Conduction coverage vs gaps
+- Shows top integration systems and ecosystem gaps
+
+**Model:** Checked at run time when invoked standalone — stops if on Opus (no reasoning needed, wastes quota), warns if on Sonnet and offers to switch. **Haiku** is the right fit for this task. Model check is skipped when this skill is called from within another skill.
+
+---
+
+### `/tender-gap-report`
+
+**Phase:** Gap Analysis
+
+Generate a gap analysis report — software categories that appear in government tenders but have no Conduction product.
+
+**Usage:**
+```
+/tender-gap-report
+```
+
+**What it does:**
+1. Queries the database for categories with tenders but no `conduction_product`
+2. Generates a markdown report at `concurrentie-analyse/reports/gap-report-{date}.md`
+3. Includes top 5 gaps with tender details, organisations, and key requirements
+4. Cross-references with `application-roadmap.md` to flag already-tracked gaps
+5. Recommends which gaps to investigate first
+
+---
+
+### `/ecosystem-investigate `
+
+**Phase:** Competitive Research
+
+Deep-dive research into a software category — find and analyze open-source competitors using GitHub, G2, Capterra, AlternativeTo, and TEC.
+
+**Usage:**
+```
+/ecosystem-investigate bookkeeping
+```
+
+**What it does:**
+1. Loads category context and related tenders from the intelligence database
+2. Uses the browser pool (browser-1 through browser-5) to scrape 5-10 competitors from multiple source types
+3. Creates competitor profiles in `concurrentie-analyse/{category}/{competitor-slug}/`
+4. Inserts competitors and feature data into the database with provenance tracking
+5. Presents a comparison table and recommendation for Nextcloud ecosystem fit
+
+**Model:** Checked at run time — stops if on Haiku. Asks which model to use and explains how to switch if the choice differs from the active model. **Sonnet** for most categories. **Opus** for high-stakes or complex categories where strategic depth matters.
+
+---
+
+### `/ecosystem-propose-app `
+
+**Phase:** Product Planning
+
+Generate a full app proposal for a software category gap, using tender requirements and competitor research as input.
+
+**Usage:**
+```
+/ecosystem-propose-app bookkeeping
+```
+
+**What it does:**
+1. Gathers all tenders, requirements, competitors, and integrations for the category
+2. Generates a structured proposal following the template in `concurrentie-analyse/application-roadmap.md`
+3. Appends the proposal to `application-roadmap.md`
+4. Inserts the proposal into the `app_proposals` database table
+5. Optionally bootstraps the app with `/app-create`
+
+**Model:** Checked at run time — stops if on Haiku. Asks which model to use and explains how to switch if the choice differs from the active model. **Sonnet** for most proposals. **Opus** for high-stakes proposals where architectural fit and market analysis need extra depth.
+
+---
+
+### `/intelligence-update [source]`
+
+**Phase:** Intelligence Maintenance
+
+Pull latest data from external sources into the intelligence database. Syncs sources that are past their scheduled interval.
+
+**Usage:**
+```
+/intelligence-update # sync all sources that are due
+/intelligence-update all # force sync every source
+/intelligence-update wikidata-software # sync one specific source
+```
+
+**Sources and intervals:**
+
+| Source | Interval |
+|--------|----------|
+| `tenderned` | 24h |
+| `wikidata-software` | 7 days |
+| `wikipedia-comparisons` | 7 days |
+| `awesome-selfhosted` | 7 days |
+| `github-issues` | 7 days |
+| `dpg-registry` | 7 days |
+| `developers-italia` | 7 days |
+| `gemma-release` | yearly |
+
+**What it does:**
+1. Checks `source_syncs` table for overdue sources
+2. Runs `concurrentie-analyse/scripts/sync/sync_{source}.py` for each
+3. Updates sync status, records count, and error messages
+4. Displays a summary table of all sources with their sync status
+
+**Model:** Checked at run time when invoked standalone — stops if on Opus (no reasoning needed, wastes quota), warns if on Sonnet and offers to switch. **Haiku** is the right fit for this task. Model check is skipped when this skill is called from within another skill.
+
+---
+
+### Tender Intelligence Workflow
+
+```
+/tender-scan (fetch & classify new tenders)
+ │
+ ▼
+/tender-status (review dashboard)
+ │
+ ▼
+/tender-gap-report (identify gaps)
+ │
+ ▼
+/ecosystem-investigate (research competitors for top gap)
+ │
+ ▼
+/ecosystem-propose-app (generate app proposal)
+ │
+ ▼
+/app-design (design the new app)
+```
+
+**Keep data fresh:** Run `/intelligence-update` weekly and `/tender-scan` daily to keep the database current.
+
+---
+
+## Command Flow Cheat Sheet
+
+```
+/opsx-explore (optional: investigate first)
+ │
+ ▼
+/opsx-new (start a change)
+ │
+ ▼
+/opsx-ff (generate all specs at once)
+ │ OR
+/opsx-continue (generate specs one by one)
+ │
+ ▼
+ [Human review & edit specs]
+ │
+ ▼
+/feature-counsel (optional: 8-persona feedback on specs)
+ │
+ ▼
+/opsx-plan-to-issues (optional: tasks → JSON + GitHub Issues)
+ │
+ ▼
+/opsx-apply (implement tasks)
+ │
+ ▼
+/opsx-verify (verify implementation against specs)
+ │
+ ▼
+/test-functional (confirm feature behaves as specced)
+/test-counsel (user acceptance — all 8 personas)
+/test-app (optional: full technical sweep)
+ │
+ ▼
+/create-pr (create PR on GitHub)
+ │
+ ▼
+/opsx-archive (complete & preserve)
+```
+
+See [testing.md](testing.md) for situational testing guidance and recommended testing order.
+
+For the app lifecycle flow (`/app-design` → `/app-create` → `/app-explore` → `/app-apply` → `/app-verify`), see [app-lifecycle.md](app-lifecycle.md).
diff --git a/docs/claude/docker.md b/docs/claude/docker.md
new file mode 100644
index 0000000..45cde7c
--- /dev/null
+++ b/docs/claude/docker.md
@@ -0,0 +1,36 @@
+# Docker Environment
+
+Uses `openregister/docker-compose.yml`. Default starts db + nextcloud + n8n.
+
+## Reset
+
+```bash
+bash clean-env.sh
+```
+Or use the `/clean-env` skill.
+
+## Available Profiles
+
+Add one or more profiles with `--profile `:
+
+| Profile | What it adds |
+|---------|-------------|
+| `ai` | AI/LLM services |
+| `ui` | Separate frontend UI container |
+| `exapps` | ExApp sidecar containers |
+| `solr` | Solr search |
+| `elasticsearch` | Elasticsearch |
+| `ollama` | Local LLM via Ollama |
+| `standalone` | Standalone mode (no Nextcloud) |
+| `mariadb` | MariaDB instead of default DB |
+| `openproject` | OpenProject |
+| `xwiki` | XWiki |
+| `ox` | Open-Xchange |
+| `valtimo` | Valtimo BPM |
+| `openzaak` | OpenZaak |
+| `openklant` | OpenKlant |
+
+**Example:**
+```bash
+docker compose --profile ui --profile exapps up -d
+```
diff --git a/docs/claude/exapp-sidecar-status.md b/docs/claude/exapp-sidecar-status.md
new file mode 100644
index 0000000..3805ef9
--- /dev/null
+++ b/docs/claude/exapp-sidecar-status.md
@@ -0,0 +1,131 @@
+# ExApp Sidecar Wrappers — Status Report
+
+**Date:** 2026-03-05
+**Goal:** Get ExApp sidecar wrappers (OpenKlant, OpenZaak, Valtimo, OpenTalk, Keycloak) up and running, following the n8n pattern.
+
+## Summary
+
+All 5 ExApp Docker containers are **running and healthy**. They are **registered with AppAPI** in Nextcloud. However, AppAPI's heartbeat mechanism is not fully completing the initialization cycle for all apps yet.
+
+## What's Done
+
+### Repositories & Submodules
+- **keycloak-nextcloud** — New repo created at ConductionNL/keycloak-nextcloud, added as submodule
+- **open-webui-nextcloud** — Added as submodule (repo already existed)
+- **openklant, openzaak, valtimo, opentalk** — Existing submodules, `ex_app/lib/main.py` rewritten to use `nc_py_api` pattern
+
+### Docker Images Built
+| App | Image | Base | Status |
+|-----|-------|------|--------|
+| Keycloak | `ghcr.io/conductionnl/keycloak-nextcloud:latest` | UBI9-minimal + microdnf Python + Keycloak 26.5.4 | Built, running |
+| OpenKlant | `ghcr.io/conductionnl/openklant-exapp:latest` | maykinmedia/open-klant:2.15.0 | Built, running |
+| OpenZaak | `ghcr.io/conductionnl/openzaak-exapp:latest` | openzaak/open-zaak:1.27.0 | Built, running |
+| Valtimo | `ghcr.io/conductionnl/valtimo-exapp:latest` | eclipse-temurin:17-jre-jammy + ritense/valtimo-backend:12.0.0 | Built, running |
+| OpenTalk | `ghcr.io/conductionnl/opentalk-exapp:latest` | python:3.11-slim + opentalk controller v0.31.0-3 | Built, running |
+
+### Docker Compose (openregister/docker-compose.yml)
+- All 5 ExApp services added under `commonground` + `exapps` profiles
+- Shared infrastructure: `exapp-redis` (Redis 7), `exapp-livekit` (LiveKit WebRTC)
+- PostgreSQL databases created: keycloak, openklant, openzaak, opentalk, valtimo
+- AppAPI-generated secrets hardcoded in compose for each ExApp
+- Healthchecks use Python urllib (no wget/curl dependency)
+
+### AppAPI Registration
+- 5 manual-install daemons registered (one per ExApp)
+- All 5 apps registered in `oc_ex_apps` table
+- Keycloak: **enabled**, OpenKlant/OpenZaak/Valtimo/OpenTalk: **disabled** (pending init)
+
+### Port Assignments (assigned by AppAPI)
+| App | Port |
+|-----|------|
+| n8n | 23000 |
+| Keycloak | 23002 |
+| OpenZaak | 23003 |
+| Valtimo | 23004 |
+| OpenKlant | 23005 |
+| OpenTalk | 23005 |
+
+## Current Container Status
+
+All 8 ExApp containers running and healthy:
+```
+openregister-exapp-keycloak healthy
+openregister-exapp-openklant healthy
+openregister-exapp-openzaak healthy
+openregister-exapp-valtimo healthy
+openregister-exapp-opentalk healthy
+openregister-exapp-livekit healthy
+openregister-exapp-redis healthy
+openregister-exapp-n8n healthy
+```
+
+## What's Left / Known Issues
+
+### 1. AppAPI Heartbeat → Init Cycle Not Completing
+AppAPI checks heartbeat on each ExApp's assigned port. Some ExApps (valtimo, opentalk) are getting heartbeat counts but others (keycloak, openklant, openzaak) are stuck at 0. This may be an AppAPI internal scheduling issue or related to the high failure count from before we fixed the heartbeat endpoints.
+
+**Possible fix:** Unregister and re-register the stuck ExApps, or investigate AppAPI's heartbeat scheduling.
+
+### 2. Internal Services Not Started
+The heartbeat endpoints return `{"status":"waiting"}` (HTTP 200) because the wrapped services (Django, Spring Boot, Rust) only start when AppAPI calls `/init` or `/enabled`. This is the chicken-and-egg: heartbeat must succeed → AppAPI calls init → internal service starts → heartbeat returns "ok".
+
+The 200 status fix should resolve this — AppAPI should proceed to call `/init` once it sees successful heartbeats.
+
+### 3. ZaakAfhandelApp Admin Settings Bug
+`/settings/admin/app_api` crashes with "Unknown named parameter $app" in `ZaakAfhandelAppAdmin.php:55`. This is unrelated to the ExApp work but blocks the AppAPI admin UI.
+
+### 4. PostGIS Not Available
+OpenZaak may need PostGIS extension but the pgvector PostgreSQL image doesn't include it. OpenZaak may need a different approach (PostGIS Docker image or skip geo features).
+
+### 5. Commits Pending
+Changes to entrypoint.sh, main.py (heartbeat fix), Dockerfiles, and docker-compose.yml are local only. Need to commit and push to feature branches for each submodule.
+
+## Files Changed
+
+### docker-compose (openregister/)
+- `docker-compose.yml` — Added 5 ExApp services, volumes, healthchecks, secrets, port assignments
+
+### keycloak-nextcloud/ (new repo)
+- Full ExApp structure: `ex_app/lib/main.py`, `Dockerfile`, `entrypoint.sh`, `appinfo/info.xml`, CI workflows
+
+### openklant/
+- `ex_app/lib/main.py` — Rewritten with nc_py_api, heartbeat returns 200
+- `Dockerfile` — Updated base image to 2.15.0
+- `entrypoint.sh` — Fixed to use `python3 ex_app/lib/main.py`
+
+### openzaak/
+- `ex_app/lib/main.py` — Rewritten with nc_py_api, heartbeat returns 200
+- `Dockerfile` — Updated base image to 1.27.0
+- `entrypoint.sh` — Fixed to use `python3 ex_app/lib/main.py`
+- `appinfo/info.xml` — Fixed registry to ghcr.io
+
+### valtimo/
+- `ex_app/lib/main.py` — Rewritten with nc_py_api, heartbeat returns 200
+- `Dockerfile` — Rewritten with proper Python install
+- `entrypoint.sh` — Fixed to use `python3 ex_app/lib/main.py`
+- `appinfo/info.xml` — Fixed registry to ghcr.io
+
+### opentalk/
+- `ex_app/lib/main.py` — Rewritten with nc_py_api, heartbeat returns 200
+- `Dockerfile` — Added controller.toml config file
+- `controller.toml` — New minimal config for OpenTalk controller
+- `entrypoint.sh` — Fixed to use `python3 ex_app/lib/main.py`
+- `appinfo/info.xml` — Fixed registry to ghcr.io
+
+## Key Learnings
+
+1. **Keycloak UBI9-micro has no package manager** — use UBI9-minimal with microdnf instead
+2. **glibc compatibility matters** — Python from Debian/Ubuntu cannot run on UBI9 (glibc 2.38 vs 2.34)
+3. **AppAPI assigns unique ports** per ExApp — containers must listen on the assigned port, not hardcoded 23000
+4. **APP_SECRET must match** between docker-compose env and AppAPI's database — get secrets from `oc_ex_apps` table
+5. **Healthcheck must return 200** even when internal service isn't running, or AppAPI won't proceed to `/init`
+6. **Docker compose `${VAR}` in healthcheck** resolves at compose level, not container level — use Python `os.environ` or `$$VAR` instead
+
+## Next Steps
+
+1. Investigate why some ExApps aren't getting heartbeat checks from AppAPI
+2. Once heartbeats work, verify `/init` is called and internal services start
+3. Enable all 4 disabled ExApps
+4. Test through browser at http://localhost:8080
+5. Commit all changes to feature branches
+6. Push Docker images to ghcr.io
diff --git a/docs/claude/frontend-standards.md b/docs/claude/frontend-standards.md
new file mode 100644
index 0000000..4e8eef7
--- /dev/null
+++ b/docs/claude/frontend-standards.md
@@ -0,0 +1,94 @@
+# Frontend Standards
+
+Standards that apply to all Conduction Nextcloud apps. These are enforced via ESLint rules and code review.
+
+## OpenRegister Dependency Check
+
+All apps that depend on OpenRegister (everything except `nldesign` and `mydash`) must show an empty state when OpenRegister is not installed, instead of a broken UI.
+
+### Backend (SettingsController)
+
+The settings endpoint must return `openRegisters` and `isAdmin` fields:
+
+```php
+use OCP\App\IAppManager;
+use OCP\IGroupManager;
+use OCP\IUserSession;
+
+// In constructor: inject IAppManager, IGroupManager, IUserSession
+
+// In the index() / settings GET endpoint:
+$user = $this->userSession->getUser();
+$isAdmin = $user !== null && $this->groupManager->isAdmin($user->getUID());
+
+return new JSONResponse([
+ 'openRegisters' => in_array(needle: 'openregister', haystack: $this->appManager->getInstalledApps()),
+ 'isAdmin' => $isAdmin,
+ 'config' => $this->settingsService->getSettings(),
+]);
+```
+
+The controller should also have the standardized `getObjectService()` and `getConfigurationService()` methods for lazy-loading OpenRegister services (see softwarecatalog/opencatalogi for reference).
+
+### Frontend Store (Pinia)
+
+The settings store must expose:
+- `openRegisters: false` in state
+- `isAdmin: false` in state
+- `hasOpenRegisters` getter
+- `getIsAdmin` getter
+- Read both from the API response in `fetchSettings()`
+
+### Frontend App.vue
+
+Three-state conditional in the template:
+
+1. **OpenRegister missing** (`storesReady && !hasOpenRegisters`): `NcEmptyContent` inside `NcAppContent` with class `open-register-missing` — no sidebar, no navigation
+2. **Normal** (`storesReady && hasOpenRegisters`): full app with menu, content, sidebar
+3. **Loading** (else): centered `NcLoadingIcon`
+
+The empty state uses:
+- `NcEmptyContent` with `:name` and `:description` props
+- `#icon` slot with the app's own icon (`imagePath('', 'app-dark.svg')`)
+- `#action` slot with `NcButton` linking to app store (admin) or text hint (non-admin)
+- Admin detection comes from the backend (`settingsStore.getIsAdmin`), NOT from `OC.isAdmin` (which doesn't exist)
+- App store URL: `generateUrl('/settings/apps/integration/openregister')`
+
+### Centering
+
+The `NcAppContent` wrapper needs `.open-register-missing` class with flex centering. This goes in `src/assets/app.css` (not in a Vue `