From 0d7f961b9b06e2ee9b2c9c4a0cc47fd02f1d092b Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Mon, 25 May 2026 00:23:16 +0200 Subject: [PATCH] retrofit: reverse-spec vth-workflow-templates (1 REQ / 1 file) Refs ConductionNL/procest#565 --- lib/Repair/SeedVthWorkflowTemplates.php | 2 + .../design.md | 3 ++ .../proposal.md | 10 ++++ .../specs/vth-workflow-templates/spec.md | 48 +++++++++++++++++++ .../tasks.md | 3 ++ openspec/specs/vth-workflow-templates/spec.md | 47 ++++++++++++++++++ 6 files changed, 113 insertions(+) create mode 100644 openspec/changes/retrofit-2026-05-25-vth-workflow-templates/design.md create mode 100644 openspec/changes/retrofit-2026-05-25-vth-workflow-templates/proposal.md create mode 100644 openspec/changes/retrofit-2026-05-25-vth-workflow-templates/specs/vth-workflow-templates/spec.md create mode 100644 openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md diff --git a/lib/Repair/SeedVthWorkflowTemplates.php b/lib/Repair/SeedVthWorkflowTemplates.php index c5c6d550..f185c03f 100644 --- a/lib/Repair/SeedVthWorkflowTemplates.php +++ b/lib/Repair/SeedVthWorkflowTemplates.php @@ -30,6 +30,8 @@ * @version GIT: * * @link https://procest.nl + * + * @spec openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md#task-1 */ declare(strict_types=1); diff --git a/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/design.md b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/design.md new file mode 100644 index 00000000..9fcde392 --- /dev/null +++ b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/design.md @@ -0,0 +1,3 @@ +# Design — retrofit vth-workflow-templates + +Retrofit change. Tasks describe retroactive annotation, not new implementation work. diff --git a/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/proposal.md b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/proposal.md new file mode 100644 index 00000000..8263ede7 --- /dev/null +++ b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/proposal.md @@ -0,0 +1,10 @@ +# Retrofit — vth-workflow-templates + +Adds 1 new REQ describing the observed behavior of the `SeedVthWorkflowTemplates` repair step — the install-time mechanism that seeds the VTH workflow catalog into OpenRegister. + +The existing spec already enumerates the four workflow template catalogs (Omgevingsvergunning / Toezichtzaak / Handhavingszaak / VTH library). This REQ documents the install-time seeding contract: idempotent per template title, deterministic IDs, graceful no-op when OpenRegister or the catalog directory is missing. + +## Affected code units +- lib/Repair/SeedVthWorkflowTemplates.php (1 public `run()` method, 9 private helpers) — `run()`, `processCatalogFile()`, `resolveCaseTypeId()`, `isAlreadySeeded()`, `buildStatusMap()`, `resolveSteps()`, `resolveTransitions()`, `deterministicId()`, `extractFirstId()`, `normalizeRow()` + +Source: openspec/coverage-report.md generated 2026-05-24. Tracks ConductionNL/procest#565. diff --git a/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/specs/vth-workflow-templates/spec.md b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/specs/vth-workflow-templates/spec.md new file mode 100644 index 00000000..0b4e03ec --- /dev/null +++ b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/specs/vth-workflow-templates/spec.md @@ -0,0 +1,48 @@ +--- +retrofit_extensions: + - REQ-001 +--- + +# VTH Workflow Templates — install-time seeder (retrofit) + +## Requirements + +### REQ-001: SeedVthWorkflowTemplates repair step SHALL idempotently seed the VTH workflow catalog from bundled JSON files + +`OCA\Procest\Repair\SeedVthWorkflowTemplates` SHALL implement `IRepairStep` and SHALL run on every app enable / upgrade. The `run(IOutput $output)` method SHALL: +- Short-circuit with a warning when `SettingsService::isOpenRegisterAvailable()` returns false — never throw. +- Short-circuit with a warning when the bundled catalog directory (`SeedVthWorkflowTemplates::CATALOG_DIR`) does not exist — never throw. +- Glob the catalog dir for `*.json` files. If no files match, emit a warning and return. +- Iterate every catalog file, delegating to `processCatalogFile()` which returns one of `seeded` / `skipped` / `crossLink` / `failed`. Per-file Throwables SHALL be caught, logged with file basename + exception message, surfaced via `$output->warning()`, and counted in the `failed` bucket — they SHALL NOT propagate to the repair runner. +- After every file is processed, emit a single summary `$output->info()` line with all four counters. + +The step SHALL be IDEMPOTENT: `isAlreadySeeded(string $caseTypeId, string $title)` SHALL check for an existing workflow row by (caseType + title) before inserting, and SHALL increment the `skipped` counter rather than re-create. Deterministic IDs SHALL be generated via `deterministicId(string $template, string $child)` so re-runs produce identical UUIDs and downstream references stay stable. + +#### Scenario: OpenRegister missing -> graceful no-op +- **GIVEN** the `openregister` app is not installed +- **WHEN** `SeedVthWorkflowTemplates::run()` executes during `occ app:enable procest` +- **THEN** the step SHALL emit `$output->warning('OpenRegister is not available. Skipping VTH workflow templates seed.')` +- **AND** SHALL return without globbing the catalog directory + +#### Scenario: Catalog directory missing -> graceful no-op +- **GIVEN** OpenRegister is installed but `CATALOG_DIR` does not exist (deleted by a misconfigured build) +- **WHEN** the repair step runs +- **THEN** the step SHALL emit a warning with the missing path +- **AND** SHALL return without touching OpenRegister + +#### Scenario: Re-running the seeder is idempotent +- **GIVEN** the seeder has already run and 4 workflow templates exist +- **WHEN** the seeder runs again on app upgrade +- **THEN** the summary line SHALL report `4 skipped` and `0 seeded` +- **AND** no duplicate rows SHALL be inserted + +#### Scenario: One bad catalog file does not block the rest +- **GIVEN** 4 catalog files exist, one of which contains invalid JSON +- **WHEN** the seeder runs +- **THEN** the bad file's exception SHALL be logged with `app=procest` + the file basename + the exception message +- **AND** the user-facing summary SHALL report `1 failed` +- **AND** the other 3 catalog files SHALL still be processed + +#### Notes +- The 9 private helpers (`processCatalogFile`, `resolveCaseTypeId`, `isAlreadySeeded`, `buildStatusMap`, `resolveSteps`, `resolveTransitions`, `deterministicId`, `extractFirstId`, `normalizeRow`) are not separately observable — they support the single `run()` contract above. Splitting them into separate REQs would inflate the spec without adding testable surface. +- `crossLink` is reserved for templates that reference an unresolved caseType; the seeder logs the reference and counts it but does not block the run. diff --git a/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md new file mode 100644 index 00000000..c145f8ff --- /dev/null +++ b/openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md @@ -0,0 +1,3 @@ +# Tasks + +- [x] task-1: vth-workflow-templates#REQ-001 — SeedVthWorkflowTemplates repair step idempotent catalog seeder (retroactive annotation) diff --git a/openspec/specs/vth-workflow-templates/spec.md b/openspec/specs/vth-workflow-templates/spec.md index 50d37871..f72f681e 100644 --- a/openspec/specs/vth-workflow-templates/spec.md +++ b/openspec/specs/vth-workflow-templates/spec.md @@ -1,3 +1,8 @@ +--- +retrofit_extensions: + - REQ-001 +--- + ## ADDED Requirements ### Requirement: Omgevingsvergunning workflow template @@ -112,3 +117,45 @@ The system SHALL provide a browsable library of VTH workflow templates that admi - **THEN** the imported workflow SHALL be editable via the standard visual workflow editor - **THEN** the beheerder SHALL be able to add, remove, or modify steps and transitions - **THEN** the original template SHALL remain unchanged in the library for future imports + +--- + +### REQ-001: SeedVthWorkflowTemplates repair step SHALL idempotently seed the VTH workflow catalog from bundled JSON files + +`OCA\Procest\Repair\SeedVthWorkflowTemplates` SHALL implement `IRepairStep` and SHALL run on every app enable / upgrade. The `run(IOutput $output)` method SHALL: +- Short-circuit with a warning when `SettingsService::isOpenRegisterAvailable()` returns false — never throw. +- Short-circuit with a warning when the bundled catalog directory (`SeedVthWorkflowTemplates::CATALOG_DIR`) does not exist — never throw. +- Glob the catalog dir for `*.json` files. If no files match, emit a warning and return. +- Iterate every catalog file, delegating to `processCatalogFile()` which returns one of `seeded` / `skipped` / `crossLink` / `failed`. Per-file Throwables SHALL be caught, logged with file basename + exception message, surfaced via `$output->warning()`, and counted in the `failed` bucket — they SHALL NOT propagate to the repair runner. +- After every file is processed, emit a single summary `$output->info()` line with all four counters. + +The step SHALL be IDEMPOTENT: `isAlreadySeeded(string $caseTypeId, string $title)` SHALL check for an existing workflow row by (caseType + title) before inserting, and SHALL increment the `skipped` counter rather than re-create. Deterministic IDs SHALL be generated via `deterministicId(string $template, string $child)` so re-runs produce identical UUIDs and downstream references stay stable. + +#### Scenario: OpenRegister missing -> graceful no-op +- **GIVEN** the `openregister` app is not installed +- **WHEN** `SeedVthWorkflowTemplates::run()` executes during `occ app:enable procest` +- **THEN** the step SHALL emit `$output->warning('OpenRegister is not available. Skipping VTH workflow templates seed.')` +- **AND** SHALL return without globbing the catalog directory + +#### Scenario: Catalog directory missing -> graceful no-op +- **GIVEN** OpenRegister is installed but `CATALOG_DIR` does not exist (deleted by a misconfigured build) +- **WHEN** the repair step runs +- **THEN** the step SHALL emit a warning with the missing path +- **AND** SHALL return without touching OpenRegister + +#### Scenario: Re-running the seeder is idempotent +- **GIVEN** the seeder has already run and 4 workflow templates exist +- **WHEN** the seeder runs again on app upgrade +- **THEN** the summary line SHALL report `4 skipped` and `0 seeded` +- **AND** no duplicate rows SHALL be inserted + +#### Scenario: One bad catalog file does not block the rest +- **GIVEN** 4 catalog files exist, one of which contains invalid JSON +- **WHEN** the seeder runs +- **THEN** the bad file's exception SHALL be logged with `app=procest` + the file basename + the exception message +- **AND** the user-facing summary SHALL report `1 failed` +- **AND** the other 3 catalog files SHALL still be processed + +#### Notes +- The 9 private helpers (`processCatalogFile`, `resolveCaseTypeId`, `isAlreadySeeded`, `buildStatusMap`, `resolveSteps`, `resolveTransitions`, `deterministicId`, `extractFirstId`, `normalizeRow`) are not separately observable — they support the single `run()` contract above. Splitting them into separate REQs would inflate the spec without adding testable surface. +- `crossLink` is reserved for templates that reference an unresolved caseType; the seeder logs the reference and counts it but does not block the run.