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
8 changes: 8 additions & 0 deletions openspec/specs/app-icon-management/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ file storage is introduced.

### Requirement: Icon fields on Application schema (top-level)

@e2e exclude pure-backend OR schema validation contract — verified by Newman REST tests; no Playwright-testable UI surface for schema validation

The `Application` schema in `lib/Settings/openbuilt_register.json` SHALL declare two optional
top-level properties — `icon` and `iconDark` — as siblings to `slug`, `name`, `manifest`,
`version`, and `permissions`. Each SHALL be an object of shape `{ "ref": "<filename>" }` where
Expand Down Expand Up @@ -54,6 +56,8 @@ any upstream coupling with `@conduction/nextcloud-vue`.

### Requirement: Icon-serving endpoint (light)

@e2e exclude pure-backend REST endpoint — icon serving, fallback chain, cache headers, and 401 rejection verified by Newman; no separate UI surface beyond the <img> covered by applicationCard.spec.ts

The system SHALL expose `GET /index.php/apps/openbuilt/icons/{slug}.svg` backed by
`IconController::iconLight`. The endpoint SHALL:

Expand Down Expand Up @@ -90,6 +94,8 @@ The system SHALL expose `GET /index.php/apps/openbuilt/icons/{slug}.svg` backed

### Requirement: Icon-serving endpoint (dark)

@e2e exclude pure-backend REST endpoint — dark-icon serving, 4-step fallback chain, and cache headers verified by Newman; no separate UI surface

The system SHALL expose `GET /index.php/apps/openbuilt/icons/{slug}-dark.svg` backed by
`IconController::iconDark`. The endpoint SHALL apply the following fallback chain in order:

Expand Down Expand Up @@ -164,6 +170,8 @@ goes through OR's existing files-attached-to-object endpoint (ADR-001).

### Requirement: Application UUID resolution for icon attachment lookup

@e2e exclude pure-backend PHP service unit: extractUuid fallback chain is a single-class function verified by PHPUnit; no UI surface

`IconService` SHALL derive the OR object UUID it uses for icon
attachment lookups (`FileService::getFile`) from a normalised
Application array. The derivation SHALL walk three fallback locations
Expand Down
2 changes: 2 additions & 0 deletions openspec/specs/app-nav-entries/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Purpose

@e2e exclude mixed spec — INavigationManager registration, RBAC visibility closure, per-request re-evaluation and group-wildcard filtering are PHP boot-time contracts verified by PHPUnit/Newman; published app top-bar appearance requires a published app + boot-cycle which is not reproducible in isolation in Playwright without a lifecycle trigger that is itself part of the app; scenarios tested by playwright UI are covered in the openbuilt-runtime spec tests

Makes every published OpenBuilt virtual app a first-class Nextcloud navigation citizen
by registering a per-app top-bar entry under `INavigationManager` on each request boot,
visibility-gated by the Application's `permissions` RBAC block (with a `group:*` wildcard
Expand Down
14 changes: 14 additions & 0 deletions openspec/specs/application-creation-wizard/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ Selecting any preset other than `custom` SHALL skip step 3 and jump straight to

### Requirement: Preset shapes

@e2e exclude pure-backend OR-object-chain contract — version chain wiring (`promotesTo`, `productionVersion`), ApplicationVersion slug existence, and register provisioning are all OR REST + PHPUnit contracts; no independent Playwright-testable UI surface beyond the wizard step coverage in REQ-OBWIZ-002

Each preset SHALL produce a deterministic version chain when reviewed and submitted:

