Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,10 @@ logs/
Thumbs.db
.DS_Store

# Web dashboard build artifacts
web/node_modules/
web/dist/
web/.env

# uv
.python-version
50 changes: 47 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,50 @@ jobs:
name: test-results-${{ matrix.python-version }}
path: junit.xml

dashboard-lint:
name: Dashboard Lint & Type Check
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: npm
cache-dependency-path: web/package-lock.json
- run: npm ci
working-directory: web
- run: npm run lint
working-directory: web
- run: npm run type-check
working-directory: web

dashboard-test:
name: Dashboard Test
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: npm
cache-dependency-path: web/package-lock.json
- run: npm ci
working-directory: web
- run: npm run test -- --coverage
working-directory: web
Comment on lines +139 to +140
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking dashboard coverage threshold configuration..."
fd -HI "vitest.config.*" web -t f
fd -HI "package.json" web -t f --exec cat {}
rg -n --glob 'web/**' 'coverage|threshold|lines|statements|functions|branches|test -- --coverage|vitest'

Repository: Aureliolo/synthorg

Length of output: 8996


🏁 Script executed:

cat -n web/vitest.config.ts

Repository: Aureliolo/synthorg

Length of output: 663


🏁 Script executed:

sed -n '130,150p' .github/workflows/ci.yml

Repository: Aureliolo/synthorg

Length of output: 644


🏁 Script executed:

cat -n .github/workflows/ci.yml | tail -50

Repository: Aureliolo/synthorg

Length of output: 2190


Add coverage threshold enforcement to block builds below 80%.

The test command runs with --coverage but vitest.config.ts lacks threshold configuration to enforce the 80% minimum. Configure coverage thresholds in the vitest config or add a validation step to fail the build when coverage falls below 80% for lines, statements, functions, and branches.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 139 - 140, The workflow runs tests
with --coverage but no thresholds are enforced; update your Vitest configuration
(vitest.config.ts) to add coverage threshold settings so the build fails under
80%. In vitest.config.ts, inside the test configuration's coverage block (the
test.coverage or coverage object used by defineConfig), set statements,
branches, functions and lines to 80 and include a CI-friendly reporter (e.g.,
"text" or "lcov") so Vitest will enforce the 80% minimum and cause the job to
fail when coverage falls below the threshold.


Comment on lines +123 to +141
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding coverage artifact upload for dashboard tests.

The backend test job uploads coverage to Codecov, but dashboard tests run coverage without uploading. For consistency and visibility, consider adding coverage artifact upload:

💡 Optional: Add coverage upload
      - name: Upload coverage to Codecov
        if: ${{ !cancelled() }}
        uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          flags: dashboard
          fail_ci_if_error: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 123 - 141, Add a Codecov upload step
to the existing dashboard-test job so coverage produced by the "npm run test --
--coverage" step is uploaded; inside the dashboard-test job (after the run step
that executes tests) add a step using the codecov/codecov-action (same major
version used in backend), conditioned with if: ${{ !cancelled() }}, pass token:
${{ secrets.CODECOV_TOKEN }}, set flags: dashboard and fail_ci_if_error: false
to mirror the backend upload behavior and ensure dashboard coverage is visible.

ci-pass:
name: CI Pass
if: always()
needs: [lint, type-check, test]
needs: [lint, type-check, test, dashboard-lint, dashboard-test]
runs-on: ubuntu-latest
permissions: {}
steps:
Expand All @@ -111,11 +151,15 @@ jobs:
LINT_RESULT: ${{ needs.lint.result }}
TYPE_CHECK_RESULT: ${{ needs.type-check.result }}
TEST_RESULT: ${{ needs.test.result }}
DASHBOARD_LINT_RESULT: ${{ needs.dashboard-lint.result }}
DASHBOARD_TEST_RESULT: ${{ needs.dashboard-test.result }}
run: |
if [[ "$LINT_RESULT" != "success" || \
"$TYPE_CHECK_RESULT" != "success" || \
"$TEST_RESULT" != "success" ]]; then
echo "CI failed: lint=$LINT_RESULT, type-check=$TYPE_CHECK_RESULT, test=$TEST_RESULT"
"$TEST_RESULT" != "success" || \
"$DASHBOARD_LINT_RESULT" != "success" || \
"$DASHBOARD_TEST_RESULT" != "success" ]]; then
echo "CI failed: lint=$LINT_RESULT, type-check=$TYPE_CHECK_RESULT, test=$TEST_RESULT, dashboard-lint=$DASHBOARD_LINT_RESULT, dashboard-test=$DASHBOARD_TEST_RESULT"
exit 1
fi
echo "All CI checks passed"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ htmlcov/
coverage.xml
.coverage
.coverage.*
web/coverage/

