Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

feat(openspec): 41 IntraVox-derived spec proposals (foundation → glue)#96

Closed
rubenvdlinde wants to merge 5 commits into
developmentfrom
feature/intravox-derived-spec-proposals
Closed

feat(openspec): 41 IntraVox-derived spec proposals (foundation → glue)#96
rubenvdlinde wants to merge 5 commits into
developmentfrom
feature/intravox-derived-spec-proposals

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Summary

41 OpenSpec change proposals derived from a clean-room behavioural analysis of the IntraVox Nextcloud app, reframed under the unified-dashboard model — IntraVox features land as widgets/extensions on existing MyDash dashboards rather than as a parallel "page" concept.

Each change folder contains: proposal.md + tasks.md + specs/<cap>/spec.md. All 41 pass openspec validate --strict.

This PR is specs-only — no implementation. Implementation will follow in dependency-aware waves once the proposals are reviewed.

Capability inventory

Foundation (3) — everything else depends on these:

  • groupfolder-storage-backend — abstract dashboard CONTENT storage so it CAN live as JSON in a managed GroupFolder
  • dashboard-tree — parent/child hierarchy on dashboards, slugs, breadcrumbs (delta to dashboards, REQ-DASH-011..018)
  • dashboard-metadata-fields — admin-defined custom fields per dashboard

Lifecycle (5):

  • dashboard-templates — gallery + save-as-template (delta to admin-templates, REQ-TMPL-012..015)
  • dashboard-locking — concurrent-edit guard with admin override
  • dashboard-versioning — version history + restore (delegates to NC Files versioning when on GroupFolder)
  • dashboard-draft-published — draft / published / scheduled workflow (delta to dashboards, REQ-DASH-019..025)
  • dashboard-language-content — per-language content variants (delta to dashboards, REQ-DASH-026..032)

Engagement (4):

  • dashboard-comments — threaded one-level-deep via NC ICommentsManager
  • dashboard-reactions — emoji reactions with admin allow-list
  • dashboard-rss-feeds — per-user RSS token; ACL-respecting public feed
  • dashboard-public-share — anonymous render with optional password

Widget extensions (4) — each is a NEW capability since the parent widget isn't yet promoted to openspec/specs/:

  • text-widget-markdown, text-widget-tables, image-widget-media-picker, link-button-widget-list-mode

New widgets (10):

  • calendar-widget, people-widget, news-widget, files-widget, video-widget, divider-widget, links-widget, header-widget, menu-widget, quicklinks-widget

Org UX (5):

  • footer-customization, nc-unified-search-integration, navigation-editor-org, demo-data-showcases, admin-roles

Maintenance (5):

  • dashboard-export-import, confluence-html-import, dashboard-view-analytics, dashboard-bulk-operations, orphaned-data-cleanup

Glue (5):

  • setup-wizard, cli-commands, background-job-feed-refresh, activity-feed-integration, dashboard-cascade-events

Coordination

  • REQ numbering deltas to existing dashboards capability are pre-allocated to avoid clash: dashboard-tree REQ-DASH-011..018, dashboard-draft-published 019..025, dashboard-language-content 026..032.
  • Several glue specs reference siblings explicitly — e.g. news-widget reads from the cache populated by background-job-feed-refresh; dashboard-cascade-events defines DashboardDeletedEvent and the listener pattern that every dependent-data capability hooks into.

Decisions baked in

  • Decision A (GroupFolders): dashboard CONTENT can live as JSON in a managed GroupFolder. Adopted as an opt-in storage backend (mydash.content_storage = groupfolder) — DB-backed remains the default. See groupfolder-storage-backend.
  • Decision B (page ↔ dashboard): dashboards ARE pages. IntraVox-style "header row", "menu", "quicklinks" become widgets on a dashboard rather than a parallel "page" concept.
  • Decision C (scope-trim): LMS / OIDC token bridge / MetaVox import / license + telemetry from the source app are out of scope and not specced.

