Files
pad/internal/models/workspace.go
T
xarmian 136fac6609 feat(import): a partly imported workspace is marked until its owner keeps or deletes it (TASK-896) (#1923)
* feat(import): a partly imported workspace is marked until its owner keeps or deletes it (TASK-896)

A bundle import that hits a DATA error after pad-export.json was imported
keeps the partial workspace (the rule is now written in docs/api.md:
transport failure, validation reject and pre-creation refusal roll back;
only a post-mint data error keeps). The 400 that said "kept" was the only
record, and a client may never read it.

- workspace_import_status (side table, SQLite 131 / PG 104): instance-
  local, never exported, purged with the workspace.
- The keep path writes {status: partial, note}. The note is SANITIZED:
  a fixed category and a correlation id (imp-...); the raw error goes to
  the server log under that id.
- GET /workspaces/{slug} carries import_status (omitempty); the note only
  for owners and instance admins. The list carries the status only.
- PATCH /workspaces/{slug} {"clear_import_status": true} clears it
  (owner-only, like every workspace PATCH).
- pad workspace list marks it "partly imported" (text only; the JSON
  mirrors the MCP workspace-list resource).
- Web: a banner on the workspace landing page and settings, with the
  reason for owners, "Keep it" (clear) and "Delete workspace".

Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS

* fix(import): a rollback that cannot remove the workspace marks it too; the docs name what keeps (TASK-896, codex r1)

Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS

* fix(import): only members who are admins read the note; the banner's note fetch is fenced on identity (TASK-896, codex r2)

Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS
2026-10-08 22:22:06 -04:00

67 lines
3.3 KiB
Go

package models
import "time"
type Workspace struct {
ID string `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
OwnerID string `json:"owner_id,omitempty"` // User ID of workspace owner
OwnerUsername string `json:"owner_username,omitempty"` // Populated by JOIN (not stored)
IsGuest bool `json:"is_guest,omitempty"` // True when user has grants but no membership
Description string `json:"description"`
Settings string `json:"settings"` // JSON
// Source records how the workspace was created — "web", "cli", or "mcp"
// (empty on rows predating migration 069). A cli/mcp origin means an
// agent surface created the workspace, so an agent is already wired up
// even before it creates its first item; the dashboard's
// has_agent_activity signal ORs this in (BUG-1557).
Source string `json:"source,omitempty"`
SortOrder int `json:"sort_order"`
Context *WorkspaceContext `json:"context,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
// ImportStatus is set only on a workspace a bundle import created and
// KEPT after a data error past the point of no return (TASK-896).
// Populated by the handlers from workspace_import_status, never stored
// on the workspaces row. Note is visible to owners and admins only.
ImportStatus *WorkspaceImportStatus `json:"import_status,omitempty"`
}
// WorkspaceImportStatus marks a workspace an import left partly built
// (TASK-896). Status is "partial"; Note is a server-composed, sanitized
// reason (a fixed category plus a correlation id the server log carries).
type WorkspaceImportStatus struct {
Status string `json:"status"`
Note string `json:"note,omitempty"`
}
type WorkspaceCreate struct {
Name string `json:"name"`
Slug string `json:"slug,omitempty"` // auto-generated if empty
OwnerID string `json:"owner_id,omitempty"` // set by server from authenticated user
Description string `json:"description,omitempty"`
Settings string `json:"settings,omitempty"`
Context *WorkspaceContext `json:"context,omitempty"`
Template string `json:"template,omitempty"` // workspace template name (e.g. "startup", "scrum", "product")
// Source is the creation surface — "web", "cli", or "mcp". It is set
// SERVER-SIDE ONLY: the HTTP create handler derives it authoritatively
// from the request's auth shape (actorFromRequest), so `json:"-"` keeps
// it out of the request body — a web client must not be able to spoof
// "cli" to self-suppress the connect-agent/onboarding prompts. Direct
// store callers (cloud auto-create, import) leave it "" so they aren't
// treated as agent-created. See BUG-1557.
Source string `json:"-"`
}
type WorkspaceUpdate struct {
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
Settings *string `json:"settings,omitempty"`
Context *WorkspaceContext `json:"context,omitempty"`
// ClearImportStatus removes a partial-import marker (TASK-896): the
// owner has inspected what an interrupted import kept and keeps it.
ClearImportStatus bool `json:"clear_import_status,omitempty"`
}