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
22 changes: 22 additions & 0 deletions openspec/specs/settings-service/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,25 @@ The service MUST call `importFromApp` from a single call site with the same, sta
- AND WHEN the true fix is determined to belong in OpenRegister rather than this app
- THEN an issue MUST be filed against the owning repository documenting the mechanism, and referenced from this app's code and docs

### Requirement: The system SHALL merge ADR-037 register fragments so disjoint fragments never collide (REQ-007)

`SettingsService::deepMergeConfig()` is the single merge primitive that folds every `register.d/*.json` fragment onto the monolith register. Its contract MUST be: nested maps merge key-by-key so that fragments touching *disjoint* keys all survive the merge; list values concatenate; scalar values from the overlay overwrite the base. This is what allows concurrent OpenSpec changes to each ship their own fragment without either one's schemas or paths being lost — the property ADR-037 exists to provide. The one deliberate exception is the `authorization` subtree, which replaces rather than concatenates; that carve-out and its rationale are specified under `catalog-ratings`, and this requirement MUST NOT be read as overriding it.

#### Scenario: Disjoint fragments union their schemas and paths

- GIVEN a base register declaring schema `Existing` and path `/existing`
- AND a fragment adding schema `AlphaComponent` and path `/alpha`
- AND a second fragment adding schema `BetaService` and path `/beta`
- WHEN the fragments are merged onto the base in sequence
- THEN all three schemas MUST be present under `components.schemas`
- AND all three paths MUST be present under `paths`
- AND no earlier fragment's contribution may be dropped by a later one

#### Scenario: Lists concatenate and scalars overwrite

- GIVEN a base with `required: ["a", "b"]` and `info.version: "0.1.0"`
- AND an overlay with `required: ["c"]` and `info.version: "0.2.0"`
- WHEN the overlay is merged onto the base
- THEN `required` MUST be `["a", "b", "c"]`
- AND `info.version` MUST be `"0.2.0"`

4 changes: 3 additions & 1 deletion tests/Unit/Service/RegisterFragmentMergeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* @link https://conduction.nl
*
* @spec openspec/changes/modular-register-manifest-fragments/specs/modular-config/spec.md
* @spec openspec/specs/settings-service/spec.md#requirement-the-system-shall-merge-adr-037-register-fragments-so-disjoint-fragments-never-collide-req-007
*/

declare(strict_types=1);
Expand Down Expand Up @@ -48,6 +48,7 @@ private function merge(array $base, array $overlay): array
* Two fragments adding disjoint OpenAPI schemas/paths union by key.
*
* @return void
* @spec openspec/specs/settings-service/spec.md#scenario-disjoint-fragments-union-their-schemas-and-paths
*/
public function testDisjointFragmentsUnionSchemasAndPaths(): void
{
Expand Down Expand Up @@ -84,6 +85,7 @@ public function testDisjointFragmentsUnionSchemasAndPaths(): void
* List arrays are concatenated; scalars overwrite.
*
* @return void
* @spec openspec/specs/settings-service/spec.md#scenario-lists-concatenate-and-scalars-overwrite
*/
public function testListsConcatenateAndScalarsOverwrite(): void
{
Expand Down
8 changes: 4 additions & 4 deletions tests/Unit/Service/SbomRegisterShapeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private function schema(string $slug): array
* actually writes them to and the status endpoint reads them from.
*
* @return void
* @spec openspec/specs/sbom-import/spec.md#requirement-a-successful-import-records-provenance-on-the-version
* @spec openspec/specs/sbom-import/spec.md#scenario-a-successful-import-records-provenance-on-the-version
*/
public function testModuleVersieCarriesSbomProvenance(): void
{
Expand Down Expand Up @@ -117,7 +117,7 @@ public function testModuleVersieCarriesSbomProvenance(): void
* that shipped the silent-drop defect.
*
* @return void
* @spec openspec/specs/sbom-import/spec.md#requirement-a-successful-import-records-provenance-on-the-version
* @spec openspec/specs/sbom-import/spec.md#scenario-a-successful-import-records-provenance-on-the-version
*/
public function testOrganisatieDoesNotCarrySbomProvenance(): void
{
Expand All @@ -136,7 +136,7 @@ public function testOrganisatieDoesNotCarrySbomProvenance(): void
* rows without these fields stays non-destructive.
*
* @return void
* @spec openspec/specs/sbom-import/spec.md#requirement-existing-versions-are-unaffected-by-the-schema-addition
* @spec openspec/specs/sbom-import/spec.md#scenario-existing-versions-are-unaffected-by-the-schema-addition
*/
public function testSbomProvenanceIsOptional(): void
{
Expand All @@ -155,7 +155,7 @@ public function testSbomProvenanceIsOptional(): void
* voorzieningen register, so imported components have a home table.
*
* @return void
* @spec openspec/specs/sbom-import/spec.md#requirement-a-parsed-component-persists-with-its-moduleversie-relation
* @spec openspec/specs/sbom-import/spec.md#scenario-a-parsed-component-persists-with-its-moduleversie-relation
*/
public function testSbomComponentIsRegisteredInVoorzieningen(): void
{
Expand Down
Loading