Hygiene

  • No vendor names ("intravox", "intra", "voxcloud") appear in any spec — capabilities are described in our own neutral terms.
  • 123 files, ~12K lines of spec across the 41 changes.
  • Each spec has 5–12 requirements with 3–5 GIVEN/WHEN/THEN scenarios per requirement.

Test plan

  • cd mydash && openspec validate <slug> --strict passes for each of the 41 slugs (already verified at author time).
  • Spot-check 3–4 specs for correctness in modelling vs. the IntraVox source (analysis doc lives at mydash/docs/intravox-analysis.md, gitignored).
  • Confirm the 41 capability names are acceptable; rename now if any feel off.
  • Approve the dependency-aware wave plan for the implementation phase (foundation → lifecycle → engagement → widget extensions → widgets → org UX → maintenance → glue).

…glue)

41 OpenSpec change proposals derived from a clean-room behavioural analysis
of the IntraVox Nextcloud app, reframed under the unified-dashboard model
(IntraVox features land as widgets/extensions on existing MyDash dashboards
rather than as a parallel "page" concept).

Each change folder contains: proposal.md + tasks.md + specs/<cap>/spec.md.
All 41 pass `openspec validate --strict`.

Foundation (3): groupfolder-storage-backend, dashboard-tree,
dashboard-metadata-fields.

Lifecycle (5): dashboard-templates, dashboard-locking, dashboard-versioning,
dashboard-draft-published, dashboard-language-content.

Engagement (4): dashboard-comments, dashboard-reactions, dashboard-rss-feeds,
dashboard-public-share.

Widget extensions (4): text-widget-markdown, text-widget-tables,
image-widget-media-picker, link-button-widget-list-mode.

New widgets (10): calendar-widget, people-widget, news-widget, files-widget,
video-widget, divider-widget, links-widget, header-widget, menu-widget,
quicklinks-widget.

Org UX (5): footer-customization, nc-unified-search-integration,
navigation-editor-org, demo-data-showcases, admin-roles.

Maintenance (5): dashboard-export-import, confluence-html-import,
dashboard-view-analytics, dashboard-bulk-operations, orphaned-data-cleanup.

Glue (5): setup-wizard, cli-commands, background-job-feed-refresh,
activity-feed-integration, dashboard-cascade-events.

No vendor names ("intravox", "intra", "voxcloud") appear in any spec —
capabilities are described in our own neutral terms.

123 files, ~12K lines of spec across the 41 changes.
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ cb01b01

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

Quality workflow — 2026-05-01 15:55 UTC

Download the full PDF report from the workflow artifacts.

Each of the 9 specs flagged HIGH in the triage now has a sibling
design.md that records the IntraVox source-derived decisions, evidence,
and remaining open follow-ups. Six of those nine specs needed material
rewrites where the original assumptions turned out to be wrong.

Specs rewritten (6):

* dashboard-locking — no clientId column, no expiresAt column
  (uses updated_at + LOCK_TIMEOUT_MINUTES = 15), same-user acquire
  is re-entrant, heartbeat is PUT not POST, admin-override is
  force-release not force-acquire, audit via PSR logger not Activity.

* people-widget — field names corrected (jobTitle→role,
  department→organisation), birthdate is ISO string not int,
  pagination is offset+hasMore not cursor, scope-filtering downgraded
  from MUST to SHOULD with v1 follow-up note (source has none),
  showFields map replaces cardFields array, columns config added.

* confluence-html-import — index.html is NOT the hierarchy source
  (breadcrumbs inside each page are), body selector is a 6-element
  XPath waterfall, macros render as rich widgets (panel/code/expand)
  not plain placeholders, and link rewriting / image upload / dry-run
  / async / CLI are now explicitly annotated as MyDash additions
  rather than implied as ports.