# Environment variables
.env
Expand Down
28 changes: 26 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- **What**: Framework for building synthetic organizations — autonomous AI agents orchestrated as a virtual company
- **Python**: 3.14+ (PEP 649 native lazy annotations)
- **License**: BUSL-1.1 (converts to Apache 2.0 on 2030-02-27)
- **Layout**: `src/ai_company/` (src layout), `tests/` (unit/integration/e2e)
- **Layout**: `src/ai_company/` (src layout), `tests/` (unit/integration/e2e), `web/` (Vue 3 dashboard)
- **Design**: [DESIGN_SPEC.md](DESIGN_SPEC.md) (pointer to `docs/design/` pages)

## Design Spec (MANDATORY)
Expand Down Expand Up @@ -43,6 +43,17 @@ uv run zensical build # build docs (output: _site/docs/)
uv run zensical serve # local docs preview (http://127.0.0.1:8000)
```

### Web Dashboard

```bash
npm --prefix web install # install frontend deps
npm --prefix web run dev # dev server (http://localhost:5173)
npm --prefix web run build # production build
npm --prefix web run lint # ESLint
npm --prefix web run type-check # vue-tsc type checking
npm --prefix web run test # Vitest unit tests
```
Comment on lines +46 to +55
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix command-style contradiction in the Web Dashboard quick commands.

Lines 49-54 use cd web && ..., which conflicts with the rule at Line 130 (“NEVER use cd in Bash commands”). Keep one style to avoid operator/automation mistakes.

Suggested doc fix
-cd web && npm install                      # install frontend deps
-cd web && npm run dev                      # dev server (http://localhost:5173)
-cd web && npm run build                    # production build
-cd web && npm run lint                     # ESLint
-cd web && npm run type-check               # vue-tsc type checking
-cd web && npm run test                     # Vitest unit tests
+npm --prefix web install                   # install frontend deps
+npm --prefix web run dev                   # dev server (http://localhost:5173)
+npm --prefix web run build                 # production build
+npm --prefix web run lint                  # ESLint
+npm --prefix web run type-check            # vue-tsc type checking
+npm --prefix web run test                  # Vitest unit tests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 46 - 55, Replace the forbidden "cd web && ..."
command style in the Web Dashboard section with the non-cd form; specifically
change lines containing "cd web && npm install", "cd web && npm run dev", "cd
web && npm run build", "cd web && npm run lint", "cd web && npm run type-check",
and "cd web && npm run test" to use npm's --prefix pattern (e.g., "npm --prefix
web install" / "npm --prefix web run dev" etc.) so the commands comply with the
“NEVER use `cd` in Bash commands” rule and will work safely in automation.


## Documentation

- **Docs source**: `docs/` (Markdown, built with Zensical)
Expand Down Expand Up @@ -75,7 +86,7 @@ curl http://localhost:3000/api/v1/health # backend (via web proxy)
```

- **Backend**: 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened
- **Web**: `nginxinc/nginx-unprivileged`, SPA routing, API/WebSocket proxy to backend
- **Web**: `nginxinc/nginx-unprivileged`, Vue 3 SPA (PrimeVue + Tailwind CSS), SPA routing, API/WebSocket proxy to backend
- **Config**: all Docker files in `docker/` — Dockerfiles, compose, `.env.example`
- **CI**: `.github/workflows/docker.yml` — build → scan → push to GHCR + cosign sign (images only pushed after Trivy/Grype scans pass)
- **Build context**: single root `.dockerignore` (both images build with `context: .`)
Expand All @@ -101,6 +112,18 @@ src/ai_company/
security/ # SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
templates/ # Pre-built company templates, personality presets, and builder
tools/ # Tool registry, built-in tools (file_system/, git, sandbox/, code_runner), MCP bridge (mcp/), role-based access

