Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.
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/Controller/HealthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public function __construct(
* @NoCSRFRequired
*
* @return JSONResponse
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-5
*/
#[NoAdminRequired]
#[NoCSRFRequired]
Expand Down
2 changes: 2 additions & 0 deletions lib/Controller/MetricsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public function __construct(
* @NoCSRFRequired
*
* @return JSONResponse
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-5
*/
#[NoAdminRequired]
#[NoCSRFRequired]
Expand Down
2 changes: 2 additions & 0 deletions lib/Controller/PreferencesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public function __construct(
*
* @NoAdminRequired
* @NoCSRFRequired
* @spec openspec/changes/retrofit-2026-05-26-frontend-foundation/tasks.md#task-5
*/
public function getPreference(string $key): JSONResponse
{
Expand Down Expand Up @@ -101,6 +102,7 @@ public function getPreference(string $key): JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
* @spec openspec/changes/retrofit-2026-05-26-frontend-foundation/tasks.md#task-5
*/
public function setPreference(string $key, string $value=''): JSONResponse
{
Expand Down
6 changes: 6 additions & 0 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public function __construct(
* @NoCSRFRequired
*
* @return JSONResponse
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-1
*/
#[NoAdminRequired]
#[NoCSRFRequired]
Expand All @@ -84,6 +86,8 @@ public function index(): JSONResponse
* @NoCSRFRequired
*
* @return JSONResponse
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-2
*/
#[NoAdminRequired]
#[NoCSRFRequired]
Expand Down Expand Up @@ -114,6 +118,8 @@ public function create(): JSONResponse
* @NoCSRFRequired
*
* @return JSONResponse
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-3
*/
#[NoAdminRequired]
#[NoCSRFRequired]
Expand Down
2 changes: 2 additions & 0 deletions lib/Listener/DeepLinkRegistrationListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class DeepLinkRegistrationListener implements IEventListener
* @param Event $event The event to handle
*
* @return void
*
* @spec openspec/changes/archive/retrofit-2026-05-24-deep-link-registration/tasks.md#task-1
*/
public function handle(Event $event): void
{
Expand Down
2 changes: 2 additions & 0 deletions lib/Repair/InitializeSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public function getName(): string
* @param IOutput $output The output interface for progress reporting
*
* @return void
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-4
*/
public function run(IOutput $output): void
{
Expand Down
2 changes: 2 additions & 0 deletions lib/Service/ApplicationVersionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,8 @@ private function canonicaliseValue(mixed $value): mixed
* @return string The register's slug, or empty string when unavailable
*
* @internal Exposed only to internal callers; not part of the public API.
*
* @spec openspec/changes/archive/retrofit-2026-05-24-annotate-openbuilt/tasks.md#task-23
*/
public function describeRegister(?Register $register): string
{
Expand Down
2 changes: 2 additions & 0 deletions lib/Service/ExportJobService.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ public function credentialKey(string $jobUuid): string
* Generate a UUIDv4.
*
* @return string UUIDv4.
*
* @spec openspec/changes/archive/retrofit-2026-05-24-annotate-openbuilt/tasks.md#task-37
*/
public function uuid4(): string
{
Expand Down
6 changes: 6 additions & 0 deletions lib/Service/ExportService.php
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ public function resolvePlaceholders(string $rootDir, array $context): void
* @param string $path File path.
*
* @return bool True when the file should be copied as-is.
*
* @spec openspec/changes/archive/retrofit-2026-05-24-annotate-openbuilt/tasks.md#task-40
*/
public function isBinary(string $path): bool
{
Expand Down Expand Up @@ -329,6 +331,8 @@ public function copyTemplate(string $source, string $dest): void
* @param string $jobUuid ExportJob UUID.
*
* @return string Local path to the scratch dir.
*
* @spec openspec/changes/archive/retrofit-2026-05-24-annotate-openbuilt/tasks.md#task-35
*/
public function prepareScratchDir(string $jobUuid): string
{
Expand Down Expand Up @@ -399,6 +403,8 @@ public function getOrCreateAppDataDir(string $name): string
* @param string $dir Directory to remove.
*
* @return void
*
* @spec openspec/changes/archive/retrofit-2026-05-24-annotate-openbuilt/tasks.md#task-35
*/
public function rrmdir(string $dir): void
{
Expand Down
4 changes: 4 additions & 0 deletions lib/Service/PlaceholderResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ public function resolve(string $content, array $map): string
* @param string $value Source value.
*
* @return string Slug.
*
* @spec openspec/changes/archive/retrofit-2026-05-24-annotate-openbuilt/tasks.md#task-40
*/
public function slug(string $value): string
{
Expand Down Expand Up @@ -125,6 +127,8 @@ public function slug(string $value): string
* @param string $value Source value.
*
* @return string PascalCase'd identifier.
*
* @spec openspec/changes/archive/retrofit-2026-05-24-annotate-openbuilt/tasks.md#task-40
*/
public function pascalCase(string $value): string
{
Expand Down
8 changes: 8 additions & 0 deletions lib/Service/SettingsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public function isOpenRegisterAvailable(): bool
* fields (openregisters, isAdmin) consumed by the frontend.
*
* @return array<string,mixed>
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-1
*/
public function getSettings(): array
{
Expand All @@ -112,6 +114,8 @@ public function getSettings(): array
* @param array<string,mixed> $data The data to update
*
* @return array<string,mixed> The updated settings
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-2
*/
public function updateSettings(array $data): array
{
Expand All @@ -132,6 +136,8 @@ public function updateSettings(array $data): array
* reloadConfiguration() to force a re-import.
*
* @return array<string,mixed> Result with success flag, message, and version.
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-3
*/
public function loadConfiguration(): array
{
Expand All @@ -145,6 +151,8 @@ public function loadConfiguration(): array
* Used by the InitializeSettings repair step and the admin "Reload" action.
*
* @return array<string,mixed> Result with success flag, message, and version.
*
* @spec openspec/changes/retrofit-2026-05-25-settings-and-observability/tasks.md#task-3
*/
public function reloadConfiguration(): array
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Design — schema-designer-ui (retrofit)

Retrofit change. Tasks describe retroactive annotation, not new implementation work.
The Schema Designer code already ships; this records its observed behaviour as numbered REQs
so gate-16 spec-coverage can trace each method to a requirement. No behaviour is changed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Retrofit — schema-designer-ui

Describes observed behaviour of the Schema Designer UI (SchemaDesigner view,
schema-editor/* sub-editors, schema modal dialogs, schemas store) as 5 new REQs.
Code already exists — this change retroactively specifies it.

## Approach
- Describe observed inputs, outputs, validation gates, and emit contracts per component
- REQs match behaviour, not aspiration
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
retrofit: true
---

# schema-designer-ui Specification

## Purpose

The Schema Designer is OpenBuilt's visual editor for authoring an
Application version's OpenRegister schemas: their header (slug, title,
description, version), property fields, lifecycle state machine,
relations, widgets, and derived-value blocks (aggregations,
calculations, notifications). The `SchemaDesigner` view orchestrates
list/detail navigation, stages edits in memory, validates them, and
persists via OpenRegister's runtime schema CRUD. Sub-editors emit
their slices upward; modal dialogs gate create/delete actions; the
schemas store resolves the per-app register.

This capability is observed behaviour of the `SchemaDesigner` view,
the `src/components/schema-editor/*` components, the schema modal
dialogs, and the `schemas` store.

## Requirements

### REQ-OBSDUI-001: Schema list panel and create/delete gating

The `SchemaListPanel` SHALL render the version's schemas with a
property count and lifecycle label per row, emit an open event when a
row is activated, and route add/delete intents through confirmation.
`AddSchemaDialog` SHALL validate the new schema's slug/title before
confirming; `DeleteSchemaDialog` SHALL require an explicit confirmation
gate before allowing delete; `DeleteFieldDialog` SHALL confirm
field-level removal. The `schemas` store SHALL resolve the register
slug for a given application (`registerSlugForApp`).

#### Scenario: Open a schema

- **WHEN** the user activates a schema row
- **THEN** the panel emits an open event carrying the schema identity
- **AND** the designer loads that schema's detail

#### Scenario: Guarded delete

- **WHEN** the user requests schema or field deletion
- **THEN** a confirmation dialog gates the destructive action until
confirmed

### REQ-OBSDUI-002: Field editor authors typed properties

`FieldEditor` SHALL let the user add, remove, reorder (move up/down),
and edit schema fields, exposing type and cardinality option lists,
coercing numeric inputs (`toIntOrNull`, `toNumberOrNull`), validating
field-name uniqueness/presence (`nameError`), and emitting the updated
field array upward. Removal SHALL be guarded by a request/confirm/cancel
flow.

#### Scenario: Add and edit a field

- **WHEN** the user adds a field and sets its type and validation
- **THEN** the editor emits the updated fields array
- **AND** numeric validation inputs are coerced to numbers or null

#### Scenario: Reorder fields

- **WHEN** the user moves a field up or down
- **THEN** the field order in the emitted array changes accordingly

### REQ-OBSDUI-003: Lifecycle editor authors the state machine

`LifecycleEditor` SHALL let the user add/remove/edit states,
transitions, and per-transition actions, expose action/state option
lists, validate state-name correctness (`stateNameValid`), track the
number of initial states (`initialCount`), enforce exactly one initial
state via `setInitial`, and emit updated states and transitions upward.

#### Scenario: Define a state machine

- **WHEN** the user adds states, marks one initial, and adds
transitions between them
- **THEN** the editor emits the updated states and transitions
- **AND** marking a new initial state clears the previous one

### REQ-OBSDUI-004: Relations, widgets, and derived-value editors

`RelationEditor` SHALL author schema relations (add/remove/update with
cardinality and target-schema option lists, emitting the relations
array). `WidgetEditor` SHALL author `x-openregister-widgets` rows
(add/remove/update, JSON-validating each widget's config and emitting
the widgets array). The `AggregationEditor`, `CalculationEditor`, and
`NotificationEditor` SHALL render a formatted, human-readable summary
of their respective derived-value blocks.

#### Scenario: Author a relation

- **WHEN** the user adds a relation with a target schema and cardinality
- **THEN** the editor emits the updated relations array

#### Scenario: Invalid widget config blocks

- **WHEN** a widget's config JSON is malformed
- **THEN** the widget row surfaces a config error

### REQ-OBSDUI-005: Designer stages, validates, and persists schema edits

The `SchemaDesigner` view SHALL resolve the active ApplicationVersion,
load the schema list and a selected schema's detail, stage edits in
memory, and compose a canonical schema body from the staged slices
(`composeSchemaBody`, `bodyToStaged`). Save SHALL be gated
(`canSave`) by: dirty-state (`hasStagedChanges`), field-name uniqueness
(`fieldNamesUnique`), exactly one initial lifecycle state
(`hasInitialLifecycleState`), and no widget config errors. Save SHALL
persist via the schemas store / OR runtime CRUD; discard SHALL revert
staged edits; add/delete SHALL update the list. Each sub-editor change
handler (`onFieldsChange`, `onStatesChange`, `onTransitionsChange`,
`onRelationsChange`, `onWidgetsChange`, `onHeaderChange`) SHALL update
the staged body.

#### Scenario: Save a valid edit

- **WHEN** the staged schema is dirty and passes all validation gates
- **THEN** Save is enabled and persists the composed body via the store

#### Scenario: Validation blocks save

- **WHEN** field names collide or no initial lifecycle state is set
- **THEN** `canSave` is false and Save is disabled
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tasks

- [x] task-1: schema-designer-ui#REQ-OBSDUI-001 — Schema list panel and create/delete gating (retroactive annotation)
- [x] task-2: schema-designer-ui#REQ-OBSDUI-002 — Field editor authors typed properties (retroactive annotation)
- [x] task-3: schema-designer-ui#REQ-OBSDUI-003 — Lifecycle editor authors the state machine (retroactive annotation)
- [x] task-4: schema-designer-ui#REQ-OBSDUI-004 — Relations, widgets, and derived-value editors (retroactive annotation)
- [x] task-5: schema-designer-ui#REQ-OBSDUI-005 — Designer stages, validates, and persists schema edits (retroactive annotation)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Design — settings-and-observability (retrofit)

Retrofit change. Tasks describe retroactive annotation, not new
implementation work. The code already exists and ships in OpenBuilt;
this change records the observed behaviour of the settings surface and
the liveness/metrics probes as numbered REQs so gate-16 spec-coverage
can trace each method to a requirement.

## Observed shape
- `SettingsService` owns the read/update/import logic; the controller
is a thin auth-gated HTTP wrapper over it.
- `InitializeSettings` repair step force-reloads the bundled register
config at install/upgrade.
- `HealthController` / `MetricsController` are CSRF/admin-exempt but
still require an authenticated session.

No behaviour is changed by this retrofit.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Retrofit — settings-and-observability

Describes observed behaviour of 10 methods under `settings-and-observability`
as 5 new REQs. Code already exists — this change retroactively specifies it.

## Affected code units
- lib/Service/SettingsService.php::getSettings()
- lib/Service/SettingsService.php::updateSettings()
- lib/Service/SettingsService.php::loadConfiguration()
- lib/Service/SettingsService.php::reloadConfiguration()
- lib/Controller/SettingsController.php::index()
- lib/Controller/SettingsController.php::create()
- lib/Controller/SettingsController.php::load()
- lib/Repair/InitializeSettings.php::run()
- lib/Controller/HealthController.php::index()
- lib/Controller/MetricsController.php::index()

## Approach
- For each method: describe observed inputs, outputs, pre/postconditions, failure modes
- Draft REQs that match behaviour (not aspirational)
- Notes section surfaces the empty metrics payload and the authenticated-probe quirk

Source: openspec/coverage-report (gate-16 spec-coverage) generated 2026-05-25.
See [retrofit playbook](../../../.github/docs/claude/retrofit.md).
Loading
Loading