* navigation-editor-org — storage is per-language JSON files on the
  Nextcloud filesystem (5 MB cap), not a 64 KB app-config blob. v1
  ships nl + en as independent trees with ?lang= query param on
  GET/PUT (default nl). groupVisibility annotated as MyDash addition
  (the source uses GroupFolder ACL).

* demo-data-showcases — bundled as ZIP archives (showcases/{id}/{id}.zip
  containing export.json + per-locale page directories + _media/),
  not flat JSON. Real showcase IDs (de-bron, de-linden, gemeente-duin,
  horizon-labs, van-der-berg) replace fictional placeholders. NL-only
  in v1. Widget schema is row-based (column/order ints + inline fields),
  not position{x,y,w,h}+config. --force flag added to install CLI.

* dashboard-cascade-events — REQ-CSC-007 rewritten from
  failure-recording table to log-and-continue (the source has no
  failure table; orphan-cleanup picks up stragglers). NOTE annotations
  added throughout marking which listeners are reference-derived
  (only the comments listener) vs MyDash additions (the other 9 +
  TreeListener + GroupDeletedListener).

Specs that mostly held up (3, design.md only):

* groupfolder-storage-backend — chose hard-fail over dual-read because
  the source app's DB index has zero content column; dual-read is
  architecturally impossible.

* dashboard-language-content — separate-row design correct, but the
  three-tier locale matching collapses to two-step because Nextcloud
  stores nl_NL (underscore) and the source always truncates to two
  characters before lookup.

* calendar-widget — server-side RRULE expansion confirmed,
  cross-source dedup intentionally avoided (source hides NC ICS
  subscriptions from the picker), SSRF guard via DNS check rather
  than hostname allow-list.

All 41 changes still pass openspec validate --strict.
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ 0a70bbb

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

Quality workflow — 2026-05-01 17:19 UTC

Download the full PDF report from the workflow artifacts.

Each of the 10 specs flagged MEDIUM in the triage now has a sibling
design.md that records the source-derived decisions, evidence, and
recommended spec deltas. Eight of the ten resolved a concrete behavioural
question against the source app; two are deliberate divergences where
MyDash improves on the reference.

Concrete behaviour calls made:

* dashboard-public-share — Throttle is IP-global across all share
  actions (matches source), with two action buckets:
  mydash_share_access (60/60s) and mydash_share_password (10/60s).
  Returns HTTP 429 on trip.

* dashboard-bulk-operations — Hard delete (matches source) but
  cascade=false is the default. Parent-with-children + cascade=false
  returns HTTP 409. Aligns with dashboard-tree's existing opt-in
  cascade convention. Soft-delete deferred to a future capability.

* background-job-feed-refresh — SimpleXMLElement (PHP built-in) with
  LIBXML_NOCDATA | LIBXML_NONET, not SimplePie. Wrapped behind a
  FeedParserInterface so SimplePie remains a drop-in upgrade if
  customers report parsing edge cases. Cache TTL 60min (vs source's
  15min). 10 MB response cap.

Deliberate divergences from the source (kept on MyDash track):

* dashboard-templates — Templates remain type='admin_template' rows
  in oc_mydash_dashboards (existing admin-templates capability).
  Source uses filesystem folders under _templates/ per language;
  rejected because (1) admin-templates is already shipped and
  switching breaks existing REQ-TMPL-001..011, (2) DB enum is a
  single indexed query vs filesystem path-prefix scan, (3) MyDash
  has two storage backends (db / groupfolder) and filesystem-only
  doesn't fit cross-backend.

* dashboard-view-analytics — Daily-rotating salt for unique-viewer
  dedup (rotated by SaltRotationJob at UTC midnight, no historical
  retention), not the source's static config.secret hash. Privacy
  improvement: makes cross-day re-identification computationally
  infeasible if the analytics table leaks.

Annotation passes (MyDash design beyond source):