web/ # Vue 3 + PrimeVue + Tailwind CSS dashboard
src/
api/ # Axios client, endpoint modules, TypeScript types (mirrors backend Pydantic models)
components/ # Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/)
composables/ # Reusable composition functions (useAuth, usePolling, useOptimisticUpdate)
router/ # Vue Router config with auth guards
stores/ # Pinia stores (auth, agents, tasks, budget, messages, approvals, websocket, analytics, company, providers)
styles/ # Global CSS and PrimeVue theme configuration
utils/ # Constants, formatters, error helpers
views/ # Page-level components (Dashboard, TaskBoard, AgentProfiles, BudgetPanel, etc.)
__tests__/ # Vitest unit tests (organized by feature)
```

## Shell Usage
Expand Down Expand Up @@ -206,3 +229,4 @@ src/ai_company/
- **Pinned**: all versions use `==` in `pyproject.toml`
- **Groups**: `test` (pytest + plugins), `dev` (includes test + ruff, mypy, pre-commit, commitizen)
- **Install**: `uv sync` installs everything (dev group is default)
- **Web dashboard**: Node.js 20+, dependencies in `web/package.json` (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ graph TB

## Status

Core framework complete — agent engine, multi-agent coordination, API, security, HR, memory, and budget systems are implemented. Remaining: Mem0 adapter backend, approval workflow gates, CLI, web dashboard. See the [roadmap](docs/roadmap/index.md) for details.
Core framework complete — agent engine, multi-agent coordination, API, security, HR, memory, and budget systems are implemented. Web dashboard (Vue 3 + PrimeVue + Tailwind CSS) is built. Remaining: Mem0 adapter backend, approval workflow gates, CLI. See the [roadmap](docs/roadmap/index.md) for details.

## License

Expand Down
14 changes: 12 additions & 2 deletions docker/web/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
# syntax=docker/dockerfile:1

# =============================================================================
# SynthOrg Web — Non-root nginx container (CIS hardening applied via compose.yml)
# SynthOrg Web — Multi-stage build: Node builder → nginx runtime
# CIS hardening applied via compose.yml (no-new-privileges, drop caps, etc.)
# =============================================================================

# Stage 1: Build Vue dashboard
FROM node:22-alpine AS builder
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '^\s*FROM\s+(?!\S+@sha256:)\S+' docker --glob '**/Dockerfile'

Repository: Aureliolo/synthorg

Length of output: 225


🏁 Script executed:

cat docker/web/Dockerfile

Repository: Aureliolo/synthorg

Length of output: 1349


Pin the builder base image by digest to match the runtime stage.

Line 9 uses a mutable tag (node:22-alpine) while the runtime stage (line 19) is pinned by digest. This inconsistency weakens reproducibility and supply-chain integrity, since the builder can pull different versions across builds.

Suggested fix
-FROM node:22-alpine AS builder
+FROM node:22-alpine@sha256:<resolved_digest> AS builder
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker/web/Dockerfile` at line 9, Replace the mutable builder image "FROM
node:22-alpine AS builder" with the same pinned digest used by the runtime stage
(i.e. use node@sha256:... instead of node:22-alpine) so both stages reference
the identical immutable image; obtain the exact digest (e.g. via docker pull and
docker inspect --format='{{index .RepoDigests 0}}' node:22-alpine) and update
the FROM line to "FROM node@sha256:<digest> AS builder".

WORKDIR /app
COPY web/package.json web/package-lock.json ./
RUN npm ci
COPY web/ ./
RUN npm run build

# Stage 2: Serve with nginx
FROM nginxinc/nginx-unprivileged:1.29.5-alpine@sha256:aec540f08f99df3c830549d5dd7bfaf63e01cbbb499e37400c5af9f8e8554e9f

LABEL org.opencontainers.image.title="synthorg-web" \
Expand All @@ -14,7 +24,7 @@ LABEL org.opencontainers.image.title="synthorg-web" \
org.opencontainers.image.vendor="Aureliolo"

