spec: Pluggable integration registry umbrella + 20 leaves#1300
spec: Pluggable integration registry umbrella + 20 leaves#1300WilcoLouwerse merged 13 commits intodevelopmentfrom
Conversation
Introduces the IntegrationProvider contract, IntegrationRegistry service, three-stage visibility filter (registry → schema linkedTypes → component excludeIntegrations), four-surface widget model with graceful fallback, OCS capabilities exposure, per-integration RBAC, external routing via OpenConnector, and reference-property auto-rendering. Backwards-compatible with existing CnObjectSidebar and LinkedEntityService. TYPE_COLUMN_MAP marked deprecated; removal in follow-up cleanup leaf. Ships with: - proposal.md, design.md (19 architecture decisions) - tasks.md (implementation checklist) - specs/generic-integrations/spec.md (capability delta) - hydra.json (umbrella leaf plan)
…sing Five leaves whose backend services already exist (from nextcloud-entity-relations and prior Talk work) but have no sidebar/widget UI. All depend on pluggable-integration-registry. - integration-calendar (Meetings via CalDAV VEVENT) - integration-email (link-only; Mail owns compose) - integration-deck (Cards with sticky default board + mini-kanban) - integration-contacts (role-grouped, canonical person chip) - integration-talk (single provider routes Chat + Conversation) Each ships: provider, sidebar tab, 4-surface widget, registration, spec, tests, nl+en.
Twelve new NC-native integrations, each a greenfield backend + UI vertical slice depending on pluggable-integration-registry. - integration-forms (responses + form-mapping for auto-link) - integration-polls (lifecycle, tally, user vote highlight) - integration-bookmarks (delegates scraping to Bookmarks app) - integration-maps (cached lat/lon, address chip) - integration-collectives (markdown preview, inline detail-page) - integration-activity (introduces query-time storage strategy) - integration-photos (filtered view of Files, EXIF, GPS strip) - integration-flow (admin-gated, coexists with OR workflow engine) - integration-analytics (apexcharts, 5-min dashboard refresh) - integration-time-tracker (configurable backend, denormalized totals) - integration-shares (query-time aggregation, revoke action) - integration-cospend (project/bill link types, per-currency totals) activity and shares introduce the new 'query-time' storage strategy; umbrella coordination flagged in those leaves.
Two external-service integrations proving the storage='external' path through ExternalIntegrationRouter + OpenConnector sources. Introduces IntegrationProvider::getOpenConnectorSource() as a small umbrella extension. - integration-openproject (first external; OAuth2; WP status badges) - integration-xwiki (breadcrumb display, text-only preview — no macro execution in NC context) openproject lands first and proves the pattern; xwiki soft-depends on it to reuse the established routing + auth-status surfacing.
Wave 0.5 cleanup leaf. Removes LinkedEntityService::TYPE_COLUMN_MAP and Schema::VALID_LINKED_TYPES constants once umbrella is archived and built-in providers stabilise. Pre-removal grep sweep of the ConductionNL org required before the removal commit. Pure refactor — no behaviour change.
…lta format - design.md: renumber ADs sequentially 1-21 (were in chronological insert order with AD-4b/4c and out-of-order AD-12/AD-10 at the bottom). Update AD-21 companion ADR text to reference the now-authored hydra ADR-020. - design.md: resolve "Ten open questions" note — all have been folded into ADs. - specs/generic-integrations/spec.md: change `## Requirements` to `## ADDED Requirements` per OpenSpec delta convention (hydra/openspec/AGENTS.md). - hydra.json: drop non-schema fields (leaf_plan etc.); keep only v2 schema fields.
…rations Per hydra/openspec/AGENTS.md, changes contribute to a shared capability via specs/<domain>/spec.md deltas. The prior structure used per-leaf capability names (specs/integration-calendar/, specs/integration-email/, ...) which would have created 20 unrelated capabilities at archive time. Restructured so every leaf writes a delta against the shared `generic-integrations` capability. For each of the 20 leaves: - Move specs/integration-<id>/spec.md → specs/generic-integrations/spec.md - Convert `## Requirements` heading to `## ADDED Requirements` - Delete the old specs/integration-<id>/ directory Also per-leaf housekeeping: - hydra.json: drop non-schema fields (wave, wave_label, soft_depends_on, depends_on_reason, notes); keep only schema-v2 valid fields. - integration-xwiki: drop soft-dep on integration-openproject (both now parallel leaves depending only on the umbrella). - design.md: add "Cross-repo note" clarifying that `nextcloud-vue/src/...` paths are expected locations in a separate repo, not binding spec.
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 147/147 | |||
| npm | ✅ | ✅ 599/599 | |||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-04-22 06:04 UTC
Download the full PDF report from the workflow artifacts.
Critical review of the capability spec surfaced gaps that would have left leaves with ambiguous contracts. Added five new Requirements and improved scenarios on two existing ones: New Requirements: - Tags and Audit-Trail as First-Class Integrations — explicit spec for what AD-7 decided, including audit-trail's admin-only permission. - Registration Collision Handling — duplicate-id behavior on both sides (PHP DI container build failure; JS throw/warn). - Error-Handling Contract — typed exceptions + HTTP mapping so providers don't leak generic 500s. ProviderUnavailable/Auth/NotFound/Validation. - Pagination on List Endpoints — default 20, max 100, consistent total + hasMore in responses. - Migration of Existing Schemas — one-time auto-populate of linkedTypes=[files, notes, tasks, tags, audit-trail] for absent values, preserving the 5-tab user-visible behaviour across the upgrade. Added scenarios: - Widget Surfaces: widget render failure is isolated to the failing widget (other widgets keep rendering). - Reference-Property Auto-Rendering: broken references render a placeholder; reference to an uninstalled integration's NC app renders a "not installed" placeholder. Tightened Backwards Compatibility: the first scenario now correctly depends on the schema migration having run.
… leaf Every leaf gets an explicit "Graceful Degradation" requirement binding it to the umbrella's Error-Handling Contract and adding at least one leaf-specific scenario covering an edge case real deployments hit. Covered edge cases per leaf: - calendar: concurrent unlink vs external delete race - email: linked message purged from Mail trash - deck: archived card still accessible with badge - contacts: vCard deleted from address book - talk: conversation deleted in Talk - forms: form deleted while response links exist - polls: closed vs deleted distinction + both paths - bookmarks: URL 4xx/5xx on link check - maps: geocoding unavailable → click-placed fallback - collectives: per-user collective access revoked - activity: Activity app disabled mid-session - photos: HEIC without preview provider - flow: rule deleted while historical fires remain - analytics: empty dataset + chart config version mismatch - time-tracker: reconcile repairs total drift - shares: user lacks share-management permission - cospend: multi-currency no-aggregation constraint - openproject: rate limit via OpenConnector (429) - xwiki: page moved + macro-output sanitisation Cleanup leaf doesn't need this — it's a pure removal change.
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 147/147 | |||
| npm | ✅ | ✅ 599/599 | |||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-04-22 07:21 UTC
Download the full PDF report from the workflow artifacts.
…e-integration-registry # Conflicts: # openspec/changes/pluggable-integration-registry/design.md # openspec/changes/pluggable-integration-registry/proposal.md # openspec/changes/pluggable-integration-registry/tasks.md
|
🟢 Minor — Inconsistent use of 'Standard four surfaces' shorthand vs explicit surface list Several leaf specs use 'Standard four surfaces; detail-page surface mirrors tab.' (activity, cospend, maps, bookmarks, collectives, others) without listing the four surfaces explicitly. This is fine for implementers who have read the umbrella, but differs from leaves like calendar and talk which enumerate all four surfaces by name in dedicated scenarios. The inconsistency makes it harder to audit that each leaf has the same surface contract. Suggested: Either (a) standardise all leaves to use the shorthand and rely on the umbrella's surface definition, or (b) consider adding a single reference sentence: 'Standard four surfaces: user-dashboard, app-dashboard, detail-page, single-entity — per umbrella AD-6/AD-18.' Minor — no blocking concern. |
|
🟢 Minor — Trailing double-period in cross-repo note boilerplate Every leaf's design.md contains the line 'The frontend implementation PR lands in that separate repo and MAY choose different paths..' with a double period. This appears in all 19 leaves and the umbrella design.md. Suggested: Remove the duplicate period. One-liner fix across all design.md files. |
|
🟢 Minor — Individual leaf tasks exceed 15 per ADR-028 for some leaves Calendar (26 tasks), contacts (23 tasks), deck (25 tasks), openproject (24 tasks), talk (24 tasks) all exceed the 15-task ADR-028 limit, though none as severely as the umbrella (68). These are boundary cases — the tasks are genuinely scoped to one leaf — but the count is notable. Suggested: Review whether some tasks can be consolidated (e.g., 'DI-tag, routes, unit tests' as a single task rather than three sub-items). Alternatively, document a waiver for complex leaves. Lower priority than F-006. |
|
🟡 Concern — No CI check runs on this PR — unexplained in spec The PR has no reported CI check runs (anomalous per review checklist). For a spec-only PR (only Suggested fix: Add a |
WilcoLouwerse
left a comment
There was a problem hiding this comment.
Strict-mode review — 5 blockers, 7 concerns, 3 minors.
Top blockers:
- 🔴 storage strategy enum in umbrella interface missing 'query-t
- 🔴 OCS capabilities exposes sensitive authStatus to all logged-
- 🔴 Two broken spec cross-references — wrong sub-directory in pa
- 🔴 Umbrella spec cross-reference to nextcloud-entity-relations
- 🔴 getOpenConnectorSource() method required by openproject leaf
Strict mode requires REQUEST_CHANGES on any 🔴 or 🟡. Please address blockers and concerns before merge.
… concerns
Umbrella + leaves: address all 5 🔴 blockers and 6 🟡 concerns plus 3 🟢 minors
from the strict-mode review.
Blockers
- Storage strategy enum: extend to `'magic-column'|'link-table'|'external'|'query-time'`
in interface docblock, AD-22, and add a Storage Strategy Enum requirement with
scenarios (timeout/degraded surface, NotImplemented mutations, external rejecting
null OpenConnector source).
- OCS capabilities: role-redact `requiresPermission`/`authStatus`/`openConnectorSource`
to admins only; non-admins see `{id,label,group,enabled,surfaces}` only.
- Two broken cross-references (shares + openproject) — sub-directory paths corrected
to `generic-integrations/`.
- Umbrella spec cross-references — added missing `../` so paths resolve to
`openspec/specs/...` not `openspec/changes/specs/...`.
- `getOpenConnectorSource(): ?string` added to umbrella `IntegrationProvider`
interface + AbstractIntegrationProvider default + tasks.md.
Concerns
- ADR-028 task-cap waiver documented in proposal.md + tasks.md (umbrella) and as a
one-line preamble on each over-cap leaf (calendar, contacts, deck, openproject,
talk).
- Flow integration: `'admin'` permission semantics defined (NC admin group via
IGroupManager::isAdmin); two-gate model (app gate vs permission gate) with
scenarios; permission-string vocabulary documented in umbrella spec.
- Migration scenario added: schemas with linkedTypes referencing mid-rollout ids
(umbrella deployed, leaf pending) — stage-1 filter drops + admin notice.
- Query-time storage risks specced (AD-22): per-render timeout (default 2s),
degraded surface, blended-feed source-of-truth, filter-chip persistence note.
- CI parity rule: executable definition of "set" — non-null/non-undefined +
`typeof === 'function'`; scenarios for null and non-component values; hydra-repo
gate wiring called out as separate change.
- OpenConnector failure paths (AD-23): lazy auth check, OpenConnector-managed token
refresh, `details.cause` distinguishing connector-down / source-missing /
upstream-down.
Minors
- `Standard four surfaces` shorthand standardised to `Per umbrella AD-6/AD-18, the
widget SHALL render on all four surfaces (user-dashboard, app-dashboard,
detail-page, single-entity)` across leaves.
- Trailing double-period typo in cross-repo design boilerplate fixed across all
leaf design.md files.
Follow-up note added to proposal.md for the absent spec-validate CI workflow that
would have caught the broken cross-references at PR-time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🟢 Standard four surfaces shorthand — resolved in 500d5e0. Standardised every leaf's |
|
🟢 Trailing double-period typo — resolved in 500d5e0. Fixed across all 18 leaf |
|
🟢 Leaf tasks exceeding 15 — resolved in 500d5e0. Documented an ADR-028 task-cap waiver as a one-line preamble on each over-cap leaf:
Rationale per leaf: each is a single integration vertical slice (provider + sub-resource controller + tab + 4-surface widget + tests + nl/en); splitting it would force interleaved |
|
🟡 No CI runs on spec PR — acknowledged with a follow-up note in 500d5e0. Added a "CI follow-up — spec validation" section in the umbrella's
Two of the blockers in this review (broken cross-references in shares + openproject + umbrella) would have been caught by such a workflow. Deliberately not bundled into this umbrella to keep its scope on the integration registry itself; tracked separately. |
WilcoLouwerse
left a comment
There was a problem hiding this comment.
All 5 🔴 blockers and 6 🟡 concerns from the prior strict review are substantively resolved in 500d5e0 — not just acknowledged, but with new normative requirements + scenarios that strengthen the umbrella contract.
Blockers resolved:
- storage enum query-time — interface docblock + AD-22 + new Storage Strategy Enum requirement (mutation/timeout/null-source scenarios)
- OCS role-redaction — admins see sensitive block, non-admins get public block only; sensitive fields omitted (not
null-stubbed) - shares + openproject cross-refs — corrected to
generic-integrations/ - umbrella cross-refs — gained the missing
../ - getOpenConnectorSource() — added to interface + Abstract default + tasks.md
Concerns resolved:
- ADR-028 task-cap waiver — option (b) chosen, waiver documented in proposal.md + per-leaf preambles
- Flow admin semantics —
IGroupManager::isAdmin($userId), two-gate model, 3 new scenarios - Migration mid-rollout ids — new scenario covering deployment-timing gap
- query-time risks — AD-22 with 2s timeout, degraded surface, blended-feed source-of-truth
- parity rule executable definition —
typeof === 'function', null/non-component scenarios - OpenConnector failure paths — AD-23 + External Provider Failure Modes requirement (lazy auth, distinct
details.causecodes)
Plus: shorthand standardised, typo fixed, spec-validate CI follow-up noted. Newman ✓ green; no failing required checks.
Looks good for merge — the umbrella contract is materially stronger than at first review.
Summary
Spec-only PR — no code yet. Proposes a pluggable integration registry as the canonical mechanism for declaring "things linked to an OR object" (files, notes, tasks, calendar, mail, deck, contacts, talk, + new NC-native + external-via-OpenConnector). Decouples integration definitions from OR core and
@conduction/nextcloud-vue.Backwards-compatible with existing CnObjectSidebar and schemas.
Contents
Review focus
Please critique the umbrella first — its contract constrains every leaf.
Key decisions to push back on:
Leaf-level review can focus on correctness of individual contract fills; umbrella-level concerns should block first.
Test plan