Skip to content

spec(leaf): app-leaf-provider-registration — sibling apps register render/data leaves into OpenRegister#2102

Open
rubenvdlinde wants to merge 1 commit into
developmentfrom
spec/app-leaf-provider-registration
Open

spec(leaf): app-leaf-provider-registration — sibling apps register render/data leaves into OpenRegister#2102
rubenvdlinde wants to merge 1 commit into
developmentfrom
spec/app-leaf-provider-registration

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Proposal only — do NOT merge without human review

This is an OpenSpec change proposal (no PHP/JS implementation). It designs the umbrella "apps hook themselves into OpenRegister" system: a sanctioned server-side seam so any sibling Nextcloud app can register itself as a leaf — contributing render surfaces (tab/widget on an object) and data (notes/records backed by the app's own store) — cleanly and generically.

The gap it fills

  • PHP: IntegrationProvider is already a rich full-CRUD data-provider contract, but every provider registers inside OpenRegister's own bootBuiltinIntegrationProviders(). There is no event/hook for a sibling app to contribute one, and the storage-strategy enum has no value for data living in another NC app's store. (ADR-041 records this exact limitation.)
  • JS: the cross-app registerIntegration() + window.OCA.OpenRegister.integrations stub-queue already exists in @conduction/nextcloud-vue — but it is unspecified in OpenRegister and invisible to the server.

The design

  • RegisterLeafProvidersEvent — a typed IEventDispatcher collect-event mirroring RegisterMcpToolProvidersEvent. Sibling apps write one listener; dispatched once, lazily; a throwing listener costs only its own leaf.
  • LeafDescriptor — server declaration of availability + capability metadata (id, label, icon, requiredApp, surfaces, referenceType, requiresPermission, kinds). No Vue components; render stays on the JS layer under the same id.
  • app-local storage strategy — data lives in the contributing app's own store; read + optional write served by the provider's own methods in the app's DI context. This is the generalisation of the built-in files/notes/calendar leaves and the concrete shape of "apps offering notes to OpenRegister."
  • Discovery + parity — descriptors surfaced via OCS capabilities; ADR-019 tab+widget parity preserved and extended cross-app; ADR-013 first-wins on collision.

Layer decision

Both layers, correlated by a shared id: server owns discovery + data + capability declaration; JS (already built) owns render-component registration.

ADR alignment

Strictly render-and-read only — no command path. Cross-app commands remain ADR-041 typed events. This change deliberately lifts the ADR-041 moratorium ("do not extend the OR registry with cross-app contribution until decided") for the collect/render/read case — so it needs a companion org-wide ADR in hydra/openspec/architecture/ and human ratification before implementation. (Recommended in design.md; not authored here.)

Out of scope / cross-refs

Command invocation (ADR-041 events); the hermiq agent-leaf consumer (separate change — only the agent-runner kind name is reserved here); the manifest type: agent action (nextcloud-vue change); IReferenceProvider convergence (deferred to the companion ADR).

Contents

  • proposal.md, design.md, tasks.md
  • Spec deltas (3 capabilities): leaf-provider-registration, leaf-data-providers, leaf-discovery-parity
  • openspec validate app-leaf-provider-registration --strictvalid

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 609fbcc

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 174/174
npm ✅ 555/555
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-07-25 08:13 UTC

Download the full PDF report from the workflow artifacts.

rubenvdlinde added a commit that referenced this pull request Jul 25, 2026
)

Add RegisterLeafProvidersEvent — a typed collect-event mirroring
RegisterMcpToolProvidersEvent — so a sibling Nextcloud app can register
itself as a leaf on OpenRegister objects (render surface + app-local
read/append data) by writing one IEventListener. Render-and-read only by
construction: descriptor and provider carry no verb; cross-app commands
stay ADR-041 typed events.

- LeafDescriptor value object: id, label, icon, requiredApp, group,
  surfaces, referenceType, requiresPermission, kinds set
  {render-surface, data-provider, agent-runner (reserved)}.
- LeafRegistry: dispatches the event once lazily on first read, validates
  (non-empty/known kinds, kebab-case id, data-provider-requires-provider),
  first-wins on duplicate id (ADR-013), swallows a throwing listener so it
  costs only its own leaf, and lands data-provider leaves on the shared
  IntegrationRegistry via a lazy loader hook so existing per-object routing
  reaches them unchanged.
- app-local storage strategy documented on the IntegrationProvider
  contract: data lives in the sibling app's own store; OpenRegister routes
  list()/optional create() and persists nothing.
- Discovery: openregister.integrations.leaves OCS capabilities block
  (id, label, requiredApp, surfaces, kinds, usability) — discoverable
  without loading any leaf app's JS.
- Tests: 22 PHPUnit tests (event registration, app-local list/create,
  kinds, first-wins collision, throwing-listener isolation, OCS usability,
  render-and-read boundary).
- Docs: docs/Integrations/leaf-system.md cross-app section + worked notes
  leaf example, cross-referencing ADR-019/041/013/066.

Spec: openspec/changes/app-leaf-provider-registration (PR #2102).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant