Commit Graph

5 Commits

Author SHA1 Message Date
Naiyuan Qing
afc7ac4bd0 feat: space rollout — space-first navigation, per-user membership, move-to-space (MUL-4142)
Squashed history of PR #4892 (pr-4784-fix). Makes spaces the primary
navigation and working surface, on the "associations bind at creation
time only" model.

Model
- Issue <-> space is the only enforced ownership (per-space numbering).
  Parent/child and project<->space associations only seed defaults at
  creation; cross-space/child and project-association validations are
  removed.
- Moving an issue renumbers it and records the old identifier in
  issue_identifier_alias; API/CLI lookups and GitHub branch/PR
  auto-linking fall back to the alias, so old identifiers resolve forever.
- Membership drives only the sidebar and personal defaults — never
  access. Anyone can configure any space's member set wholesale
  (PUT /api/spaces/{id}/members); saving an empty set archives the
  space behind a confirm.
- Per-user space order (workspace_space_member.sort_order, fractional):
  drag-sorted sidebar, "my first space" is the personal issue-creation
  default; the workspace default space backs headless creation
  (agents/CLI/Slack) and system placement.

Surfaces
- Sidebar: joined-spaces section (drag reorder, row -> space page,
  per-group persisted collapse), Workspace group with a More menu,
  Settings demoted to a footer icon.
- /space/:key/{issues,projects,autopilots,settings} — space surfaces
  reuse shared page components; a routed /space/new create page
  (replacing the earlier create-space modal), reserved key "NEW" so it
  can never collide with a real space's /space/:key detail page.
- Issue detail moves to /issue/:id (identifier-first, Linear-style; old
  /issues/:id redirects); create dialogs lead with a required space pill.
- Agent runtime brief now carries Space context (id/key/name) through
  the daemon claim -> TaskContextForEnv -> prompt pipeline, with a
  "## Space Context" section and --space on issue create/update in both
  brief renderers, matching Project's existing treatment.
- zh-Hans: Space translated to 空间 across locales and conventions.zh.mdx.

Fixes along the way
- Cache membership judgment gains the space dimension + space_changed
  WS flag.
- Silent skip on default-space lookup during invite acceptance is now a
  hard failure (no space-less members).
- Backfilled space_id into ~28 raw-SQL Go test fixtures across
  internal/handler and cmd/server that predated migration 132's NOT
  NULL cutover.
- Fixed a resolve-loop bug in the IssueDetail identifier wrapper (mount/
  unmount cycle on resolution failure) and gave it its own loading
  skeleton instead of a blank screen while resolving.
- reserved-slugs generator's stale hardcoded doc-comment example synced
  back to /create-space.

Verification
- go build ./..., go vet ./..., go test ./... all clean.
- pnpm typecheck (core/views/web/desktop) clean.
- packages/views: 162 files / 1656 tests passing.
- pnpm generate:reserved-slugs produces no diff.

Follow-ups tracked in docs/follow-ups/space-rollout.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 20:57:38 +08:00
Tom Qiao
1c91c2a3b2 security(db): scope DELETE/UpdateIssueStatus by workspace_id (defense-in-depth) (#3027)
* fix(security): scope DELETE/UpdateIssueStatus by workspace_id

Add workspace_id to the WHERE clause of DeleteIssue, DeleteComment,
DeleteProject, DeleteSkill, DeleteChatSession, and UpdateIssueStatus
as SQL-layer defense-in-depth.

Handler loaders (loadIssueForUser / loadSkillForUser / etc.) already
enforce workspace membership today, so this is not patching a known
live vuln. But the tenant invariant is currently a handler-layer
guarantee — a future loader bypass or a new caller skipping the
loader would be silently catastrophic. Making workspace_id part of
the SQL identity collapses the trust surface to the schema itself:
forging a sibling-workspace UUID becomes ErrNoRows instead of a
cross-tenant write.

Reference: incident #1661 (util.ParseUUID silent zero UUID returning
204 on a DELETE that matched zero rows) — same class of failure,
prevented at a different layer.

Scope:
- 5 DELETE queries: issue, comment, project, skill, chat_session
- 1 simple UPDATE: UpdateIssueStatus (2 narg, no SET ordering risk)
- All callers updated (handlers, service, runtime sweeper fallback)

Multi-narg UPDATE queries (UpdateIssue, UpdateProject, UpdateSkill,
UpdateComment, UpdateChatSession*) are deferred to a follow-up to
keep this change reviewable: each needs its narg pinning shifted
and per-caller verification.

sqlc was regenerated by hand (no local sqlc toolchain); CI's
backend job is the authoritative compile check.

* test(security): add workspace_scope_guard regression test

Locks in the SQL-layer tenant guard added in this PR. For each of the 6
scoped queries (DeleteIssue, DeleteComment, DeleteProject, DeleteSkill,
DeleteChatSession, UpdateIssueStatus), creates the resource in workspace
A, invokes the query with a foreign workspace UUID, and asserts the row
is untouched (0 rows affected with no error for :exec; pgx.ErrNoRows for
:one). A future refactor that drops the workspace_id arg from any of
these queries will now fail loudly instead of silently regressing.

Includes a sanity sub-test that the in-workspace path still mutates, so
a buggy guard that returns no-op for every call would not pass.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

---------

Co-authored-by: Tom Qiao <tomqiaozc@users.noreply.github.com>
Co-authored-by: Claude Opus 4 <noreply@anthropic.com>
2026-05-22 14:39:47 +08:00
Jiayuan Zhang
356ff002dd feat(projects): show completion progress in project list (#651)
* feat(projects): show completion progress (done/total issues) in project list

Add a progress column to the projects list page that displays a mini progress
bar and done/total issue count for each project. Backend batch-fetches issue
stats per project using a single query for efficiency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(projects): show progress on project overview page

Add a progress bar with done/total (percentage) to the project detail
overview tab, computed from the already-loaded project issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:36:49 +08:00
Bohan Jiang
245beed829 feat(projects): add priority attribute to projects (#565)
Add priority field (urgent/high/medium/low/none) to projects, matching
the existing issue priority system. Includes database migration, API
support for create/update/list filtering, and UI for the create dialog,
project list table, and project detail page.
2026-04-09 16:31:05 +08:00
Bohan Jiang
68e2a14ba2 feat(projects): add Project entity with full-stack CRUD support (#552)
Implements the Project concept as a higher-level grouping for issues.
Hierarchy: workspace → project → issue → sub-issue.

Backend:
- Migration 034: project table + issue.project_id FK
- sqlc queries for project CRUD
- Project handler with list/get/create/update/delete
- Issue handler updated to support project_id in create/update
- Routes at /api/projects, WebSocket event constants

Frontend (new monorepo structure):
- @multica/core: Project types, API client methods, queries/mutations,
  status config, realtime sync
- @multica/views: Projects list page, detail page (overview + issues
  tabs), project picker for issue detail panel
- apps/web: Route pages, sidebar navigation entry

All TypeScript type checks and tests pass.
2026-04-09 14:59:16 +08:00