Skip to content

feat(ai-project-board-view): add AI Project Space Status Board#520

Open
LukasHirt wants to merge 12 commits into
mainfrom
ext/2026-07-07-ai-project-board-view
Open

feat(ai-project-board-view): add AI Project Space Status Board#520
LukasHirt wants to merge 12 commits into
mainfrom
ext/2026-07-07-ai-project-board-view

Conversation

@LukasHirt

@LukasHirt LukasHirt commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

AI-generated · OSPO-70 · Gate: ✅ 1.00

Problem

Project spaces render as a flat grid, so a manager can't tell which deliverables are draft, in review, or final without opening every folder.

Solution

Registers an alternate project-space folder view: a three-lane board (Draft / In Review / Final) built from the same file list already loaded. The LLM classifies each file's lane from its name plus a short excerpt, returned as structured {fileId, lane} pairs. Degrades: no structured output → one lane-per-file text line, pattern-matched; no tool use → single classification pass on load, no re-run button; small context → excerpt dropped to filename only.

Extension points

app.files.folder-views.project-spaces

Why ship this now

Managers today infer status from naming conventions alone; this surfaces it visually for free from data the view already fetches.

What was built

The extension is web-app-ai-project-board-view, which adds an LLM-classified status board as an alternate view mode for project-space folders. Rather than the flat file grid, it groups the same resource list already loaded into the view into three lanes — Draft, In Review, Final — inferred from each file's name and a short content excerpt. It's registered in src/index.ts as a folderView extension; note that despite the spec targeting app.files.folder-views.project-spaces, the implementation actually registers on app.files.folder-views.folder (documented with rationale in the package README).

Classification is driven by composables/useBoardClassification.ts, which reads activeResources from useResourcesStore(), batches up to 60 files per prompt, and asks the BYO-LLM endpoint (via useLLM().complete(), configured through applicationConfig.llm) to return structured {fileId, lane} JSON. composables/useExcerpt.ts supplies a capped 2KB text excerpt per file over WebDAV (Range-header fetch), skipping folders, non-text mime types, and oversized files so filename-only classification kicks in when content isn't available. Presentation is split across BoardCard.vue (icon, name, last-modified), BoardLane.vue (header with icon/label/count plus a scrollable card list and empty state), and ProjectBoardView.vue, which orchestrates loading/empty/error/cross-origin/unconfigured states, a truncation notice when the 60-file cap is hit, and a manual "Re-run classification" button.

The design leans on graceful degradation rather than hard failure: if the model doesn't support structured output, utils/lane.ts falls back to a regex line-parser (laneFromKeyword/parseLaneLines) that reads "<name>: <lane>"-style text, and any file the classifier can't confidently place defaults to Draft. useLLM.ts gained a responseFormat option (wired to response_format in the request body) to request JSON output where the endpoint supports it, reusing the existing same-origin check and bearer-token completion path from the BYO-LLM composable pattern. Unit coverage (55 tests) exercises the excerpt fetch/truncation boundaries, both JSON and malformed-JSON classification paths, the full LLM error-code-to-panelError mapping, and all view states. The end-to-end Playwright test drives the real view-mode switch and lane placement for uploaded fixture files, then re-runs classification after a fourth upload — its LLM response is mocked by intercepting the proxy call and deriving a realistic non-JSON reply from the actual outgoing prompt, deliberately exercising the fallback line-parser instead of depending on real oCIS file IDs.

screenshot-1

Gate

Check Result
Hygiene ✅ ok
Build ✅ ok
Lint ✅ ok
Unit tests ✅ ok
E2E tests ✅ ok
Score 1.00

Effort: M · 🤖 Generated by extctl

@LukasHirt
LukasHirt requested a review from a team as a code owner July 20, 2026 14:20
@kw-security

kw-security commented Jul 20, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@LukasHirt
LukasHirt requested a review from dj4oC July 20, 2026 14:21
@LukasHirt LukasHirt self-assigned this Jul 20, 2026
@LukasHirt
LukasHirt force-pushed the ext/2026-07-07-ai-project-board-view branch from 459dd22 to 4f6ab26 Compare July 20, 2026 14:30
LukasHirt added 12 commits July 20, 2026 16:32
Signed-off-by: Lukas Hirt <info@hirt.cz>
…sables/useLLM.ts` with the BYO-LLM pattern (same-origin check against `OCIS_URL`, `status` states, `complete()` posting to the proxy with bearer token); add `src/utils/lane.ts` (Lane type, label/icon metadata, regex-based fallback line parser); add `src/composables/useExcerpt.ts` (capped text excerpt fetch via `clientService.webdav.getFileContents`, skipped for binary/oversized files); add `src/composables/useBoardClassification.ts` (reads `useResourcesStore().activeResources`, builds batched per-file prompt, calls `useLLM().complete()` with JSON response format, parses/falls back to line parsing, defaults unmatched files to Draft, exposes `lanes`/`status`/`isClassifying`/`panelError`/`classify()`); update `src/index.ts` to read `applicationConfig.llm` and register the `folderView` extension against `app.files.folder-views.project-spaces` with `componentAttrs: () => ({ llmConfig })`.

Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…ard.vue` (file icon, name, last-modified), `src/components/BoardLane.vue` (lane header with icon/label/count, scrollable card list), and `src/components/ProjectBoardView.vue` (the `FolderView.component` rendering the three lanes via ODS `oc-*` components and utility classes, loading/empty/error states, and a "Re-run classification" button wired to `useBoardClassification().classify()`); wire `ProjectBoardView` as the `component` in the `folderView` extension registered in `src/index.ts`.

Signed-off-by: Lukas Hirt <info@hirt.cz>
…eExcerpt.spec.ts` (text excerpt fetched/truncated, binary/unsupported mime skipped, oversized file skipped), `tests/unit/useBoardClassification.spec.ts` (happy-path JSON classification, malformed-JSON fallback to line parsing, excerpt-omitted filename-only fallback, LLM error mapping to `panelError`, empty folder state), and `tests/unit/ProjectBoardView.spec.ts` (three lanes render, cards placed correctly, loading/error/empty states, re-run button behavior) following the `InsightsPanel.spec.ts` stub-and-mock pattern.

Signed-off-by: Lukas Hirt <info@hirt.cz>
…in `tests/e2e/acceptance.spec.ts` with real Playwright coverage mirroring `web-app-file-comments/tests/e2e/fileComments.spec.ts` — create a project space, upload files, switch to the Status Board view mode, and assert the three lanes render with files placed into cards; also cover the re-run classification button updating lane placement after a new file lands.

Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
Signed-off-by: Lukas Hirt <info@hirt.cz>
…if present) for the extension

Signed-off-by: Lukas Hirt <info@hirt.cz>
…matrix, and oCIS apps config

Signed-off-by: Lukas Hirt <info@hirt.cz>
…s with other packages

Bump @ownclouders/web-client, @ownclouders/web-pkg, @ownclouders/extension-sdk
to 12.5.0, and vitest, vue, vue-router, vue-tsc, happy-dom, prettier to match
versions used across the rest of the monorepo after rebasing onto main.

Signed-off-by: Lukas Hirt <info@hirt.cz>
@LukasHirt
LukasHirt force-pushed the ext/2026-07-07-ai-project-board-view branch from 4f6ab26 to 0b0812b Compare July 20, 2026 14:34
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.

2 participants