From f324b4141361b2259c1193464b5c90daa37e438c Mon Sep 17 00:00:00 2001 From: Specter Intelligence Date: Fri, 22 May 2026 05:36:50 +0000 Subject: [PATCH 1/4] feat: Add OpenSpec change kerngegevens-stelsel-registratie from Specter Co-Authored-By: Claude Haiku 4.5 --- .../.openspec.yaml | 2 + .../context-brief.md | 130 ++++++ .../design.md | 85 ++++ .../proposal.md | 15 + .../kerngegevens-stelsel-registratie/spec.md | 401 ++++++++++++++++++ .../kerngegevens-stelsel-registratie/tasks.md | 200 +++++++++ 6 files changed, 833 insertions(+) create mode 100644 openspec/changes/kerngegevens-stelsel-registratie/.openspec.yaml create mode 100644 openspec/changes/kerngegevens-stelsel-registratie/context-brief.md create mode 100644 openspec/changes/kerngegevens-stelsel-registratie/design.md create mode 100644 openspec/changes/kerngegevens-stelsel-registratie/proposal.md create mode 100644 openspec/changes/kerngegevens-stelsel-registratie/specs/kerngegevens-stelsel-registratie/spec.md create mode 100644 openspec/changes/kerngegevens-stelsel-registratie/tasks.md diff --git a/openspec/changes/kerngegevens-stelsel-registratie/.openspec.yaml b/openspec/changes/kerngegevens-stelsel-registratie/.openspec.yaml new file mode 100644 index 000000000..4a1c67743 --- /dev/null +++ b/openspec/changes/kerngegevens-stelsel-registratie/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-22 diff --git a/openspec/changes/kerngegevens-stelsel-registratie/context-brief.md b/openspec/changes/kerngegevens-stelsel-registratie/context-brief.md new file mode 100644 index 000000000..1d3f86cf1 --- /dev/null +++ b/openspec/changes/kerngegevens-stelsel-registratie/context-brief.md @@ -0,0 +1,130 @@ +status: draft + +# Kerngegevensstelsel Registratie + +## Purpose + +Het Nederlandse overheidslandschap kent een gelaagde structuur van data-registers: tien basisregistraties (BRP, BAG, BRT, BRK, NHR, BGT, BRO, BRV, WOZ, BRI) met wettelijke status, daarnaast kerngegevens uit het sectorbrede stelsel (zoals NLX-aangesloten registers, gemeentelijke kerntabellen, en de nieuwe Stelselcatalogus 2026), en tenslotte sectorbrede registers per domein (zorg, onderwijs, justitie, mobiliteit). Voor elke dataset die een organisatie publiceert is het cruciaal te markeren welke stelselpositie deze inneemt: is dit een wettelijke basisregistratie waarop terugmelding-plicht rust, een kerngegeven dat ander beleid voedt, of een afgeleid sectoraal product? + +De huidige opencatalogi mist deze gelaagdheid expliciet. Datasets krijgen wel DCAT-AP-metadata, maar de stelsel-positie wordt vaak alleen in een vrij-tekst beschrijving genoemd - waardoor automatische federatie met data.overheid.nl, het Forum Standaardisatie-register, en de Stelselcatalogus niet betrouwbaar werkt. Bij hergebruik door derden ontbreekt vaak duidelijkheid over brondatum, bijhoudbron, geldigheidsperiode en terugmeld-mechanisme - allemaal verplichte velden onder DCAT-AP-NL 2.1 voor publieke datasets. + +Deze spec voegt een gestructureerde Stelselregistratie-extensie toe aan opencatalogi-datasets: per dataset een stelselpositie-classificatie, een brondatum-velden-set, een geldigheidsregime, en (waar van toepassing) een terugmeld-endpoint. Volledig DCAT-AP-NL 2.1 conform met de Nederlandse profielen (data.overheid.nl harvest-conform). Maakt federatie naar de landelijke Stelselcatalogus mogelijk. + +## Data Model + +**StelselPositie** (nieuw schema, register `opencatalogi-stelsel`): +- `datasetId` (relatie naar Dataset) +- `positieType` (enum: basisregistratie / kerngegevensstelsel / sectorbreed-register / afgeleide-set / lokale-registratie) +- `basisregistratieNaam` (enum, indien type=basisregistratie: BRP / BAG / BRT / BRK / NHR / BGT / BRO / BRV / WOZ / BRI) +- `wettelijkeGrondslag` (string, bv. "Wet BAG art. 31") +- `bronhouder` (string, KvK + naam) +- `gegevensbeheerder` (string, KvK + naam, vaak gelijk aan bronhouder) +- `verstrekker` (string, organisatie die ontsluit) +- `aansluitDatum` (date, eerste opname in stelsel) + +**KerngegevensSet** (nieuw schema): +- `datasetId` (relatie) +- `setNaam` (string, bv. "Identificerende persoonsgegevens", "Vestigingsadres") +- `bronAttributen` (array van attribuut-referenties met paden naar dataset-velden) +- `gerelateerdeBasisregistratie` (string, naar welke BR dit een kerngegeven van is) + +**BijhoudCyclus** (nieuw schema): +- `datasetId` (relatie) +- `bijhoudFrequentie` (enum: realtime / dagelijks / wekelijks / maandelijks / kwartaal / jaarlijks / event-driven) +- `bijhoudBron` (string, systeem-referentie) +- `laatsteUpdate` (datetime) +- `volgendeGeplandeUpdate` (datetime) +- `peildatum` (date, voor snapshot-georienteerde sets) + +**Geldigheidsperiode** (nieuw schema, voor temporele datasets): +- `datasetId` (relatie) +- `geldigVanaf` (date) +- `geldigTotEnMet` (date, nullable voor open-einde) +- `historischeVersies` (array van dataset-versie-references) + +**TerugmeldEndpoint** (nieuw schema): +- `datasetId` (relatie) +- `terugmeldUrl` (uri) +- `terugmeldProtocol` (enum: digikoppeling-ebms / rest-json / nlx) +- `responsTermijn` (string, bv. "5 werkdagen") +- `contactgegevens` (string) + +## Requirements + +### REQ-001: Stelselpositie verplicht bij publicatie + +GIVEN een dataset in opencatalogi met status `concept` +WHEN de gebruiker de status naar `gepubliceerd` wil zetten +THEN valideert het systeem dat een StelselPositie-object is gekoppeld met minimaal `positieType` ingevuld, en blokkeert publicatie als dit ontbreekt - met een melding die linkt naar de stelselpositie-wizard. + +### REQ-002: Basisregistratie-validatie + +GIVEN een StelselPositie met `positieType = basisregistratie` +WHEN het object wordt opgeslagen +THEN eist het systeem dat `basisregistratieNaam` is gekozen uit de officiele lijst van tien, `wettelijkeGrondslag` is ingevuld, en `bronhouder` matcht met een entiteit uit het register Nederlandse Overheids-organisaties (ROO). + +### REQ-003: Kerngegevens-afleiding documenteren + +GIVEN een dataset met StelselPositie van type `kerngegevensstelsel` of `afgeleide-set` +WHEN de gebruiker kerngegevens-sets definieert +THEN kan per set een relatie worden gelegd naar de bronnen-basisregistratie via `gerelateerdeBasisregistratie`, en moeten de `bronAttributen` mappen op feitelijke veldnamen in de dataset-schema-definitie (validatie tegen schema). + +### REQ-004: Bijhoudfrequentie + actualiteit-indicator + +GIVEN een gepubliceerde dataset met BijhoudCyclus +WHEN een gebruiker of harvester de dataset bekijkt +THEN toont opencatalogi een actualiteit-indicator (groen/oranje/rood) gebaseerd op of `laatsteUpdate` valt binnen de verwachte `bijhoudFrequentie`-band, en exporteert deze status in de DCAT-output als `dct:modified` + custom `dn:bijhoudStatus`. + +### REQ-005: Geldigheidsperiode + historische versies + +GIVEN een dataset met temporele scope +WHEN een nieuwe Geldigheidsperiode wordt geopend (oude wordt afgesloten met `geldigTotEnMet`) +THEN snapshot het systeem de oude dataset-versie als immutable record toegankelijk via versie-permalinks, en linkt deze in `historischeVersies` - zodat hergebruikers kunnen terugkijken op welke versie ze op een peildatum gebruikten. + +### REQ-006: Terugmelding-mechanisme registreren + +GIVEN een dataset met `positieType = basisregistratie` +WHEN de publicerende organisatie de dataset wil gepubliceerd zien +THEN eist het systeem een TerugmeldEndpoint-koppeling conform de Terugmeldvoorziening-richtlijnen (artikel 38a Wet BAG en analogen), en toont op de dataset-detailpagina een prominente "Onjuist gegeven melden"-knop die het juiste protocol uitvoert. + +### REQ-007: DCAT-AP-NL 2.1 export + +GIVEN een dataset met volledig ingevulde Stelselregistratie-extensies +WHEN data.overheid.nl (of een andere harvester) de DCAT-feed ophaalt +THEN exporteert opencatalogi een DCAT-AP-NL 2.1 conforme RDF/XML- en JSON-LD-representatie met de Nederlandse profiel-uitbreidingen (kerngegevens, bronhouder, terugmeld-uri) - inclusief geldige IRI's voor stelsel-vocabulaires. + +### REQ-008: Federatie naar Stelselcatalogus + +GIVEN een opencatalogi-instantie met datasets die `positieType = kerngegevensstelsel` hebben +WHEN de geconfigureerde Stelselcatalogus-federatie-job draait (dagelijks) +THEN pusht het systeem de relevante datasets via de Stelselcatalogus-API met de juiste vocabulaire-mappings, en logt fouten als open-bevindingen voor de catalogus-beheerder. + +## Standards + +- **DCAT-AP-NL 2.1** (Forum Standaardisatie, verplicht op pas-toe-of-leg-uit-lijst) +- **NL GOV Profile of DCAT-AP** (2.1) +- **Stelselcatalogus** (Logius, vocabulaire-set voor kerngegevens) +- **Wet BAG, BRP, BRK, etc.** (wettelijke grondslag basisregistraties) +- **Terugmeldvoorziening** (Logius-richtlijnen artikel 38a Wet BAG en analogen) +- **NORA** (Nederlandse Overheid Referentie Architectuur, principes hergebruik) +- **Digikoppeling ebMS / NLX** (transport voor terugmeldingen) +- **W3C DCAT 3** (basis-vocabulaire) + +## Cross-app + +- **openregister**: schema-koppeling voor `bronAttributen`-validatie +- **opencatalogi base**: dataset-entiteit waarop deze spec extensies bouwt +- **openconnector**: federatie-source-definities (Stelselcatalogus, data.overheid.nl) +- **softwarecatalog**: API-leveranciers-referentie voor verstrekkers +- **mydash**: actualiteit-dashboard per stelselpositie +- **docudesk**: opslag wettelijke-grondslag-PDFs en bijhouder-overeenkomsten + +## Target users + +- **Catalogusbeheerder** (gemeente/provincie/UWV): registratie + onderhoud StelselPositie +- **Data-steward** (per domein): bijhoudCyclus + Geldigheidsperiode-beheer +- **Architect** (NORA-aligned): stelsel-positionering bij nieuwe dataset +- **Privacy Officer**: validatie kerngegevens (privacy-impact) +- **Terugmelder** (extern, burger/bedrijf): gebruik terugmeld-endpoint +- **Federatie-beheerder** (Logius/VNG): consumeert DCAT-feed +- **Hergebruiker** (data-journalist, ontwikkelaar): consulteert peildatum + historische versies diff --git a/openspec/changes/kerngegevens-stelsel-registratie/design.md b/openspec/changes/kerngegevens-stelsel-registratie/design.md new file mode 100644 index 000000000..5f9a013f8 --- /dev/null +++ b/openspec/changes/kerngegevens-stelsel-registratie/design.md @@ -0,0 +1,85 @@ +# Design: kerngegevens-stelsel-registratie + +## Architecture Overview + +This spec adds a comprehensive stelselregistratie layer to opencatalogi datasets, enabling structured classification and federation of datasets within the Dutch government data governance structure. + +### Core Entities + +**StelselPositie** registers the systemic role of a dataset: +- Maps datasets to one of five position types: basisregistratie (statutory), kerngegevensstelsel (core data), sectorbreed-register (sector-specific), afgeleide-set (derived), lokale-registratie (local) +- For basis registers: enforces legal grounding, source organization identity (KvK), and enlistment date +- Anchors all other metadata (kerngegevens, maintenance, validity, feedback) + +**KerngegevensSet** documents core data derivations: +- Identifies which attributes within a dataset serve as "kerngegevens" (core data used by other policy areas) +- Maps attribute references to actual dataset schema fields (validated against openregister schema) +- Traces back to source basis register (e.g., "Identificatie-attributen" → BRP) + +**BijhoudCyclus** tracks data freshness: +- Records maintenance frequency (realtime, daily, weekly, monthly, quarterly, yearly, event-driven) +- Stores system source, last update timestamp, next planned update, and (for snapshots) reference date +- Powers actualiteit-indicator (green/orange/red) showing whether data meets SLA + +**Geldigheidsperiode** manages temporal scope: +- Marks valid-from and valid-through dates for datasets with temporal bounds +- Stores immutable snapshots of prior dataset versions as historical records +- Enables reusers to query: "what version was current on 2024-01-15?" + +**TerugmeldEndpoint** enables feedback loops: +- Registers the feedback submission endpoint (URI) for basis registers and core data +- Specifies protocol (Digikoppeling ebMS, REST-JSON, NLX) +- Records response SLA and contact details +- Mandatory for datasets claiming basisregistratie status (required by Wet BAG art. 38a analog) + +### Integration Points + +- **OpenRegister** (`openregister` app): Schema validation for `bronAttributen` field mappings; reuses schema-declarative-business-logic pattern (ADR-031) +- **OpenCatalogi core**: Extends existing Dataset entity; DCAT-AP-NL 2.1 export hooks; publication validation pipeline +- **OpenConnector** (`openconnector` app): Federation sources for Stelselcatalogus push; harvest jobs; vocab mapping +- **SoftwareCatalog** (`softwarecatalog` app): Organization reference resolution (KvK ↔ name); verstrekker (distributor) lookup +- **MyDash** (`mydash` app): Actualiteit dashboard per stelsel position; heat map of SLA adherence +- **DocuDesk** (`docudesk` app): Storage of legal-basis PDFs, source-organization agreements + +### Data Flow + +1. **Catalogusbeheerder** creates Dataset and clicks "Publish" +2. **Validation hook** checks for attached StelselPositie; if missing, shows wizard +3. **Catalogusbeheerder** selects `positieType`; UI conditionally shows schema based on type: + - **basisregistratie**: Demands legal-basis text, bronhouder KvK (validated against ROO), terugmeld-endpoint, wettelijkeGrondslag + - **kerngegevensstelsel** / **afgeleide-set**: Offer KerngegevensSet builder to map schema fields to kerngegevens + - **All types**: BijhoudCyclus (frequency, last-update, next-update) and Geldigheidsperiode optional +4. **Backend** validates all fields; on publish, records aansluitDatum (now) +5. **Export pipeline** translates StelselPositie + KerngegevensSet into DCAT-AP-NL 2.1 RDF/XML with proper vocabulary URIs +6. **OpenConnector federation job** (daily) collects all datasets with `positieType = kerngegevensstelsel`, pushes via Stelselcatalogus API +7. **Actualiteit monitor** (hourly) checks `BijhoudCyclus.laatsteUpdate` against `bijhoudFrequentie`; updates indicator +8. **Reuser** (data journalist, developer) sees on Dataset detail page: + - Stelsel position label (e.g. "Kerngegevensstelsel") + - Last-update + freshness indicator + - Link to historical versions (if Geldigheidsperiode exists) + - Terugmeld button (if TerugmeldEndpoint configured) + +### Seed Data (Examples) + +**StelselPositie examples:** +- BRP extract: `positieType=basisregistratie`, `basisregistratieNaam=BRP`, `wettelijkeGrondslag=Wet BRP art. 3`, `bronhouder=Logius (KvK 34272727)`, `aansluitDatum=1997-01-01` +- NHR dataset: `positieType=basisregistratie`, `basisregistratieNaam=NHR`, `wettelijkeGrondslag=Handelsregisterwet art. 5`, `bronhouder=KvK (KvK 27373977)`, `gegevensbeheerder=KvK (KvK 27373977)`, `verstrekker=KvK (KvK 27373977)` +- Gemeentelijke kerngegevens: `positieType=kerngegevensstelsel`, `gerelateerdeBasisregistratie=BRP`, `aansluitDatum=2024-01-15` + +**KerngegevensSet examples:** +- Identificatie-persoonsgegevens: `setNaam="Identificerende persoonsgegevens"`, `bronAttributen=[{pad: "/bsn", label: "BSN"}, {pad: "/geslachtsnaam", label: "Geslachtsnaam"}]`, `gerelateerdeBasisregistratie=BRP` +- Vestigingsadres: `setNaam="Vestigingsadres"`, `bronAttributen=[{pad: "/adres/straatnaam", label: "Straatnaam"}, {pad: "/adres/huisnummer", label: "Huisnummer"}]`, `gerelateerdeBasisregistratie=BAG` + +**BijhoudCyclus examples:** +- BRP realtime: `bijhoudFrequentie=realtime`, `bijhoudBron=BRP-systeem`, `laatsteUpdate=2026-05-22T14:37:00Z`, `volgendeGeplandeUpdate=null` +- Maandelijkse UAV snapshot: `bijhoudFrequentie=maandelijks`, `peildatum=2026-05-01`, `laatsteUpdate=2026-05-01T08:00:00Z`, `volgendeGeplandeUpdate=2026-06-01T08:00:00Z` + +**Geldigheidsperiode examples:** +- Historical BRP: `geldigVanaf=1997-01-01`, `geldigTotEnMet=2010-12-31`, `historischeVersies=[{version: "1997-01", uri: "/datasets/brp/versions/1997-01"}]` +- Current BAG: `geldigVanaf=2010-01-01`, `geldigTotEnMet=null` (open-ended) + +**TerugmeldEndpoint examples:** +- BRP feedback: `terugmeldUrl=https://logius.nl/terugmelding-api/brp`, `terugmeldProtocol=digikoppeling-ebms`, `responsTermijn=5 werkdagen`, `contactgegevens=terugmelding@logius.nl` +- NHR feedback: `terugmeldUrl=https://kvk.nl/api/feedback/nhr`, `terugmeldProtocol=rest-json`, `responsTermijn=10 werkdagen`, `contactgegevens=support@kvk.nl` + +See specs/kerngegevens-stelsel-registratie/spec.md for detailed requirements and scenarios. diff --git a/openspec/changes/kerngegevens-stelsel-registratie/proposal.md b/openspec/changes/kerngegevens-stelsel-registratie/proposal.md new file mode 100644 index 000000000..1de398cf1 --- /dev/null +++ b/openspec/changes/kerngegevens-stelsel-registratie/proposal.md @@ -0,0 +1,15 @@ +# Proposal: kerngegevens-stelsel-registratie + +## Summary +Add structured stelselregistratie (system positioning registry) metadata to opencatalogi datasets, enabling proper classification of datasets within the Dutch government data stelsel layers and supporting federation with national data catalogs, compliance with DCAT-AP-NL 2.1, and data quality tracking through maintenance cycles and validity periods. + +## Motivation +Dutch government datasets exist within a layered structure: ten statutory basis registers (basisregistraties), sector-wide core data sets (kerngegevens), domain-specific registers (healthcare, education, justice, mobility), and derived local datasets. Current opencatalogi metadata only captures DCAT-AP basics, leaving dataset positioning ambiguous in free-text descriptions. This breaks automatic federation with data.overheid.nl, Forum Standaardisatie registries, and the Stelselcatalogus (2026), and leaves reusers without reliable source dates, maintenance information, validity windows, or feedback mechanisms - all required under DCAT-AP-NL 2.1. + +## Scope +- **Stelselregistratie metadata schemas**: StelselPositie, KerngegevensSet, BijhoudCyclus, Geldigheidsperiode, TerugmeldEndpoint +- **Validation and UI wizards**: Enforce mandatory metadata per stelsel type (basis register requires legal basis + feedback endpoint, etc.) +- **Maintenance and lifecycle tracking**: Data freshness indicators, historical snapshots, validity periods +- **DCAT-AP-NL 2.1 export**: RDF/XML and JSON-LD output with proper vocabularies and IRI mappings +- **Federation API integration**: Automated push to Stelselcatalogus with vocabulaire mappings +- **Integration**: Links to openregister (schema validation), opencatalogi (dataset base entity), openconnector (federation targets), softwarecatalog (organization reference) diff --git a/openspec/changes/kerngegevens-stelsel-registratie/specs/kerngegevens-stelsel-registratie/spec.md b/openspec/changes/kerngegevens-stelsel-registratie/specs/kerngegevens-stelsel-registratie/spec.md new file mode 100644 index 000000000..1bc49db7f --- /dev/null +++ b/openspec/changes/kerngegevens-stelsel-registratie/specs/kerngegevens-stelsel-registratie/spec.md @@ -0,0 +1,401 @@ +--- +status: draft +--- + +# Kerngegevensstelsel Registratie Specification + +## Purpose +Defines how opencatalogi datasets are registered within the Dutch government data stelsel hierarchy (basisregistraties, kerngegevens, sectorale registers, derived and local datasets), enabling proper classification, federation with national catalogs, compliance with DCAT-AP-NL 2.1, and support for data freshness tracking and feedback mechanisms. + +## Context +Dutch government data governance operates within a five-tier structure: ten statutory basis registers (BRP, BAG, BRT, BRK, NHR, BGT, BRO, BRV, WOZ, BRI) with legal grounding; kerngegevens derived from these registers for use across policy domains; sector-wide registers (healthcare, education, justice, mobility); local derived datasets; and foreign/private data. Each dataset's role within this hierarchy is crucial for reusers to understand authority, currency, feedback routes, and legal compliance (DCAT-AP-NL 2.1, Wet BAG art. 38a, NORA principles). + +Current opencatalogi captures DCAT-AP basics but leaves stelsel positioning implicit in prose descriptions, breaking: +- Automated federation to data.overheid.nl and Stelselcatalogus +- Reliable discovery of source dates, maintenance frequency, and validity windows +- Feedback loops for error reporting (Wet BAG art. 38a) +- Compliance audits for mandatory metadata on public datasets + +This spec adds five interconnected metadata schemas (StelselPositie, KerngegevensSet, BijhoudCyclus, Geldigheidsperiode, TerugmeldEndpoint) and integrates them into the dataset publication pipeline, export (DCAT-AP-NL 2.1 RDF/XML and JSON-LD), and federation infrastructure. + +**Relation to existing specs:** +- **opencatalogi base**: This extends the Dataset entity with stelsel-specific metadata and validation hooks +- **openregister `register-i18n`**: Schema-field validation for kerngegevens mappings +- **openconnector federation**: Consumes StelselPositie for Stelselcatalogus push targets +- **DCAT-AP-NL 2.1 (Forum Standaardisatie)**: Normative standard for RDF export format and vocabulary URIs +- **Stelselcatalogus (Logius)**: Federation target and vocabulaire authority for kerngegevens + +## Requirements + +### Requirement: Stelsel position MUST be registered before dataset publication + +Every dataset published to a public catalog MUST have a StelselPositie object attached, specifying its role within the government data hierarchy. + +#### Scenario: Dataset without position blocks publication +- GIVEN a dataset in `concept` status with no attached StelselPositie +- WHEN the user attempts to change status to `gepubliceerd` +- THEN the system MUST refuse and show a validation error: "Stelselregistratie ontbreekt. Kies of dit een basisregistratie, kerngegeven, sectoraal register, afgeleide of lokale set is." +- AND the UI MUST display a "Configureer stelselregistratie" button linking to the stelsel-wizard + +#### Scenario: Dataset with position allows publication +- GIVEN a dataset with a complete StelselPositie object (minimally `positieType` set) +- WHEN the user changes status to `gepubliceerd` +- THEN the system MUST allow publication and record the StelselPositie as published metadata + +#### Scenario: StelselPositie GUID is recorded on publish +- GIVEN a StelselPositie with `positieType = kerngegevensstelsel` +- WHEN the dataset is published +- THEN the StelselPositie.aansluitDatum MUST be set to the current date (if not already set) +- AND the association between Dataset and StelselPositie MUST be immutable after publication + +### Requirement: Basis register position MUST enforce legal and organizational requirements + +Datasets claiming `positieType = basisregistratie` MUST satisfy strict validation for legal basis, source organization, and feedback mechanism. + +#### Scenario: Basis register without legal basis is rejected +- GIVEN a StelselPositie with `positieType = basisregistratie` and no `wettelijkeGrondslag` +- WHEN the form is submitted +- THEN the system MUST reject with error: "Wettelijke grondslag is verplicht voor basisregistraties (bijv. 'Wet BRP art. 3')" +- AND the field MUST accept prose (e.g., "Wet BAG art. 31") not just codes + +#### Scenario: Basis register name MUST be from official list +- GIVEN a StelselPositie with `positieType = basisregistratie` +- WHEN the user selects `basisregistratieNaam` +- THEN the dropdown MUST show exactly ten options: BRP, BAG, BRT, BRK, NHR, BGT, BRO, BRV, WOZ, BRI +- AND selecting any other value MUST be impossible + +#### Scenario: Bronhouder MUST match official organization registry +- GIVEN a StelselPositie with `positieType = basisregistratie` and `basisregistratieNaam = BRP` +- WHEN the user enters a `bronhouder` value +- THEN the system MUST validate against the Register Nederlandse Overheids-organisaties (ROO) API +- AND only KvK numbers recognized as valid government entities MUST be accepted +- AND the response MUST display the official organization name (e.g., "Logius (KvK 34272727)") + +#### Scenario: Basis register example — BRP +- GIVEN a dataset published by Logius containing BRP extracts +- WHEN the catalogusbeheerder fills the stelsel-form +- THEN the form MUST accept: + - `positieType = basisregistratie` + - `basisregistratieNaam = BRP` + - `wettelijkeGrondslag = Wet BRP art. 3` + - `bronhouder = Logius (KvK 34272727)` + - `gegevensbeheerder = Logius (KvK 34272727)` (typically same) + - `verstrekker = Logius (KvK 34272727)` + - `aansluitDatum = 1997-01-01` + +### Requirement: Core data sets MUST document kerngegevens mappings + +Datasets marked as `kerngegevensstelsel` or `afgeleide-set` MUST define which attributes serve as core data and map them to schema fields. + +#### Scenario: Kerngegevens set defines attribute sources +- GIVEN a dataset with `positieType = kerngegevensstelsel` and `gerelateerdeBasisregistratie = BRP` +- WHEN the data-steward creates a KerngegevensSet named "Identificatie-persoonsgegevens" +- THEN the form MUST allow adding one or more `bronAttributen` entries +- AND each entry MUST specify: + - `pad`: Schema path (e.g., `/bsn`, `/geslachtsnaam`) — validated against openregister schema + - `label`: Human-readable name (e.g., "BSN", "Geslachtsnaam") +- AND the form MUST reject paths that do not exist in the dataset schema + +#### Scenario: Multiple kerngegevens sets per dataset +- GIVEN a dataset representing merged BRP + BAG data +- WHEN the data-steward creates kerngegevens sets +- THEN the system MUST allow multiple KerngegevensSet objects (e.g., "Identificatie", "Vestigingsadres") +- AND each set MUST independently specify `gerelateerdeBasisregistratie` (BRP for one, BAG for the other) +- AND the sets MUST coexist without conflict in the published metadata + +#### Scenario: Kerngegevens validation against schema +- GIVEN a KerngegevensSet with `bronAttributen` pointing to path `/nonexistent` +- WHEN the form is submitted +- THEN the system MUST query the openregister API for the dataset schema +- AND MUST reject with error: "Pad '/nonexistent' bestaat niet in het dataset schema" +- AND MUST display a list of valid paths for autocomplete + +### Requirement: Data maintenance frequency MUST be tracked and expose a freshness indicator + +Every dataset MUST record its maintenance cycle and expose a freshness status (green/orange/red) based on SLA compliance. + +#### Scenario: Maintenance frequency options +- GIVEN the BijhoudCyclus form +- WHEN the user opens the `bijhoudFrequentie` dropdown +- THEN the system MUST show: realtime, dagelijks, wekelijks, maandelijks, kwartaal, jaarlijks, event-driven + +#### Scenario: Freshness indicator calculation +- GIVEN a dataset with: + - `bijhoudFrequentie = wekelijks` (weekly) + - `laatsteUpdate = 2026-05-15T10:00Z` (one week ago) +- WHEN the dataset is viewed today (2026-05-22) +- THEN the actualiteit-indicator MUST show GREEN (within SLA) +- AND the label MUST read "Actueel (bijgewerkt 7 dagen geleden)" + +#### Scenario: Orange indicator for stale data +- GIVEN a dataset with: + - `bijhoudFrequentie = wekelijks` + - `laatsteUpdate = 2026-05-08T10:00Z` (9 days ago, beyond the 1-week window) +- WHEN the dataset is viewed on 2026-05-22 +- THEN the actualiteit-indicator MUST show ORANGE (approaching SLA breach) +- AND the label MUST read "Verouderd (bijgewerkt 14 dagen geleden)" + +#### Scenario: Red indicator for broken SLA +- GIVEN a dataset with: + - `bijhoudFrequentie = dagelijks` + - `laatsteUpdate = 2026-05-10T08:00Z` (12 days without update) +- WHEN the dataset is viewed on 2026-05-22 +- THEN the actualiteit-indicator MUST show RED (SLA breached) +- AND the label MUST read "Kritiek (niet bijgewerkt sinds 12 dagen)" +- AND an alert MUST suggest contacting the data steward + +#### Scenario: Realtime datasets never show orange/red +- GIVEN a dataset with `bijhoudFrequentie = realtime` and `laatsteUpdate` from 6 months ago +- WHEN the dataset is viewed +- THEN the actualiteit-indicator MUST remain GREEN +- AND the label MUST read "Realtime (geen update-SLA)" + +#### Scenario: DCAT export includes freshness status +- GIVEN a dataset with freshness indicator GREEN +- WHEN the DCAT-AP-NL 2.1 export is generated +- THEN the RDF MUST include: + - `dct:modified` = the `laatsteUpdate` timestamp + - Custom property `dn:bijhoudStatus = "groen"` (or "oranje"/"rood") + - Vocabulaire URI for status: `http://data.overheid.nl/vocab/bijhoudstatus/groen` + +### Requirement: Dataset validity windows MUST support historical snapshots + +Datasets with temporal scope MUST record validity periods and enable access to historical versions. + +#### Scenario: Open-ended validity (current data) +- GIVEN a dataset with `Geldigheidsperiode.geldigVanaf = 2010-01-01` and `geldigTotEnMet = null` +- WHEN the dataset is viewed +- THEN the UI MUST display "Geldig vanaf 1 januari 2010 (huidig)" +- AND no historical versions selector MUST appear + +#### Scenario: Closed validity period (historical snapshot) +- GIVEN a dataset with `Geldigheidsperiode.geldigVanaf = 1997-01-01` and `geldigTotEnMet = 2009-12-31` +- WHEN the dataset is viewed +- THEN the UI MUST display "Geldig van 1 januari 1997 tot 31 december 2009 (historisch)" +- AND the historical-versions list MUST be visible + +#### Scenario: Snapshot creation on validity close +- GIVEN a dataset currently in Geldigheidsperiode with `geldigTotEnMet = null` (open) +- WHEN a new Geldigheidsperiode is created with a start date +- THEN the system MUST automatically: + 1. Set the OLD period's `geldigTotEnMet` to yesterday + 2. Create an immutable snapshot of the current dataset state + 3. Assign the snapshot a version URI (e.g., `/datasets/{id}/versions/2010-01-01`) + 4. Add the snapshot reference to the OLD period's `historischeVersies` array + 5. Create a NEW Geldigheidsperiode with the new start date and `geldigTotEnMet = null` + +#### Scenario: Reuser queries historical data on a reference date +- GIVEN a dataset with multiple Geldigheidsperioden (snapshots at 1997, 2010, 2024) +- WHEN a reuser wants to know "which version was current on 2012-06-15?" +- THEN the API MUST accept `?peildatum=2012-06-15` and return the snapshot valid on that date +- AND the response MUST include a `_snapshot: true` flag and `_validFrom` / `_validUntil` metadata + +### Requirement: Feedback mechanism MUST be registered for basis registers and core data + +Datasets claiming mandatory status (`basisregistratie`, `kerngegevensstelsel`) MUST have a TerugmeldEndpoint registered. + +#### Scenario: Basis register publication requires feedback endpoint +- GIVEN a dataset with `positieType = basisregistratie` +- WHEN the user attempts to publish +- THEN the system MUST check for an attached TerugmeldEndpoint +- AND if missing, MUST reject with error: "Terugmeldendpoint is verplicht voor basisregistraties (Wet BAG art. 38a e.d.)" + +#### Scenario: Feedback endpoint validation +- GIVEN a TerugmeldEndpoint form +- WHEN the user enters `terugmeldUrl = https://invalid-url` +- THEN the system MUST validate the URL is a valid HTTPS URI +- AND MUST reject with error if the protocol is not HTTPS + +#### Scenario: Feedback protocols +- GIVEN the `terugmeldProtocol` dropdown +- WHEN the user clicks +- THEN the system MUST show three options: + - `digikoppeling-ebms`: For government-to-government (G2G) feedback + - `rest-json`: For HTTP REST API endpoints + - `nlx`: For NLX-routed feedback + +#### Scenario: Feedback button on dataset detail page +- GIVEN a published dataset with `positieType = basisregistratie` and a TerugmeldEndpoint configured +- WHEN a visitor views the public dataset detail page +- THEN a prominent "Onjuist gegeven melden" (Report inaccuracy) button MUST be visible +- AND clicking the button MUST: + - Open a feedback form with fields for dataset identifier, error description, source + - Pre-fill the `terugmeldUrl` from the TerugmeldEndpoint + - Route submission via the configured protocol (Digikoppeling, REST, or NLX) + - Display confirmation: "Bedankt. Uw melding wordt verwerkt binnen {responsTermijn}" + +#### Scenario: Feedback endpoint example — BRP +- GIVEN a BRP dataset published by Logius +- WHEN the data-steward configures the TerugmeldEndpoint +- THEN the form MUST accept: + - `terugmeldUrl = https://logius.nl/terugmelding-api/brp` + - `terugmeldProtocol = digikoppeling-ebms` + - `responsTermijn = 5 werkdagen` + - `contactgegevens = terugmelding@logius.nl` + +### Requirement: DCAT-AP-NL 2.1 export MUST include stelsel metadata with proper vocabularies + +The DCAT export pipeline MUST translate all StelselPositie, KerngegevensSet, BijhoudCyclus, Geldigheidsperiode, and TerugmeldEndpoint metadata into valid DCAT-AP-NL 2.1 RDF/XML and JSON-LD with proper vocabulary URIs. + +#### Scenario: DCAT export includes stelsel position +- GIVEN a published dataset with `StelselPositie.positieType = kerngegevensstelsel` +- WHEN the DCAT-AP-NL 2.1 RDF/XML feed is exported +- THEN the RDF MUST include a property mapping the position to a vocabulary URI: + - Property: `dcat:theme` or custom `dn:stelselPositie` + - Value: `http://data.overheid.nl/vocab/stelselPositie/kerngegevensstelsel` + +#### Scenario: DCAT export includes basis register metadata +- GIVEN a published dataset with `StelselPositie.positieType = basisregistratie` and `basisregistratieNaam = BRP` +- WHEN the DCAT-AP-NL 2.1 export is generated +- THEN the RDF MUST include: + - `dn:basisregistratie = http://data.overheid.nl/vocab/basisregistraties/brp` + - `dct:issued` or equivalent for `aansluitDatum` + - `dn:bronhouder` with organization IRI (from ROO) + - `dn:wettelijkeGrondslag` as literal text + +#### Scenario: DCAT export includes feedback endpoint +- GIVEN a TerugmeldEndpoint with `terugmeldUrl = https://logius.nl/terugmelding` +- WHEN the DCAT-AP-NL 2.1 export is generated +- THEN the RDF MUST include: + - `dn:terugmeldEndpoint = ` + - `dn:terugmeldProtocol = "digikoppeling-ebms"` (as typed literal or vocabulaire reference) + +#### Scenario: DCAT export includes kerngegevens mappings +- GIVEN a dataset with KerngegevensSet "Identificatie-persoonsgegevens" with `bronAttributen = [{pad: "/bsn", label: "BSN"}]` +- WHEN the DCAT-AP-NL 2.1 JSON-LD export is generated +- THEN the JSON-LD MUST include a `dn:kerngegevens` array with objects: + ```json + { + "@id": "http://data.overheid.nl/kerngegevens/identificatie-persoonsgegevens", + "rdfs:label": "Identificatie-persoonsgegevens", + "dn:bronAttributen": [ + { + "dn:pad": "/bsn", + "rdfs:label": "BSN" + } + ], + "dn:gerelateerdeBasisregistratie": "http://data.overheid.nl/vocab/basisregistraties/brp" + } + ``` + +#### Scenario: DCAT Turtle and RDF/XML are both available +- GIVEN a dataset with complete stelsel metadata +- WHEN a harvester requests the DCAT feed in different formats +- THEN `GET /api/dcat.rdf` MUST return valid RDF/XML (Content-Type: `application/rdf+xml`) +- AND `GET /api/dcat.jsonld` MUST return valid JSON-LD (Content-Type: `application/ld+json`) +- AND both representations MUST be lossless (no vocabulaire terms omitted) + +### Requirement: Federation to Stelselcatalogus MUST push core data automatically + +The openconnector federation job MUST collect all datasets with `positieType = kerngegevensstelsel` and push them to the Stelselcatalogus API daily. + +#### Scenario: Federation job collects and transforms data +- GIVEN opencatalogi instances configured to federate to Stelselcatalogus +- WHEN the federation job runs (daily) +- THEN the job MUST: + 1. Query all published datasets with `StelselPositie.positieType = kerngegevensstelsel` + 2. For each dataset, construct a Stelselcatalogus API payload with `StelselPositie` + `KerngegevensSet` data + 3. Transform stelsel vocabulaires to Stelselcatalogus-expected URIs (e.g., `dn:stelselPositie/kerngegevensstelsel` → `https://stelselcatalogus.logius.nl/kerngegevens/kerngegevensstelsel`) + 4. POST to `https://api.stelselcatalogus.logius.nl/v1/kerngegevens` with authorization credentials + 5. Log success or failure for each dataset + +#### Scenario: Federation push includes metadata validation +- GIVEN a dataset with incomplete `KerngegevensSet` (missing required attributes) +- WHEN the federation job attempts to push +- THEN the job MUST validate all required fields before attempting push +- AND if validation fails, MUST log the error and mark the dataset as "pending manual review" +- AND the catalogusbeheerder MUST receive a notification: "Dataset {id} could not be federated to Stelselcatalogus — please review kerngegevens mappings" + +#### Scenario: Federation error handling +- GIVEN the Stelselcatalogus API is temporarily unavailable +- WHEN the federation job calls the API +- THEN the job MUST: + - Capture the HTTP error (5xx) + - Log the incident (timestamp, dataset ID, error) + - Retry up to 3 times with exponential backoff + - If all retries fail, record the open finding and skip to next dataset + - Do NOT block other datasets from pushing + +### Requirement: UI MUST provide stelsel-position wizard on dataset creation + +The dataset creation / publication flow MUST include a guided wizard for StelselPositie configuration. + +#### Scenario: Stelsel wizard appears on first publish attempt +- GIVEN a user creating a new dataset with status `concept` +- WHEN they click "Publiceren" (Publish) +- THEN if no StelselPositie exists, a modal MUST open: "Wat is de stelselposition van deze dataset?" +- AND the modal MUST show five radio buttons for position types: + - "Basisregistratie (wettelijk, m.a.w. één van: BRP, BAG, etc.)" + - "Kerngegevensstelsel (afgeleide kerngegevens uit basisregistraties)" + - "Sectorbreed register (domeinspecifiek zoals zorg, onderwijs)" + - "Afgeleide set (bewerking van andere dataset)" + - "Lokale registratie (alleen voor deze gemeente/provincie)" + +#### Scenario: Basis register branch of wizard +- GIVEN the user selects "Basisregistratie" +- WHEN they click "Volgende" +- THEN the wizard MUST show: + - "Welke basisregistratie?" dropdown (BRP, BAG, BRT, BRK, NHR, BGT, BRO, BRV, WOZ, BRI) + - "Wettelijke grondslag" text field (e.g., "Wet BRP art. 3") + - "Bronhouder KvK-nummer" field with ROO validation + - "Terugmeldendpoint (verplicht voor basis registers)" section with URL + protocol + SLA + +#### Scenario: Kerngegevens branch of wizard +- GIVEN the user selects "Kerngegevensstelsel" +- WHEN they click "Volgende" +- THEN the wizard MUST show: + - "Gerelateerde basisregistratie" dropdown (BRP, BAG, etc.) + - "Kerngegevenssетten" builder: "Voeg een kerngegevensset toe" + - For each set: "Naam", "Bronattributen" schema-field mapper + - Maintenance cycle section: "Bijhoudfrequentie", "Laatste update", "Volgende geplande update" + +#### Scenario: Wizard saves StelselPositie and returns to dataset +- GIVEN the user completes the wizard form with all required fields +- WHEN they click "Opslaan" (Save) +- THEN the system MUST: + 1. Create or update the StelselPositie object + 2. Validate all fields + 3. Close the modal + 4. Re-display the dataset with stelsel metadata visible + 5. Allow the user to click "Publiceren" again without being prompted + +### Requirement: Cross-app integrations MUST be documented and validated + +The five new schemas interact with existing opencatalogi, openregister, openconnector, softwarecatalog, and mydash infrastructure. These integration points MUST be tested and documented. + +#### Scenario: Schema validation against openregister +- GIVEN a KerngegevensSet with `bronAttributen` referencing paths like `/bsn`, `/geslachtsnaam` +- WHEN the form is submitted +- THEN the system MUST call the openregister `ObjectService` API to fetch the dataset schema +- AND MUST validate each path exists in the schema +- AND MUST display a helpful error if paths are invalid + +#### Scenario: Organization lookup from softwarecatalog +- GIVEN the user entering a bronhouder KvK +- WHEN the field loses focus +- THEN the system MUST call softwarecatalog's organization lookup API +- AND MUST resolve the KvK to an official name (e.g., "Logius") +- AND MUST display the resolved name below the input field + +#### Scenario: Actualiteit dashboard in mydash +- GIVEN the mydash actualiteit-dashboard configured for a catalogusbeheerder +- WHEN the dashboard loads +- THEN mydash MUST query all datasets in the catalog and their `BijhoudCyclus` metadata +- AND MUST display a heatmap showing: + - Green cells: datasets with freshness GREEN (within SLA) + - Orange cells: datasets with freshness ORANGE (within 50% SLA overrun) + - Red cells: datasets with freshness RED (SLA breached) +- AND MUST allow drilling down into RED datasets to contact the data steward + +## Standards and Compliance + +- **DCAT-AP-NL 2.1** (Forum Standaardisatie, pass-toe-of-leg-uit-verplicht) +- **NL GOV Profile of DCAT-AP** (v2.1) +- **Stelselcatalogus vocabulaire** (Logius, kerngegevens URIs) +- **Wet BRP, Wet BAG, etc.** (wettelijke grondslag, artikel 38a feedback) +- **Terugmeldvoorziening richtlijnen** (Logius) +- **NORA** (Nederlandse Overheid Referentie Architectuur) +- **Digikoppeling** (ebMS 2.0 profile for G2G feedback) +- **NLX** (Nederlandse logistieke uitwisseling, optional transport) +- **W3C DCAT 3.0** (base vocabulaire) +- **Register Nederlandse Overheids-organisaties (ROO)** (organization reference data, Logius) diff --git a/openspec/changes/kerngegevens-stelsel-registratie/tasks.md b/openspec/changes/kerngegevens-stelsel-registratie/tasks.md new file mode 100644 index 000000000..0eae41e82 --- /dev/null +++ b/openspec/changes/kerngegevens-stelsel-registratie/tasks.md @@ -0,0 +1,200 @@ +# Tasks: kerngegevens-stelsel-registratie + +## Task 1: Data model and schema registration +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirements 1-5) +- **Status**: todo +- **Description**: Register the five new OpenRegister schemas: StelselPositie, KerngegevensSet, BijhoudCyclus, Geldigheidsperiode, TerugmeldEndpoint. Include enum validators, relationship definitions (datasetId foreign key), and seed data examples. +- **Acceptance criteria**: + - [ ] All five schemas registered in openregister `registers/opencatalogi-stelsel.register.yaml` + - [ ] StelselPositie.positieType enum: basisregistratie / kerngegevensstelsel / sectorbreed-register / afgeleide-set / lokale-registratie + - [ ] StelselPositie.basisregistratieNaam enum locked to ten official basis registers (BRP, BAG, BRT, BRK, NHR, BGT, BRO, BRV, WOZ, BRI) + - [ ] KerngegevensSet.bronAttributen allows array of {pad: string, label: string} + - [ ] BijhoudCyclus.bijhoudFrequentie enum: realtime / dagelijks / wekelijks / maandelijks / kwartaal / jaarlijks / event-driven + - [ ] TerugmeldEndpoint.terugmeldProtocol enum: digikoppeling-ebms / rest-json / nlx + - [ ] All schemas include dct:description metadata (Dutch + English) + - [ ] Seed data (3-5 example objects per schema) present in documentation + +## Task 2: Publication validation pipeline (dataset → stelselregistratie check) +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 1) +- **Status**: todo +- **Description**: Extend opencatalogi's dataset publication workflow to enforce StelselPositie presence. Add a pre-publish validation hook that checks for attached StelselPositie and shows a wizard if missing. +- **Acceptance criteria**: + - [ ] Publication action checks for attached StelselPositie before allowing status transition to `gepubliceerd` + - [ ] If missing, publication is blocked with user-facing error message + - [ ] Error message includes a "Configureer stelselregistratie" button that opens the stelsel-wizard modal + - [ ] Wizard modal component created at `src/views/DatasetPublicationWizard.vue` + - [ ] POST handler saves StelselPositie and updates dataset status in one transaction + - [ ] Unit tests: dataset without position, dataset with partial position (missing required fields), dataset with valid position + +## Task 3: Basis register validation and ROO lookup +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 2) +- **Status**: todo +- **Description**: Implement strict validation for `positieType = basisregistratie`: enforce `basisregistratieNaam` from official list, validate `wettelijkeGrondslag` format, and validate `bronhouder` KvK against the Register Nederlandse Overheids-organisaties (ROO). +- **Acceptance criteria**: + - [ ] `basisregistratieNaam` dropdown shows exactly ten official names, no custom input + - [ ] `wettelijkeGrondslag` field accepts prose (e.g., "Wet BRP art. 3") and is required for basisregistratie type + - [ ] `bronhouder` KvK lookup calls ROO API (Logius) and validates only government entities + - [ ] Resolved organization name displayed below KvK field + - [ ] Form rejects invalid KvK numbers with error message + - [ ] Unit tests: invalid KvK, valid KvK (Logius), non-government KvK, network timeout handling + +## Task 4: Kerngegevens set builder with schema validation +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 3) +- **Status**: todo +- **Description**: Build UI component for KerngegevensSet definition, allowing data-stewards to map dataset schema fields to kerngegevens attributes. Integrate with openregister to validate field paths against dataset schema. +- **Acceptance criteria**: + - [ ] KerngegevensSet list view shows all sets for a dataset (create/edit/delete) + - [ ] "Create kerngegevens set" form with fields: setNaam, gerelateerdeBasisregistratie, bronAttributen + - [ ] `gerelateerdeBasisregistratie` dropdown shows all ten basis registers + - [ ] bronAttributen builder allows adding/removing attribute mappings + - [ ] Per-attribute form: pad (string, validated), label (string, required) + - [ ] Path validation calls openregister ObjectService to fetch dataset schema + - [ ] Autocomplete suggestions for valid paths based on schema + - [ ] Error handling for schema fetch failures or missing schema + - [ ] Multiple sets per dataset supported (no conflict) + - [ ] Unit tests: valid paths, invalid paths, schema not found, multiple sets + +## Task 5: Maintenance cycle (BijhoudCyclus) tracker and freshness indicator +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 4) +- **Status**: todo +- **Description**: Implement BijhoudCyclus data entry and calculate actualiteit (freshness) indicator based on `bijhoudFrequentie` vs. `laatsteUpdate`. Expose indicator on dataset detail page and in DCAT export. +- **Acceptance criteria**: + - [ ] BijhoudCyclus form with fields: bijhoudFrequentie (enum), bijhoudBron, laatsteUpdate, volgendeGeplandeUpdate, peildatum + - [ ] Freshness indicator calculation: compare `laatsteUpdate` against `bijhoudFrequentie` window + - [ ] Indicators: GREEN (within SLA), ORANGE (>66% overdue), RED (>100% overdue), or always GREEN for realtime + - [ ] Indicator displayed on dataset detail page with tooltip showing SLA status + - [ ] Hourly background job updates freshness indicators for all datasets + - [ ] DCAT export includes `dn:bijhoudStatus` with vocabulaire URI (`http://data.overheid.nl/vocab/bijhoudstatus/{groen|oranje|rood}`) + - [ ] Unit tests: within-SLA, approaching-SLA, SLA-breached, realtime, no bijhoudCyclus + +## Task 6: Validity period (Geldigheidsperiode) and historical snapshots +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 5) +- **Status**: todo +- **Description**: Manage dataset temporal scope and enable snapshot creation when validity period closes. Support reuser queries for historical data via peildatum parameter. +- **Acceptance criteria**: + - [ ] Geldigheidsperiode form with fields: geldigVanaf, geldigTotEnMet (nullable), historischeVersies (read-only array) + - [ ] When new period is created (old period is closed): + - [ ] System automatically sets old period's `geldigTotEnMet` to yesterday + - [ ] Immutable snapshot created and stored with version URI (e.g., `/datasets/{id}/versions/2010-01-01`) + - [ ] Snapshot reference added to old period's `historischeVersies` + - [ ] New period created with provided start date and `geldigTotEnMet = null` + - [ ] Dataset detail page shows validity label: "Geldig vanaf ... tot ..." or "Geldig vanaf ... (huidig)" + - [ ] Historical versions list shown for closed periods + - [ ] API endpoint supports `?peildatum=YYYY-MM-DD` to retrieve snapshot valid on date + - [ ] API response includes `_snapshot: true`, `_validFrom`, `_validUntil` metadata + - [ ] Unit tests: open period, closed period, snapshot creation, peildatum query, multiple versions + +## Task 7: Terugmeld endpoint configuration and feedback button +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 6) +- **Status**: todo +- **Description**: Register TerugmeldEndpoint for datasets (mandatory for basisregistratie), validate protocol and SLA, and display "Onjuist gegeven melden" feedback button on dataset detail pages with protocol-aware submission routing. +- **Acceptance criteria**: + - [ ] TerugmeldEndpoint form with fields: terugmeldUrl (HTTPS URI validation), terugmeldProtocol (enum), responsTermijn (string), contactgegevens (string) + - [ ] TerugmeldEndpoint is mandatory for `positieType = basisregistratie`, optional for others + - [ ] Publication validation rejects basisregistratie without terugmeldendpoint + - [ ] "Onjuist gegeven melden" button displayed on dataset detail page if endpoint configured + - [ ] Feedback modal form with fields: dataset-identifier, error-description, source, attachments (optional) + - [ ] Form submission routes via terugmeldProtocol: + - [ ] digikoppeling-ebms: POST to terugmeldUrl with Digikoppeling envelope (ebMS 2.0) + - [ ] rest-json: POST to terugmeldUrl with JSON body + - [ ] nlx: POST to terugmeldUrl via NLX gateway + - [ ] Confirmation message displays SLA: "Bedankt. Uw melding wordt verwerkt binnen {responsTermijn}" + - [ ] Error handling for unreachable endpoints (5xx, timeout) with user-facing message + - [ ] Unit tests: missing endpoint, invalid URL, each protocol type, network errors + +## Task 8: DCAT-AP-NL 2.1 export with stelsel vocabulaires +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 7) +- **Status**: todo +- **Description**: Extend opencatalogi DCAT export to serialize StelselPositie, KerngegevensSet, BijhoudCyclus, Geldigheidsperiode, and TerugmeldEndpoint into DCAT-AP-NL 2.1 RDF/XML and JSON-LD with proper vocabulaire URIs and Forum Standaardisatie compliance. +- **Acceptance criteria**: + - [ ] DCAT RDF/XML export includes `dcat:theme` or `dn:stelselPositie` with vocabulaire URI for position type + - [ ] DCAT RDF/XML includes `dn:basisregistratie` URI for basis register type + - [ ] DCAT RDF/XML includes `dn:bronhouder`, `dn:wettelijkeGrondslag`, `dn:gegevensbeheerder`, `dn:verstrekker` properties + - [ ] DCAT JSON-LD export includes `dn:kerngegevens` array with kerngegevens set details + - [ ] Each kerngegevens set serialized with `rdfs:label`, `dn:bronAttributen` (array of path/label), `dn:gerelateerdeBasisregistratie` + - [ ] DCAT export includes `dct:issued` (aansluitDatum) for basisregistratie + - [ ] DCAT export includes `dn:bijhoudStatus` with vocabulaire URI for freshness indicator + - [ ] DCAT export includes `dn:terugmeldEndpoint` with URL and protocol + - [ ] DCAT export includes `dcat:temporalResolution` or `dn:geldigheid` for validity periods + - [ ] Vocabulaire base URIs: `http://data.overheid.nl/vocab/` + - [ ] GET `/api/dcat.rdf` returns RDF/XML, GET `/api/dcat.jsonld` returns JSON-LD, both lossless + - [ ] Unit tests: all entity types, RDF/XML validity, JSON-LD validity, missing optional fields, vocabulaire URIs + +## Task 9: Federation to Stelselcatalogus (OpenConnector integration) +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 8) +- **Status**: todo +- **Description**: Integrate with openconnector to collect all datasets with `positieType = kerngegevensstelsel` and push them to the Stelselcatalogus API (Logius) daily. Include transformation of vocabulaires and error handling. +- **Acceptance criteria**: + - [ ] Federation job defined in openconnector with schedule: daily (configurable) + - [ ] Job queries opencatalogi for published datasets with `StelselPositie.positieType = kerngegevensstelsel` + - [ ] For each dataset, construct Stelselcatalogus API payload: + - [ ] StelselPositie data (positieType, kerngegevens relation, dates) + - [ ] KerngegevensSet data (setNaam, bronAttributen, gerelateerdeBasisregistratie) + - [ ] Transform vocaulaires to Stelselcatalogus-expected URIs + - [ ] POST each payload to `https://api.stelselcatalogus.logius.nl/v1/kerngegevens` with configured API key + - [ ] Validation before push: check required fields, validate relationships + - [ ] Error handling: log validation errors, retry 3x with exponential backoff for network errors + - [ ] On error: record as open finding, skip dataset, continue with others + - [ ] Catalogusbeheerder notification on validation error: "Dataset {id} could not be federated — review kerngegevens" + - [ ] Job logs success/failure per dataset (structured logs: timestamp, dataset-id, status, error-message) + - [ ] Unit tests: all validations, successful push, API errors, network timeout, payload transformation + +## Task 10: Stelsel-position wizard UI +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 9) +- **Status**: todo +- **Description**: Build multi-step guided wizard for StelselPositie configuration, triggered on first publish attempt. Include conditional form branches for each position type. +- **Acceptance criteria**: + - [ ] Wizard modal triggered when dataset lacks StelselPositie on publish attempt + - [ ] Step 1: "Wat is de stelselposition?" with 5 radio buttons and descriptions + - [ ] Step 2 (basisregistratie branch): + - [ ] "Welke basisregistratie?" dropdown (10 official names) + - [ ] "Wettelijke grondslag" text field + - [ ] "Bronhouder KvK" field with ROO validation and name resolution + - [ ] "Terugmeldendpoint" section (URL + protocol + SLA) + - [ ] Step 2 (kerngegevensstelsel branch): + - [ ] "Gerelateerde basisregistratie" dropdown (10 options) + - [ ] Kerngegevensset builder + - [ ] Maintenance cycle (bijhoudFrequentie, lastUpdate, nextUpdate) + - [ ] Step 2 (sectorbreed-register branch): + - [ ] "Sectordomein" field (healthcare, education, justice, mobility, other) + - [ ] Optional kerngegevens sets + - [ ] Step 2 (afgeleide-set branch): + - [ ] "Afleidingslogica" text field (prose description) + - [ ] Step 2 (lokale-registratie branch): + - [ ] (No additional required fields; simple confirmation) + - [ ] "Opslaan" button validates all fields, creates/updates StelselPositie, closes wizard, returns to dataset view + - [ ] "Annuleren" button closes wizard without saving + - [ ] Wizard progress indicator showing current step + - [ ] Form validation: red borders on required fields, inline error messages + - [ ] Unit tests: all branches, validation, save, cancel, missing required fields + +## Task 11: Actualiteit dashboard integration (MyDash) +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 10) +- **Status**: todo +- **Description**: Integrate with mydash to display a heatmap dashboard of dataset freshness by stelsel position, allowing catalogusbeheerders to monitor SLA compliance across the catalog. +- **Acceptance criteria**: + - [ ] Dashboard widget queryable from mydash: "Actualiteit per stelselposition" + - [ ] Widget queries opencatalogi for all published datasets and their BijhoudCyclus + freshness + - [ ] Heatmap display: rows = stelsel positions, columns = time periods (weekly/monthly) + - [ ] Cell colors: GREEN (SLA met), ORANGE (approaching breach), RED (breached) + - [ ] Cell tooltip shows dataset count, update count, SLA status + - [ ] Drill-down: clicking RED cells shows list of breached datasets with data-steward contact + - [ ] Filter by catalog, date range + - [ ] Export to CSV with dataset-id, position-type, last-update, freshness-status + - [ ] Unit tests: data aggregation, color assignment, drill-down data accuracy + +## Task 12: Cross-app integration tests and documentation +- **Spec ref**: specs/kerngegevens-stelsel-registratie/spec.md (Requirement 11) +- **Status**: todo +- **Description**: Validate integration with openregister, softwarecatalog, openconnector, mydash. Document integration points and APIs. Create end-to-end tests. +- **Acceptance criteria**: + - [ ] OpenRegister integration docs: schema validation API, error handling, timeouts + - [ ] SoftwareCatalog integration docs: organization lookup, KvK resolution + - [ ] OpenConnector federation docs: Stelselcatalogus API format, request/response examples, error codes + - [ ] MyDash integration docs: dashboard query format, heatmap data structure + - [ ] End-to-end test: create dataset → set position → publish → verify DCAT export → verify federation push + - [ ] Integration test: ROO lookup with invalid KvK → softwarecatalog fallback + - [ ] Integration test: schema validation with missing schema → graceful degradation + - [ ] Timeout handling tests: all external APIs (ROO, Stelselcatalogus, SoftwareCatalog) + - [ ] Updated CLAUDE.md with stelselregistratie-specific instructions (l10n keys, validation patterns) + - [ ] API documentation: POST /stelselPositie, GET /stelselPositie/{id}, etc. From 2a177a6d412c88adec56c594035b4a3e8ab27fa2 Mon Sep 17 00:00:00 2001 From: Specter Intelligence Date: Fri, 22 May 2026 07:42:06 +0200 Subject: [PATCH 2/4] feat: Add OpenSpec change kerngegevens-stelsel-registratie 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 `