From 83a04035285ab41d8199a20b124a4b80f73d87a5 Mon Sep 17 00:00:00 2001 From: Specter Intelligence Date: Wed, 20 May 2026 09:40:08 +0000 Subject: [PATCH 1/4] feat: Add OpenSpec change publications from Specter Co-Authored-By: Claude Sonnet 4.6 --- openspec/changes/publications/.openspec.yaml | 2 + .../changes/publications/context-brief.md | 159 +++++++++++ openspec/changes/publications/design.md | 207 ++++++++++++++ openspec/changes/publications/proposal.md | 32 +++ .../publications/specs/publications/spec.md | 259 ++++++++++++++++++ openspec/changes/publications/tasks.md | 95 +++++++ 6 files changed, 754 insertions(+) create mode 100644 openspec/changes/publications/.openspec.yaml create mode 100644 openspec/changes/publications/context-brief.md create mode 100644 openspec/changes/publications/design.md create mode 100644 openspec/changes/publications/proposal.md create mode 100644 openspec/changes/publications/specs/publications/spec.md create mode 100644 openspec/changes/publications/tasks.md diff --git a/openspec/changes/publications/.openspec.yaml b/openspec/changes/publications/.openspec.yaml new file mode 100644 index 000000000..8b7691498 --- /dev/null +++ b/openspec/changes/publications/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-20 diff --git a/openspec/changes/publications/context-brief.md b/openspec/changes/publications/context-brief.md new file mode 100644 index 000000000..d88671200 --- /dev/null +++ b/openspec/changes/publications/context-brief.md @@ -0,0 +1,159 @@ +--- +status: reviewed +--- + +# Publications + +## Purpose + +Publications are the core content objects in OpenCatalogi. They represent individual published documents, records, or data entries within a catalog. The publications API provides public, read-only access to publications scoped by catalog slug, including support for attachments, file downloads, and object relation traversal (uses/used-by). Publications are consumed by external frontends like tilburg-woo-ui. + +## Requirements + +| ID | Requirement | Priority | Status | +|----|------------|----------|--------| +| PUB-001 | List publications scoped to a catalog slug with pagination and facets | Must | Implemented | +| PUB-002 | Retrieve a single publication by catalog slug and object UUID | Must | Implemented | +| PUB-003 | Publication list endpoint must filter by the catalog's configured registers and schemas | Must | Implemented | +| PUB-004 | Support multi-schema catalogs with UNION-based search across multiple magic tables | Must | Implemented | +| PUB-005 | Support `_extend` parameter for including related object data | Should | Implemented | +| PUB-006 | Retrieve publication attachments (files linked to a publication) | Must | Implemented | +| PUB-007 | Download publication files | Must | Implemented | +| PUB-008 | Retrieve outgoing relations (objects this publication references) via `/uses` | Must | Implemented | +| PUB-009 | Retrieve incoming relations (objects that reference this publication) via `/used` | Must | Implemented | +| PUB-010 | All public endpoints must include CORS headers | Must | Implemented | +| PUB-011 | Return 404 with descriptive error when catalog slug or publication ID not found | Must | Implemented | +| PUB-012 | Publication endpoints use wildcard `{catalogSlug}` routes (must be last in route order) | Must | Implemented | +| PUB-013 | Support filter parameter extraction from various formats (single, array, OR/AND operators) | Should | Implemented | +| PUB-014 | Fallback object location lookup across all magic tables when catalog register/schema search fails | Should | Implemented | +| PUB-015 | Schema authorization (RBAC) is enabled on the publication list for conditional access rules | Should | Implemented | + +## Data Model + +The publication schema is defined in `publication_register.json`. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| title | string | Yes | The title of the publication | +| summary | string | No | Brief description of the publication | +| description | string | No | Detailed description of the publication | +| organization | string | No | Reference to the owning organization (facetable) | +| themes | array(string) | No | List of theme references (facetable) | + +Note: Publications also inherit OpenRegister system fields (`@self.uuid`, `@self.created`, `@self.updated`, `@self.published`, `@self.depublished`, `@self.schema`, `@self.register`, `@self.files`, etc.). + +## User Interface + +The Nextcloud admin UI provides: +- **PublicationIndex.vue** (`/publications/{catalogSlug}`) - Publications list filtered by catalog +- **PublicationDetail.vue** (`/publications/{catalogSlug}/{id}`) - Single publication detail view +- **PublicationList.vue** - Publication list component +- **PublicationTable.vue** - Tabular publication display +- **PublishPublicationDialog.vue** - Dialog for publishing/depublishing +- **Various object modals** - ViewObject, DeleteObject, MergeObject, LockObject, etc. + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/{catalogSlug}` | List publications in a catalog (public, paginated, with facets) | +| GET | `/api/{catalogSlug}/{id}` | Get single publication detail | +| GET | `/api/{catalogSlug}/{id}/attachments` | Get publication attachments/files | +| GET | `/api/{catalogSlug}/{id}/download` | Download publication files | +| GET | `/api/{catalogSlug}/{id}/uses` | Get outgoing relations (this publication references) | +| GET | `/api/{catalogSlug}/{id}/used` | Get incoming relations (other objects reference this) | +| OPTIONS | `/api/{catalogSlug}` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/uses` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/used` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/attachments` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/download` | CORS preflight | + +All `{catalogSlug}` routes have the requirement `[a-z0-9-]+` and must be placed LAST in `routes.php` to avoid matching specific named routes like `/api/themes`, `/api/glossary`, etc. + +## PublicationsController Helper Methods (Gap 22) + +### findObjectLocation() - Magic Table Scanning + +The `PublicationsController` contains a private `findObjectLocation(string $uuid)` method that scans all OpenRegister magic tables to find which register/schema combination an object belongs to: + +1. **Discovery**: Queries `information_schema.tables` for all tables matching `oc_openregister_table_%` +2. **UNION ALL**: Builds a single SQL query that searches all magic tables simultaneously: + ```sql + (SELECT 1 AS register_id, 2 AS schema_id FROM oc_openregister_table_1_2 WHERE _uuid = 'abc-123') + UNION ALL + (SELECT 1 AS register_id, 3 AS schema_id FROM oc_openregister_table_1_3 WHERE _uuid = 'abc-123') + ... + LIMIT 1 + ``` +3. **Table name parsing**: Register and schema IDs are extracted from table names via regex: `oc_openregister_table_(\d+)_(\d+)` +4. **Returns**: `{ register: int, schema: int }` or null if not found + +This is used as a fallback when the catalog's register/schema combinations don't contain the requested object (e.g., object was moved or catalog config is stale). + +### extractFilterValues() - Filter Syntax with [or] Support + +The `extractFilterValues(mixed $filter)` method normalizes various filter formats into an array of integer values: + +| Input Format | Example | Result | +|-------------|---------|--------| +| Single numeric | `1` | `[1]` | +| Simple array | `[1, 2, 3]` | `[1, 2, 3]` | +| OR operator (array) | `{ "or": [1, 2, 3] }` | `[1, 2, 3]` | +| OR operator (string) | `{ "or": "1,2,3" }` | `[1, 2, 3]` | +| AND operator (array) | `{ "and": [1, 2] }` | `[1, 2]` | +| AND operator (string) | `{ "and": "1,2" }` | `[1, 2]` | +| Comma-separated string | `"1,2,3"` | `[1, 2, 3]` | + +This supports the `[or]` query parameter syntax used by frontends: `?schemas[or]=1,2,3`. + +## Scenarios + +### Scenario: List publications in a catalog +- GIVEN a catalog with slug "publications" exists with schemas [1, 2] and registers [1] +- WHEN a GET request is made to `/api/publications` +- THEN the catalog is resolved by slug (from cache or DB) +- AND ObjectService.searchObjectsPaginated is called with `_schemas: [1, 2]`, `_register: 1` +- AND results include pagination (results, total, page, pages, limit, offset) +- AND `@catalog` metadata is added to the response (slug, title, schemas, registers) +- AND CORS headers are included + +### Scenario: Multi-schema catalog strips non-universal ordering +- GIVEN a catalog spanning schemas with different property names (e.g., "name" vs "naam") +- WHEN ordering by a non-universal field is requested +- THEN only universal order fields (uuid, created, updated, published, depublished) are allowed +- AND non-universal order fields are stripped from the query + +### Scenario: Get single publication with fallback location +- GIVEN a publication with UUID "xyz-789" exists +- WHEN a GET request is made to `/api/publications/xyz-789` +- THEN the controller first tries the catalog's register/schema combinations (fast path) +- AND if not found, findObjectLocation() searches all magic tables via UNION ALL query (fallback) +- AND if found, renders the entity with `_extend` parameters +- AND returns 404 if not found in any location + +### Scenario: Get publication attachments +- GIVEN a published publication with UUID "abc-123" and attached files +- WHEN a GET request is made to `/api/publications/abc-123/attachments` +- THEN the publication is verified to exist in the catalog +- AND PublicationService.attachments() returns the file metadata + +### Scenario: Traverse publication relations +- GIVEN a publication "abc" that references objects "def" and "ghi" +- WHEN a GET request is made to `/api/publications/abc/uses` +- THEN ObjectService.getObjectUses() returns "def" and "ghi" with RBAC enabled +- AND register/schema context is set via findObjectLocation for magic table routing + +### Scenario: Filter extraction with OR syntax +- GIVEN a request with `?schemas[or]=1,2,3` +- WHEN extractFilterValues() processes the filter +- THEN it returns `[1, 2, 3]` as integer values +- AND these can be used to filter across multiple schemas + +## Dependencies + +- **OpenRegister ObjectService** - searchObjectsPaginated, searchObjects, find, renderEntity, getObjectUses, getObjectUsedBy, buildSearchQuery +- **CatalogiService** - getCatalogBySlug for catalog resolution and caching +- **PublicationService** - attachments(), download() for file operations +- **IDBConnection** - Direct SQL for findObjectLocation across all magic tables +- **Nextcloud IAppConfig** - Configuration for schema/register mappings diff --git a/openspec/changes/publications/design.md b/openspec/changes/publications/design.md new file mode 100644 index 000000000..0f69dbbd6 --- /dev/null +++ b/openspec/changes/publications/design.md @@ -0,0 +1,207 @@ +# Design: publications + +## Architecture Overview + +Publications are served through a dedicated public controller (`PublicationsController`) that wraps OpenRegister's `ObjectService` and OpenCatalogi's `CatalogiService` and `PublicationService`. All endpoints are unauthenticated (`#[PublicPage]`, `#[NoCSRFRequired]`) and carry CORS headers so external frontends can call them cross-origin. + +``` +External frontend (tilburg-woo-ui, etc.) + │ + │ GET /api/{catalogSlug} + │ GET /api/{catalogSlug}/{id} + │ GET /api/{catalogSlug}/{id}/attachments + │ GET /api/{catalogSlug}/{id}/download + │ GET /api/{catalogSlug}/{id}/uses + │ GET /api/{catalogSlug}/{id}/used + ▼ +PublicationsController (lib/Controller/PublicationsController.php) + │ + ├── CatalogiService::getCatalogBySlug() catalog resolution + in-process cache + │ ▼ + │ ObjectService::searchObjectsPaginated() (list endpoint: _schemas, _register, _rbac) + │ ObjectService::find() (single-object fast path) + │ ObjectService::renderEntity() (expand related objects via _extend) + │ ObjectService::getObjectUses() (/uses endpoint) + │ ObjectService::getObjectUsedBy() (/used endpoint) + │ + ├── PublicationService::attachments() (/attachments endpoint) + │ PublicationService::download() (/download endpoint) + │ + └── IDBConnection (direct SQL) findObjectLocation fallback + ▼ + information_schema.tables → UNION ALL across oc_openregister_table_* +``` + +## Route ordering constraint + +The wildcard `{catalogSlug}` routes use the regex `[a-z0-9-]+` and **must appear last** in `appinfo/routes.php`. Placing them before named routes like `/api/themes` or `/api/glossary` would cause the catalog slug pattern to absorb those paths. + +```php +// appinfo/routes.php — order matters +['name' => 'themes#index', 'url' => '/api/themes', ...], // specific first +['name' => 'glossary#index', 'url' => '/api/glossary', ...], +// ... all other named routes ... +['name' => 'publications#list', 'url' => '/api/{catalogSlug}', // wildcard last + 'verb' => 'GET', 'requirements' => ['catalogSlug' => '[a-z0-9-]+']] +``` + +## findObjectLocation — magic table fallback + +When the primary path (catalog's registered schemas/registers) does not contain the requested UUID, the controller falls back to scanning every `oc_openregister_table_*` table via a single UNION ALL query. This handles moved objects and stale catalog configurations. + +```sql +(SELECT 1 AS register_id, 2 AS schema_id + FROM oc_openregister_table_1_2 WHERE _uuid = :uuid) +UNION ALL +(SELECT 1 AS register_id, 3 AS schema_id + FROM oc_openregister_table_1_3 WHERE _uuid = :uuid) +... +LIMIT 1 +``` + +Table name parsing: `oc_openregister_table_(\d+)_(\d+)` → register_id, schema_id. + +## extractFilterValues — filter syntax normaliser + +Converts the various filter formats accepted by the public API into a uniform `int[]`: + +| Input format | Example | Output | +|---|---|---| +| Single numeric | `1` | `[1]` | +| Simple array | `[1, 2, 3]` | `[1, 2, 3]` | +| `{ "or": [1,2,3] }` | OR array | `[1, 2, 3]` | +| `{ "or": "1,2,3" }` | OR string | `[1, 2, 3]` | +| `{ "and": [1,2] }` | AND array | `[1, 2]` | +| `{ "and": "1,2" }` | AND string | `[1, 2]` | +| Comma-separated string | `"1,2,3"` | `[1, 2, 3]` | + +The `[or]` query parameter syntax used by tilburg-woo-ui (`?schemas[or]=1,2,3`) flows through this normaliser before being passed to `ObjectService::searchObjectsPaginated`. + +## Multi-schema catalog: universal ordering restriction + +When a catalog spans multiple schemas whose properties differ (e.g., one schema uses `name`, another uses `naam`), ordering by schema-specific fields is rejected. Only the OpenRegister universal fields are permitted: + +``` +uuid | created | updated | published | depublished +``` + +Non-universal `_order` parameters are stripped before the `searchObjectsPaginated` call. + +## API endpoint table + +| Method | Path | Auth | Description | +|---|---|---|---| +| GET | `/api/{catalogSlug}` | `#[PublicPage]` | Paginated list with facets; adds `@catalog` metadata | +| GET | `/api/{catalogSlug}/{id}` | `#[PublicPage]` | Single publication; fast-path + fallback location | +| GET | `/api/{catalogSlug}/{id}/attachments` | `#[PublicPage]` | File metadata for attached files | +| GET | `/api/{catalogSlug}/{id}/download` | `#[PublicPage]` | Stream file download | +| GET | `/api/{catalogSlug}/{id}/uses` | `#[PublicPage]` | Outgoing object relations | +| GET | `/api/{catalogSlug}/{id}/used` | `#[PublicPage]` | Incoming object relations | +| OPTIONS | `/api/{catalogSlug}` | `#[PublicPage]` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}` | `#[PublicPage]` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/uses` | `#[PublicPage]` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/used` | `#[PublicPage]` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/attachments` | `#[PublicPage]` | CORS preflight | +| OPTIONS | `/api/{catalogSlug}/{id}/download` | `#[PublicPage]` | CORS preflight | + +All `{catalogSlug}` patterns carry `requirements: ['catalogSlug' => '[a-z0-9-]+']`. + +## Reuse Analysis + +This change delegates all data access to existing OpenRegister and OpenCatalogi services. No custom data layer is built. + +| Capability | Provided by | Usage | +|---|---|---| +| Paginated object search | `ObjectService::searchObjectsPaginated` | List endpoint | +| Single object retrieval | `ObjectService::find` + `renderEntity` | Detail endpoint | +| Relation traversal | `ObjectService::getObjectUses` / `getObjectUsedBy` | uses/used endpoints | +| File metadata | `PublicationService::attachments` | attachments endpoint | +| File streaming | `PublicationService::download` | download endpoint | +| Catalog resolution | `CatalogiService::getCatalogBySlug` | All endpoints | +| CORS headers | Nextcloud response helpers | All public endpoints | + +`findObjectLocation` is the only bespoke query in this controller; it exists because `ObjectService` has no cross-schema fallback scan. `extractFilterValues` is a small normalisation utility with no equivalent in OpenRegister's public API. Both are private methods — candidates for extraction to `PublicationService` in a future cleanup. + +## Seed Data + +`lib/Settings/publication_register.json` must include 3–5 realistic Dutch publication objects under `components.objects[]` so the app is testable immediately after installation. + +**Object 1 — WOO verzoek besluit (Gemeente Westerveld)** +```json +{ + "@self": { + "register": "publication-register", + "schema": "publication", + "slug": "pub-woo-2025-001" + }, + "title": "WOO-besluit inzake vergunningverlening Industrieweg 14", + "summary": "Besluit op WOO-verzoek over verleende omgevingsvergunning aan Bouwbedrijf De Vries BV.", + "description": "Op 12 februari 2025 ontving de gemeente een verzoek om openbaarmaking van alle documenten betreffende de omgevingsvergunning voor Industrieweg 14 te Diever. Het college heeft besloten de stukken openbaar te maken met uitzondering van bedrijfsvertrouwelijke informatie.", + "organization": "gemeente-westerveld", + "themes": ["woo", "omgevingsrecht"] +} +``` + +**Object 2 — Raadsbesluit (Gemeente Meppel)** +```json +{ + "@self": { + "register": "publication-register", + "schema": "publication", + "slug": "pub-raad-2025-003" + }, + "title": "Raadsbesluit vaststelling bestemmingsplan Buitengebied 2025", + "summary": "De gemeenteraad van Meppel heeft op 27 maart 2025 het bestemmingsplan Buitengebied 2025 vastgesteld.", + "description": "Het bestemmingsplan Buitengebied 2025 vervangt het bestemmingsplan uit 2012 en integreert de wijzigingen uit de omgevingsvisie 2022. Het plan is opgesteld conform de Omgevingswet en is digitaal raadpleegbaar via het Omgevingsloket.", + "organization": "gemeente-meppel", + "themes": ["ruimtelijke-ordening", "bestemmingsplan"] +} +``` + +**Object 3 — Jaarverslag (Gemeente Hoogeveen)** +```json +{ + "@self": { + "register": "publication-register", + "schema": "publication", + "slug": "pub-jaarverslag-2024" + }, + "title": "Jaarverslag en Jaarrekening 2024 Gemeente Hoogeveen", + "summary": "Het jaarverslag en de jaarrekening 2024 van de gemeente Hoogeveen, vastgesteld door de gemeenteraad op 19 juni 2025.", + "description": "Dit document bevat het bestuurlijk jaarverslag, de programmaverantwoording en de jaarrekening 2024. Het financieel resultaat over 2024 bedraagt een positief saldo van €1.247.000.", + "organization": "gemeente-hoogeveen", + "themes": ["financien", "verantwoording"] +} +``` + +**Object 4 — Subsidieregeling (Provincie Drenthe)** +```json +{ + "@self": { + "register": "publication-register", + "schema": "publication", + "slug": "pub-subsidie-drenthe-2025-04" + }, + "title": "Subsidieregeling Duurzame Energie Particulieren Drenthe 2025", + "summary": "Provinciale subsidieregeling voor particulieren die zonnepanelen, warmtepompen of isolatiemaatregelen treffen.", + "description": "Op grond van de Algemene Subsidieverordening Provincie Drenthe 2020 stelt de provincie subsidie beschikbaar voor duurzame energiemaatregelen door particuliere huiseigenaren. Het subsidieplafond bedraagt € 2.500.000 voor het jaar 2025.", + "organization": "provincie-drenthe", + "themes": ["duurzaamheid", "subsidie", "energie"] +} +``` + +**Object 5 — Vergunning (Gemeente Emmen)** +```json +{ + "@self": { + "register": "publication-register", + "schema": "publication", + "slug": "pub-vergunning-emmen-2025-012" + }, + "title": "Omgevingsvergunning uitbreiding distributiecentrum Rietlanden", + "summary": "Verleende omgevingsvergunning voor uitbreiding van het distributiecentrum aan de Rietlanden 44 te Emmen.", + "description": "De omgevingsvergunning betreft de uitbreiding van een bestaand distributiecentrum met 4.800 m². De aanvraag is getoetst aan het bestemmingsplan Erica-Klazienaveen Bedrijventerrein. Bezwaar is mogelijk tot en met 3 juli 2025.", + "organization": "gemeente-emmen", + "themes": ["omgevingsrecht", "vergunning", "bedrijventerrein"] +} +``` diff --git a/openspec/changes/publications/proposal.md b/openspec/changes/publications/proposal.md new file mode 100644 index 000000000..f9565ee28 --- /dev/null +++ b/openspec/changes/publications/proposal.md @@ -0,0 +1,32 @@ +# Proposal: publications + +## Summary +Formalize the Publications public API as a tracked OpenSpec change: the `PublicationsController` wildcard endpoints that serve catalog-scoped publication lists, single-object retrieval, attachment access, file download, and relation traversal for external frontends such as tilburg-woo-ui. + +## Motivation +Publications are the primary read surface of OpenCatalogi for external consumers. The endpoints are live and marked Implemented in the canonical spec (`openspec/specs/publications/spec.md`), but no change artifact exists to trace implementation decisions, record the two private helper methods (`findObjectLocation` and `extractFilterValues`) and their edge cases, or drive follow-up quality work (CORS verification, route-ordering regression tests, RBAC on the list endpoint). This change captures the full spec, design, and task backlog so future modifications have a traceable baseline. + +## Scope + +### In scope +- Public list endpoint `GET /api/{catalogSlug}` with pagination, facets, and `@catalog` metadata +- Single-object endpoint `GET /api/{catalogSlug}/{id}` with fast-path + fallback via `findObjectLocation` +- Attachments endpoint `GET /api/{catalogSlug}/{id}/attachments` +- Download endpoint `GET /api/{catalogSlug}/{id}/download` +- Relation endpoints `GET /api/{catalogSlug}/{id}/uses` and `GET /api/{catalogSlug}/{id}/used` +- CORS preflight `OPTIONS` routes for all six endpoint variants +- `findObjectLocation` UNION ALL fallback across magic tables +- `extractFilterValues` filter-syntax normaliser (single, array, OR/AND, comma-separated) +- Route-ordering constraint: wildcard `{catalogSlug}` routes placed last in `routes.php` +- Multi-schema catalog universal-ordering restriction +- RBAC-enabled list endpoint (`_rbac: true` on `searchObjectsPaginated`) +- Seed data: 3–5 realistic Dutch publication objects in `publication_register.json` + +### Out of scope +- Admin CRUD for publications (covered by the standard OpenRegister + `@conduction/nextcloud-vue` CRUD pattern) +- Catalog creation and configuration (covered by the catalogs spec) +- WOO-specific publication workflow (covered by `woo-transparency` spec) +- Authentication or authorisation changes — all endpoints are intentionally public (`#[PublicPage]`) + +## kind +`code` — centre of mass is PHP controller and service logic. No schema changes proposed. diff --git a/openspec/changes/publications/specs/publications/spec.md b/openspec/changes/publications/specs/publications/spec.md new file mode 100644 index 000000000..7fac0ecef --- /dev/null +++ b/openspec/changes/publications/specs/publications/spec.md @@ -0,0 +1,259 @@ +--- +status: implemented +--- + +# Publications Specification + +## Purpose + +Publications are the core content objects in OpenCatalogi. They represent individual published documents, records, or data entries within a catalog. The Publications API provides public, read-only access to publications scoped by catalog slug, including support for attachments, file downloads, and object relation traversal (uses/used-by). Publications are consumed by external frontends like tilburg-woo-ui. + +## Context + +OpenCatalogi exposes a wildcard `{catalogSlug}` route that resolves a catalog by its human-readable slug and returns its publications. A catalog may span multiple OpenRegister schemas and registers; the controller maps the slug to a `Catalog` entity (with its configured `schemas` and `registers` lists) and forwards those constraints to `ObjectService::searchObjectsPaginated`. + +For single-object requests the controller first tries the catalog's own schemas/registers (fast path). If the object is not found there it falls back to `findObjectLocation`, a private method that scans every `oc_openregister_table_*` table via a UNION ALL SQL query to locate the register/schema combination — handling the case where an object was moved or the catalog configuration is stale. + +**Relation to other specs:** +- `catalogs` spec: manages the catalog entity including slug assignment and schema/register configuration. +- `woo-transparency` spec: WOO-specific publication workflow built on top of the publications infrastructure. +- `search` spec: full-text and faceted search across publications; this spec covers only the public read API. + +## Requirements + +### REQ-PUB-001: List publications scoped to a catalog slug + +The system MUST provide a paginated, faceted list of publications scoped to a catalog identified by its slug. + +#### Scenario: Successful list request + +- GIVEN a catalog with slug `gemeentenieuws` exists, configured with `schemas: [3, 5]` and `registers: [1]` +- WHEN a GET request is made to `/api/gemeentenieuws` +- THEN the response status MUST be `200 OK` +- AND `ObjectService::searchObjectsPaginated` MUST be called with `_schemas: [3, 5]`, `_register: 1`, `_rbac: true` +- AND the response body MUST include pagination fields: `results`, `total`, `page`, `pages`, `limit`, `offset` +- AND the response body MUST include a top-level `@catalog` object with `slug`, `title`, `schemas`, and `registers` +- AND the response MUST include `Access-Control-Allow-Origin` and related CORS headers + +#### Scenario: Catalog not found returns 404 + +- GIVEN no catalog with slug `onbekend-portaal` exists +- WHEN a GET request is made to `/api/onbekend-portaal` +- THEN the response status MUST be `404 Not Found` +- AND the response body MUST contain a descriptive `message` field (no stack trace or internal SQL) + +### REQ-PUB-002: Retrieve a single publication by catalog slug and UUID + +The system MUST return full publication data for a single object identified by UUID within a catalog. + +#### Scenario: Object found on fast path + +- GIVEN a catalog with slug `vergunningen` configured with `schemas: [2]`, `registers: [1]` +- AND a publication with UUID `b3f9e721-1234-4abc-8def-000000000001` exists in schema 2, register 1 +- WHEN a GET request is made to `/api/vergunningen/b3f9e721-1234-4abc-8def-000000000001` +- THEN `ObjectService::find` MUST be called with register `1`, schema `2`, UUID `b3f9e721-…` +- AND the response MUST include the rendered object (including `_extend` expansions if requested) +- AND the response status MUST be `200 OK` + +#### Scenario: Object found via fallback location scan + +- GIVEN a publication with UUID `c4e0f832-5678-4cde-9fab-000000000002` that exists in schema 4, register 2 (not in the catalog's configured schemas/registers) +- WHEN a GET request is made to `/api/vergunningen/c4e0f832-5678-4cde-9fab-000000000002` +- THEN the controller MUST first try the catalog's schemas/registers and find nothing +- AND the controller MUST execute `findObjectLocation` — a UNION ALL query across all `oc_openregister_table_*` tables +- AND on locating the object in schema 4, register 2, `ObjectService::find` MUST be called with those values +- AND the response status MUST be `200 OK` + +#### Scenario: Object not found anywhere returns 404 + +- GIVEN a UUID `00000000-0000-0000-0000-000000000000` that does not exist in any magic table +- WHEN a GET request is made to `/api/vergunningen/00000000-0000-0000-0000-000000000000` +- THEN `findObjectLocation` MUST scan all tables and return null +- AND the response status MUST be `404 Not Found` +- AND the response body MUST contain a descriptive `message` field + +### REQ-PUB-003: Filter by catalog's configured registers and schemas + +The list endpoint MUST restrict results to objects in the schemas and registers declared on the resolved catalog. + +#### Scenario: Multi-register catalog + +- GIVEN a catalog configured with `schemas: [1, 3, 7]` and `registers: [1, 2]` +- WHEN the list endpoint is called +- THEN `searchObjectsPaginated` MUST receive `_schemas: [1, 3, 7]` and `_registers: [1, 2]` +- AND objects from schemas outside `[1, 3, 7]` MUST NOT appear in the results + +### REQ-PUB-004: Multi-schema catalogs support UNION-based search + +When a catalog spans multiple schemas, the controller MUST pass all schema IDs so OpenRegister's UNION ALL table strategy is applied. + +#### Scenario: Two-schema catalog search + +- GIVEN a catalog with `schemas: [2, 5]` +- WHEN the list endpoint is called with `?_search=bestemmingsplan` +- THEN `searchObjectsPaginated` receives `_schemas: [2, 5]` +- AND the UNION ALL strategy in OpenRegister returns matching objects from both magic tables + +### REQ-PUB-005: Support `_extend` parameter for related object data + +The single-object endpoint MUST accept an `_extend` query parameter and pass it to `ObjectService::renderEntity` to inline related objects. + +#### Scenario: Extend organization relation + +- GIVEN a publication that has an `organization` field referencing another object +- WHEN a GET request is made to `/api/woo/b3f9e721-…?_extend[]=organization` +- THEN `ObjectService::renderEntity` MUST receive `_extend: ['organization']` +- AND the response MUST include the organization object nested under the `organization` key + +### REQ-PUB-006: Retrieve publication attachments + +The attachments endpoint MUST return the list of files linked to a publication. + +#### Scenario: Publication with two attached files + +- GIVEN a publication `abc-123` in catalog `raadsstukken` with two attached PDF files +- WHEN a GET request is made to `/api/raadsstukken/abc-123/attachments` +- THEN the publication MUST first be verified to exist in the catalog +- AND `PublicationService::attachments(uuid)` MUST be called +- AND the response MUST contain an array of file metadata objects (name, mimeType, size, downloadUrl) +- AND the response status MUST be `200 OK` + +### REQ-PUB-007: Download publication files + +The download endpoint MUST stream file contents to the client. + +#### Scenario: Download a PDF attachment + +- GIVEN a publication `abc-123` with an attached file `besluit.pdf` +- WHEN a GET request is made to `/api/raadsstukken/abc-123/download` +- THEN `PublicationService::download(uuid, request)` MUST be called +- AND the response MUST have `Content-Type: application/pdf` +- AND the response MUST stream the file bytes + +### REQ-PUB-008: Retrieve outgoing relations via `/uses` + +The uses endpoint MUST return objects that the publication references (forward relations). + +#### Scenario: Publication references two objects + +- GIVEN a publication `pub-abc` that references objects `obj-def` and `obj-ghi` +- WHEN a GET request is made to `/api/woo/pub-abc/uses` +- THEN `findObjectLocation(pub-abc)` MUST be called to determine the register/schema context +- AND `ObjectService::getObjectUses(register, schema, uuid, _rbac: true)` MUST be called +- AND the response MUST contain `obj-def` and `obj-ghi` + +### REQ-PUB-009: Retrieve incoming relations via `/used` + +The used endpoint MUST return objects that reference the publication (reverse relations). + +#### Scenario: Publication is referenced by one other object + +- GIVEN object `obj-xyz` references publication `pub-abc` +- WHEN a GET request is made to `/api/woo/pub-abc/used` +- THEN `ObjectService::getObjectUsedBy(register, schema, uuid, _rbac: true)` MUST be called +- AND the response MUST contain `obj-xyz` + +### REQ-PUB-010: All public endpoints include CORS headers + +Every response from the publications controller MUST include the appropriate CORS headers to allow cross-origin access. + +#### Scenario: CORS headers on list response + +- GIVEN a browser on `https://tilburg.nl` makes a cross-origin GET to `/api/publicaties` +- WHEN the response is returned +- THEN the response MUST include `Access-Control-Allow-Origin: *` (or a configured origin) +- AND `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` MUST be present + +#### Scenario: CORS preflight response + +- GIVEN a browser sends OPTIONS `/api/publicaties` +- WHEN the OPTIONS handler fires +- THEN the response status MUST be `200 OK` +- AND all required CORS headers MUST be present +- AND the response body MAY be empty + +### REQ-PUB-011: 404 with descriptive error on missing resource + +The controller MUST return `404 Not Found` with a human-readable message when either the catalog slug or the publication UUID cannot be resolved. + +#### Scenario: Descriptive error, no internal details + +- GIVEN a request for a non-existent catalog slug or UUID +- WHEN the controller returns 404 +- THEN the response body MUST include a `message` field describing what was not found +- AND the response MUST NOT include PHP stack traces, SQL queries, or internal file paths + +### REQ-PUB-012: Wildcard `{catalogSlug}` routes placed last in route order + +The `{catalogSlug}` routes MUST be declared after all specific named routes in `appinfo/routes.php` to prevent the slug pattern from matching paths intended for other controllers. + +#### Scenario: Route ordering prevents slug collision + +- GIVEN `routes.php` declares `GET /api/themes` before `GET /api/{catalogSlug}` +- WHEN a GET request is made to `/api/themes` +- THEN the ThemesController MUST handle the request +- AND the PublicationsController MUST NOT intercept it + +### REQ-PUB-013: Filter parameter extraction supports OR/AND syntax + +The `extractFilterValues` method MUST normalise all supported filter input formats into a uniform integer array. + +#### Scenario: OR syntax from tilburg-woo-ui + +- GIVEN a request with query parameter `?schemas[or]=2,5,7` +- WHEN `extractFilterValues` processes the filter value `{ "or": "2,5,7" }` +- THEN the result MUST be `[2, 5, 7]` as integer values +- AND these values MUST be passed to `searchObjectsPaginated` as `_schemas` + +#### Scenario: Single integer filter + +- GIVEN a request with `?registers=1` +- WHEN `extractFilterValues(1)` is called +- THEN the result MUST be `[1]` + +### REQ-PUB-014: Fallback object location via magic table scan + +When the catalog's configured schemas/registers do not contain the requested object, the controller MUST scan all OpenRegister magic tables via UNION ALL to find the object's true location. + +#### Scenario: Object located in non-catalog schema + +- GIVEN an object `uuid-xyz` was moved from schema 2 (catalog's schema) to schema 9 (not in catalog) +- WHEN `GET /api/vergunningen/uuid-xyz` is requested +- THEN the fast-path check (schema 2, register 1) MUST return no result +- AND `findObjectLocation(uuid-xyz)` MUST query `information_schema.tables` for `oc_openregister_table_%` tables +- AND the UNION ALL query MUST find `uuid-xyz` in `oc_openregister_table_2_9` +- AND the response MUST return the object as if found normally + +#### Scenario: `findObjectLocation` returns null for unknown UUID + +- GIVEN a UUID that exists nowhere in the database +- WHEN `findObjectLocation` runs the UNION ALL query +- THEN the query MUST return zero rows +- AND the method MUST return `null` +- AND the controller MUST return `404` + +### REQ-PUB-015: RBAC enabled on the publication list + +The list endpoint MUST pass `_rbac: true` to `ObjectService::searchObjectsPaginated` so schema-level access-control rules are enforced. + +#### Scenario: RBAC restricts schema access + +- GIVEN a schema configured with an RBAC rule that limits visibility to group `woo-medewerkers` +- WHEN an unauthenticated (public) request hits the list endpoint +- THEN `searchObjectsPaginated` MUST receive `_rbac: true` +- AND objects governed by the RBAC rule MUST be excluded from the response for non-members + +## Non-Requirements + +- This spec does NOT cover admin CRUD for publications (create, update, delete) — handled by the standard OpenRegister + `@conduction/nextcloud-vue` CRUD pattern. +- This spec does NOT cover catalog creation or configuration. +- This spec does NOT cover the WOO document assessment workflow. +- This spec does NOT cover full-text / semantic search — covered by the `search` spec. + +## Dependencies + +- **OpenRegister `ObjectService`** — `searchObjectsPaginated`, `find`, `renderEntity`, `getObjectUses`, `getObjectUsedBy` +- **OpenCatalogi `CatalogiService`** — `getCatalogBySlug` (with in-process caching) +- **OpenCatalogi `PublicationService`** — `attachments()`, `download()` +- **Nextcloud `IDBConnection`** — direct SQL for `findObjectLocation` UNION ALL query +- **Nextcloud `IAppConfig`** — configuration for default schema/register mappings diff --git a/openspec/changes/publications/tasks.md b/openspec/changes/publications/tasks.md new file mode 100644 index 000000000..3a8672a37 --- /dev/null +++ b/openspec/changes/publications/tasks.md @@ -0,0 +1,95 @@ +# Tasks: publications + +## Phase 1 — Deduplication check + +- [ ] 1. **Deduplication check**: search `openspec/specs/` and `lib/Service/` for any capability that overlaps with `findObjectLocation` (cross-schema UUID scan) and `extractFilterValues` (filter normalisation). Document findings in a comment on this task. If no overlap is found, record "no overlap found". If overlap is found, evaluate whether the helper should be extracted to `PublicationService` or another shared service rather than remaining a private controller method. + - **Files to check**: `lib/Service/PublicationService.php`, `lib/Service/CatalogiService.php`, OpenRegister `lib/Service/ObjectService.php` + - **Acceptance criteria**: findings documented; any extractable helper identified and tracked as a follow-up issue. + +## Phase 2 — Route ordering verification + +- [ ] 2. **Verify wildcard route placement** (`appinfo/routes.php`): confirm that all `{catalogSlug}` routes (GET, OPTIONS) are declared after every specific named route (`/api/themes`, `/api/glossary`, etc.). + - **Spec ref**: `specs/publications/spec.md` REQ-PUB-012 + - **Acceptance criteria**: a grep or automated test confirms no specific named route appears after the first `{catalogSlug}` route in `routes.php`. Route-ordering regression test added to the PHPUnit suite. + +## Phase 3 — CORS header verification + +- [ ] 3. **Verify CORS headers on all six endpoint variants**: make real HTTP requests (curl or PHPUnit HTTP test) to each of the six public endpoints and confirm `Access-Control-Allow-Origin` and friends are present on both GET and OPTIONS responses. + - **Spec ref**: `specs/publications/spec.md` REQ-PUB-010 + - **Endpoints**: `/{slug}`, `/{slug}/{id}`, `/{slug}/{id}/attachments`, `/{slug}/{id}/download`, `/{slug}/{id}/uses`, `/{slug}/{id}/used` + - **Acceptance criteria**: all 12 responses (6 × GET + 6 × OPTIONS) carry required CORS headers. Missing headers are fixed in `PublicationsController`. + +## Phase 4 — findObjectLocation coverage + +- [ ] 4. **Add unit test for `findObjectLocation` — object found**: mock `IDBConnection` to return a row from the UNION ALL query; assert the method returns `['register' => int, 'schema' => int]`. + - **Spec ref**: REQ-PUB-002, REQ-PUB-014 + - **File**: `tests/Unit/Controller/PublicationsControllerTest.php` + +- [ ] 5. **Add unit test for `findObjectLocation` — object not found**: mock `IDBConnection` to return zero rows; assert the method returns `null`. + - **Spec ref**: REQ-PUB-014 + - **Acceptance criteria**: test passes; controller returns `404` when `findObjectLocation` returns null. + +- [ ] 6. **Add unit test for `findObjectLocation` — table name parsing**: assert that tables named `oc_openregister_table_3_7` are correctly parsed as `register_id = 3`, `schema_id = 7`. + - **Spec ref**: REQ-PUB-014 + +## Phase 5 — extractFilterValues coverage + +- [ ] 7. **Add unit tests for `extractFilterValues` covering all seven input formats**: + - Single numeric (`1` → `[1]`) + - Simple array (`[1,2,3]` → `[1,2,3]`) + - OR array (`{ "or": [1,2,3] }` → `[1,2,3]`) + - OR string (`{ "or": "1,2,3" }` → `[1,2,3]`) + - AND array (`{ "and": [1,2] }` → `[1,2]`) + - AND string (`{ "and": "1,2" }` → `[1,2]`) + - Comma-separated string (`"2,5,7"` → `[2,5,7]`) + - **Spec ref**: REQ-PUB-013 + - **File**: `tests/Unit/Controller/PublicationsControllerTest.php` + - **Acceptance criteria**: all seven formats produce correct integer arrays; existing `[or]` behaviour for tilburg-woo-ui confirmed. + +## Phase 6 — Multi-schema ordering restriction + +- [ ] 8. **Add test for universal-ordering restriction on multi-schema catalogs**: pass a non-universal `_order` parameter (e.g., `_order[naam]=asc`) to the list endpoint for a catalog with two schemas; assert the parameter is stripped and the request succeeds without a 500 or malformed query. + - **Spec ref**: REQ-PUB-004 + - **Acceptance criteria**: non-universal order fields are absent from the `searchObjectsPaginated` call; universal fields (`uuid`, `created`, `updated`, `published`, `depublished`) are passed through unchanged. + +## Phase 7 — 404 error response quality + +- [ ] 9. **Verify 404 responses contain `message` and no internal details**: for both the "unknown slug" and "unknown UUID" cases, assert the response body has a `message` key and does NOT include stack traces, SQL, or internal paths. + - **Spec ref**: REQ-PUB-011 + - **Acceptance criteria**: existing PHPUnit assertions confirm `$response->getData()['message']` is set; `assertStringNotContainsString('SELECT', ...)` guard added. + +## Phase 8 — Seed data + +- [ ] 10. **Add seed data to `lib/Settings/publication_register.json`**: add the 5 Dutch publication objects defined in `design.md` (Seed Data section) under `components.objects[]`. Each object must use the `@self` envelope with matching `register`, `schema`, and a unique `slug`. + - **Spec ref**: ADR-001 seed data requirements + - **Files**: `lib/Settings/publication_register.json` + - **Acceptance criteria**: + - `importFromApp()` imports all 5 objects without error on a clean install. + - Re-import (idempotency) skips objects whose slug already exists. + - After import, the admin UI shows 5 publications in the default catalog. + +## Phase 9 — @spec traceability + +- [ ] 11. **Add `@spec` PHPDoc tags** to `PublicationsController` and `PublicationService` linking to this change: + - File-level header docblock: `@spec openspec/changes/publications/tasks.md` + - `list()` method: `@spec openspec/changes/publications/tasks.md#task-2` (REQ-PUB-001) + - `show()` method: `@spec openspec/changes/publications/tasks.md#task-4` (REQ-PUB-002) + - `findObjectLocation()`: `@spec openspec/changes/publications/tasks.md#task-4` (REQ-PUB-014) + - `extractFilterValues()`: `@spec openspec/changes/publications/tasks.md#task-7` (REQ-PUB-013) + - **Spec ref**: ADR-003 backend spec-traceability requirement + - **Files**: `lib/Controller/PublicationsController.php`, `lib/Service/PublicationService.php` + +## Phase 10 — Spec sync + +- [ ] 12. **Sync delta spec to canonical spec**: after all tasks above are verified, run `/opsx:sync` to fold `openspec/changes/publications/specs/publications/spec.md` back into `openspec/specs/publications/spec.md` and confirm the canonical spec is up-to-date. + - **Acceptance criteria**: `openspec/specs/publications/spec.md` reflects all REQ-PUB-001 through REQ-PUB-015 entries with their final implementation notes. + +## Phase 11 — tilburg-woo-ui compatibility + +- [ ] 13. **Test with tilburg-woo-ui**: start a local OpenCatalogi dev environment, point tilburg-woo-ui at the local instance, and confirm: + - Publication list loads with pagination and facets. + - `?schemas[or]=…` filter syntax returns correct results. + - Single publication detail page loads including attached files. + - Relation traversal (`/uses`, `/used`) returns expected objects. + - No CORS errors appear in the browser console. + - **Acceptance criteria**: all five checks pass; any regression is filed as a follow-up bug. From c4b7f0bee5212141896ccd1bfd93f7f25b0ec601 Mon Sep 17 00:00:00 2001 From: Specter Intelligence Date: Wed, 20 May 2026 11:59:00 +0200 Subject: [PATCH 2/4] feat: Add OpenSpec change publications from Specter --- .../architecture/adr-001-data-layer.md | 223 +++ .claude/openspec/architecture/adr-002-api.md | 6 + .../openspec/architecture/adr-003-backend.md | 14 + .../openspec/architecture/adr-004-frontend.md | 255 ++++ .../openspec/architecture/adr-005-security.md | 42 + .../openspec/architecture/adr-006-metrics.md | 3 + .claude/openspec/architecture/adr-007-i18n.md | 57 + .../openspec/architecture/adr-008-testing.md | 32 + .claude/openspec/architecture/adr-009-docs.md | 2 + .../architecture/adr-010-nl-design.md | 8 + .../architecture/adr-011-schema-standards.md | 8 + .../architecture/adr-012-deduplication.md | 7 + .../architecture/adr-013-container-pool.md | 178 +++ .../architecture/adr-014-licensing.md | 62 + .../architecture/adr-015-common-patterns.md | 98 ++ .../openspec/architecture/adr-016-routes.md | 28 + .../adr-017-component-composition.md | 86 ++ .../adr-018-widget-header-actions.md | 105 ++ .../adr-019-integration-registry.md | 113 ++ .../adr-020-gate-scope-to-pr-diff.md | 61 + .../adr-021-bounded-fix-scope-by-shape.md | 76 + .../adr-022-apps-consume-or-abstractions.md | 125 ++ .../adr-023-action-authorization.md | 183 +++ .../architecture/adr-024-app-manifest.md | 113 ++ .../adr-025-i18n-source-of-truth.md | 157 ++ .../adr-029-route-reachability-gate.md | 163 ++ .../adr-030-journeydoc-pattern.md | 188 +++ ...r-031-schema-declarative-business-logic.md | 357 +++++ .../adr-032-spec-sizing-and-chaining.md | 293 ++++ .claude/skills/clean-env/SKILL.md | 48 + .../clean-env/examples/expected-output.md | 64 + .claude/skills/create-pr/SKILL.md | 399 +++++ .claude/skills/create-pr/evals/evals.json | 110 ++ .claude/skills/create-pr/evals/grading.json | 76 + .../workspace/iteration-1/benchmark.json | 457 ++++++ .../evals/workspace/iteration-1/benchmark.md | 13 + .../eval-branch-protection/eval_metadata.json | 12 + .../with_skill/grading.json | 11 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../with_skill/timing.json | 5 + .../without_skill/grading.json | 10 + .../without_skill/outputs/summary.md | 50 + .../without_skill/run-1/grading.json | 10 + .../without_skill/run-1/timing.json | 5 + .../without_skill/timing.json | 5 + .../eval_metadata.json | 12 + .../with_skill/grading.json | 10 + .../with_skill/outputs/summary.md | 66 + .../with_skill/run-1/grading.json | 10 + .../with_skill/run-1/timing.json | 5 + .../with_skill/timing.json | 5 + .../without_skill/grading.json | 10 + .../without_skill/outputs/summary.md | 32 + .../without_skill/run-1/grading.json | 10 + .../without_skill/run-1/timing.json | 5 + .../without_skill/timing.json | 5 + .../eval_metadata.json | 12 + .../with_skill/grading.json | 11 + .../with_skill/outputs/summary.md | 55 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../with_skill/timing.json | 5 + .../without_skill/grading.json | 10 + .../without_skill/outputs/summary.md | 38 + .../without_skill/run-1/grading.json | 10 + .../without_skill/run-1/timing.json | 5 + .../without_skill/timing.json | 5 + .../eval-quality-checks/eval_metadata.json | 13 + .../with_skill/grading.json | 12 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 5 + .../with_skill/timing.json | 5 + .../without_skill/grading.json | 11 + .../without_skill/outputs/summary.md | 86 ++ .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../without_skill/timing.json | 5 + .../eval-standard-pr/eval_metadata.json | 12 + .../eval-standard-pr/with_skill/grading.json | 10 + .../with_skill/outputs/summary.md | 137 ++ .../with_skill/run-1/grading.json | 10 + .../with_skill/run-1/timing.json | 5 + .../eval-standard-pr/with_skill/timing.json | 5 + .../without_skill/grading.json | 10 + .../without_skill/outputs/summary.md | 73 + .../without_skill/run-1/grading.json | 10 + .../without_skill/run-1/timing.json | 5 + .../without_skill/timing.json | 5 + .claude/skills/create-pr/learnings.md | 37 + .../references/branch-protection-guide.md | 95 ++ .../references/check-tool-verification.md | 71 + .../ci-step-categorisation-guide.md | 128 ++ .../create-pr/references/local-checks.md | 82 + .claude/skills/feature-counsel/SKILL.md | 288 ++++ .../skills/feature-counsel/evals/evals.json | 82 + .../skills/feature-counsel/evals/grading.json | 48 + .../with_skill/run-1/grading.json | 22 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 22 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 27 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 27 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 27 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 27 + .../without_skill/run-1/timing.json | 5 + .../examples/feature-suggestions.md | 174 +++ .claude/skills/feature-counsel/learnings.md | 16 + .../skills/hydra-gate-admin-router/SKILL.md | 49 + .../hydra-gate-admin-router/examples/fail.log | 2 + .../hydra-gate-admin-router/examples/pass.log | 2 + .../skills/hydra-gate-composer-audit/SKILL.md | 46 + .../examples/fail.log | 2 + .../examples/pass.log | 2 + .../hydra-gate-forbidden-patterns/SKILL.md | 55 + .../examples/fail.log | 2 + .../examples/pass.log | 2 + .../skills/hydra-gate-initial-state/SKILL.md | 57 + .../examples/fail.log | 2 + .../examples/pass.log | 2 + .../hydra-gate-modal-isolation/SKILL.md | 53 + .../examples/fail.log | 2 + .../examples/pass.log | 2 + .../hydra-gate-nc-input-labels/SKILL.md | 56 + .../examples/fail.log | 2 + .../examples/pass.log | 2 + .../skills/hydra-gate-no-admin-idor/SKILL.md | 119 ++ .../examples/fail.log | 2 + .../examples/pass.log | 2 + .../skills/hydra-gate-orphan-auth/SKILL.md | 87 ++ .../hydra-gate-orphan-auth/examples/fail.log | 2 + .../hydra-gate-orphan-auth/examples/pass.log | 2 + .claude/skills/hydra-gate-route-auth/SKILL.md | 103 ++ .../hydra-gate-route-auth/examples/fail.log | 2 + .../hydra-gate-route-auth/examples/pass.log | 2 + .../skills/hydra-gate-semantic-auth/SKILL.md | 116 ++ .../examples/fail.log | 2 + .../examples/pass.log | 2 + .claude/skills/hydra-gate-spdx/SKILL.md | 94 ++ .../skills/hydra-gate-spdx/examples/fail.log | 2 + .../skills/hydra-gate-spdx/examples/pass.log | 2 + .claude/skills/hydra-gate-stub-scan/SKILL.md | 79 + .../hydra-gate-stub-scan/examples/fail.log | 2 + .../hydra-gate-stub-scan/examples/pass.log | 2 + .../hydra-gate-unsafe-auth-resolver/SKILL.md | 103 ++ .../examples/fail.log | 2 + .../examples/pass.log | 2 + .claude/skills/hydra-gates/SKILL.md | 62 + .claude/skills/journeydoc-add-story/SKILL.md | 160 ++ .claude/skills/journeydoc-init/SKILL.md | 342 +++++ .claude/skills/journeydoc-instrument/SKILL.md | 165 ++ .claude/skills/local-run/SKILL.md | 61 + .claude/skills/opsx-annotate/SKILL.md | 315 ++++ .claude/skills/opsx-annotate/evals/README.md | 67 + .claude/skills/opsx-annotate/evals/evals.json | 120 ++ .../skills/opsx-annotate/evals/grading.json | 85 ++ .../workspace/iteration-1/benchmark.json | 480 ++++++ .../evals/workspace/iteration-1/benchmark.md | 13 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 12 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 17 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 17 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../opsx-annotate/learning-candidates.md | 35 + .claude/skills/opsx-annotate/learnings.md | 47 + .../opsx-annotate/references/standards.md | 84 ++ .claude/skills/opsx-apply-loop/SKILL.md | 394 +++++ .../assets/apply-loop.Dockerfile | 38 + .../skills/opsx-apply-loop/evals/evals.json | 83 ++ .../skills/opsx-apply-loop/evals/grading.json | 49 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .claude/skills/opsx-apply-loop/learnings.md | 11 + .../references/apply-verify-loop-protocol.md | 139 ++ .../references/container-auth-check.md | 41 + .../references/container-limitations.md | 39 + .../references/container-setup-guide.md | 287 ++++ .../references/host-test-loop-protocol.md | 122 ++ .../references/loop-report-template.md | 83 ++ .../scripts/apply-loop-check.sh | 89 ++ .claude/skills/opsx-apply/SKILL.md | 347 +++++ .claude/skills/opsx-apply/evals/evals.json | 83 ++ .claude/skills/opsx-apply/evals/grading.json | 47 + .../workspace/iteration-1/benchmark.json | 293 ++++ .../evals/workspace/iteration-1/benchmark.md | 13 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 12 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 10 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 9 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../opsx-apply/examples/apply-output.md | 165 ++ .claude/skills/opsx-apply/learnings.md | 29 + .claude/skills/opsx-archive/SKILL.md | 349 +++++ .claude/skills/opsx-archive/evals/evals.json | 84 ++ .../skills/opsx-archive/evals/grading.json | 48 + .../workspace/iteration-1/benchmark.json | 303 ++++ .../evals/workspace/iteration-1/benchmark.md | 13 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 10 + .../without_skill/run-1/timing.json | 5 + .../opsx-archive/examples/output-templates.md | 70 + .claude/skills/opsx-archive/learnings.md | 19 + .../templates/test-scenario-template.md | 45 + .claude/skills/opsx-bulk-archive/SKILL.md | 300 ++++ .../examples/bulk-archive-output.md | 124 ++ .claude/skills/opsx-continue/SKILL.md | 152 ++ .../opsx-continue/examples/continue-output.md | 64 + .claude/skills/opsx-coverage-scan/SKILL.md | 288 ++++ .../skills/opsx-coverage-scan/evals/README.md | 60 + .../opsx-coverage-scan/evals/evals.json | 118 ++ .../opsx-coverage-scan/evals/grading.json | 68 + .../workspace/iteration-1/benchmark.json | 450 ++++++ .../evals/workspace/iteration-1/benchmark.md | 13 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 12 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 16 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 15 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../opsx-coverage-scan/learning-candidates.md | 31 + .../skills/opsx-coverage-scan/learnings.md | 51 + .../references/standards.md | 76 + .claude/skills/opsx-explore/SKILL.md | 221 +++ .claude/skills/opsx-explore/evals/evals.json | 86 ++ .../skills/opsx-explore/evals/grading.json | 68 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 10 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 13 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 12 + .../without_skill/run-1/timing.json | 5 + .../opsx-explore/examples/explore-output.md | 104 ++ .claude/skills/opsx-ff/SKILL.md | 218 +++ .claude/skills/opsx-ff/evals/evals.json | 84 ++ .claude/skills/opsx-ff/evals/grading.json | 65 + .../with_skill/run-1/grading.json | 39 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 39 + .../without_skill/run-1/timing.json | 1 + .claude/skills/opsx-ff/examples/ff-output.md | 114 ++ .claude/skills/opsx-new/SKILL.md | 95 ++ .../skills/opsx-new/examples/new-output.md | 57 + .claude/skills/opsx-onboard/SKILL.md | 238 +++ .claude/skills/opsx-onboard/evals/evals.json | 83 ++ .../skills/opsx-onboard/evals/grading.json | 49 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../opsx-onboard/examples/design-template.md | 29 + .../examples/proposal-template.md | 32 + .../opsx-onboard/examples/specs-template.md | 23 + .../opsx-onboard/examples/tasks-template.md | 20 + .../opsx-onboard/references/phase-messages.md | 291 ++++ .claude/skills/opsx-pipeline/SKILL.md | 326 ++++ .claude/skills/opsx-pipeline/evals/evals.json | 83 ++ .../skills/opsx-pipeline/evals/grading.json | 49 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../opsx-pipeline/examples/pipeline-output.md | 146 ++ .claude/skills/opsx-pipeline/learnings.md | 16 + .../templates/pr-body-template.md | 38 + .../templates/sub-agent-prompt-template.md | 139 ++ .claude/skills/opsx-plan-to-issues/SKILL.md | 245 +++ .../opsx-plan-to-issues/evals/evals.json | 83 ++ .../opsx-plan-to-issues/evals/grading.json | 63 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../examples/plan-to-issues-output.md | 118 ++ .../skills/opsx-plan-to-issues/learnings.md | 25 + .claude/skills/opsx-reverse-spec/SKILL.md | 287 ++++ .../skills/opsx-reverse-spec/evals/evals.json | 125 ++ .../opsx-reverse-spec/evals/grading.json | 93 ++ .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 20 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 19 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 15 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 14 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 1 + .../examples/openregister-contact-matching.md | 251 ++++ .claude/skills/opsx-reverse-spec/learnings.md | 58 + .claude/skills/opsx-sync/SKILL.md | 139 ++ .../skills/opsx-sync/examples/sync-output.md | 56 + .claude/skills/opsx-verify/SKILL.md | 529 +++++++ .claude/skills/opsx-verify/evals/evals.json | 83 ++ .claude/skills/opsx-verify/evals/grading.json | 63 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 5 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 5 + .../opsx-verify/examples/verify-report.md | 130 ++ .claude/skills/persistence-audit/SKILL.md | 242 +++ .../skills/persistence-audit/evals/evals.json | 114 ++ .../persistence-audit/evals/grading.json | 81 + .../workspace/iteration-1/benchmark.json | 49 + .../eval-hydra-pipeline/eval_metadata.json | 13 + .../with_skill/grading.json | 43 + .../with_skill/outputs/report.md | 219 +++ .../with_skill/timing.json | 6 + .../eval-post-incident/eval_metadata.json | 12 + .../with_skill/grading.json | 38 + .../with_skill/outputs/report.md | 123 ++ .../eval-post-incident/with_skill/timing.json | 6 + .../eval-spec-only/eval_metadata.json | 11 + .../eval-spec-only/with_skill/grading.json | 33 + .../with_skill/outputs/report.md | 111 ++ .../eval-spec-only/with_skill/timing.json | 6 + .../references/hydra-persistence-vectors.md | 115 ++ .claude/skills/report-out/SKILL.md | 419 ++++++ .claude/skills/report-out/evals/evals.json | 167 +++ .claude/skills/report-out/evals/grading.json | 285 ++++ .../iteration-1/grade_programmatic.py | 208 +++ .../evals/workspace/iteration-1/timing.json | 1 + .../report-out/examples/full-day-output.md | 111 ++ .claude/skills/report-out/learnings.md | 43 + .../references/branch-pr-detection.md | 108 ++ .../references/comment-update-protocol.md | 78 + .../report-out/references/git-discovery.md | 94 ++ .../references/interaction-discovery.md | 87 ++ .../report-out/references/issue-lifecycle.md | 177 +++ .../references/pr-update-protocol.md | 79 + .../report-out/references/tracking-issues.md | 86 ++ .../report-out/templates/issue-comment.md | 108 ++ .../templates/report-out-message.md | 87 ++ .claude/skills/review-pr/SKILL.md | 480 ++++++ .claude/skills/review-pr/evals/evals.json | 172 +++ .claude/skills/review-pr/evals/grading.json | 165 ++ .../with_skill/run-1/grading.json | 54 + .../with_skill/run-1/timing.json | 5 + .../without_skill/run-1/grading.json | 54 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../skills/review-pr/learning-candidates.md | 10 + .claude/skills/review-pr/learnings.md | 71 + .../review-pr/references/analysis-brief.md | 46 + .../review-pr/references/comment-format.md | 67 + .../review-pr/references/merged-pr-context.md | 76 + .../persistence-audit-integration.md | 73 + .../references/post-inline-comments.md | 64 + .../review-pr/references/re-review-context.md | 99 ++ .../review-pr/references/review-checklist.md | 105 ++ .../review-pr/references/strictness-modes.md | 90 ++ .../review-pr/scripts/resolve-threads.py | 14 + .../skills/skill-creator/.upstream-version | 1 + .claude/skills/skill-creator/LICENSE.txt | 202 +++ .claude/skills/skill-creator/NOTICE | 11 + .claude/skills/skill-creator/SKILL.md | 293 ++++ .../skills/skill-creator/agents/analyzer.md | 274 ++++ .../skills/skill-creator/agents/comparator.md | 202 +++ .claude/skills/skill-creator/agents/grader.md | 223 +++ .../skill-creator/assets/eval_review.html | 146 ++ .../eval-viewer/generate_review.py | 471 ++++++ .../skill-creator/eval-viewer/viewer.html | 1325 +++++++++++++++++ .../references/description-optimization.md | 72 + .../skill-creator/references/eval-runner.md | 148 ++ .../skill-creator/references/schemas.md | 430 ++++++ .../skills/skill-creator/scripts/__init__.py | 0 .../scripts/aggregate_benchmark.py | 401 +++++ .../skill-creator/scripts/generate_report.py | 326 ++++ .../scripts/improve_description.py | 247 +++ .../skill-creator/scripts/package_skill.py | 136 ++ .../skill-creator/scripts/quick_validate.py | 103 ++ .../skill-creator/scripts/rotate_workspace.py | 482 ++++++ .../skills/skill-creator/scripts/run_eval.py | 310 ++++ .../skills/skill-creator/scripts/run_loop.py | 328 ++++ .claude/skills/skill-creator/scripts/utils.py | 47 + .claude/skills/skill-creator/update.sh | 17 + .../skill-creator/update_from_upstream.py | 309 ++++ .claude/skills/skill-level-overview.html | 728 +++++++++ .claude/skills/sync-docs/SKILL.md | 387 +++++ .claude/skills/sync-docs/evals/README.md | 67 + .claude/skills/sync-docs/evals/evals.json | 123 ++ .claude/skills/sync-docs/evals/grading.json | 76 + .../with_skill/run-1/grading.json | 13 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 12 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 11 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 10 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 12 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 11 + .../without_skill/run-1/timing.json | 1 + .../skills/sync-docs/learning-candidates.md | 29 + .claude/skills/sync-docs/learnings.md | 33 + .../sync-docs/references/audit-categories.md | 17 + .../references/commands-skills-review.md | 92 ++ .../references/doc-structure-review.md | 19 + .../sync-docs/references/preflight-checks.md | 65 + .../references/skill-health-check.md | 62 + .claude/skills/team-architect/SKILL.md | 325 ++++ .../skills/team-architect/evals/evals.json | 84 ++ .../skills/team-architect/evals/grading.json | 49 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .claude/skills/team-architect/learnings.md | 11 + .../references/bio2-security-checklist.md | 70 + .../dutch-gov-architecture-standards.md | 153 ++ .../references/nlgov-api-design-rules.md | 88 ++ .claude/skills/team-backend/SKILL.md | 345 +++++ .claude/skills/team-backend/evals/evals.json | 85 ++ .../skills/team-backend/evals/grading.json | 50 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 39 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 39 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .claude/skills/team-backend/learnings.md | 11 + .../references/dutch-gov-backend-standards.md | 102 ++ .claude/skills/team-frontend/SKILL.md | 334 +++++ .claude/skills/team-frontend/evals/evals.json | 84 ++ .../skills/team-frontend/evals/grading.json | 49 + .../eval-i18n/with_skill/run-1/grading.json | 34 + .../eval-i18n/with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../eval-i18n/without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../eval-webpack/with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .claude/skills/team-frontend/learnings.md | 11 + .../dutch-gov-frontend-standards.md | 132 ++ .../references/nextcloud-layout-patterns.md | 84 ++ .../references/pinia-store-pattern.md | 104 ++ .claude/skills/team-po/SKILL.md | 172 +++ .claude/skills/team-po/examples/po-review.md | 66 + .claude/skills/team-qa/SKILL.md | 219 +++ .claude/skills/team-qa/evals/evals.json | 84 ++ .claude/skills/team-qa/evals/grading.json | 49 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .claude/skills/team-qa/learnings.md | 11 + .../references/dutch-gov-compliance.md | 120 ++ .../team-qa/templates/qa-report-template.md | 58 + .claude/skills/team-reviewer/SKILL.md | 300 ++++ .../team-reviewer/examples/code-review.md | 132 ++ .claude/skills/team-sm/SKILL.md | 228 +++ .../skills/team-sm/examples/standup-report.md | 82 + .claude/skills/test-accessibility/SKILL.md | 229 +++ .../examples/accessibility-report.md | 106 ++ .claude/skills/test-api/SKILL.md | 276 ++++ .../skills/test-api/examples/api-report.md | 101 ++ .claude/skills/test-app/SKILL.md | 215 +++ .claude/skills/test-app/evals/evals.json | 86 ++ .../evals/fixtures/sample-app/README.md | 16 + .../test-scenarios/TS-001-list-objects.md | 22 + .../test-scenarios/TS-002-unauth-blocked.md | 21 + .../test-scenarios/TS-003-export-draft.md | 22 + .../test-scenarios/TS-004-other-command.md | 22 + .claude/skills/test-app/evals/grading.json | 50 + .../workspace/eval-review-iteration-1.html | 1325 +++++++++++++++++ .../workspace/eval-review-iteration-2.html | 1325 +++++++++++++++++ .../workspace/eval-review-iteration-3.html | 1325 +++++++++++++++++ .../workspace/iteration-1/benchmark.json | 139 ++ .../evals/workspace/iteration-1/benchmark.md | 13 + .../eval_metadata.json | 11 + .../with_skill/outputs/report.md | 57 + .../with_skill/run-1/grading.json | 53 + .../with_skill/timing.json | 5 + .../without_skill/outputs/metrics.json | 34 + .../without_skill/outputs/report.md | 120 ++ .../without_skill/run-1/grading.json | 54 + .../without_skill/timing.json | 5 + .../workspace/iteration-2/benchmark.json | 293 ++++ .../evals/workspace/iteration-2/benchmark.md | 13 + .../with_skill/run-1/grading.json | 33 + .../with_skill/run-1/outputs/report.md | 122 ++ .../with_skill/run-1/timing.json | 6 + .../without_skill/run-1/grading.json | 33 + .../without_skill/run-1/outputs/report.md | 25 + .../without_skill/run-1/timing.json | 6 + .../with_skill/run-1/grading.json | 33 + .../with_skill/run-1/outputs/report.md | 32 + .../with_skill/run-1/timing.json | 6 + .../without_skill/run-1/grading.json | 33 + .../without_skill/run-1/outputs/report.md | 361 +++++ .../without_skill/run-1/timing.json | 6 + .../with_skill/run-1/grading.json | 33 + .../with_skill/run-1/outputs/report.md | 37 + .../with_skill/run-1/timing.json | 6 + .../without_skill/run-1/grading.json | 33 + .../without_skill/run-1/outputs/report.md | 42 + .../without_skill/run-1/timing.json | 6 + .../workspace/iteration-3/benchmark.json | 303 ++++ .../evals/workspace/iteration-3/benchmark.md | 13 + .../with_skill/run-1/grading.json | 33 + .../with_skill/run-1/outputs/report.md | 66 + .../with_skill/run-1/timing.json | 6 + .../without_skill/run-1/grading.json | 33 + .../without_skill/run-1/outputs/report.md | 48 + .../without_skill/run-1/timing.json | 6 + .../with_skill/run-1/grading.json | 33 + .../with_skill/run-1/outputs/report.md | 56 + .../with_skill/run-1/timing.json | 6 + .../without_skill/run-1/grading.json | 33 + .../without_skill/run-1/outputs/report.md | 55 + .../without_skill/run-1/timing.json | 6 + .../with_skill/run-1/grading.json | 38 + .../with_skill/run-1/outputs/report.md | 69 + .../with_skill/run-1/timing.json | 6 + .../without_skill/run-1/grading.json | 38 + .../without_skill/run-1/outputs/report.md | 69 + .../without_skill/run-1/timing.json | 6 + .claude/skills/test-app/learnings.md | 25 + .../templates/agent-prompt-template.md | 159 ++ .../templates/perspective-instructions.md | 104 ++ .../templates/summary-report-template.md | 74 + .claude/skills/test-counsel/SKILL.md | 207 +++ .claude/skills/test-counsel/evals/evals.json | 83 ++ .../skills/test-counsel/evals/grading.json | 55 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../with_skill/run-1/grading.json | 34 + .../with_skill/run-1/timing.json | 1 + .../without_skill/run-1/grading.json | 34 + .../without_skill/run-1/timing.json | 1 + .../test-counsel/examples/counsel-report.md | 201 +++ .claude/skills/test-counsel/learnings.md | 16 + .../templates/sub-agent-prompt-template.md | 162 ++ .../templates/synthesis-report-template.md | 151 ++ .claude/skills/test-functional/SKILL.md | 163 ++ .../examples/functional-report.md | 72 + .claude/skills/test-performance/SKILL.md | 231 +++ .../examples/performance-report.md | 111 ++ .../skills/test-persona-annemarie/SKILL.md | 214 +++ .../examples/persona-report.md | 79 + .claude/skills/test-persona-fatima/SKILL.md | 165 ++ .../examples/persona-report.md | 69 + .claude/skills/test-persona-henk/SKILL.md | 172 +++ .../examples/persona-report.md | 71 + .../skills/test-persona-janwillem/SKILL.md | 176 +++ .../examples/persona-report.md | 70 + .claude/skills/test-persona-mark/SKILL.md | 175 +++ .../examples/persona-report.md | 71 + .claude/skills/test-persona-noor/SKILL.md | 194 +++ .../examples/persona-report.md | 78 + .claude/skills/test-persona-priya/SKILL.md | 202 +++ .../examples/persona-report.md | 81 + .claude/skills/test-persona-sem/SKILL.md | 175 +++ .../examples/persona-report.md | 70 + .claude/skills/test-regression/SKILL.md | 224 +++ .../examples/regression-report.md | 110 ++ .claude/skills/test-scenario-create/SKILL.md | 323 ++++ .../examples/scenario-file.md | 85 ++ .claude/skills/test-scenario-edit/SKILL.md | 229 +++ .../examples/edit-output.md | 47 + .claude/skills/test-scenario-run/SKILL.md | 252 ++++ .../examples/scenario-run-output.md | 80 + .claude/skills/test-security/SKILL.md | 275 ++++ .../test-security/examples/security-report.md | 107 ++ .claude/skills/update-skill-overview.sh | 330 ++++ .../verify-global-settings-version/SKILL.md | 136 ++ .../examples/check-output.md | 69 + .specter-prompt.txt | 49 + 702 files changed, 53365 insertions(+) create mode 100644 .claude/openspec/architecture/adr-001-data-layer.md create mode 100644 .claude/openspec/architecture/adr-002-api.md create mode 100644 .claude/openspec/architecture/adr-003-backend.md create mode 100644 .claude/openspec/architecture/adr-004-frontend.md create mode 100644 .claude/openspec/architecture/adr-005-security.md create mode 100644 .claude/openspec/architecture/adr-006-metrics.md create mode 100644 .claude/openspec/architecture/adr-007-i18n.md create mode 100644 .claude/openspec/architecture/adr-008-testing.md create mode 100644 .claude/openspec/architecture/adr-009-docs.md create mode 100644 .claude/openspec/architecture/adr-010-nl-design.md create mode 100644 .claude/openspec/architecture/adr-011-schema-standards.md create mode 100644 .claude/openspec/architecture/adr-012-deduplication.md create mode 100644 .claude/openspec/architecture/adr-013-container-pool.md create mode 100644 .claude/openspec/architecture/adr-014-licensing.md create mode 100644 .claude/openspec/architecture/adr-015-common-patterns.md create mode 100644 .claude/openspec/architecture/adr-016-routes.md create mode 100644 .claude/openspec/architecture/adr-017-component-composition.md create mode 100644 .claude/openspec/architecture/adr-018-widget-header-actions.md create mode 100644 .claude/openspec/architecture/adr-019-integration-registry.md create mode 100644 .claude/openspec/architecture/adr-020-gate-scope-to-pr-diff.md create mode 100644 .claude/openspec/architecture/adr-021-bounded-fix-scope-by-shape.md create mode 100644 .claude/openspec/architecture/adr-022-apps-consume-or-abstractions.md create mode 100644 .claude/openspec/architecture/adr-023-action-authorization.md create mode 100644 .claude/openspec/architecture/adr-024-app-manifest.md create mode 100644 .claude/openspec/architecture/adr-025-i18n-source-of-truth.md create mode 100644 .claude/openspec/architecture/adr-029-route-reachability-gate.md create mode 100644 .claude/openspec/architecture/adr-030-journeydoc-pattern.md create mode 100644 .claude/openspec/architecture/adr-031-schema-declarative-business-logic.md create mode 100644 .claude/openspec/architecture/adr-032-spec-sizing-and-chaining.md create mode 100644 .claude/skills/clean-env/SKILL.md create mode 100644 .claude/skills/clean-env/examples/expected-output.md create mode 100644 .claude/skills/create-pr/SKILL.md create mode 100644 .claude/skills/create-pr/evals/evals.json create mode 100644 .claude/skills/create-pr/evals/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/benchmark.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/benchmark.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/eval_metadata.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/with_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/with_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/with_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/with_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/without_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/without_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/without_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/without_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-branch-protection/without_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/eval_metadata.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/with_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/with_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/with_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/with_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/with_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/without_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/without_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/without_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/without_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-conventional-commits-title/without_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/eval_metadata.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/with_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/with_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/with_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/with_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/with_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/without_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/without_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/without_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/without_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-merge-freshness-warning/without_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/eval_metadata.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/with_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/with_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/with_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/with_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/without_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/without_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/without_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/without_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-quality-checks/without_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/eval_metadata.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/with_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/with_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/with_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/with_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/with_skill/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/without_skill/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/without_skill/outputs/summary.md create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/without_skill/run-1/grading.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/without_skill/run-1/timing.json create mode 100644 .claude/skills/create-pr/evals/workspace/iteration-1/eval-standard-pr/without_skill/timing.json create mode 100644 .claude/skills/create-pr/learnings.md create mode 100644 .claude/skills/create-pr/references/branch-protection-guide.md create mode 100644 .claude/skills/create-pr/references/check-tool-verification.md create mode 100644 .claude/skills/create-pr/references/ci-step-categorisation-guide.md create mode 100644 .claude/skills/create-pr/references/local-checks.md create mode 100644 .claude/skills/feature-counsel/SKILL.md create mode 100644 .claude/skills/feature-counsel/evals/evals.json create mode 100644 .claude/skills/feature-counsel/evals/grading.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-model-selection/with_skill/run-1/grading.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-model-selection/with_skill/run-1/timing.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-model-selection/without_skill/run-1/grading.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-model-selection/without_skill/run-1/timing.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-multi-persona/with_skill/run-1/grading.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-multi-persona/with_skill/run-1/timing.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-multi-persona/without_skill/run-1/grading.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-multi-persona/without_skill/run-1/timing.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-suggestions/with_skill/run-1/grading.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-suggestions/with_skill/run-1/timing.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-suggestions/without_skill/run-1/grading.json create mode 100644 .claude/skills/feature-counsel/evals/workspace/iteration-1/eval-suggestions/without_skill/run-1/timing.json create mode 100644 .claude/skills/feature-counsel/examples/feature-suggestions.md create mode 100644 .claude/skills/feature-counsel/learnings.md create mode 100644 .claude/skills/hydra-gate-admin-router/SKILL.md create mode 100644 .claude/skills/hydra-gate-admin-router/examples/fail.log create mode 100644 .claude/skills/hydra-gate-admin-router/examples/pass.log create mode 100644 .claude/skills/hydra-gate-composer-audit/SKILL.md create mode 100644 .claude/skills/hydra-gate-composer-audit/examples/fail.log create mode 100644 .claude/skills/hydra-gate-composer-audit/examples/pass.log create mode 100644 .claude/skills/hydra-gate-forbidden-patterns/SKILL.md create mode 100644 .claude/skills/hydra-gate-forbidden-patterns/examples/fail.log create mode 100644 .claude/skills/hydra-gate-forbidden-patterns/examples/pass.log create mode 100644 .claude/skills/hydra-gate-initial-state/SKILL.md create mode 100644 .claude/skills/hydra-gate-initial-state/examples/fail.log create mode 100644 .claude/skills/hydra-gate-initial-state/examples/pass.log create mode 100644 .claude/skills/hydra-gate-modal-isolation/SKILL.md create mode 100644 .claude/skills/hydra-gate-modal-isolation/examples/fail.log create mode 100644 .claude/skills/hydra-gate-modal-isolation/examples/pass.log create mode 100644 .claude/skills/hydra-gate-nc-input-labels/SKILL.md create mode 100644 .claude/skills/hydra-gate-nc-input-labels/examples/fail.log create mode 100644 .claude/skills/hydra-gate-nc-input-labels/examples/pass.log create mode 100644 .claude/skills/hydra-gate-no-admin-idor/SKILL.md create mode 100644 .claude/skills/hydra-gate-no-admin-idor/examples/fail.log create mode 100644 .claude/skills/hydra-gate-no-admin-idor/examples/pass.log create mode 100644 .claude/skills/hydra-gate-orphan-auth/SKILL.md create mode 100644 .claude/skills/hydra-gate-orphan-auth/examples/fail.log create mode 100644 .claude/skills/hydra-gate-orphan-auth/examples/pass.log create mode 100644 .claude/skills/hydra-gate-route-auth/SKILL.md create mode 100644 .claude/skills/hydra-gate-route-auth/examples/fail.log create mode 100644 .claude/skills/hydra-gate-route-auth/examples/pass.log create mode 100644 .claude/skills/hydra-gate-semantic-auth/SKILL.md create mode 100644 .claude/skills/hydra-gate-semantic-auth/examples/fail.log create mode 100644 .claude/skills/hydra-gate-semantic-auth/examples/pass.log create mode 100644 .claude/skills/hydra-gate-spdx/SKILL.md create mode 100644 .claude/skills/hydra-gate-spdx/examples/fail.log create mode 100644 .claude/skills/hydra-gate-spdx/examples/pass.log create mode 100644 .claude/skills/hydra-gate-stub-scan/SKILL.md create mode 100644 .claude/skills/hydra-gate-stub-scan/examples/fail.log create mode 100644 .claude/skills/hydra-gate-stub-scan/examples/pass.log create mode 100644 .claude/skills/hydra-gate-unsafe-auth-resolver/SKILL.md create mode 100644 .claude/skills/hydra-gate-unsafe-auth-resolver/examples/fail.log create mode 100644 .claude/skills/hydra-gate-unsafe-auth-resolver/examples/pass.log create mode 100644 .claude/skills/hydra-gates/SKILL.md create mode 100644 .claude/skills/journeydoc-add-story/SKILL.md create mode 100644 .claude/skills/journeydoc-init/SKILL.md create mode 100644 .claude/skills/journeydoc-instrument/SKILL.md create mode 100644 .claude/skills/local-run/SKILL.md create mode 100644 .claude/skills/opsx-annotate/SKILL.md create mode 100644 .claude/skills/opsx-annotate/evals/README.md create mode 100644 .claude/skills/opsx-annotate/evals/evals.json create mode 100644 .claude/skills/opsx-annotate/evals/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/benchmark.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/benchmark.md create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-dirty-tree-refuses/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-dirty-tree-refuses/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-dirty-tree-refuses/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-dirty-tree-refuses/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-happy-path-small-app/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-happy-path-small-app/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-happy-path-small-app/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-happy-path-small-app/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-idempotent-rerun/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-idempotent-rerun/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-idempotent-rerun/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-idempotent-rerun/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-stale-coverage-report/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-stale-coverage-report/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-stale-coverage-report/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-annotate/evals/workspace/iteration-1/eval-stale-coverage-report/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-annotate/learning-candidates.md create mode 100644 .claude/skills/opsx-annotate/learnings.md create mode 100644 .claude/skills/opsx-annotate/references/standards.md create mode 100644 .claude/skills/opsx-apply-loop/SKILL.md create mode 100644 .claude/skills/opsx-apply-loop/assets/apply-loop.Dockerfile create mode 100644 .claude/skills/opsx-apply-loop/evals/evals.json create mode 100644 .claude/skills/opsx-apply-loop/evals/grading.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-auto-archive/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-auto-archive/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-auto-archive/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-auto-archive/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-loop-until-pass/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-loop-until-pass/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-loop-until-pass/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-loop-until-pass/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-max-iterations/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-max-iterations/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-max-iterations/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply-loop/evals/workspace/iteration-1/eval-max-iterations/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply-loop/learnings.md create mode 100644 .claude/skills/opsx-apply-loop/references/apply-verify-loop-protocol.md create mode 100644 .claude/skills/opsx-apply-loop/references/container-auth-check.md create mode 100644 .claude/skills/opsx-apply-loop/references/container-limitations.md create mode 100644 .claude/skills/opsx-apply-loop/references/container-setup-guide.md create mode 100644 .claude/skills/opsx-apply-loop/references/host-test-loop-protocol.md create mode 100644 .claude/skills/opsx-apply-loop/references/loop-report-template.md create mode 100644 .claude/skills/opsx-apply-loop/scripts/apply-loop-check.sh create mode 100644 .claude/skills/opsx-apply/SKILL.md create mode 100644 .claude/skills/opsx-apply/evals/evals.json create mode 100644 .claude/skills/opsx-apply/evals/grading.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/benchmark.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/benchmark.md create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-implement-tasks/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-implement-tasks/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-implement-tasks/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-implement-tasks/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-model-guard/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-model-guard/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-model-guard/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-model-guard/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-quality-gate/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-quality-gate/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-quality-gate/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-apply/evals/workspace/iteration-1/eval-quality-gate/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-apply/examples/apply-output.md create mode 100644 .claude/skills/opsx-apply/learnings.md create mode 100644 .claude/skills/opsx-archive/SKILL.md create mode 100644 .claude/skills/opsx-archive/evals/evals.json create mode 100644 .claude/skills/opsx-archive/evals/grading.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/benchmark.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/benchmark.md create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-archive-complete/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-archive-complete/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-archive-complete/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-archive-complete/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-block-incomplete/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-block-incomplete/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-block-incomplete/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-block-incomplete/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-spec-sync/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-spec-sync/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-spec-sync/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-archive/evals/workspace/iteration-1/eval-spec-sync/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-archive/examples/output-templates.md create mode 100644 .claude/skills/opsx-archive/learnings.md create mode 100644 .claude/skills/opsx-archive/templates/test-scenario-template.md create mode 100644 .claude/skills/opsx-bulk-archive/SKILL.md create mode 100644 .claude/skills/opsx-bulk-archive/examples/bulk-archive-output.md create mode 100644 .claude/skills/opsx-continue/SKILL.md create mode 100644 .claude/skills/opsx-continue/examples/continue-output.md create mode 100644 .claude/skills/opsx-coverage-scan/SKILL.md create mode 100644 .claude/skills/opsx-coverage-scan/evals/README.md create mode 100644 .claude/skills/opsx-coverage-scan/evals/evals.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/benchmark.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/benchmark.md create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-empty-specs-stops/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-empty-specs-stops/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-empty-specs-stops/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-empty-specs-stops/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-happy-path-small-app/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-happy-path-small-app/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-happy-path-small-app/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-happy-path-small-app/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-large-app-asks-confirmation/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-large-app-asks-confirmation/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-large-app-asks-confirmation/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-large-app-asks-confirmation/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-python-exapp-deferred/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-python-exapp-deferred/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-python-exapp-deferred/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-coverage-scan/evals/workspace/iteration-1/eval-python-exapp-deferred/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-coverage-scan/learning-candidates.md create mode 100644 .claude/skills/opsx-coverage-scan/learnings.md create mode 100644 .claude/skills/opsx-coverage-scan/references/standards.md create mode 100644 .claude/skills/opsx-explore/SKILL.md create mode 100644 .claude/skills/opsx-explore/evals/evals.json create mode 100644 .claude/skills/opsx-explore/evals/grading.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-explore-existing-change/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-explore-existing-change/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-explore-existing-change/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-explore-existing-change/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-model-guard/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-model-guard/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-model-guard/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-model-guard/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-open-ended-exploration/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-open-ended-exploration/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-open-ended-exploration/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-explore/evals/workspace/iteration-1/eval-open-ended-exploration/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-explore/examples/explore-output.md create mode 100644 .claude/skills/opsx-ff/SKILL.md create mode 100644 .claude/skills/opsx-ff/evals/evals.json create mode 100644 .claude/skills/opsx-ff/evals/grading.json create mode 100644 .claude/skills/opsx-ff/evals/workspace/iteration-1/eval-full-artifacts/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-ff/evals/workspace/iteration-1/eval-full-artifacts/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-ff/evals/workspace/iteration-1/eval-full-artifacts/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-ff/evals/workspace/iteration-1/eval-full-artifacts/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-ff/examples/ff-output.md create mode 100644 .claude/skills/opsx-new/SKILL.md create mode 100644 .claude/skills/opsx-new/examples/new-output.md create mode 100644 .claude/skills/opsx-onboard/SKILL.md create mode 100644 .claude/skills/opsx-onboard/evals/evals.json create mode 100644 .claude/skills/opsx-onboard/evals/grading.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-educational/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-educational/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-educational/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-educational/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-guided-tour/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-guided-tour/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-guided-tour/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-guided-tour/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-real-work/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-real-work/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-real-work/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-onboard/evals/workspace/iteration-1/eval-real-work/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-onboard/examples/design-template.md create mode 100644 .claude/skills/opsx-onboard/examples/proposal-template.md create mode 100644 .claude/skills/opsx-onboard/examples/specs-template.md create mode 100644 .claude/skills/opsx-onboard/examples/tasks-template.md create mode 100644 .claude/skills/opsx-onboard/references/phase-messages.md create mode 100644 .claude/skills/opsx-pipeline/SKILL.md create mode 100644 .claude/skills/opsx-pipeline/evals/evals.json create mode 100644 .claude/skills/opsx-pipeline/evals/grading.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-model-selection/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-model-selection/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-model-selection/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-model-selection/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-parallel-changes/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-parallel-changes/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-parallel-changes/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-parallel-changes/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-pr-creation/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-pr-creation/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-pr-creation/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-pipeline/evals/workspace/iteration-1/eval-pr-creation/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-pipeline/examples/pipeline-output.md create mode 100644 .claude/skills/opsx-pipeline/learnings.md create mode 100644 .claude/skills/opsx-pipeline/templates/pr-body-template.md create mode 100644 .claude/skills/opsx-pipeline/templates/sub-agent-prompt-template.md create mode 100644 .claude/skills/opsx-plan-to-issues/SKILL.md create mode 100644 .claude/skills/opsx-plan-to-issues/evals/evals.json create mode 100644 .claude/skills/opsx-plan-to-issues/evals/grading.json create mode 100644 .claude/skills/opsx-plan-to-issues/evals/workspace/iteration-1/eval-create-issue/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-plan-to-issues/evals/workspace/iteration-1/eval-create-issue/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-plan-to-issues/evals/workspace/iteration-1/eval-create-issue/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-plan-to-issues/evals/workspace/iteration-1/eval-create-issue/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-plan-to-issues/examples/plan-to-issues-output.md create mode 100644 .claude/skills/opsx-plan-to-issues/learnings.md create mode 100644 .claude/skills/opsx-reverse-spec/SKILL.md create mode 100644 .claude/skills/opsx-reverse-spec/evals/evals.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-cap-enforcement/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-cap-enforcement/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-cap-enforcement/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-cap-enforcement/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-path-openregister-contact-matching/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-path-openregister-contact-matching/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-path-openregister-contact-matching/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-cluster-path-openregister-contact-matching/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-extend-path-existing-capability/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-extend-path-existing-capability/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-extend-path-existing-capability/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-extend-path-existing-capability/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-model-guard-haiku/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-model-guard-haiku/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-model-guard-haiku/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-model-guard-haiku/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-stale-coverage-report/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-stale-coverage-report/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-stale-coverage-report/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-reverse-spec/evals/workspace/iteration-1/eval-stale-coverage-report/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-reverse-spec/examples/openregister-contact-matching.md create mode 100644 .claude/skills/opsx-reverse-spec/learnings.md create mode 100644 .claude/skills/opsx-sync/SKILL.md create mode 100644 .claude/skills/opsx-sync/examples/sync-output.md create mode 100644 .claude/skills/opsx-verify/SKILL.md create mode 100644 .claude/skills/opsx-verify/evals/evals.json create mode 100644 .claude/skills/opsx-verify/evals/grading.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-fail-gracefully/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-fail-gracefully/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-fail-gracefully/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-fail-gracefully/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-sync-github/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-sync-github/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-sync-github/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-sync-github/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-three-dimensions/with_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-three-dimensions/with_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-three-dimensions/without_skill/run-1/grading.json create mode 100644 .claude/skills/opsx-verify/evals/workspace/iteration-1/eval-three-dimensions/without_skill/run-1/timing.json create mode 100644 .claude/skills/opsx-verify/examples/verify-report.md create mode 100644 .claude/skills/persistence-audit/SKILL.md create mode 100644 .claude/skills/persistence-audit/evals/evals.json create mode 100644 .claude/skills/persistence-audit/evals/grading.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/benchmark.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-hydra-pipeline/eval_metadata.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-hydra-pipeline/with_skill/grading.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-hydra-pipeline/with_skill/outputs/report.md create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-hydra-pipeline/with_skill/timing.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-post-incident/eval_metadata.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-post-incident/with_skill/grading.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-post-incident/with_skill/outputs/report.md create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-post-incident/with_skill/timing.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-spec-only/eval_metadata.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-spec-only/with_skill/grading.json create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-spec-only/with_skill/outputs/report.md create mode 100644 .claude/skills/persistence-audit/evals/workspace/iteration-1/eval-spec-only/with_skill/timing.json create mode 100644 .claude/skills/persistence-audit/references/hydra-persistence-vectors.md create mode 100644 .claude/skills/report-out/SKILL.md create mode 100644 .claude/skills/report-out/evals/evals.json create mode 100644 .claude/skills/report-out/evals/grading.json create mode 100644 .claude/skills/report-out/evals/workspace/iteration-1/grade_programmatic.py create mode 100644 .claude/skills/report-out/evals/workspace/iteration-1/timing.json create mode 100644 .claude/skills/report-out/examples/full-day-output.md create mode 100644 .claude/skills/report-out/learnings.md create mode 100644 .claude/skills/report-out/references/branch-pr-detection.md create mode 100644 .claude/skills/report-out/references/comment-update-protocol.md create mode 100644 .claude/skills/report-out/references/git-discovery.md create mode 100644 .claude/skills/report-out/references/interaction-discovery.md create mode 100644 .claude/skills/report-out/references/issue-lifecycle.md create mode 100644 .claude/skills/report-out/references/pr-update-protocol.md create mode 100644 .claude/skills/report-out/references/tracking-issues.md create mode 100644 .claude/skills/report-out/templates/issue-comment.md create mode 100644 .claude/skills/report-out/templates/report-out-message.md create mode 100644 .claude/skills/review-pr/SKILL.md create mode 100644 .claude/skills/review-pr/evals/evals.json create mode 100644 .claude/skills/review-pr/evals/grading.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-large-diff-handling/with_skill/run-1/grading.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-large-diff-handling/with_skill/run-1/timing.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-large-diff-handling/without_skill/run-1/grading.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-large-diff-handling/without_skill/run-1/timing.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-line-number-accuracy/with_skill/run-1/grading.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-line-number-accuracy/with_skill/run-1/timing.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-line-number-accuracy/without_skill/run-1/grading.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-line-number-accuracy/without_skill/run-1/timing.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-no-duplicate-comments/with_skill/run-1/grading.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-no-duplicate-comments/with_skill/run-1/timing.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-no-duplicate-comments/without_skill/run-1/grading.json create mode 100644 .claude/skills/review-pr/evals/workspace/iteration-1/eval-no-duplicate-comments/without_skill/run-1/timing.json create mode 100644 .claude/skills/review-pr/learning-candidates.md create mode 100644 .claude/skills/review-pr/learnings.md create mode 100644 .claude/skills/review-pr/references/analysis-brief.md create mode 100644 .claude/skills/review-pr/references/comment-format.md create mode 100644 .claude/skills/review-pr/references/merged-pr-context.md create mode 100644 .claude/skills/review-pr/references/persistence-audit-integration.md create mode 100644 .claude/skills/review-pr/references/post-inline-comments.md create mode 100644 .claude/skills/review-pr/references/re-review-context.md create mode 100644 .claude/skills/review-pr/references/review-checklist.md create mode 100644 .claude/skills/review-pr/references/strictness-modes.md create mode 100644 .claude/skills/review-pr/scripts/resolve-threads.py create mode 100644 .claude/skills/skill-creator/.upstream-version create mode 100644 .claude/skills/skill-creator/LICENSE.txt create mode 100644 .claude/skills/skill-creator/NOTICE create mode 100644 .claude/skills/skill-creator/SKILL.md create mode 100644 .claude/skills/skill-creator/agents/analyzer.md create mode 100644 .claude/skills/skill-creator/agents/comparator.md create mode 100644 .claude/skills/skill-creator/agents/grader.md create mode 100644 .claude/skills/skill-creator/assets/eval_review.html create mode 100644 .claude/skills/skill-creator/eval-viewer/generate_review.py create mode 100644 .claude/skills/skill-creator/eval-viewer/viewer.html create mode 100644 .claude/skills/skill-creator/references/description-optimization.md create mode 100644 .claude/skills/skill-creator/references/eval-runner.md create mode 100644 .claude/skills/skill-creator/references/schemas.md create mode 100644 .claude/skills/skill-creator/scripts/__init__.py create mode 100644 .claude/skills/skill-creator/scripts/aggregate_benchmark.py create mode 100644 .claude/skills/skill-creator/scripts/generate_report.py create mode 100644 .claude/skills/skill-creator/scripts/improve_description.py create mode 100644 .claude/skills/skill-creator/scripts/package_skill.py create mode 100644 .claude/skills/skill-creator/scripts/quick_validate.py create mode 100644 .claude/skills/skill-creator/scripts/rotate_workspace.py create mode 100644 .claude/skills/skill-creator/scripts/run_eval.py create mode 100644 .claude/skills/skill-creator/scripts/run_loop.py create mode 100644 .claude/skills/skill-creator/scripts/utils.py create mode 100644 .claude/skills/skill-creator/update.sh create mode 100644 .claude/skills/skill-creator/update_from_upstream.py create mode 100644 .claude/skills/skill-level-overview.html create mode 100644 .claude/skills/sync-docs/SKILL.md create mode 100644 .claude/skills/sync-docs/evals/README.md create mode 100644 .claude/skills/sync-docs/evals/evals.json create mode 100644 .claude/skills/sync-docs/evals/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-app-docs-openregister-feature-drift/with_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-app-docs-openregister-feature-drift/with_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-app-docs-openregister-feature-drift/without_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-app-docs-openregister-feature-drift/without_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-dev-docs-branch-divergence/with_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-dev-docs-branch-divergence/with_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-dev-docs-branch-divergence/without_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-dev-docs-branch-divergence/without_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-md-only-guardrail-schema-yaml/with_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-md-only-guardrail-schema-yaml/with_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-md-only-guardrail-schema-yaml/without_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-md-only-guardrail-schema-yaml/without_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-preflight-sources-of-truth-drift/with_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-preflight-sources-of-truth-drift/with_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-preflight-sources-of-truth-drift/without_skill/run-1/grading.json create mode 100644 .claude/skills/sync-docs/evals/workspace/iteration-1/eval-preflight-sources-of-truth-drift/without_skill/run-1/timing.json create mode 100644 .claude/skills/sync-docs/learning-candidates.md create mode 100644 .claude/skills/sync-docs/learnings.md create mode 100644 .claude/skills/sync-docs/references/audit-categories.md create mode 100644 .claude/skills/sync-docs/references/commands-skills-review.md create mode 100644 .claude/skills/sync-docs/references/doc-structure-review.md create mode 100644 .claude/skills/sync-docs/references/preflight-checks.md create mode 100644 .claude/skills/sync-docs/references/skill-health-check.md create mode 100644 .claude/skills/team-architect/SKILL.md create mode 100644 .claude/skills/team-architect/evals/evals.json create mode 100644 .claude/skills/team-architect/evals/grading.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-adr-assessment/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-adr-assessment/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-adr-assessment/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-adr-assessment/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-pattern-check/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-pattern-check/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-pattern-check/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-pattern-check/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-review-design/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-review-design/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-review-design/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-architect/evals/workspace/iteration-1/eval-review-design/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-architect/learnings.md create mode 100644 .claude/skills/team-architect/references/bio2-security-checklist.md create mode 100644 .claude/skills/team-architect/references/dutch-gov-architecture-standards.md create mode 100644 .claude/skills/team-architect/references/nlgov-api-design-rules.md create mode 100644 .claude/skills/team-backend/SKILL.md create mode 100644 .claude/skills/team-backend/evals/evals.json create mode 100644 .claude/skills/team-backend/evals/grading.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-github-sync/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-github-sync/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-github-sync/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-github-sync/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-implement-task/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-implement-task/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-implement-task/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-implement-task/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-quality-gate/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-quality-gate/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-quality-gate/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-backend/evals/workspace/iteration-1/eval-quality-gate/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-backend/learnings.md create mode 100644 .claude/skills/team-backend/references/dutch-gov-backend-standards.md create mode 100644 .claude/skills/team-frontend/SKILL.md create mode 100644 .claude/skills/team-frontend/evals/evals.json create mode 100644 .claude/skills/team-frontend/evals/grading.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-i18n/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-i18n/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-i18n/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-i18n/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-implement-vue/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-implement-vue/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-implement-vue/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-implement-vue/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-webpack/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-webpack/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-webpack/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-frontend/evals/workspace/iteration-1/eval-webpack/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-frontend/learnings.md create mode 100644 .claude/skills/team-frontend/references/dutch-gov-frontend-standards.md create mode 100644 .claude/skills/team-frontend/references/nextcloud-layout-patterns.md create mode 100644 .claude/skills/team-frontend/references/pinia-store-pattern.md create mode 100644 .claude/skills/team-po/SKILL.md create mode 100644 .claude/skills/team-po/examples/po-review.md create mode 100644 .claude/skills/team-qa/SKILL.md create mode 100644 .claude/skills/team-qa/evals/evals.json create mode 100644 .claude/skills/team-qa/evals/grading.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-acceptance/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-acceptance/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-acceptance/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-acceptance/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-defect-report/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-defect-report/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-defect-report/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-defect-report/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-test-coverage/with_skill/run-1/grading.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-test-coverage/with_skill/run-1/timing.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-test-coverage/without_skill/run-1/grading.json create mode 100644 .claude/skills/team-qa/evals/workspace/iteration-1/eval-test-coverage/without_skill/run-1/timing.json create mode 100644 .claude/skills/team-qa/learnings.md create mode 100644 .claude/skills/team-qa/references/dutch-gov-compliance.md create mode 100644 .claude/skills/team-qa/templates/qa-report-template.md create mode 100644 .claude/skills/team-reviewer/SKILL.md create mode 100644 .claude/skills/team-reviewer/examples/code-review.md create mode 100644 .claude/skills/team-sm/SKILL.md create mode 100644 .claude/skills/team-sm/examples/standup-report.md create mode 100644 .claude/skills/test-accessibility/SKILL.md create mode 100644 .claude/skills/test-accessibility/examples/accessibility-report.md create mode 100644 .claude/skills/test-api/SKILL.md create mode 100644 .claude/skills/test-api/examples/api-report.md create mode 100644 .claude/skills/test-app/SKILL.md create mode 100644 .claude/skills/test-app/evals/evals.json create mode 100644 .claude/skills/test-app/evals/fixtures/sample-app/README.md create mode 100644 .claude/skills/test-app/evals/fixtures/sample-app/test-scenarios/TS-001-list-objects.md create mode 100644 .claude/skills/test-app/evals/fixtures/sample-app/test-scenarios/TS-002-unauth-blocked.md create mode 100644 .claude/skills/test-app/evals/fixtures/sample-app/test-scenarios/TS-003-export-draft.md create mode 100644 .claude/skills/test-app/evals/fixtures/sample-app/test-scenarios/TS-004-other-command.md create mode 100644 .claude/skills/test-app/evals/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/eval-review-iteration-1.html create mode 100644 .claude/skills/test-app/evals/workspace/eval-review-iteration-2.html create mode 100644 .claude/skills/test-app/evals/workspace/eval-review-iteration-3.html create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/benchmark.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/benchmark.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/eval_metadata.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/with_skill/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/with_skill/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/without_skill/outputs/metrics.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/without_skill/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-1/eval-single-browser-test/without_skill/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/benchmark.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/benchmark.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-1-single-mode/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-1-single-mode/with_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-1-single-mode/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-1-single-mode/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-1-single-mode/without_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-1-single-mode/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-2-all-perspectives/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-2-all-perspectives/with_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-2-all-perspectives/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-2-all-perspectives/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-2-all-perspectives/without_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-2-all-perspectives/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-3-with-scenarios/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-3-with-scenarios/with_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-3-with-scenarios/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-3-with-scenarios/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-3-with-scenarios/without_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-2/eval-3-with-scenarios/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/benchmark.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/benchmark.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-1-single-mode/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-1-single-mode/with_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-1-single-mode/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-1-single-mode/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-1-single-mode/without_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-1-single-mode/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-2-all-perspectives/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-2-all-perspectives/with_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-2-all-perspectives/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-2-all-perspectives/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-2-all-perspectives/without_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-2-all-perspectives/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-3-with-scenarios/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-3-with-scenarios/with_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-3-with-scenarios/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-3-with-scenarios/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-3-with-scenarios/without_skill/run-1/outputs/report.md create mode 100644 .claude/skills/test-app/evals/workspace/iteration-3/eval-3-with-scenarios/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-app/learnings.md create mode 100644 .claude/skills/test-app/templates/agent-prompt-template.md create mode 100644 .claude/skills/test-app/templates/perspective-instructions.md create mode 100644 .claude/skills/test-app/templates/summary-report-template.md create mode 100644 .claude/skills/test-counsel/SKILL.md create mode 100644 .claude/skills/test-counsel/evals/evals.json create mode 100644 .claude/skills/test-counsel/evals/grading.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-multi-persona/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-multi-persona/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-multi-persona/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-multi-persona/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-report-format/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-report-format/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-report-format/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-report-format/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-synthesize/with_skill/run-1/grading.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-synthesize/with_skill/run-1/timing.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-synthesize/without_skill/run-1/grading.json create mode 100644 .claude/skills/test-counsel/evals/workspace/iteration-1/eval-synthesize/without_skill/run-1/timing.json create mode 100644 .claude/skills/test-counsel/examples/counsel-report.md create mode 100644 .claude/skills/test-counsel/learnings.md create mode 100644 .claude/skills/test-counsel/templates/sub-agent-prompt-template.md create mode 100644 .claude/skills/test-counsel/templates/synthesis-report-template.md create mode 100644 .claude/skills/test-functional/SKILL.md create mode 100644 .claude/skills/test-functional/examples/functional-report.md create mode 100644 .claude/skills/test-performance/SKILL.md create mode 100644 .claude/skills/test-performance/examples/performance-report.md create mode 100644 .claude/skills/test-persona-annemarie/SKILL.md create mode 100644 .claude/skills/test-persona-annemarie/examples/persona-report.md create mode 100644 .claude/skills/test-persona-fatima/SKILL.md create mode 100644 .claude/skills/test-persona-fatima/examples/persona-report.md create mode 100644 .claude/skills/test-persona-henk/SKILL.md create mode 100644 .claude/skills/test-persona-henk/examples/persona-report.md create mode 100644 .claude/skills/test-persona-janwillem/SKILL.md create mode 100644 .claude/skills/test-persona-janwillem/examples/persona-report.md create mode 100644 .claude/skills/test-persona-mark/SKILL.md create mode 100644 .claude/skills/test-persona-mark/examples/persona-report.md create mode 100644 .claude/skills/test-persona-noor/SKILL.md create mode 100644 .claude/skills/test-persona-noor/examples/persona-report.md create mode 100644 .claude/skills/test-persona-priya/SKILL.md create mode 100644 .claude/skills/test-persona-priya/examples/persona-report.md create mode 100644 .claude/skills/test-persona-sem/SKILL.md create mode 100644 .claude/skills/test-persona-sem/examples/persona-report.md create mode 100644 .claude/skills/test-regression/SKILL.md create mode 100644 .claude/skills/test-regression/examples/regression-report.md create mode 100644 .claude/skills/test-scenario-create/SKILL.md create mode 100644 .claude/skills/test-scenario-create/examples/scenario-file.md create mode 100644 .claude/skills/test-scenario-edit/SKILL.md create mode 100644 .claude/skills/test-scenario-edit/examples/edit-output.md create mode 100644 .claude/skills/test-scenario-run/SKILL.md create mode 100644 .claude/skills/test-scenario-run/examples/scenario-run-output.md create mode 100644 .claude/skills/test-security/SKILL.md create mode 100644 .claude/skills/test-security/examples/security-report.md create mode 100644 .claude/skills/update-skill-overview.sh create mode 100644 .claude/skills/verify-global-settings-version/SKILL.md create mode 100644 .claude/skills/verify-global-settings-version/examples/check-output.md create mode 100644 .specter-prompt.txt diff --git a/.claude/openspec/architecture/adr-001-data-layer.md b/.claude/openspec/architecture/adr-001-data-layer.md new file mode 100644 index 000000000..7d451b929 --- /dev/null +++ b/.claude/openspec/architecture/adr-001-data-layer.md @@ -0,0 +1,223 @@ +- ALL domain data → OpenRegister objects. NO custom Entity/Mapper for domain data. +- App config → `IAppConfig`. NOT OpenRegister. +- Cross-entity references: OpenRegister relations (register+schema+objectId). NO foreign keys. + MUST NOT store foreign keys or embed full objects. + +### Schema standards + +- Schemas: PascalCase, schema.org vocabulary, explicit types + required flags + description field. +- MUST NOT invent custom property names when a schema.org equivalent exists. +- Contact schemas MUST align with vCard properties (fn, email, tel, adr). +- Dutch government fields SHOULD use a mapping layer translating between international standards + and Dutch specs — do not hardcode Dutch field names as primary. +- Schema changes that remove or rename properties are BREAKING. Adding optional properties is non-breaking. + +### Register templates + +- Location: `lib/Settings/{app}_register.json` (OpenAPI 3.0 + `x-openregister` extensions). +- Three template categories: + - **App configuration** — define data models (schemas/registers/views/mappings). + Mark with `x-openregister.type: "application"`. + - **Mock data** — fictional but realistic seed data for dev/test. + Mark with `x-openregister.type: "mock"`. + - **Government standards** — aligned to Dutch API specs (BAG, BRP, KVK, DSO). +- Import mechanism: `ConfigurationService::importFromApp(appId, data, version, force)` → + `ImportHandler::importFromApp()`. Called from repair step or `SettingsLoadService`. +- Idempotency: re-importing with `force: false` MUST NOT create duplicates. Match by slug + using `ObjectService::searchObjects` with `_rbac: false` and `_multitenancy: false`. + Use `version_compare` for skip logic. + +### Seed data + +Apps that store data in OpenRegister are empty on first install. An empty app cannot be +meaningfully tested — there are no objects to view, search, filter, or interact with. +This blocks both automated browser testing and manual QA. The Loadable Register Template +pattern (see Register templates above) already supports seed data via `components.objects[]` +with the `@self` envelope. + +**Requirements:** + +- Every app using OpenRegister MUST include 3-5 realistic objects per schema in + `lib/Settings/{app}_register.json`. +- Use `@self` envelope: `{ "@self": { "register": ..., "schema": ..., "slug": ... }, ...properties }`. + Register/schema MUST match keys; slug is unique human-readable identifier for matching. +- Use general organisation data (municipality, consultancy, travel agency, non-profit) — + NOT context-specific. Varied, realistic field values. +- Mock data quality: real Dutch street names, valid postcodes (`[1-9][0-9]{3}[A-Z]{2}`), + correct municipality/KVK codes, BSNs that pass 11-proef. Fictional but distinguishable from real. +- Cross-register consistency: BRP→BAG, KVK→BAG, DSO→BAG references must be valid. +- Loaded on install alongside schemas via same `importFromApp()` pipeline. +- MUST be idempotent — re-importing skips existing objects matched by slug. + +**In OpenSpec artifacts:** + +- **In design.md**: MUST include a Seed Data section when change introduces/modifies schemas — + define seed objects per schema with concrete field values and related items (files, notes, tasks, contacts). +- **In tasks.md**: MUST include a seed data generation task when change introduces/modifies schemas. + +**Exceptions** (no seed data required): + +- **nldesign** — has no OpenRegister schemas. +- **ExApp sidecar wrappers** (openklant, opentalk, openzaak, valtimo, n8n-nextcloud) — proxy + external services and do not use OpenRegister. +- **nextcloud-vue** — shared library, no seed data applicable. +- Changes that only modify frontend components or non-schema backend logic (e.g., settings, + permissions) do not require seed data. + +**Limitations:** OpenRegister's `ImportHandler` currently supports only flat seed objects. +Related items (files, notes, tasks, contacts) linked through the relation system are tracked +in OpenRegister's pending `seed-related-items` openspec change (see +`openregister/openspec/changes/seed-related-items/`). Until that lands, seed data is limited +to object properties defined in schemas. + +### Deduplication check + +- Before proposing new capability: search `openspec/specs/` and `openregister/lib/Service/` for overlap + with ObjectService, RegisterService, SchemaService, ConfigurationService, and shared Vue components. +- If similar capability exists: MUST reference it and explain why new code is needed rather than extending. +- Proposals duplicating existing functionality without justification MUST be rejected. +- **In design.md**: MUST include a "Reuse Analysis" section listing existing OpenRegister services leveraged. +- **In tasks.md**: MUST include a "Deduplication Check" task verifying no overlap — document findings + even if "no overlap found". + +### Schema migrations + +- Breaking schema changes → new migration in repair step. NEVER modify existing migrations. + +### OpenRegister + @conduction/nextcloud-vue — DO NOT REBUILD + +The platform provides 258+ backend methods and 69+ frontend components. Apps ONLY build +custom logic for domain-specific business rules. Everything below is provided for FREE. + +**CRUD & Data Management** (use ObjectService + CnIndexPage + CnDetailPage): +- Single & bulk create, read, update, delete — `ObjectService.saveObject()`, `deleteObject()` +- List with pagination, sorting, filtering — `ObjectService.findAll()` + `CnDataTable` +- Schema-driven forms — `CnFormDialog` (auto-generates from schema) or `CnAdvancedFormDialog` +- Detail views — `CnDetailPage` with `CnDetailGrid`, `CnDetailCard` sections +- Record merging/deduplication — `ObjectService.mergeObjects()` +- Object locking — `ObjectService.lockObject()` / `unlockObject()` + +**Import & Export** (use ImportService/ExportService + CnMassImportDialog/CnMassExportDialog): +- CSV, Excel, JSON import with intelligent field mapping — `ImportService` +- CSV, Excel, JSON export with column selection — `ExportService` +- Bulk import with validation and progress — `CnMassImportDialog` +- Filtered export with format picker — `CnMassExportDialog` +- NO custom import dialogs, parsers, upload handlers, or export controllers + +**Search & Discovery** (use IndexService + CnFilterBar + CnFacetSidebar): +- Full-text search with field weighting — `IndexService` +- Faceted navigation with counts — `FacetBuilder` + `CnFacetSidebar` +- Semantic search with embeddings — `VectorizationService` +- Hybrid search (keyword + semantic) — automatic +- Search analytics — `SearchTrailService` (popular terms, activity) +- NO custom search endpoints, query builders, or search pages + +**File Management** (use FileService + CnObjectSidebar): +- Upload (single/multipart), download, share links — `FileService` +- File tagging, public/private toggle — `FileService` +- Bulk download as ZIP — `createObjectFilesZip()` +- Text extraction from PDFs/Office docs — `TextExtractionService` +- File tab in object sidebar — `CnObjectSidebar` → `CnFilesTab` +- NO custom file upload components, file controllers, or download handlers + +**Audit & Compliance** (use AuditTrailService + CnObjectSidebar): +- Full change tracking with before/after snapshots — automatic +- Audit trail tab — `CnObjectSidebar` → `CnAuditTrailTab` +- GDPR data subject access requests — `inzageverzoek()`, `verwerkingsregister()` +- Audit export and analytics — `AuditTrailController` +- NO custom audit logging, change tracking, or compliance controllers + +**Dashboard & Analytics** (use CnDashboardPage + CnChartWidget + CnStatsBlock): +- Drag-drop widget dashboard — `CnDashboardPage` with GridStack +- KPI cards — `CnKpiGrid`, `CnStatsBlock`, `CnStatsPanel` +- Charts (line/bar/pie/donut) — `CnChartWidget` (ApexCharts) +- Data tables as widgets — `CnTableWidget` +- Editable data grids — `CnObjectDataWidget` +- NO custom dashboard layouts, chart components, or KPI cards + +**Forms & Dialogs** (use CnFormDialog + schema-driven generation): +- Auto-generated create/edit forms — `CnFormDialog` reads schema → generates fields +- JSON/metadata editing — `CnAdvancedFormDialog` with Properties/Data/Metadata tabs +- Schema editor — `CnSchemaFormDialog` +- Delete/Copy/Mass operations — `CnDeleteDialog`, `CnCopyDialog`, `CnMassDeleteDialog` +- NO custom form components, validation logic, or dialog wrappers + +**Navigation & Pagination** (use CnPagination + CnActionsBar + useListView): +- Pagination control with size selector — `CnPagination` +- Action bar (add, search, toggle views) — `CnActionsBar` +- List state management — `useListView` composable (handles search, filter, sort, page) +- Detail state management — `useDetailView` composable +- NO custom pagination logic, debounced search, or list state management + +**Authorization & RBAC** (use AuthorizationService + PropertyRbacHandler): +- Role-based access control — `AuthorizationService` +- Field-level permissions — `PropertyRbacHandler` +- Object-level restrictions — `PermissionHandler` +- Authorization audit — `AuthorizationAuditService` +- NO custom permission checks, role systems, or access control middleware + +**Webhooks & Events** (use WebhookService): +- Create, test, retry webhooks — `WebhookService` +- CloudEvents format — automatic +- Event subscriptions — selective per schema/action +- NO custom webhook controllers or event dispatchers + +**Notifications & Activity** (use NotificationService + ActivityService): +- Nextcloud notifications — `NotificationService` +- Activity feed — `ActivityService` +- Calendar events — `CalendarEventService` +- Deck/Kanban cards — `DeckCardService` + +**Store & State** (use createObjectStore + plugins): +- Object stores — `createObjectStore(name)` generates Pinia CRUD store +- Store plugins: `auditTrails`, `files`, `lifecycle`, `relations`, `search`, `selection` +- Column/field/filter generation from schema — `columnsFromSchema()`, `fieldsFromSchema()` +- NO custom Pinia stores for CRUD, Vuex, or manual API call management + +**Chat & AI** (use ChatService): +- Multi-turn conversation — `ChatService` +- RAG-based knowledge retrieval — `ContextRetrievalHandler` +- LLM response generation — `ResponseGenerationHandler` + +**Data Retention & Archival** (use ArchivalService): +- Legal hold — `LegalHoldService` +- Destruction schedules — `DestructionService` +- Retention policies — `RetentionService` + +**Semantic & Hybrid Search** (use SolrController + SettingsController): +- Semantic search via vector embeddings — `SettingsController.semanticSearch()` +- Hybrid search (keyword + semantic combined) — `SolrController.hybridSearch()` +- Vector embedding generation — `VectorizationService` +- NO custom search algorithms — configure via OpenRegister settings + +**GraphQL API** (use GraphQLController): +- Query objects across schemas via GraphQL — `GraphQLController.execute()` +- Alternative to REST for complex cross-entity queries + +**Organization / Multi-Tenancy** (use OrganisationController): +- Organization CRUD — `OrganisationController` +- Tenant-scoped data isolation — automatic via `TenantLifecycleService` +- NO custom multi-tenancy logic + +**Task & Workflow Management** (use TasksController + WorkflowEngineController): +- Task creation and tracking — `TasksController` +- Workflow orchestration — `WorkflowEngineRegistry` +- Scheduled workflows — `ScheduledWorkflowController` +- NO custom task/workflow systems + +**Text Extraction** (use FileTextController): +- Extract text from PDFs and Office docs — `TextExtractionService` +- Entity recognition (PII detection) — `EntityRecognitionHandler` +- Content anonymization — automatic + +**Timeline & Stages** (use CnTimelineStages): +- Workflow progression visualization — `CnTimelineStages` component +- Stage tracking with status colors + +### What apps SHOULD build (custom business logic only): +- External API integrations (SAP, Peppol, TenderNed, etc.) +- PDF/document generation with business-specific templates +- Workflow triggers and business rules specific to the domain +- Notification dispatch with app-specific event types +- Custom settings pages with app-specific configuration +- Background jobs for domain-specific processing diff --git a/.claude/openspec/architecture/adr-002-api.md b/.claude/openspec/architecture/adr-002-api.md new file mode 100644 index 000000000..4f956593f --- /dev/null +++ b/.claude/openspec/architecture/adr-002-api.md @@ -0,0 +1,6 @@ +- URL pattern: `/index.php/apps/{app}/api/{resource}` — lowercase plural, hyphens. +- Methods: GET=read, POST=create, PUT=update, DELETE=remove. No custom methods. +- Pagination: support `_page` + `_limit`. Response includes `total`, `page`, `pages`. +- Errors: appropriate HTTP status + `message` field. NO stack traces in responses. +- Auth: Nextcloud built-in only. NO custom login/session/token flows. +- Public endpoints: annotate `#[PublicPage]` + `#[NoCSRFRequired]`. Register CORS OPTIONS route. diff --git a/.claude/openspec/architecture/adr-003-backend.md b/.claude/openspec/architecture/adr-003-backend.md new file mode 100644 index 000000000..82abe764b --- /dev/null +++ b/.claude/openspec/architecture/adr-003-backend.md @@ -0,0 +1,14 @@ +- **Controller → Service → Mapper** (strict 3-layer). Controllers NEVER call mappers directly. +- Controllers: thin (<10 lines/method). Routing + validation + response only. +- Services: ALL business logic. Stateless — no instance state between requests. +- Mappers: DB CRUD only. No business logic. +- DI: constructor injection with `private readonly`. NO `\OC::$server` or static locators. +- Entity setters: POSITIONAL args only. `$e->setName('val')` — NEVER `$e->setName(name: 'val')`. + (`__call` passes `['name' => val]` but `setter()` uses `$args[0]`.) +- Routes: `appinfo/routes.php`. Specific routes BEFORE wildcard `{slug}` routes. +- Config: `IAppConfig` with sensitive flag for secrets. NEVER read DB directly. +- Lifecycle: schema init via repair steps (`IRepairStep`), background via job queue, events via dispatcher. +- **Spec traceability**: every class and public method MUST have `@spec` PHPDoc tag(s) linking to + the OpenSpec change that caused it: `@spec openspec/changes/{name}/tasks.md#task-N`. + Multiple `@spec` tags allowed (code touched by multiple changes). File-level `@spec` in header docblock. + This enables: code → docblock → spec traceability alongside code → git blame → commit → issue → spec. diff --git a/.claude/openspec/architecture/adr-004-frontend.md b/.claude/openspec/architecture/adr-004-frontend.md new file mode 100644 index 000000000..077371178 --- /dev/null +++ b/.claude/openspec/architecture/adr-004-frontend.md @@ -0,0 +1,255 @@ +- **Vue 2 + Pinia + @nextcloud/vue + @conduction/nextcloud-vue**. NO Vuex. Options API only. +- State: Pinia stores in `src/store/modules/`. Use `createObjectStore` for OpenRegister CRUD. +- API calls: `axios` from `@nextcloud/axios` — auto-attaches CSRF token. NEVER raw `fetch()` for mutations. + Loading state with `try/finally`. +- Translations: ALL user-visible strings via `t(appName, 'text')`. NO hardcoded strings. + Translation keys MUST be English — Dutch translations go in `l10n/nl.json`. +- CSS: ONLY Nextcloud CSS variables (`var(--color-primary-element)`, etc.). NO hardcoded colors. + NEVER reference `--nldesign-*` directly — nldesign app handles theming. +- Router: history mode, base `generateUrl('/apps/{app}/')`. Requires matching PHP routes in `routes.php`. + Deep link URL templates MUST match the router mode — use path format (`/apps/{app}/entities/{uuid}`), + NOT hash format (`/apps/{app}/#/entities/{uuid}`). +- OpenRegister dependency: settings returns `openRegisters` (bool) + `isAdmin`. + Show empty state if OR missing. NEVER use `OC.isAdmin` — get from backend. +- NEVER `window.confirm()` or `window.alert()` — use `NcDialog` or `CnFormDialog` (WCAG, theming). +- NEVER read app state from DOM (`document.getElementById`, `dataset`) — use backend API or store. +- NEVER pass server-side data (e.g. app version) via DOM attributes. Use `IInitialState::provideInitialState('key', $value)` in PHP and `loadState('appid', 'key', default)` from `@nextcloud/initial-state` in Vue. DOM data-attributes are not the Nextcloud-idiomatic pattern and break on CSP-hardened instances. +- NEVER add admin settings Vue components (e.g. `AdminRoot.vue`) to the vue-router. Admin settings are registered via `AdminSettings.php` and rendered by Nextcloud's settings framework — adding them to the router makes them publicly accessible as frontend routes, bypassing all server-side access checks. +- NEVER create manual `