Files
pad/internal/collections/defaults.go
T
xarmian f5a00d5b13 feat: a collection can be reference material, not work (PLAN-3535 PR 1) (#1996)
* feat: a collection can be reference material, not work (PLAN-3535 PR 1)

The switch, with no counting change yet (PR 2 server, PR 3 web).
CollectionSettings.tracks_work: false makes a collection REFERENCE
material; absent means work, today's behaviour, so old rows, old writers
and a client/server skew are unchanged. One Go predicate
(models.CollectionTracksWork) and one TS twin (collectionTracksWork).

- New collections: the creator's value, else system collections are
  reference and others are work iff their done field can finish. New
  template Docs collections are created as reference (Dave).
- Migration 135 / PG 107: existing Conventions and Playbooks become
  reference; everything else, Docs included, stays work.
- Settings are written wholesale, so an update whose settings omit the
  key KEEPS the stored value (CarryTracksWork), for every writer.
- A typed top-level `tracks_work` on POST/PATCH sets just that key
  server-side. CLI: --tracks-work=true|false (a string flag, so false
  survives stdio MCP). MCP: pad_collection tracks_work, v0.81. A
  non-boolean is refused 400; the CLI checks the response, since an
  older server ignores the member.
- Web: the Display tab's "Track as work" toggle; the editor now starts
  its settings write from the STORED settings (buildCollectionSettings),
  so keys it does not render (content_template, tracks_work) survive.

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

* fix(store): carry tracks_work from the locked row; order the SQLite migration's JSON checks (PLAN-3535, codex)

The update now computes the carried settings from the row it re-read under
the write lock, so a concurrent settings write cannot be reverted by a carry
read before the lock. The SQLite migration evaluates json_type/json_extract
only inside a CASE guarded by json_valid, so malformed settings on a system
row cannot abort it.

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

* fix(models): a collection whose options the default terminals close is work (PLAN-3535)

DefaultTracksWork required declared terminal_options, but every lifecycle
reader falls back to DefaultTerminalStatuses when a done field declares
none, so a `status: open, done` collection finishes items and was being
created as reference. Work now means one of the done field's options is
terminal by the same resolver the readers use. Found by PR 2's store tests,
whose fixture collection is exactly that shape. Also corrects api.md's
MCP version (0.81).

Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS
2026-10-09 21:47:50 -04:00

126 lines
4.5 KiB
Go

package collections
import "github.com/PerpetualSoftware/pad/internal/models"
// DefaultCollection holds the definition for a default collection that gets
// created when a workspace is initialized.
type DefaultCollection struct {
Name string
Slug string
Prefix string // Optional explicit issue-ID prefix; empty means derive from Name
Icon string
Description string
Schema models.CollectionSchema
Settings models.CollectionSettings
// Traits declares the collection's kernel traits (SPEC-5). Templates
// declare them so a NEW workspace gets them at seed time; existing
// workspaces are backfilled by migration 080 / pg 058. Between the two,
// no kernel behavior is inferred from a collection's slug any more.
// TASK-2657.
Traits models.CollectionTraits
SortOrder int
IsSystem bool // System collections (conventions, playbooks) are always visible to members
}
// Defaults returns the six default collections for a new workspace.
//
// Tasks and Ideas are extracted into tasksCollection/ideasCollection
// (templates.go, alongside docsCollection) so templates that want them
// without a Plans collection — e.g. `spec` (IDEA-2527), where
// implementation-plan material lives in the spec body instead — can compose
// the same seeded schema directly. Seeded output here is unchanged.
func Defaults() []DefaultCollection {
return []DefaultCollection{
tasksCollection(0),
ideasCollection(1),
{
Name: "Plans",
Slug: "plans",
Icon: "🗺️",
Description: "Plan and track project plans and milestones",
SortOrder: 2,
Schema: models.CollectionSchema{
Fields: []models.FieldDef{
{
Key: "status",
Label: "Status",
Type: "select",
Options: []string{"planned", "active", "completed", "paused"},
TerminalOptions: []string{"completed"},
Default: "planned",
Required: true,
},
{
Key: "start_date",
Label: "Start Date",
Type: "date",
},
{
Key: "end_date",
Label: "End Date",
Type: "date",
},
{
Key: "progress",
Label: "Progress",
Type: "number",
Suffix: "%",
Computed: true,
},
},
},
Settings: models.CollectionSettings{
Layout: "content-primary",
DefaultView: "board",
ListSortBy: "sort_order",
QuickActions: []models.QuickAction{
{Label: "Plan this", Prompt: "/pad plan {ref} \"{title}\" — outline goals, deliverables, and timeline", Scope: "item", Icon: "📐"},
{Label: "Break into tasks", Prompt: "/pad break {ref} \"{title}\" into PR-sized tasks", Scope: "item", Icon: "📝"},
{Label: "Run a retro", Prompt: "/pad run a retrospective on {ref} \"{title}\"", Scope: "item", Icon: "🔄"},
{Label: "Compare progress", Prompt: "/pad compare progress across all plans", Scope: "collection", Icon: "📊"},
},
},
},
{
Name: "Docs",
Slug: "docs",
Icon: "📄",
Description: "Documentation, notes, and reference material",
SortOrder: 3,
Schema: models.CollectionSchema{
Fields: []models.FieldDef{
{
Key: "status",
Label: "Status",
Type: "select",
Options: []string{"draft", "published", "archived"},
TerminalOptions: []string{"archived"},
Default: "draft",
Required: true,
},
{
Key: "category",
Label: "Category",
Type: "text",
},
},
},
Settings: models.CollectionSettings{
Layout: "content-primary",
DefaultView: "board",
ListSortBy: "updated_at",
ListGroupBy: "category",
TracksWork: reference(), // reference material (PLAN-3535)
QuickActions: []models.QuickAction{
{Label: "Review this doc", Prompt: "/pad review {ref} \"{title}\" for accuracy and completeness", Scope: "item", Icon: "👀"},
{Label: "Update this doc", Prompt: "/pad update {ref} \"{title}\" to reflect the current state of the codebase", Scope: "item", Icon: "✏️"},
{Label: "Summarize this", Prompt: "/pad summarize {ref} \"{title}\"", Scope: "item", Icon: "📋"},
{Label: "Find outdated docs", Prompt: "/pad review all docs and identify which are outdated", Scope: "collection", Icon: "🔍"},
},
},
},
conventionsCollection(4, SoftwareConventionTriggers, SoftwareConventionScopes),
playbooksCollection(5, SoftwarePlaybookTriggers, SoftwarePlaybookScopes),
}
}