feat(hermiq): rail-based agent detail, fuller dashboard, and the CSS that made either visible - #162
Open
rubenvdlinde wants to merge 2 commits into
Open
feat(hermiq): rail-based agent detail, fuller dashboard, and the CSS that made either visible#162rubenvdlinde wants to merge 2 commits into
rubenvdlinde wants to merge 2 commits into
Conversation
…that made either visible The layout the manifest already described was never reaching the screen. Hermiq shipped no GridStack stylesheet: nc-vue imports it as a side-effect import from an aliased package and webpack tree-shakes those away, and it is absent from dist/nextcloud-vue.css too (0 occurrences of its base rules, while leaflet's 174 ARE there). GridStack >= 11 sets left/top INLINE but takes `position: absolute` on items and `position: relative` on the container from that sheet, so every widget fell into normal flow: correct widths, no horizontal placement, all 11 cells stacked in one column down the left third of a 1600px viewport. Measured before the fix: gs-x="3" carried `left: calc(3 * var(--gs-column-width))` resolving to 25% while `position` computed to `static`, and every item reported left=371px. procest (same renderer, gridstack 10) positioned correctly, which is what localised it to hermiq. The stylesheet is fixed IN THE LIBRARY, not here — @conduction/nextcloud-vue's src/css/index.css now @imports it so postcss-import folds it into the extracted sheet, covering both the published-dist and aliased-to-src resolution paths. An app-level import would duplicate those rules in every consumer. main.js carries only a note recording why it is deliberately absent. Layout, on top of that: - AgentDetail adopts procest CaseDetail's 8/4 rail — an 8-column main column (Configuration, Run operations, Memory) beside a persistent 4-column rail (the four KPI tiles stacked, Skills, Eval baseline), both closing exactly at row 16. 33 rows -> 28, zero unfilled cells, and zero widgets whose scrollHeight exceeds their cell (ADR-062), all re-measured in a browser. - Tool governance halves from gridHeight 14 to 8: its two surfaces are peers over one capability, so they are now tabbed rather than stacked, with a real tablist (roving tabindex, aria-selected/aria-controls, arrow-key nav). It draws its own heading and sets showTitle:false, because with the chrome title on that title is a SIBLING of the widget root inside a display:block grid item, so height:100% resolved against the full box and ignored it — a permanent 6px overflow no gridHeight could remove. - Dashboard closes its grid (the bottom row left 6 of 12 columns empty) and gains a runs-by-agent chart, an agents table, header actions and a KPI drilldown. 12 rows, zero unfilled cells. AnalyticsService enriches perAgent with `name`: the aggregate carried only agentId, and a chart labelled with UUIDs is unreadable. This instance has zero runs, so the chart was verified by a positive control — the endpoint was intercepted with a synthetic payload and it rendered 6 bars across 3 name-labelled categories. Also fixes a pre-existing 500: AgentsController::stats bound PHP's (string)false — the empty string — against a boolean column, so every call died with SQLSTATE[22P02] and the agent counters had never worked. Booleans are now normalised to 'true'/'false'. Verified against DB ground truth with a deliberately-inactive probe agent (18/17/1), since with 17/17 active a broken filter and a working one are indistinguishable. Security postures declared rather than inherited (hydra gates 5/7/9/30): - SettingsController::create, SetupController::saveConfig and ::runAction were admin-only by Nextcloud's default for an un-attributed method, which is silently lost the moment anyone adds #[NoAdminRequired]. Now #[AuthorizedAdminSetting(AdminSettings::class)]. runAction matters most: test-llm makes the server issue an outbound request, so unauthenticated it is an SSRF primitive. - MetricsController::index stays admin-authed per ADR-006 ("/api/metrics Prometheus text, admin auth" vs "/api/health JSON, public") and gains #[NoCSRFRequired], which is what a scraper actually needs. - Four endpoints taking no caller-supplied object id carry reason-bearing @no-admin-idor-exempt tags. create() was checked first: `owner` and `organisation` are in PROTECTED_KEYS and assigned server-side. Accessibility: scope="col" on every column header across four components, a visually-hidden label on a bare <th />, and prefers-reduced-motion fallbacks (WCAG 2.2 AA 2.3.3). The chat typing indicator is an infinite bounce — the pattern that triggers vestibular symptoms — and is replaced with a static opacity rather than removed, so "assistant is typing" survives the motion. The e2e login no longer waits for 'networkidle', which never settles on Nextcloud (ADR-074 rule 4): the wait always ran to its timeout and only worked because .catch() swallowed it — a disguised fixed delay, not a readiness signal. It now waits for the submit control to be present and enabled.
rubenvdlinde
requested review from
SudoThijn,
WilcoLouwerse,
bbrands02,
remko48 and
rjzondervan
as code owners
August 6, 2026 07:41
Contributor
Quality Report — ConductionNL/hermiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 117/117 | |||
| npm | ✅ | ✅ 744/744 | |||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-06 08:02 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work that had been sitting uncommitted-adjacent on this machine — one commit, now brought up to date with
development.What it is
Rail-based agent detail, a fuller dashboard, and the CSS without which neither rendered.
Two things repaired while landing it
The dependency pin was pointing at a version that no longer exists. The working tree had
@conduction/nextcloud-vueedited to3.0.0-vue3.6— one of the six3.0.0-vue3.*builds unpublished when that line was withdrawn and renumbered onto 2.x.npm cicould not have resolved it. Mergingdevelopmentbrings the correct2.2.0-vue3.3; the stale edit is stashed rather than discarded (git stash list, top entry) in case anything else in it was wanted.Brought current with
development— the branch was 12 behind, now 0. That was the conflict risk worth removing.Not included, deliberately
An untracked
src/Chat.vuesits in the working tree. It is not the file the app uses:src/registry.jsimports./views/Chat.vue, which is tracked and is strictly better — it carries thedata-testid="chat-conversation-row"the e2e suite selects on, and aprefers-reduced-motionblock (WCAG 2.3.3) that replaces the infinite typing-indicator bounce with a static opacity. The stray copy has neither, so it is an older duplicate. Left in place rather than deleted, since it is untracked and git could not recover it.