* admin-roles — Source has no role concept (only GroupFolder ACL
  bitmask). MyDash's Admin/Editor/Viewer roles are net-new, stored
  in oc_mydash_role_assignments table. Effective-role resolution:
  NC admin always wins; otherwise direct user assignment overrides
  group assignment; max-privilege wins among multiple group hits.

* setup-wizard — Source ships CLI-only (occ intravox:setup); the
  7-step frontend wizard is entirely a MyDash addition. Each step
  embeds the corresponding sibling capability's admin UI rather
  than duplicating settings surfaces.

* activity-feed-integration — 13-event catalogue: 5 source-derived
  (dashboard_created/updated/deleted/commented/reacted) + 8 MyDash
  additions emerging from sibling capabilities (publication,
  sharing, public-share, versioning, locking, roles). page_viewed
  intentionally NOT in the Activity stream — view tracking is
  analytics-only via dashboard-view-analytics. 15-min debounce on
  high-volume types prevents spam at default-group scale.

* footer-customization — Source has global per-language footer only;
  per-dashboard override (dashboardFooterMode: 'inherit'|'hidden'|
  'custom') is a MyDash addition. Multi-language fallback chain:
  viewer NC locale > dashboard primary language > first key.

Source-confirmed (no spec change beyond NOTE annotations):

* dashboard-draft-published — Migration adds publicationStatus
  column with DEFAULT 'published' (matches source's lazy-backfill
  pattern). New dashboards default to 'draft' via application code,
  not column default. PublicationSettingsService name is reserved
  in the source for an unrelated MetaVox concern; MyDash should
  pick a different class name (suggested: DashboardPublicationService).

Specs already aligned with source (no design.md needed):

* dashboard-versioning — Uses OCP\IVersionManager exactly as spec
  assumes.
* dashboard-rss-feeds — One-token-per-user enforced via
  generateToken() pre-delete, matches spec.

10 design.md files added (~1230 lines total). All 41 changes still
pass openspec validate --strict.
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ 93b25af

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

Quality workflow — 2026-05-01 17:36 UTC

Download the full PDF report from the workflow artifacts.

Each of the 10 MEDIUM design.md files from commit fbc36d5 listed a
"Spec changes implied" section. This commit applies those deltas to the
corresponding spec.md files. All 10 specs still pass openspec validate
--strict.

Material rewrites (4 specs — scenarios changed, not just NOTEs):

* dashboard-public-share — Throttle is IP-global across all share
  actions, not per-share-per-IP. Two action buckets:
  mydash_share_access (60/60s) and mydash_share_password (10/60s).
  Trip returns HTTP 429. REQ-PSHR-009 + REQ-PSHR-005 scenarios
  rewritten; REQ-PSHR-010 service-account note added.

* dashboard-bulk-operations — Hard delete (no soft-delete flag),
  cascade=false default. Parent-with-children + cascade=false → HTTP 409
  with {error: "has_children", childCount: N}. Two new scenarios for
  cascade behaviour; 4 existing scenarios updated to drop "soft-delete"
  wording.

* admin-roles — REQ-ROLE-005 algorithm rewritten as ordered 4-step
  (NC admin override → direct user assignment used as-is → max of
  matching group assignments → null). REQ-ROLE-009 reconciled with -005
  (direct assignment is sole source when present, not a rank
  comparison). 8 NOTE annotations across 4 requirements.

* background-job-feed-refresh — Parser pinned as
  simplexml_load_string + LIBXML_NOCDATA | LIBXML_NONET (PHP built-in,
  not SimplePie). FeedParserInterface kept as drop-in upgrade path.
  IClientService 10s/30s timeouts pinned. 10MB response cap pinned.

NOTE-only annotations (6 specs — bodies unchanged, NOTEs added to
mark deliberate divergence or pin concrete config):

* dashboard-templates — Top-of-spec divergence block added
  documenting the deliberate keep-DB-enum decision vs source's
  filesystem-folder model. 3 additional NOTEs on REQ-TMPL-012/013/015
  pinning composite index, deep-copy semantics, and custom-icon-upload
  shape reuse.

