18 KiB
NuQloud Scrum Implementation Plan
Plan date: 2026-07-03
1. Direction
NuQloud Scrum is a Talk-first scrum layer for NuQloud deployments.
Canonical model:
| Nextcloud surface | NuQloud Scrum meaning |
|---|---|
| Talk conversation | Scrum workspace |
| Talk thread / reply chain | Task |
| Talk messages in thread | Task discussion/activity |
| Files shared in conversation | Workspace files |
| Files shared in thread/reply chain | Task files |
| Deck board | Workspace board mirror |
| Deck card | Task mirror |
| NuQloud Scrum DB | Mapping, index, sync state, audit |
Product rule for v1:
- Talk is the primary UX, especially on mobile.
- Threads/reply chains are tasks.
- Files and Deck are mirrors.
- NuQloud Scrum stores indexes and sync state only.
- No custom mobile client is required.
2. App Boundary
Create a new app:
nextcloud/custom_apps/nuqloud_scrum/
Do not merge this into:
qortal_integration- account, broker, OIDC, publishing controls.qortal_files_bridge- QDN/files bridge.qortal_talk_bridge- temporary Talk/Q-Chat bridge.chd_admin- internal CHD/admin-only work.
Suggested app metadata:
id: nuqloud_scrum
name: NuQloud Scrum
namespace: NuqloudScrum
category: integration
dependencies:
nextcloud min-version: 32
nextcloud max-version: 34 initially
optional apps: spreed, deck
Use Powered Mode language by default. Avoid crypto-native labels in this app UI.
3. Compatibility Baseline
Verified documentation assumptions on 2026-07-03:
- Talk chat API base endpoint is
/ocs/v2.php/apps/spreed/api/v1. - Talk chat fetch supports
lastKnownMessageId, pagination headers, parent message data, and reaction fields. - Talk send-message supports
replyTofor replies. - Talk file share uses Files Sharing OCS with
shareType=10for conversation shares. - Talk shared item listings support
lastKnownMessageIdpagination when the relevant capability exists. - Deck REST API is authenticated and exposes boards, stacks, cards, assignments, labels, comments, and attachments.
- Nextcloud app schema changes belong in
lib/Migration. - Background jobs belong in the app bootstrap/job registration path.
Implementation rule:
Native Talk thread metadata available?
Use Talk thread identifiers directly.
Only parent/reply chain available?
Treat root message ID + parent relationship as task identity.
No stable thread creation API available?
Bot posts an anchor task message and users reply to that message.
4. Target App Structure
nextcloud/custom_apps/nuqloud_scrum/
appinfo/
info.xml
routes.php
lib/
AppInfo/
Application.php
BackgroundJob/
SyncConversationsJob.php
SyncConversationMessagesJob.php
SyncDeckJob.php
SyncFilesJob.php
SummarizeWorkspacesJob.php
Controller/
PageController.php
SettingsController.php
WorkspaceController.php
TaskController.php
Db/
Workspace.php
WorkspaceMapper.php
ThreadTask.php
ThreadTaskMapper.php
ThreadMessage.php
ThreadMessageMapper.php
MirroredFile.php
MirroredFileMapper.php
DeckSync.php
DeckSyncMapper.php
SyncCursor.php
SyncCursorMapper.php
Migration/
Version000001DateYYYYMMDDHHMMSS.php
Service/
ConfigService.php
TalkApiClient.php
TalkCapabilityService.php
WorkspaceService.php
ThreadTaskService.php
FolderMirrorService.php
FileIndexService.php
DeckApiClient.php
DeckMirrorService.php
ReactionWorkflowService.php
BotCommandService.php
SummaryService.php
SlugService.php
AuditService.php
templates/
index.php
admin.php
js/
index.js
admin.js
css/
style.css
admin.css
img/
app.svg
l10n/
.gitkeep
Keep the first UI thin:
- Admin settings: enable/disable, service user config, sync intervals, workspace filters.
- User page: workspace index, task links, sync state, Deck links, folder links.
- No replacement scrum board in v1.
5. Data Model
nuqloud_scrum_workspaces
id bigint primary key
talk_token varchar(128) not null unique
talk_room_name varchar(255) not null
talk_room_type varchar(64) null
deck_board_id bigint null
root_folder_path varchar(1024) not null
sync_enabled smallint default 1
last_message_id bigint default 0
last_share_message_id bigint default 0
created_by varchar(128) null
created_at bigint not null
updated_at bigint not null
Indexes:
uniq talk_token
idx sync_enabled
idx updated_at
nuqloud_scrum_threads
id bigint primary key
workspace_id bigint not null
talk_thread_id varchar(128) null
talk_root_message_id bigint not null
title varchar(255) not null
slug varchar(255) not null
folder_path varchar(1024) not null
deck_card_id bigint null
deck_stack_id bigint null
status varchar(32) default 'inbox'
priority varchar(32) default 'normal'
created_by varchar(128) null
assigned_to varchar(128) null
due_at bigint null
last_activity_message_id bigint default 0
created_at bigint not null
updated_at bigint not null
Indexes:
uniq workspace_id + talk_root_message_id
uniq workspace_id + talk_thread_id when available
idx workspace_id + status
idx deck_card_id
nuqloud_scrum_thread_messages
id bigint primary key
thread_id bigint not null
talk_message_id bigint not null
actor_type varchar(64) not null
actor_id varchar(128) not null
actor_display_name varchar(255) null
message_text clob null
message_hash varchar(128) not null
raw_payload clob null
created_at bigint not null
updated_at bigint not null
Indexes:
uniq thread_id + talk_message_id
idx actor_id
idx created_at
nuqloud_scrum_files
id bigint primary key
workspace_id bigint not null
thread_id bigint null
talk_message_id bigint null
nc_file_id bigint null
original_path varchar(1024) not null
workspace_path varchar(1024) not null
checksum varchar(128) null
size bigint default 0
mime_type varchar(255) null
mirror_state varchar(32) default 'pending'
sync_error clob null
created_at bigint not null
updated_at bigint not null
Indexes:
idx workspace_id + thread_id
idx nc_file_id
idx mirror_state
nuqloud_scrum_deck_sync
id bigint primary key
workspace_id bigint not null
thread_id bigint null
deck_board_id bigint null
deck_stack_id bigint null
deck_card_id bigint null
last_sync_at bigint null
sync_state varchar(32) default 'pending'
sync_error clob null
created_at bigint not null
updated_at bigint not null
nuqloud_scrum_sync_cursors
id bigint primary key
cursor_type varchar(64) not null
scope_key varchar(255) not null
cursor_value varchar(255) not null
locked_until bigint null
updated_at bigint not null
Indexes:
uniq cursor_type + scope_key
idx locked_until
6. Folder Layout
Root folder:
/NuQloud Scrum/
Workspace folder:
/NuQloud Scrum/{workspace-name}/
_workspace.md
_manifest.json
_open-tasks.md
_decisions.md
Files/
Threads/
Summaries/
Task folder:
/NuQloud Scrum/{workspace-name}/Threads/
{root-message-id}-{slug}/
_task.md
_activity.jsonl
_summary.md
Files/
Decisions/
Rules:
- Prefix folders with immutable IDs.
- Slugs are display-only and may be regenerated.
- DB identity always wins over path names.
- Folder moves are detected and healed through
file_idwhere possible. - Generated markdown must escape raw user text.
7. Sync Flow
Conversation discovery
SyncConversationsJob:
- Fetch configured Talk conversations visible to the automation/service user.
- Apply workspace filters:
- all conversations
- named allowlist
- group/member filter
ensureWorkspace(token).- Create root folder and manifest.
- Ensure Deck board when Deck is enabled.
Message sync
SyncConversationMessagesJob:
- For each enabled workspace, fetch Talk messages after
last_message_id. - Store raw payload only server-side.
- Detect task root:
- native
threadIdroot when available - parent/reply root message ID fallback
- bot anchor message fallback
- native
ensureThreadTask.- Append message record if it belongs to a task.
- Apply reactions.
- Queue Deck and file sync.
- Advance cursor only after successful persistence.
File sync
SyncFilesJob:
- Fetch shared items from Talk where capability allows it.
- Resolve target:
- thread/task folder when message has thread/root context
- workspace
Files/YYYY-MM/otherwise
- Copy or link according to admin setting:
- v1 default: copy into NuQloud Scrum folder
- future option: retain original and write folder link only
- Record file ID, checksum, size, MIME type.
- Avoid duplicate copies by
nc_file_id + target_path.
Deck sync
SyncDeckJob:
- Ensure one Deck board per workspace.
- Ensure default stacks:
- Inbox
- Ready
- In Progress
- Blocked
- Review
- Done
- Archived
- Ensure one Deck card per thread task.
- Mirror status to stack.
- Update card description with:
- Talk room link
- thread/task folder link
- latest summary
- source IDs
- Avoid syncing every Talk reply as a Deck comment in v1 unless explicitly enabled.
8. Reaction Workflow
Default mappings:
| Reaction | Status/action |
|---|---|
| check mark | Done |
| construction | Blocked |
| eyes | Review |
| fire | Priority |
| pushpin | Decision |
| folder | File/context marker |
Implementation notes:
- Store reaction-derived status changes in DB.
- Prefer latest trusted actor reaction if conflicts exist.
- Allow admins to disable reaction mappings.
- Do not rely on client-only state.
- Reaction values must be normalized before matching.
9. Bot Commands
Use bot-style messages, not legacy slash commands.
Supported v1 commands:
@NuQloud status blocked
@NuQloud status done
@NuQloud assign @username
@NuQloud due 2026-07-10
@NuQloud summarize
@NuQloud help
Rules:
- Commands only work in authenticated Talk conversations.
- Only room participants can affect that workspace.
- Admin-configured moderators can override any task.
- User input is parsed with strict allowlists.
- Bot responses must not expose raw payloads, secrets, or stack traces.
10. Security Requirements
Public endpoints:
- None in v1.
Authenticated endpoints:
- Workspace/task index pages.
- Per-user workspace/task JSON APIs.
Admin endpoints:
- App enable/disable.
- Service user/app-password settings.
- Workspace sync filters.
- Reaction/Deck/file mirroring settings.
- Manual resync/repair actions.
Required controls:
- Use Nextcloud controller auth for all routes.
- Use CSRF checks for state-changing browser routes.
- Validate all IDs, paths, statuses, dates, and command arguments.
- Escape user message text in generated markdown and UI.
- Never log app passwords, raw auth headers, invite tokens, or raw message bodies.
- Store service credentials in app config with admin-only access.
- Enforce participant/admin checks server-side.
- Treat Deck and folder mirrors as derived state, not authority.
11. Admin Settings
Initial settings:
nuqloud_scrum_enabled: 0|1
talk_service_user: string
talk_service_app_password: secret
workspace_filter_mode: all|allowlist|disabled
workspace_allowlist: JSON array of Talk tokens/names
deck_sync_enabled: 0|1
file_mirror_enabled: 0|1
file_mirror_mode: copy|link
reaction_workflow_enabled: 0|1
bot_commands_enabled: 0|1
summary_enabled: 0|1
sync_interval_minutes: integer
Admin UI should use theme variables, l10n strings, and existing Nextcloud settings patterns.
12. Implementation Phases
Phase 0 - API validation spike
Goal: prove target Talk/Deck behavior on devcloud before committing to event semantics.
Tasks:
- Confirm Talk capabilities for target Nextcloud/Talk version.
- Confirm whether native thread identifiers appear in fetched message payloads.
- Confirm reply-chain fallback using
parentandreplyTo. - Confirm reaction payload shape.
- Confirm file share metadata includes usable thread/root context.
- Confirm Deck card create/update/comment/attachment endpoints.
- Decide service account pattern for background jobs.
Deliverable:
docs/engineering/nuqloud-scrum-api-validation.md
Phase 1 - app skeleton and settings
Tasks:
- Add
nuqloud_scrumapp scaffold. - Add
info.xml, bootstrap, routes, admin settings page. - Add config service.
- Add app icon and minimal themed CSS.
- Add l10n-ready strings.
- Register background jobs disabled until config is valid.
Acceptance:
- App installs/enables.
- Admin can save settings.
- Missing Talk/Deck apps show clear admin warnings.
- No public routes exist.
Phase 2 - migrations and persistence
Tasks:
- Add migration for workspace/thread/message/file/deck/cursor tables.
- Add entities/mappers.
- Add repository-level unit tests where practical.
- Add idempotent upsert methods.
- Add sync cursor locking.
Acceptance:
- Install creates tables.
- Upgrade path is repeatable.
- Duplicate Talk messages do not create duplicate tasks.
Phase 3 - Talk workspace discovery
Tasks:
- Implement
TalkApiClient. - Implement
TalkCapabilityService. - Implement
WorkspaceService. - Implement
SyncConversationsJob. - Create
/NuQloud Scrum/{conversation}/folders. - Write
_workspace.mdand_manifest.json.
Acceptance:
- Allowed Talk conversations become workspaces.
- Re-running sync is idempotent.
- Folder names are sanitized.
- Immutable Talk token remains the workspace key.
Phase 4 - thread/task indexing
Tasks:
- Implement
ThreadTaskService. - Implement task root detection.
- Implement title extraction:
- explicit thread title when available
- first sentence/message summary fallback
- safe length cap
- Create task folders.
- Write
_task.mdand_activity.jsonl. - Track
last_message_idper workspace.
Acceptance:
- Talk replies create/update task records.
- Existing reply chains backfill correctly.
- Generated files escape raw Talk content.
- Task folders use
{root-message-id}-{slug}.
Phase 5 - file mirroring
Tasks:
- Implement
FileIndexService. - Implement
FolderMirrorService. - Resolve root conversation files vs task files.
- Copy files into workspace/task folders.
- Deduplicate by source file ID/checksum.
- Record mirror errors without exposing secrets.
Acceptance:
- Conversation file shares appear under
Files/YYYY-MM/. - Task file shares appear under task
Files/. - Duplicate shares do not create uncontrolled copies.
- Failed copies remain retryable.
Phase 6 - Deck board/card mirror
Tasks:
- Implement
DeckApiClient. - Implement
DeckMirrorService. - Create workspace board.
- Create default stacks.
- Create/update one card per task.
- Sync status to stack.
- Add card links back to Talk/folder.
Acceptance:
- One board exists per workspace.
- One card exists per task.
- Moving Deck card updates local task status.
- Talk remains canonical for task identity.
Phase 7 - mobile status controls
Tasks:
- Implement
ReactionWorkflowService. - Implement admin-configurable mappings.
- Implement conflict resolution.
- Implement bot command parser.
- Add concise bot replies.
Acceptance:
- Mobile Talk reactions update task status.
- Bot commands update status/assignee/due date.
- Invalid commands fail safely.
- Status changes are mirrored to Deck.
Phase 8 - summaries
Tasks:
- Implement
SummaryService. - Generate
_summary.mdper task. - Generate daily summaries under
Summaries/. - Generate
_open-tasks.mdand_decisions.md. - Keep AI extraction as optional suggestions only.
Acceptance:
- Summaries are regenerated idempotently.
- No raw secrets or private auth data are included.
- Summaries link back to source task folders.
Phase 9 - hardening and release
Tasks:
- Add repair command/job for missing folders/cards.
- Add admin sync status dashboard.
- Add logging with redaction.
- Add docs for admin setup and user workflow.
- Add compatibility notes for Nextcloud 32-34 and Talk thread differences.
- Add rollback notes.
Acceptance:
- Failed syncs are visible and retryable.
- App degrades gracefully without Deck.
- App degrades gracefully without native Talk thread metadata.
- Operators have devcloud verification commands.
13. Verification Commands
Do not run or simulate Docker/Nextcloud locally. Use devcloud.
docker compose -f docker-compose.devprod.yml --env-file .env.devprod ps
docker compose -f docker-compose.devprod.yml --env-file .env.devprod exec app php occ app:list
docker compose -f docker-compose.devprod.yml --env-file .env.devprod exec app php occ app:enable nuqloud_scrum
docker compose -f docker-compose.devprod.yml --env-file .env.devprod exec app php occ migrations:status nuqloud_scrum
docker compose -f docker-compose.devprod.yml --env-file .env.devprod exec app php occ background-job:list | grep -i nuqloud
docker compose -f docker-compose.devprod.yml --env-file .env.devprod logs --tail=200 app
If using the no-SSL devprod stack:
docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod ps
docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec app php occ app:enable nuqloud_scrum
docker compose -f docker-compose.devprod.nossl.yml --env-file .env.devprod exec app php occ migrations:status nuqloud_scrum
14. Risks
- Talk native thread payloads may differ by Talk version.
- Mitigation: implement reply-chain fallback and API validation spike.
- Deck API authentication from background jobs needs a clear service-account pattern.
- Mitigation: configure a dedicated automation user/app password.
- Mirroring files can duplicate storage.
- Mitigation: admin-selectable
copy|link, dedupe, size limits.
- Mitigation: admin-selectable
- Reaction status conflicts can be ambiguous.
- Mitigation: deterministic precedence and admin-configurable mappings.
- App-to-app internals may change.
- Mitigation: isolate Talk/Deck clients behind service interfaces.
15. External References
- Nextcloud Talk chat API: https://nextcloud-talk.readthedocs.io/en/latest/chat/
- Nextcloud Deck REST API: https://deck.readthedocs.io/en/latest/API/
- Nextcloud migrations: https://docs.nextcloud.com/server/stable/developer_manual/basics/storage/migrations.html
- Nextcloud developer manual: https://docs.nextcloud.com/server/stable/developer_manual/