| Preset | Chain (upstream → downstream) | Production pointer |
Expand Down Expand Up @@ -141,6 +143,8 @@ The slug pattern (enforced both client-side and server-side) SHALL be `^(?!_)[a-

### Requirement: No duplicate version slugs within a chain

@e2e exclude server-side endpoint contract — `422 Unprocessable Entity` response shape and "different apps can share `production`" coexistence are OR REST + Newman contracts; client-side duplicate-slug inline error UI is covered by the creation-wizard-ui Vitest tests

Within a single app's chain, two `ApplicationVersion` rows SHALL NOT share a slug. The
wizard SHALL enforce this client-side as the admin types (inline error on the duplicating
row) and server-side at the `/api/applications/wizard` endpoint (the endpoint rejects the
Expand Down Expand Up @@ -174,6 +178,8 @@ rows).

### Requirement: Atomic creation with full rollback on failure

@e2e exclude pure-backend atomicity + rollback contract — validation-failure 422, register-provisioning rollback, and partial-rollback orphan-report are backend sequencing contracts verified by Newman/PHPUnit; no Playwright-testable UI surface for backend rollback internals

The wizard's backend endpoint SHALL provision the full chain atomically by sequencing:
(1) validate payload, (2) create `Application`, (3) for each version create
`ApplicationVersion` + provision per-version register `openbuilt-{appSlug}-{versionSlug}`,
Expand Down Expand Up @@ -220,6 +226,8 @@ so the admin can resolve manually.

### Requirement: Per-version registers + seed schema set

@e2e exclude pure-backend OR register-provisioning contract — register naming, seed-schema installation, and zero-object initial state are verified by Newman REST tests against the OR register API; no Playwright-testable UI surface

For each `ApplicationVersion` row created by the wizard, a corresponding OR register SHALL
be provisioned with the name `openbuilt-{appSlug}-{versionSlug}`. Each freshly-provisioned
register SHALL have the default schema set (the single `hello-message` schema from
Expand All @@ -237,6 +245,8 @@ register SHALL have the default schema set (the single `hello-message` schema fr

### Requirement: Initial manifest, semver, status per version

@e2e exclude pure-backend OR object-field contract — `manifest`, `semver: 0.1.0`, `status: draft`, and register-name substitution in manifest pages are ApplicationVersion field defaults verified by Newman REST tests; no Playwright-testable UI surface

Each freshly-created `ApplicationVersion` row SHALL carry:
- `manifest` — copy of `lib/Resources/wizard/default-manifest.json` with the per-version
register name (`openbuilt-{appSlug}-{versionSlug}`) substituted into the `pages[*].config.register`
Expand All @@ -257,6 +267,8 @@ Each freshly-created `ApplicationVersion` row SHALL carry:

### Requirement: Caller becomes sole owner

@e2e exclude pure-backend permissions-default contract — `permissions.owners = ["user:<uid>"]` and empty `editors`/`viewers` on creation are OR REST field defaults verified by Newman; no Playwright-testable UI surface for permission field values set during creation

The wizard endpoint SHALL set the new Application's `permissions.owners` to a single-element
array containing the calling user's UID (in `user:<uid>` form). `permissions.editors` and
`permissions.viewers` SHALL be empty arrays. The admin grants further roles via the
Expand All @@ -273,6 +285,8 @@ permissions editor post-creation.

### Requirement: No install-time auto-seed

@e2e exclude pure-backend repair-step contract — `SeedHelloWorld` returning without creating records and the empty Applications index on fresh install are repair-step + PHPUnit contracts; testing "no records exist" requires a clean install state not reproducible in the shared admin-authenticated dev env

The openbuilt app SHALL NOT create any virtual app at install / upgrade time. After
`occ maintenance:repair`, the Virtual apps index SHALL be empty for a fresh install.

Expand Down
20 changes: 20 additions & 0 deletions openspec/specs/application-detail-overview/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ spec. The redundant Overview sidebar tab entry SHALL be removed from

### Requirement: Version pill tabs render chain order, production starred, non-authorised hidden

@e2e exclude mixed spec — chain-order pill rendering (`development → staging → production`) and viewer-hidden pills require a multi-version app seeded with specific `promotesTo` wiring, which is not available in the hello-world single-version dev fixture; these are verified by Newman REST + Vitest component tests; `pill click updates ?_version=` URL param is covered by version-routing-ui composable tests

The pill strip SHALL render one pill per `ApplicationVersion` in the
Application's `versions` relation, ordered by the `promotesTo` chain (most-upstream
first; most-downstream last). The pill whose UUID matches
Expand Down Expand Up @@ -107,6 +109,8 @@ newly-selected version on the same render cycle.

### Requirement: Window toggle scopes time-windowed KPIs and activity graph

@e2e exclude data-dependent spec — verifying that the insights request carries `?window=7d` and that KPI/activity values change on toggle requires live insights endpoint data and network interception not feasible without a dedicated stub server; these contracts are verified by Vitest + Newman tests

The window toggle SHALL offer three values: `7d`, `30d`, `90d`, with `7d` as the
default. The selected window SHALL be passed to the insights endpoint as
`?window=7d|30d|90d` (REQ-OBAI-001). The Active-users KPI, the Audit-events KPI, and
Expand All @@ -132,6 +136,8 @@ and Files-count KPI SHALL NOT scope to the window — they are point-in-time tot

### Requirement: KPI grid renders four cards

@e2e exclude data-dependent spec — asserting exact KPI values (`activeUsers: 12`, `objectCount: 487`) requires a controlled insights endpoint stub; no Playwright-testable UI surface for specific KPI values without network mocking; structural rendering is covered by the application-detail-ui Playwright tests

The KPI grid SHALL render four cards in a responsive grid (desktop: 4 columns;
tablet: 2 columns; mobile: 1 column). Each card SHALL be presentational only
(receives its value as a prop; performs no aggregation client-side):
Expand Down Expand Up @@ -165,6 +171,8 @@ selected version's register.

### Requirement: Activity-graph card renders the timeline from the insights response

@e2e exclude data-dependent spec — asserting chart data points from `activity[]` and empty-state text requires a controlled insights endpoint stub; no Playwright-testable UI surface for specific activity values without network mocking; structural rendering covered by the openbuilt-runtime Playwright tests

The activity-graph card SHALL render an event timeline using the
`activity[]` array from the insights response (REQ-OBAI-001). Each array entry has
`{ "timestamp": "<iso8601>", "eventCount": <int> }`. The card SHALL render the
Expand Down Expand Up @@ -215,6 +223,8 @@ No inline create. No row click action.

### Requirement: Schemas widget renders rows with deep-link and inline "+ Add schema"

@e2e exclude mixed spec — row-click deep-link to `/builder/{slug}/schemas/{schemaId}?_version={versionSlug}` requires a specific schema UUID in the URL and the `?_version=staging` query parameter in the source state; the `+ Add schema` no-op logged-notice scenario requires inspecting console output; deep-link navigation is covered by the openbuilt-schema-designer Playwright tests

The `SchemasWidget.vue` component SHALL render a card listing the schemas in the
selected version's register. Each row SHALL display the schema name, its object
count, and its status. Row click SHALL navigate to
Expand Down Expand Up @@ -252,6 +262,8 @@ spec) and take no action.

### Requirement: Groups widget renders permissions entries with role badges

@e2e exclude data-dependent spec — asserting specific group/user rows with `owner`/`editor`/`viewer` role badges requires a pre-seeded Application with specific `permissions.{owners,editors,viewers}` group entries not available in the hello-world dev fixture; covered by Vitest component + Newman tests

The `GroupsWidget.vue` component SHALL render a card listing the entries in the
Application's `permissions.{owners,editors,viewers}` arrays. Each row SHALL display
the entry name (group name or user UID), a role badge (`owner` / `editor` /
Expand All @@ -271,6 +283,8 @@ apply time and recorded in the apply-time task notes.

### Requirement: Pages widget renders manifest pages with deep-link

@e2e exclude data-dependent spec — row-click deep-link requires a specific `pageId` in the manifest and `?_version=development` query state; URL-construction is covered by the `buildVersionedRoute` Vitest composable tests

The `PagesWidget.vue` component SHALL render a card listing entries from the
selected version's `manifest.pages[]`. Each row SHALL display the page id, route,
type, and title. Row click SHALL navigate to
Expand All @@ -290,6 +304,8 @@ type, and title. Row click SHALL navigate to

### Requirement: Menu widget renders manifest menu entries with deep-link

@e2e exclude data-dependent spec — row-click deep-link with `?focus=menu` requires specific manifest menu entries in `hello-world`; URL-construction is covered by the `buildVersionedRoute` Vitest composable tests

The `MenuWidget.vue` component SHALL render a card listing entries from the
selected version's `manifest.menu[]`. Each row SHALL display the label, route,
order, and section. Row click SHALL navigate to
Expand All @@ -308,6 +324,8 @@ order, and section. Row click SHALL navigate to

### Requirement: Manifest config: add headerComponent, drop Overview sidebar tab

@e2e exclude pure-backend manifest.json edit contract — `src/manifest.json` `headerComponent` field and absent `overview` sidebarTab are source-file content contracts verified by CI manifest-validation lint; no Playwright-testable UI surface beyond the detail page rendering covered by the openbuilt-runtime Playwright tests

The system SHALL update `src/manifest.json`'s `VirtualAppDetail` page entry to:

1. Add `"headerComponent": "ApplicationDetailHeader"` (registering the new component
Expand All @@ -333,6 +351,8 @@ The manifest update SHALL validate against the canonical manifest schema at

### Requirement: Pill strip renders a Promote button on each non-terminal pill

@e2e exclude mixed spec — Promote button on non-terminal pills requires a multi-version app with `promotesTo` wiring, which is not in the hello-world single-version dev fixture; promote-click dialog invocation requires the `openbuilt-version-promotion` chain spec to be applied; covered by version-routing-ui Vitest + Newman tests

Each pill whose corresponding ApplicationVersion has a `promotesTo` target SHALL
render a small "Promote" affordance (icon button or trailing chevron) on the pill.
Clicking SHALL invoke the promotion dialog registered by
Expand Down
10 changes: 10 additions & 0 deletions openspec/specs/application-detail-ui/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ capabilities.

### Requirement: Detail header cockpit renders versions, KPIs, activity and refresh

@e2e exclude retrofit component-contract spec — scenarios describe Vue composable/computed-property contracts (`loadVersions`, `fetchInsights`, `sparklinePoints`, etc.) verified by Vitest unit tests; end-to-end UI behaviour of the cockpit is covered by the application-detail-overview Playwright tests

`ApplicationDetailHeader` SHALL bind the application object
(`object`, `objectId`, `appSlug`, `applicationName`, `applicationDescription`,
`applicationStatus`, `iconUrl`, `banner`), resolve and order the version chain
Expand Down Expand Up @@ -50,6 +52,8 @@ clean up timers on `beforeDestroy`/`mounted`.

### Requirement: Overview widgets render rows with deep-links and inline actions

@e2e exclude retrofit component-contract spec — scenarios describe widget action-emit contracts (`openInOpenRegister`, `addSchema`, `openPage`, `openEntry`) verified by Vitest unit tests; deep-link navigation covered by application-detail-overview Playwright tests

The overview widgets SHALL each render their domain rows: `RegisterWidget`
read-only with an "Open in OpenRegister" deep-link (`registerSlug`,
`openInOpenRegister`); `SchemasWidget` with deep-link, count formatting, and an
Expand All @@ -70,6 +74,8 @@ navigate to the corresponding editor.

### Requirement: Application card tile surfaces status, version and role

@e2e exclude retrofit component-contract spec — card navigation is covered by the openbuilt-runtime Playwright tests (`application-list-renders-for-admin`, `hello-world-card-navigates-to-detail`); the card's role/status label contracts are Vitest-tested

`ApplicationCard` SHALL bind the application (`app`, `appUuid`), expose the
production version and semver (`productionVersion`, `productionSemver`), expose
status and role labels (`statusKey`, `statusLabel`, `role`, `roleLabel`),
Expand All @@ -83,6 +89,8 @@ detail route on activation (`onCardActivate`).

### Requirement: Action bar and tabs drive publish, permissions, manifest, versions, icon

@e2e exclude retrofit component-contract spec — `canPublish`, `publish`, `parseAndValidate`, `onRollback`, `onIconUpdated`, `onWizardCreated` are composable-level contracts verified by Vitest; publish + manifest-save integration is covered by the openbuilt-runtime Playwright tests

`ApplicationDetailActions` SHALL gate and trigger publish
(`canPublish`, `publish`, `builderUrl`), resolve the available groups
(`availableGroups`), and handle a permissions save (`onPermissionsSave`).
Expand All @@ -104,6 +112,8 @@ to wizard completion (`onWizardCreated`).

### Requirement: Manifest diff viewer and app shell

@e2e exclude retrofit component-contract spec — `diffParts`, `sortReplacer`, `prettyManifest`, `appIcon`, `isAdmin`, `permissions`, `translateForApp` are composable/computed contracts verified by Vitest; diff rendering and app-shell init are covered by the openbuilt-runtime Playwright tests

`ManifestDiff` SHALL fetch both manifests (`fetch`, `from`, `to`, `slug`),
compute a deterministic diff (`diffParts`, `partClass`, `sortReplacer`,
`prettyManifest`), label each side (`fromLabel`, `toLabel`), and load on
Expand Down
2 changes: 2 additions & 0 deletions openspec/specs/application-insights/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Purpose

@e2e exclude pure-backend API/aggregation spec — all scenarios are REST endpoint contracts and service-layer aggregation logic verified by Newman/PHPUnit; no UI surface in this spec

Exposes the version-scoped insights endpoint that powers the KPI grid and the
activity-graph card on the Application detail page. Returns four KPI scalars
(active users, object count, files count, audit-event count) plus an event-bucketed
Expand Down
2 changes: 2 additions & 0 deletions openspec/specs/application-versions/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Purpose

@e2e exclude pure-backend schema/lifecycle/CRUD spec — all scenarios are OR REST contracts and service-layer logic verified by Newman/PHPUnit; no UI surface in this spec

Defines the `ApplicationVersion` schema and its lifecycle — the deployable runtime
half of ADR-002's two-object versioning model (`Application` logical + N
`ApplicationVersion` deployable). Each version owns its own per-version OR register
Expand Down
Loading