Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/Repair/SeedVthWorkflowTemplates.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
* @version GIT: <git-id>
*
* @link https://procest.nl
*
* @spec openspec/changes/retrofit-2026-05-25-vth-workflow-templates/tasks.md#task-1
*/

declare(strict_types=1);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Design — retrofit vth-workflow-templates

Retrofit change. Tasks describe retroactive annotation, not new implementation work.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Tasks

- [x] task-1: vth-workflow-templates#REQ-001 — SeedVthWorkflowTemplates repair step idempotent catalog seeder (retroactive annotation)
47 changes: 47 additions & 0 deletions openspec/specs/vth-workflow-templates/spec.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
retrofit_extensions:
- REQ-001
---

## ADDED Requirements

### Requirement: Omgevingsvergunning workflow template
Expand Down Expand Up @@ -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.
Loading