COPY web/nginx.conf /etc/nginx/conf.d/default.conf
COPY web/index.html web/style.css web/app.js /usr/share/nginx/html/
COPY --from=builder /app/dist/ /usr/share/nginx/html/

EXPOSE 8080

Expand Down
8 changes: 4 additions & 4 deletions docs/design/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ future CLI tool are thin clients that call the API -- they contain no business l
| |
+-------v--+ +---v--------+
| Web UI | | CLI Tool |
| (Future) | | (Future) |
| (Vue 3) | | (Future) |
+----------+ +-----------+
```

Expand Down Expand Up @@ -966,10 +966,10 @@ future CLI tool are thin clients that call the API -- they contain no business l

### Web UI Features

!!! warning "Planned"
!!! info "Implemented"

The Web UI is a planned future component (Vue 3). The API is fully self-sufficient for
all operations.
The Web UI is implemented as a Vue 3 + PrimeVue + Tailwind CSS dashboard. The API
remains fully self-sufficient for all operations — the dashboard is a thin client.

- **Dashboard**: Real-time company overview, active tasks, spending
- **Org Chart**: Visual hierarchy, click to inspect any agent
Expand Down
15 changes: 14 additions & 1 deletion docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,26 @@ synthorg/
e2e/ # Full system tests
docs/ # Developer documentation
docker/ # Dockerfiles, Compose, .env.example
web/ # Web UI scaffold (nginx + placeholder)
web/ # Vue 3 web dashboard (PrimeVue + Tailwind CSS)
.github/ # CI workflows, dependabot, actions
pyproject.toml # Project config (deps, tools, linters)
DESIGN_SPEC.md # Pointer to design specification pages
CLAUDE.md # AI assistant quick reference
```

## Web Dashboard Development

The Vue 3 dashboard lives in `web/`. Prerequisites: **Node.js 20+**.

```bash
npm --prefix web install # install frontend deps
npm --prefix web run dev # dev server at http://localhost:5173
npm --prefix web run lint # ESLint
npm --prefix web run type-check # vue-tsc type checking
npm --prefix web run test # Vitest unit tests
npm --prefix web run build # production build
```

## IDE Setup

### VS Code / Cursor
Expand Down
7 changes: 6 additions & 1 deletion docs/roadmap/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ The SynthOrg core framework is complete. The following subsystems are built and
- Configuration (YAML loading, Pydantic validation, company templates with inheritance)
- Container packaging (Docker, Chainguard distroless, CI/CD pipelines)

## Current

| Area | Description |
|------|-------------|
| **Web dashboard** | Vue 3 + PrimeVue + Tailwind CSS frontend for monitoring and managing the synthetic organization (implemented) |

## Remaining Work

| Area | Description |
|------|-------------|
| **Mem0 adapter** | Concrete `MemoryBackend` implementation using the Mem0 library |
| **Approval workflow gates** | Runtime wiring for human-in-the-loop approval queues |
| **CLI** | Terminal interface wrapping the REST API (may not be needed) |
| **Web dashboard** | Vue 3 frontend for monitoring and managing the synthetic organization |

## Tracking

Expand Down
6 changes: 3 additions & 3 deletions docs/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ docker compose -f docker/compose.yml up -d