* dashboard-draft-published — Migration column DEFAULT changed from
  'draft' to 'published' (lazy-backfill at re-index time). New
  dashboards default to 'draft' via DashboardService::createDashboard()
  application code, not column default.

* activity-feed-integration — 13-event catalogue confirmed (5 source +
  8 MyDash). dashboard_viewed explicitly excluded with rationale.
  Four-scope audience table pinned as normative (REQ-ACT-004).
  900-second debounce TTL made normative in REQ-ACT-007 + REQ-ACT-008
  bodies.

* footer-customization — REQ-FTR-006 marked MyDash-original
  (per-dashboard override is beyond the source's global-only model).
  REQ-FTR-007 fallback chain pinned as normative three-step (viewer
  locale → dashboard primary language → first key); locale-fallback
  scenario split into per-step sub-scenarios.

* dashboard-view-analytics — REQ-ANLT-002/003 pin daily-rotating
  salt + SaltRotationJob (UTC midnight, no history). TTL is
  seconds-until-next-UTC-midnight, not fixed 86400s. REQ-ANLT-009
  retention clamp 30-3650 days documented.

* setup-wizard — 4 NOTEs added (REQ-WIZ-001/002/008/010) marking
  this as net-new MyDash, the per-step embed pattern, the
  intentional auto-launch SHOULD/MAY scope, and the cli-commands
  YAML schema cross-reference.

All 41 changes still pass openspec validate --strict.
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ 5dbe58f

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

Quality workflow — 2026-05-01 21:15 UTC

Download the full PDF report from the workflow artifacts.

…overage)

The conduction OpenSpec schema marks design.md as a required artifact
for every change. After the HIGH (9) and MEDIUM (10) deep-dives, 22
specs still lacked a design.md — the LOW-triaged ones that the audit
judged decisive enough to implement against as-is.

This commit completes coverage: every one of the 41 changes now has
proposal + tasks + spec + design.

The 22 design.md files are intentionally tighter than the HIGH/MEDIUM
ones (50-100 lines each, ~1.9K lines total). They document the
implementation choices the spec implies but doesn't pin (which library,
which OCP interface, which file layout), acknowledge any deliberate
divergences from the source app, and capture open follow-ups that
don't block implementation.

Files added (one per slug, all under openspec/changes/<slug>/design.md):

Dashboard core (6):
* dashboard-tree, dashboard-metadata-fields, dashboard-versioning,
  dashboard-comments, dashboard-reactions, dashboard-rss-feeds

Widget extensions (4):
* text-widget-markdown, text-widget-tables, image-widget-media-picker,
  link-button-widget-list-mode

Content widgets (3):
* news-widget, files-widget, video-widget

Simple widgets (5):
* divider-widget, links-widget, header-widget, menu-widget,
  quicklinks-widget

System / glue (4):
* nc-unified-search-integration, dashboard-export-import,
  orphaned-data-cleanup, cli-commands

Each follows the conduction-schema design.md instruction: Context,
Goals/Non-Goals, Decisions (with Decision/Alternatives/Rationale per
decision), Risks/Trade-offs, Open follow-ups.

All 41 changes now pass openspec validate --strict and have full
artifact coverage per the conduction schema (proposal + spec + design
+ tasks). Apply-ready.
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ 1df1808

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

Quality workflow — 2026-05-01 21:38 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Superseded — all 25 spec names in this PR already exist in dev (either as active proposals in openspec/changes/ or archived in openspec/changes/archive/ after implementation in PRs #99-#118). Re-merging would conflict with archived versions and reintroduce the "intravox" / "voxcloud" naming that was deliberately swept out earlier in the same stream. Closing as superseded; if any specific spec needs to be brought forward, please open a fresh PR with just that change.

Audit:

  • 25 unique change names in this PR
  • 25 / 25 already present in dev (active or archived)
  • 0 truly new

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant