Skip to content

Release v0.3.1: Web Dashboard & Workspace UX - #114

Merged
espdesign merged 22 commits into
mainfrom
release/v0.3.1
Nov 5, 2026
Merged

espdesign merged 22 commits into
mainfrom
release/v0.3.1

Conversation

@espdesign

Copy link
Copy Markdown
Collaborator

Release v0.3.1

This release follows v0.3.0-beta.1 and focuses on the Web Dashboard portal, workspace visibility and safety, forum lifecycle, and UI consistency. The version is now 0.3.1 (no beta suffix).

Key Highlights

1. Web Dashboard & Portal (#21)

  • Added a read-only dashboard API with session authentication and project/task views.
  • Added a static single-page dashboard with project, squad, and task detail screens.
  • Added Discord deep links from task/project surfaces into the dashboard.

2. Workspace Visibility & Safety

  • Filter active project visibility by authorization and standardize permission toasts.
  • Add safe dismissal controls for ephemeral hub menus with public-message guardrails.
  • Remove the broken and redundant Server Overview button from the /pm menu.
  • Harden ephemeral/public interaction handling to avoid accidental public message deletion.

3. Forum Lifecycle

  • Prune archived project forum tags within Discord's 20-tag limit.
  • Preserve standard PM tags while reconciling project-specific tags.

4. UI/UX Consistency

  • Unify ephemeral messages and layout across slash commands and views (unify ephemeral messages. #107).
  • Convert ephemeral toasts to color-coded embed cards without emoji clutter.
  • Streamline the calendar feed to a universal .ics link and enforce the button emoji policy.

5. Tooling & Documentation

  • Add the OpenSpec workflow, configuration, and specs.
  • Update release version metadata in pyproject.toml, src/__init__.py, and uv.lock.

Verification

  • make check: Ruff lint, formatting check, and 536 tests passed.
  • make test-e2e: 6 E2E user journeys passed.
  • Pre-push E2E hook passed.

Full Changelog: v0.3.0-beta.1...develop

…al-messages

Arch/107 unify ephemeral messages
…-project-tags

feat(adapters): prune forum tags upon project archive within 20-tag limit
…sibility

feat(discord_bot): filter active project visibility and standardize permission toasts
…enus

feat(discord_bot): dismissable hub menus and public message guardrails
…portal

feat(api): add read-only Web Dashboard & Portal Overlay (fixes #21)

@espdesign espdesign left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review: Release v0.3.1: Web Dashboard & Workspace UX

release/v0.3.1 → main · 19 commits · +12,613/−1,292 · all 8 CI checks green. Layering looks clean (adapters → services/ports → domain) and the dashboard tests are thorough. Main concerns are in the new web portal.

Blocking

1. Stored XSS in the dashboard frontend — src/adapters/api/static/app.js
Task titles, bodies, assignee display names, squad names, tag names, and dependency titles are interpolated into innerHTML unescaped:

  • app.js:442 (col.title from assignee/group), :467 (t.title), :469 (t.assignee_name), :499 (t.title), :503-504 (assignee_name, combinedMeta), :572-573 (item.title)

These values originate from Discord users (task title/body, display names), so a member who can create a task can run JS in the session of anyone viewing the dashboard (leads/admins). Cookies are HttpOnly, but same-origin fetch can still exfiltrate all visible project data. Fix by escaping (escapeHtml) or building nodes with textContent / createElement, as openInspector already does.

2. Forgeable sessions when SESSION_SECRET_KEY is unset — src/config.py:71, auth_routes.py:112,186, dependencies.py:67
The secret defaults to a public constant (dev-session-secret-change-in-production) with no production validation. If an operator forgets to set it, anyone can mint a valid session for a known Discord user ID (IDs are public) and get that member's dashboard access. Add a model_validator that refuses to start when ENVIRONMENT != development and the secret is empty/default.

Should fix

3. OAuth state parameter missing — auth_routes.py:139-206
Login/callback have no state cookie, so the flow is open to login-CSRF (attacker forces a victim into the attacker's session). Generate a random state, store it in a short-lived SameSite=Lax cookie, and verify it in /auth/callback.

4. Cookies lack secure — auth_routes.py:130-137, 200-207
Set secure=True outside development (and consider the __Host- prefix). Also note /auth/logout is reachable via GET, enabling forced-logout CSRF.

5. Third-party CDN scripts without SRI or CSP — index.html:9-11
unpkg.com scripts (dagre/cytoscape) run on an authenticated dashboard with no integrity/crossorigin and no Content-Security-Policy anywhere in src/. A CDN compromise yields full XSS. Self-host the bundles or add SRI hashes plus a CSP.

Performance

6. N+1 queries in dashboard endpoints — dashboard_routes.py:146,217,262-263

  • list_visible_projects calls get_project_workspace per project.
  • get_project_workspace_endpoint calls squad_service.list_members per squad, and can_mutate_task/can_delete_task per task; each of those re-fetches the project and re-lists squad roles (auth_service.py:can_mutate_task). For a few hundred tasks this is hundreds of queries per request.
    Resolve project role IDs once per request and pass them down / batch the auth checks.

Minor / nits

  • prune_archived_project_tags can delete standard tags (forum_tags.py:352-375): name-only _matches_project_tag means an archived project named Completed, High Priority, or Overdue prunes the standard PM tag (it gets re-added by setup_forum_tags, but there's a window of missing tags and unnecessary churn). Exclude STANDARD_PM_TAG_DEFINITIONS names from pruning.
  • Dev fallback is powerful and easy to leave on: ENVIRONMENT defaults to development; with DISCORD_CLIENT_SECRET unset, /auth/login?user_id=…&is_guild_admin=true grants admin (auth_routes.py:108-128, dependencies.py:110-118). Consider requiring an explicit opt-in flag rather than inferring from ENVIRONMENT.
  • Duplicated agent assets: the OpenSpec skills/commands are committed twice (~5k lines) under .agents/ and .opencode/. Pick one source of truth to avoid drift.
  • dashboard_routes.py:183/345 look up projects by ID without guild scoping; safe today under a single-guild deployment, but worth asserting project.guild_id == principal.guild_id if multi-guild is ever added.

Positives

  • Public-message deletion guard in MenuSessionManager.register_menu (menu_manager.py:37-60) correctly enforces the AGENTS.md invariant, with tests.
  • Toast unification (ui_constants.py + send_toast/edit_toast) removes emoji clutter and standardizes dismissal timing.
  • Version bump is consistent across pyproject.toml, src/__init__.py, and uv.lock; CI including Docker build and migration drift is green.

@espdesign
espdesign merged commit f354c56 into main Nov 5, 2026
8 checks passed
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.

1 participant