The web dashboard is at [http://localhost:3000](http://localhost:3000).

Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup and templates will be configurable through the dashboard once available.
Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup is done via the dashboard. Template configuration through the UI is planned for a future release.

!!! danger "Work in Progress"
SynthOrg is under active development. The web dashboard, templates, and many features described here are **not yet available**. Check the [GitHub repository](https://github.com/Aureliolo/synthorg) for current status.
!!! info "Active Development"
SynthOrg is under active development. The web dashboard is available for monitoring and managing the organization. Templates and some features described here may evolve. Check the [GitHub repository](https://github.com/Aureliolo/synthorg) for current status.
Comment on lines +22 to +23
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Resolve contradictory dashboard availability messaging.

Line 23 says the dashboard is available, but Line 20 still says setup/templates are configurable “once available.” Please align those statements to avoid confusing users.

📝 Suggested doc fix
-Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup and templates will be configurable through the dashboard once available.
+Container configuration (ports, storage paths, log level) is defined in `docker/.env`. Organization setup is available through the dashboard, while template options may continue to evolve.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/user_guide.md` around lines 22 - 23, The "Active Development" info block
contains contradictory messaging about the web dashboard (saying it's both
"available" and "once available"); update the admonition text so it is
consistent—either state the dashboard is currently available and remove "once
available" wording, or state it's not yet available and change the sentence
referencing the dashboard to indicate "coming soon" or "when available"; edit
the "Active Development" admonition (the block starting with !!! info "Active
Development") to use one clear, aligned phrase about dashboard availability and
adjust the adjacent sentence about templates/setup to match that single status.


## Templates

Expand Down
2 changes: 2 additions & 0 deletions web/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# API base URL — empty for production (relative paths), set for dev if not using Vite proxy
VITE_API_BASE_URL=
29 changes: 0 additions & 29 deletions web/app.js

This file was deleted.

15 changes: 15 additions & 0 deletions web/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/// <reference types="vite/client" />

declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<object, object, unknown>
export default component
}

interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string
}

interface ImportMeta {
readonly env: ImportMetaEnv
}
31 changes: 31 additions & 0 deletions web/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pluginVue from 'eslint-plugin-vue'
import pluginSecurity from 'eslint-plugin-security'
import tsParser from '@typescript-eslint/parser'

export default [
...pluginVue.configs['flat/essential'],
pluginSecurity.configs.recommended,
{
files: ['**/*.vue'],
languageOptions: {
parserOptions: {
parser: tsParser,
},
},
},
{
files: ['**/*.ts'],
languageOptions: {
parser: tsParser,
},
},
{
rules: {
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'warn',
},
},
{
ignores: ['dist/'],
},
]
28 changes: 10 additions & 18 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SynthOrg</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="container">
<h1>SynthOrg</h1>
<p class="subtitle">Dashboard &mdash; Coming Soon</p>
<div id="status" class="status status-checking" aria-live="polite">
<span class="status-dot"></span>
<span id="status-text">Checking backend...</span>
</div>
</div>
<script src="/app.js"></script>
</body>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SynthOrg Dashboard</title>
</head>
<body class="bg-slate-950 text-slate-200 antialiased">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
Comment on lines 1 to 12
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding noscript fallback and favicon.

The SPA bootstrap is clean, but consider adding a <noscript> tag for users with JavaScript disabled and a favicon link for better UX.

♻️ Optional enhancements
   <head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
     <title>SynthOrg Dashboard</title>
   </head>
   <body class="bg-slate-950 text-slate-200 antialiased">
+    <noscript>
+      <div style="padding: 2rem; text-align: center;">
+        JavaScript is required to use this dashboard.
+      </div>
+    </noscript>
     <div id="app"></div>
📝 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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SynthOrg</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="container">
<h1>SynthOrg</h1>
<p class="subtitle">Dashboard &mdash; Coming Soon</p>
<div id="status" class="status status-checking" aria-live="polite">
<span class="status-dot"></span>
<span id="status-text">Checking backend...</span>
</div>
</div>
<script src="/app.js"></script>
</body>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SynthOrg Dashboard</title>
</head>
<body class="bg-slate-950 text-slate-200 antialiased">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>SynthOrg Dashboard</title>
</head>
<body class="bg-slate-950 text-slate-200 antialiased">
<noscript>
<div style="padding: 2rem; text-align: center;">
JavaScript is required to use this dashboard.
</div>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/index.html` around lines 1 - 12, Add a non-JS fallback and favicon to the
SPA bootstrap: update the HTML head to include a <link rel="icon" href="...">
pointing to your favicon (e.g., /favicon.ico or an SVG/PNG asset) and add a
<noscript> element inside the body (near the <div id="app">) that displays a
concise message or link for users with JavaScript disabled; modify the existing
index.html head and body (target the <head>, <body>, and the <div id="app">
region) to insert these elements so the app degrades gracefully and shows a
favicon in browser tabs.

Loading
Loading