Skip to content

feat(console): add the Ariada accessibility audit console (OSS) - #257

Open
alexander-brichkin wants to merge 1 commit into
mainfrom
feat/ariada-console-oss
Open

feat(console): add the Ariada accessibility audit console (OSS)#257
alexander-brichkin wants to merge 1 commit into
mainfrom
feat/ariada-console-oss

Conversation

@alexander-brichkin

@alexander-brichkin alexander-brichkin commented Jul 30, 2026

Copy link
Copy Markdown
Member

Adds the OSS accessibility audit console + its self-contained vendored kit (@ariada-org/admin-surface + @ariada-org/admin-svelte). SvelteKit static build; live demo at app.ariada.org. First project = GNU Taler ecosystem. New-package publication.

Summary by CodeRabbit

  • New Features

    • Added an accessibility audit console with project selection, audit profiles, summary metrics, sortable grids, and row actions.
    • Added public tokenized report and live subject viewers.
    • Added reusable admin surface, grid, chart, localization, color, and validation contracts.
    • Added Svelte components for interactive grids, charts, detail drawers, themes, formatting, and accessibility-focused styling.
  • Documentation

    • Added package documentation, usage guidance, and contract examples.
  • Tests

    • Added comprehensive validation, rendering, formatting, chart, and component coverage.

A SvelteKit (adapter-static) operator console for managing accessibility
audits across projects — the whole GNU Taler ecosystem is the first project.
Self-contained: depends only on its own vendored kit — @ariada-org/admin-surface
(a framework-neutral surface/grid/profile contract) and @ariada-org/admin-svelte
(a Svelte 5 renderer over ag-grid, zero runtime deps) — plus ag-grid-community.
No external UI framework, no shared/private dependency. Live demo: app.ariada.org.

Signed-off-by: Alexander Brichkin (Agonist Development AB) <git@ariada.org>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Admin surface contracts

Layer / File(s) Summary
Contract definitions and validation
packages/admin-surface/src/index.ts, packages/admin-surface/src/*test.ts
Adds validated and deeply frozen surface, grid, dashboard profile, chart, locale, and color contracts with focused tests.
Package and authoring support
packages/admin-surface/package.json, packages/admin-surface/templates/*, packages/admin-surface/README.md, packages/admin-surface/tsconfig*.json
Adds package metadata, build configuration, example surface template, documentation, and ignored dependency directories.

Svelte renderer

Layer / File(s) Summary
Renderer foundations
packages/admin-svelte/src/format.ts, packages/admin-svelte/src/chart.ts, packages/admin-svelte/src/theme.ts, packages/admin-svelte/src/i18n.ts, packages/admin-svelte/src/icons.ts, packages/admin-svelte/src/tokens.css
Adds formatting, chart geometry, theme, localization, icon, and styling primitives with helper tests.
Grid and visualization components
packages/admin-svelte/src/AdminGrid.svelte, packages/admin-svelte/src/MetricChart.svelte, packages/admin-svelte/src/RowDetailDrawer.svelte, packages/admin-svelte/src/renderers.ts
Adds contract-driven AG Grid rendering, SVG charts, row detail editing, action confirmation, contextual help, and renderer wiring.
Package validation and rendering tests
packages/admin-svelte/src/*test.ts, packages/admin-svelte/package.json, packages/admin-svelte/README.md, packages/admin-svelte/tsconfig*.json, packages/admin-svelte/vitest.config.ts
Adds component compilation, SSR, helper, renderer, and package-invariant tests plus package exports and documentation.

Console application

Layer / File(s) Summary
Audit console and routes
apps/ariada-console/src/lib/*, apps/ariada-console/src/routes/*
Adds the audit board and profiles, Taler project data, demo authentication gate, audit dashboard, tokenized report route, and subject iframe route.
SvelteKit application setup
apps/ariada-console/package.json, apps/ariada-console/svelte.config.js, apps/ariada-console/vite.config.ts, apps/ariada-console/tsconfig.json, apps/ariada-console/src/app.*, apps/ariada-console/.gitignore
Configures the private SvelteKit SPA, static fallback, Svelte/Vite integration, TypeScript settings, document shell, and ignored build artifacts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Console
  participant AdminGrid
  participant AuditAPI
  Browser->>Console: authenticate and open dashboard
  Console->>AdminGrid: pass board, profile, and audit rows
  AdminGrid->>AuditAPI: submit selected row action
  AuditAPI-->>AdminGrid: return action response
Loading

Suggested reviewers: predopta

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the Ariada accessibility audit console as an OSS feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ariada-console-oss

Comment @coderabbitai help to get the list of available commands.

@socket-security

Copy link
Copy Markdown

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Critical
Critical CVE: When Vitest UI server is listening, arbitrary file can be read and executed

CVE: GHSA-5xrq-8626-4rwp When Vitest UI server is listening, arbitrary file can be read and executed (CRITICAL)

Affected versions: >= 4.0.0 < 4.1.0; < 3.2.6

Patched version: 3.2.6

From: packages/admin-surface/package.jsonnpm/vitest@2.1.9

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/vitest@2.1.9. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report


it('renders nothing when no row is open', () => {
const { body } = render(RowDetailDrawer, { props: { surface: SURFACE, row: null, onClose: () => {} } });
expect(body.replace(/<!--[\s\S]*?-->/g, '').trim()).toBe('');
/** a 24x24 stroke-icon SVG string for an action key (falls back to a dot). */
export function actionIconSvg(key: string, size = 14): string {
const paths = ACTION_ICON_PATHS[key] ?? FALLBACK_ICON;
return `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${paths}</svg>`;
/** a 24x24 stroke-icon SVG string for an action key (falls back to a dot). */
export function actionIconSvg(key: string, size = 14): string {
const paths = ACTION_ICON_PATHS[key] ?? FALLBACK_ICON;
return `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${paths}</svg>`;

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Nitpick comments (3)
packages/admin-surface/.gitignore (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Also ignore the build output.

tsconfig.build.json emits to dist/, which isn't ignored here.

♻️ Proposed change
 node_modules/
+dist/
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-surface/.gitignore` at line 1, Update the ignore rules in the
project’s .gitignore to include the dist/ directory emitted by
tsconfig.build.json, while preserving the existing node_modules/ rule.
packages/admin-svelte/package.json (1)

58-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use workspace:* instead of file: for the sibling package.

The README's own install example uses "@ariada-org/admin-surface": "workspace:*", but this devDependency pins file:../admin-surface. Mixing link protocols for the same in-repo package across the monorepo increases the odds of exactly the kind of lockfile-specifier drift currently failing CI.

♻️ Proposed fix
   "devDependencies": {
-    "`@ariada-org/admin-surface`": "file:../admin-surface",
+    "`@ariada-org/admin-surface`": "workspace:*",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-svelte/package.json` at line 58, Update the
`@ariada-org/admin-surface` devDependency in packages/admin-svelte/package.json
from the file:../admin-surface protocol to workspace:*, matching the monorepo’s
established workspace dependency specifier and README example.
packages/admin-svelte/src/format.ts (1)

101-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Leftover domain-specific vocabulary in a supposedly generic render layer.

TAG_COLOR bakes in ad-tech/traffic-specific values (feeder, barter, ftp, block, widget, in-article, header, footer) right next to a SEVERITY map explicitly commented as "Generic operational vocabulary only; no product-specific statuses" — and DEFAULT_WIKI hardcodes wiki.klarads.com, an unrelated product's domain. This package is now vendored for Ariada/accessibility-audit use, and the README emphasizes "no product name appears in the render layer." Unused, unrelated fallback vocabulary and a foreign default domain undercut that guarantee and can mislead the next integrator about what's actually generic.

Also applies to: 169-177

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-svelte/src/format.ts` around lines 101 - 131, Remove the
ad-tech/traffic-specific entries from TAG_COLOR, retaining only vocabulary that
is generic to the render layer and preserving the existing fallback behavior in
tagColor. Also update DEFAULT_WIKI to eliminate the hardcoded Klarads domain,
using the package’s generic/configurable default instead. Ensure no
product-specific names or fallback vocabulary remain in the render-layer
formatting definitions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/ariada-console/package.json`:
- Around line 14-16: Regenerate the workspace lockfile after adding the linked
dependencies in the apps/ariada-console manifest, ensuring the lockfile reflects
the packages/admin-surface specifiers and allows pnpm install --frozen-lockfile
to succeed. Commit the updated pnpm-lock.yaml without changing unrelated
dependency definitions.

In `@apps/ariada-console/src/lib/projects/taler.ts`:
- Line 7: Remove the internal “grants/” repository-path reference from the
comment text in taler.ts, replacing it with public-facing wording or removing
that reference while preserving the surrounding documentation meaning.
- Around line 48-49: Update the project resource definitions for taler-wallet
and taler-popup so scans target distinct reachable surfaces instead of sharing
the same URL and results. Prefer adding an explicit scope or selector supported
by AuditRow; otherwise use distinct valid targets or consolidate them into one
resource, ensuring findings are not duplicated or misattributed.

In `@apps/ariada-console/src/routes/`+page.svelte:
- Around line 11-23: Wire apps/ariada-console/src/routes/+page.svelte lines
11-23 through the action service so scan/remediation execute, report actions use
the returned report token, and unavailable actions are disabled or omitted
instead of only logged. In apps/ariada-console/src/routes/r/[token]/+page.svelte
lines 20-27, resolve the token via the report service and enforce TTL/expiry and
revocation checks before rendering the report.

In `@apps/ariada-console/src/routes/subject/`+page.svelte:
- Around line 6-7: Validate the query-derived url before it reaches the DOM:
update the url derivation in the subject page to parse the value and retain it
only when its protocol is http: or https:, otherwise use an empty value. Ensure
both the subject link href and iframe src consume this validated value,
preserving the existing heal behavior.

In `@packages/admin-surface/package.json`:
- Around line 26-30: Update the root pnpm-lock.yaml by running pnpm install from
the repository root so the importers for packages/admin-surface,
packages/admin-svelte, and apps/ariada-console include their declared
devDependencies, including `@types/node`, typescript, and vitest. Commit the
regenerated lockfile without changing the package manifests.

In `@packages/admin-surface/README.md`:
- Around line 3-6: Retarget the README’s product and package references from
KlarAds, Audiofirst, Agonist, and non-vendored consumers to the Ariada console
and the vendored `@ariada-org/admin-surface` and `@ariada-org/admin-svelte`
packages. Update the renderer table accordingly, removing the
`@ariada-org/admin-ui/React` row or clearly marking it as not yet vendored.

In `@packages/admin-surface/src/index.ts`:
- Around line 372-373: Validate the profile `accent` property with the existing
literal `CSS_HEX` rule before it reaches styling consumers, rejecting arbitrary
CSS values such as URLs or declarations. Update the related profile validation
around `accent` and ensure `CSS_HEX` is declared before that validator, or
otherwise arrange the check without relying on a later declaration; preserve
valid hex accent values.
- Around line 366-368: Require actions in validateOperatorDashboardProfile so
every accepted OperatorDashboardProfile includes the non-optional readonly
actions array. Update the validator’s optional-field handling to reject profiles
that omit actions while preserving its existing validation for provided action
keys.

In `@packages/admin-svelte/package.json`:
- Around line 1-67: Regenerate the workspace pnpm-lock.yaml so its specifiers
match the current packages/admin-surface/package.json dependency declarations,
then include the updated lockfile with the change. Use pnpm install
--no-frozen-lockfile before committing so frozen-lockfile CI can install and run
the admin-svelte build, test, and typecheck scripts.

In `@packages/admin-svelte/src/renderers.ts`:
- Around line 243-253: Update the column-help affordance in the renderer around
info and its mouse listeners so it is keyboard- and touch-operable: make the
element focusable, open the popover on focus and an appropriate click/activation
event, and schedule closing on blur while preserving the existing mouse behavior
and propagation handling.

In `@packages/admin-svelte/src/RowDetailDrawer.svelte`:
- Around line 53-63: Update setField and save in RowDetailDrawer so numeric-kind
inputs are validated before persistence: track whether any numeric field
contains a non-finite or otherwise invalid value, show the existing validation
feedback for that field, and prevent save() from invoking onSave while invalid
numeric input remains. Preserve current coercion for valid numeric values and
normal string-field saving.

In `@packages/admin-svelte/src/tokens.css`:
- Around line 79-83: Insert a blank line before the color-scheme declaration in
the token block containing --adm-shadow-sm, --adm-shadow-md, and
--adm-shadow-lg, satisfying the declaration-empty-line-before Stylelint rule
without changing the declaration values.
- Around line 58-59: Update the font-family declarations `--adm-font` and
`--adm-font-mono` in `tokens.css` with a narrowly scoped Stylelint disable for
`value-keyword-case`, preserving the canonical casing of all font names and
keeping the suppression limited to these declarations.

---

Nitpick comments:
In `@packages/admin-surface/.gitignore`:
- Line 1: Update the ignore rules in the project’s .gitignore to include the
dist/ directory emitted by tsconfig.build.json, while preserving the existing
node_modules/ rule.

In `@packages/admin-svelte/package.json`:
- Line 58: Update the `@ariada-org/admin-surface` devDependency in
packages/admin-svelte/package.json from the file:../admin-surface protocol to
workspace:*, matching the monorepo’s established workspace dependency specifier
and README example.

In `@packages/admin-svelte/src/format.ts`:
- Around line 101-131: Remove the ad-tech/traffic-specific entries from
TAG_COLOR, retaining only vocabulary that is generic to the render layer and
preserving the existing fallback behavior in tagColor. Also update DEFAULT_WIKI
to eliminate the hardcoded Klarads domain, using the package’s
generic/configurable default instead. Ensure no product-specific names or
fallback vocabulary remain in the render-layer formatting definitions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0595799b-60ae-4fc8-a99b-0c555f68eb3f

📥 Commits

Reviewing files that changed from the base of the PR and between 1516524 and 6d88694.

⛔ Files ignored due to path filters (1)
  • apps/ariada-console/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • apps/ariada-console/.gitignore
  • apps/ariada-console/package.json
  • apps/ariada-console/src/app.d.ts
  • apps/ariada-console/src/app.html
  • apps/ariada-console/src/lib/boards/audits.ts
  • apps/ariada-console/src/lib/projects/taler.ts
  • apps/ariada-console/src/routes/+layout.svelte
  • apps/ariada-console/src/routes/+layout.ts
  • apps/ariada-console/src/routes/+page.svelte
  • apps/ariada-console/src/routes/app.css
  • apps/ariada-console/src/routes/r/[token]/+page.svelte
  • apps/ariada-console/src/routes/subject/+page.svelte
  • apps/ariada-console/svelte.config.js
  • apps/ariada-console/tsconfig.json
  • apps/ariada-console/vite.config.ts
  • packages/admin-surface/.gitignore
  • packages/admin-surface/README.md
  • packages/admin-surface/package.json
  • packages/admin-surface/src/chart.test.ts
  • packages/admin-surface/src/grid.test.ts
  • packages/admin-surface/src/index.test.ts
  • packages/admin-surface/src/index.ts
  • packages/admin-surface/templates/admin-surface.ts.template
  • packages/admin-surface/tsconfig.build.json
  • packages/admin-surface/tsconfig.json
  • packages/admin-svelte/.gitignore
  • packages/admin-svelte/README.md
  • packages/admin-svelte/package.json
  • packages/admin-svelte/src/AdminGrid.svelte
  • packages/admin-svelte/src/MetricChart.svelte
  • packages/admin-svelte/src/RowDetailDrawer.svelte
  • packages/admin-svelte/src/chart.test.ts
  • packages/admin-svelte/src/chart.ts
  • packages/admin-svelte/src/components.test.ts
  • packages/admin-svelte/src/format.test.ts
  • packages/admin-svelte/src/format.ts
  • packages/admin-svelte/src/i18n.ts
  • packages/admin-svelte/src/icons.ts
  • packages/admin-svelte/src/index.ts
  • packages/admin-svelte/src/renderers.test.ts
  • packages/admin-svelte/src/renderers.ts
  • packages/admin-svelte/src/ssr.test.ts
  • packages/admin-svelte/src/theme.ts
  • packages/admin-svelte/src/tokens.css
  • packages/admin-svelte/tsconfig.build.json
  • packages/admin-svelte/tsconfig.json
  • packages/admin-svelte/vitest.config.ts

Comment on lines +14 to +16
"@ariada-org/admin-surface": "file:../../packages/admin-surface",
"@ariada-org/admin-svelte": "file:../../packages/admin-svelte",
"ag-grid-community": "^36.0.2"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Regenerate and commit the workspace lockfile.

pnpm install --frozen-lockfile currently fails because pnpm-lock.yaml does not match the linked package manifests, so CI cannot install or build this app. Run the workspace install and commit the updated lockfile, including the packages/admin-surface specifiers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ariada-console/package.json` around lines 14 - 16, Regenerate the
workspace lockfile after adding the linked dependencies in the
apps/ariada-console manifest, ensuring the lockfile reflects the
packages/admin-surface specifiers and allows pnpm install --frozen-lockfile to
succeed. Commit the updated pnpm-lock.yaml without changing unrelated dependency
definitions.

Source: Pipeline failures

// The first dogfood PROJECT: the GNU Taler / GNUnet ("никс") ecosystem. One
// selectable entity grouping every resource — core surfaces, the two pilot
// banks, partners, and the grant-funded module landings (see
// grants/TALER_ECOSYSTEM_CATALOG_2026-07-30.md). Rows feed AUDITS_BOARD.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔴 Critical | ⚡ Quick win

Remove the internal repository-path reference.

The content gate fails on grants/, blocking this PR. Replace it with public-facing text or remove the reference.

🧰 Tools
🪛 GitHub Actions: content-gate / 0_content-gate.txt

[error] 7-7: Content policy scan failed in node packages/ariada-content-policy/dist/cli.js $FILES. Reported internal-path violation: "grants/"

🪛 GitHub Actions: content-gate / content-gate

[error] 7-7: Content policy scan failed: internal path violation flagged for "grants/".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ariada-console/src/lib/projects/taler.ts` at line 7, Remove the internal
“grants/” repository-path reference from the comment text in taler.ts, replacing
it with public-facing wording or removing that reference while preserving the
surrounding documentation meaning.

Source: Pipeline failures

Comment on lines +48 to +49
{ id: 'taler-wallet', label: 'Taler wallet — full UI', url: 'https://addons.mozilla.org/firefox/addon/taler-wallet/', group: 'core', owner: 'GNU Taler', scanStatus: 'scanned', score: 9, critical: 0, serious: 4, findings: 8, blastRadius: null, gate: 'blocked', reverter: 'recommended' },
{ id: 'taler-popup', label: 'Taler wallet — confirm popup', url: 'https://addons.mozilla.org/firefox/addon/taler-wallet/', group: 'core', owner: 'GNU Taler', scanStatus: 'scanned', score: 9, critical: 0, serious: 4, findings: 8, blastRadius: null, gate: 'blocked', reverter: 'recommended' },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Give these distinct surfaces distinct scan targets.

Both rows use the same Mozilla Add-ons URL and identical results, while AuditRow has no scope/selector field. Scans cannot distinguish the wallet UI from its confirmation popup, so counts can be duplicated or attributed to the wrong surface. Use distinct reachable targets, add an explicit scan scope, or represent this as one resource.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ariada-console/src/lib/projects/taler.ts` around lines 48 - 49, Update
the project resource definitions for taler-wallet and taler-popup so scans
target distinct reachable surfaces instead of sharing the same URL and results.
Prefer adding an explicit scope or selector supported by AuditRow; otherwise use
distinct valid targets or consolidate them into one resource, ensuring findings
are not duplicated or misattributed.

Comment on lines +11 to +23
function onAction(row: any, action: { key: string; endpoint: string }, reason?: string) {
if (action.key === 'plugin') {
// Live-plugin loop: open the console's subject-viewer with the URL so the
// Chrome extension can show the healed before/after. Placeholder route.
window.open(`/subject?url=${encodeURIComponent(row.url)}&heal=1`, '_blank');
return;
}
if (action.key === 'report' && row.reportToken) {
window.open(`/r/${row.reportToken}`, '_blank');
return;
}
// scan / remediate → guarded API (not wired in the scaffold)
console.info('action', action.key, row.id, reason);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Wire the advertised action and report flows before exposing them.

AuditRow has no reportToken, so “Open report” always falls through to logging; scan and remediation also only log. The public token route then renders no report and performs no TTL/revocation validation.

  • apps/ariada-console/src/routes/+page.svelte#L11-L23: invoke the action service, use its returned report token, and disable or omit unavailable actions.
  • apps/ariada-console/src/routes/r/[token]/+page.svelte#L20-L27: resolve the token through the report service and enforce its expiry/revocation policy before rendering.
📍 Affects 2 files
  • apps/ariada-console/src/routes/+page.svelte#L11-L23 (this comment)
  • apps/ariada-console/src/routes/r/[token]/+page.svelte#L20-L27
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ariada-console/src/routes/`+page.svelte around lines 11 - 23, Wire
apps/ariada-console/src/routes/+page.svelte lines 11-23 through the action
service so scan/remediation execute, report actions use the returned report
token, and unavailable actions are disabled or omitted instead of only logged.
In apps/ariada-console/src/routes/r/[token]/+page.svelte lines 20-27, resolve
the token via the report service and enforce TTL/expiry and revocation checks
before rendering the report.

Comment on lines +6 to +7
const url = $derived(page.url.searchParams.get('url') ?? '');
const heal = $derived(page.url.searchParams.get('heal') === '1');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Validate the subject URL before binding it to the DOM.

url is attacker-controlled query-string data and is used directly in href and iframe src. Svelte escapes the attribute value but does not reject executable schemes such as javascript:. Parse the value and allow only the intended http:/https: protocols before rendering either element.

Proposed fix
-  const url = $derived(page.url.searchParams.get('url') ?? '');
+  const url = $derived.by(() => {
+    const raw = page.url.searchParams.get('url') ?? '';
+    if (!raw) return '';
+    try {
+      const parsed = new URL(raw, page.url.origin);
+      return parsed.protocol === 'http:' || parsed.protocol === 'https:'
+        ? parsed.href
+        : '';
+    } catch {
+      return '';
+    }
+  });

Also applies to: 16-16, 23-24

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ariada-console/src/routes/subject/`+page.svelte around lines 6 - 7,
Validate the query-derived url before it reaches the DOM: update the url
derivation in the subject page to parse the value and retain it only when its
protocol is http: or https:, otherwise use an empty value. Ensure both the
subject link href and iframe src consume this validated value, preserving the
existing heal behavior.

Comment on lines +1 to +67
{
"name": "@ariada-org/admin-svelte",
"version": "0.1.0",
"description": "Shared Svelte 5 render layer for Agonist admin surfaces: contract-driven AG Grid, declarative charts, design tokens. The Svelte twin of @ariada-org/admin-ui.",
"license": "EUPL-1.2",
"author": "Agonist Development AB",
"keywords": [
"design-system",
"svelte",
"ag-grid",
"admin-ui",
"agonist"
],
"type": "module",
"sideEffects": [
"*.css"
],
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"svelte": "./src/index.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
},
"./AdminGrid.svelte": {
"svelte": "./src/AdminGrid.svelte",
"default": "./src/AdminGrid.svelte"
},
"./MetricChart.svelte": {
"svelte": "./src/MetricChart.svelte",
"default": "./src/MetricChart.svelte"
},
"./RowDetailDrawer.svelte": {
"svelte": "./src/RowDetailDrawer.svelte",
"default": "./src/RowDetailDrawer.svelte"
},
"./tokens.css": "./src/tokens.css",
"./package.json": "./package.json"
},
"files": [
"dist",
"src",
"README.md"
],
"peerDependencies": {
"@ariada-org/admin-surface": ">=0.1.0",
"ag-grid-community": ">=36",
"svelte": ">=5"
},
"scripts": {
"build": "tsc -p tsconfig.build.json",
"test": "vitest run src",
"typecheck": "tsc --noEmit -p tsconfig.json && svelte-check --tsconfig ./tsconfig.json --threshold error"
},
"devDependencies": {
"@ariada-org/admin-surface": "file:../admin-surface",
"@sveltejs/vite-plugin-svelte": "^4.0.4",
"ag-grid-community": "^36.0.2",
"svelte": "^5.1.0",
"svelte-check": "^4.4.6",
"typescript": "^5.8.3",
"vite": "^5.4.21",
"vitest": "^2.1.9"
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

CI is failing on frozen-lockfile install; this package's install/build/test will not run.

Pipeline logs show pnpm install --frozen-lockfile fails because pnpm-lock.yaml doesn't match packages/admin-surface/package.json specifiers. Since this package depends on admin-surface in the same workspace, the whole install (and therefore this package's build/test/typecheck scripts) is blocked until the lockfile is regenerated.

# regenerate the lockfile after any package.json dependency change
pnpm install --no-frozen-lockfile
git add pnpm-lock.yaml
🧰 Tools
🪛 GitHub Actions: Snyk / Snyk SCA + Code

[error] 1-1: pnpm install --frozen-lockfile failed (ERR_PNPM_OUTDATED_LOCKFILE). pnpm-lock.yaml is not up to date with packages/admin-surface/package.json. specifiers in the lockfile ({}) don't match specs in package.json ({"@types/node":"^22.0.0","typescript":"^5.8.3","vitest":"^2.1.9"}).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-svelte/package.json` around lines 1 - 67, Regenerate the
workspace pnpm-lock.yaml so its specifiers match the current
packages/admin-surface/package.json dependency declarations, then include the
updated lockfile with the change. Use pnpm install --no-frozen-lockfile before
committing so frozen-lockfile CI can install and run the admin-svelte build,
test, and typecheck scripts.

Source: Pipeline failures

Comment on lines +243 to +253
const help = params.help;
if (!help) return;
const info = document.createElement('span');
info.className = 'adm-header-info';
info.textContent = 'ⓘ';
info.setAttribute('role', 'note');
info.setAttribute('aria-label', help.description);
info.addEventListener('click', (event) => event.stopPropagation());
info.addEventListener('mouseenter', () => this.open(info, params, help));
info.addEventListener('mouseleave', () => this.scheduleClose());
this.gui.append(info);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Column help popover is mouse-only — unreachable by keyboard or touch.

info only listens for mouseenter/mouseleave, has no tabindex, and no focus/blur/click handling. Since role="note" is already set, Svelte's compiler a11y check for click/hover-without-keyboard won't flag this (role suppresses that warning), so components.test.ts's "zero warnings" assertion won't catch it — but keyboard and touch users still have no way to see the column description/formula/wiki link this popover exists to surface.

♿ Suggested fix: make the info affordance focusable and keyboard-operable
     const info = document.createElement('span');
     info.className = 'adm-header-info';
     info.textContent = 'ⓘ';
-    info.setAttribute('role', 'note');
+    info.setAttribute('role', 'button');
+    info.setAttribute('tabindex', '0');
     info.setAttribute('aria-label', help.description);
     info.addEventListener('click', (event) => event.stopPropagation());
     info.addEventListener('mouseenter', () => this.open(info, params, help));
     info.addEventListener('mouseleave', () => this.scheduleClose());
+    info.addEventListener('focus', () => this.open(info, params, help));
+    info.addEventListener('blur', () => this.scheduleClose());
+    info.addEventListener('keydown', (event) => {
+      if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.open(info, params, help); }
+    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-svelte/src/renderers.ts` around lines 243 - 253, Update the
column-help affordance in the renderer around info and its mouse listeners so it
is keyboard- and touch-operable: make the element focusable, open the popover on
focus and an appropriate click/activation event, and schedule closing on blur
while preserving the existing mouse behavior and propagation handling.

Comment on lines +53 to +63
const NUMERIC_KINDS = new Set<string>(['count', 'currency', 'percent', 'score', 'duration', 'ratio']);
const isNumeric = (column: AdminMetricColumn) => NUMERIC_KINDS.has(column.kind);

function setField(column: AdminMetricColumn, raw: string): void {
draft = { ...draft, [column.key]: isNumeric(column) ? toNumber(raw) : raw };
}

function save(): void {
onSave?.({ ...draft });
editing = false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

No validation feedback for numeric-kind edits — invalid input can be saved silently.

setField coerces numeric-kind values via toNumber(raw) into draft, and save() forwards draft to onSave unconditionally. A non-numeric or cleared value in a numeric field produces an invalid (e.g. NaN) value with no visual cue, and nothing blocks save() from persisting it.

🛡️ Proposed guard before allowing save
   function save(): void {
+    for (const column of surface.columns) {
+      if (isNumeric(column) && Number.isNaN(draft[column.key])) return;
+    }
     onSave?.({ ...draft });
     editing = false;
   }

Also applies to: 117-123

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-svelte/src/RowDetailDrawer.svelte` around lines 53 - 63,
Update setField and save in RowDetailDrawer so numeric-kind inputs are validated
before persistence: track whether any numeric field contains a non-finite or
otherwise invalid value, show the existing validation feedback for that field,
and prevent save() from invoking onSave while invalid numeric input remains.
Preserve current coercion for valid numeric values and normal string-field
saving.

Comment on lines +58 to +59
--adm-font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--adm-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stylelint value-keyword-case failure on font names.

Font-family identifiers are conventionally kept in their canonical casing (Inter, BlinkMacSystemFont, SFMono-Regular, Menlo); a scoped disable is safer than lowercasing them.

🔧 Proposed fix
-  --adm-font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
-  --adm-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
+  /* stylelint-disable-next-line value-keyword-case -- font names must keep canonical casing */
+  --adm-font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+  /* stylelint-disable-next-line value-keyword-case -- font names must keep canonical casing */
+  --adm-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--adm-font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--adm-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
/* stylelint-disable-next-line value-keyword-case -- font names must keep canonical casing */
--adm-font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
/* stylelint-disable-next-line value-keyword-case -- font names must keep canonical casing */
--adm-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
🧰 Tools
🪛 Stylelint (17.14.1)

[error] 58-58: Expected "Inter" to be "inter" (value-keyword-case)

(value-keyword-case)


[error] 58-58: Expected "BlinkMacSystemFont" to be "blinkmacsystemfont" (value-keyword-case)

(value-keyword-case)


[error] 59-59: Expected "SFMono-Regular" to be "sfmono-regular" (value-keyword-case)

(value-keyword-case)


[error] 59-59: Expected "Menlo" to be "menlo" (value-keyword-case)

(value-keyword-case)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-svelte/src/tokens.css` around lines 58 - 59, Update the
font-family declarations `--adm-font` and `--adm-font-mono` in `tokens.css` with
a narrowly scoped Stylelint disable for `value-keyword-case`, preserving the
canonical casing of all font names and keeping the suppression limited to these
declarations.

Source: Linters/SAST tools

Comment on lines +79 to +83
--adm-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.3);
--adm-shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.35);
--adm-shadow-lg: 0 28px 60px rgba(0, 0, 0, 0.62), 0 10px 20px rgba(0, 0, 0, 0.4);
color-scheme: dark;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stylelint declaration-empty-line-before failure.

Missing blank line before color-scheme: dark;.

🔧 Proposed fix
   --adm-shadow-lg: 0 28px 60px rgba(0, 0, 0, 0.62), 0 10px 20px rgba(0, 0, 0, 0.4);
+
   color-scheme: dark;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--adm-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.3);
--adm-shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.35);
--adm-shadow-lg: 0 28px 60px rgba(0, 0, 0, 0.62), 0 10px 20px rgba(0, 0, 0, 0.4);
color-scheme: dark;
}
--adm-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.3);
--adm-shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.35);
--adm-shadow-lg: 0 28px 60px rgba(0, 0, 0, 0.62), 0 10px 20px rgba(0, 0, 0, 0.4);
color-scheme: dark;
}
🧰 Tools
🪛 Stylelint (17.14.1)

[error] 82-82: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/admin-svelte/src/tokens.css` around lines 79 - 83, Insert a blank
line before the color-scheme declaration in the token block containing
--adm-shadow-sm, --adm-shadow-md, and --adm-shadow-lg, satisfying the
declaration-empty-line-before Stylelint rule without changing the declaration
values.

Source: Linters/SAST tools

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